diff --git a/GlassVPN/PacketTunnelProvider.swift b/GlassVPN/PacketTunnelProvider.swift
index 10699fd..0a19055 100644
--- a/GlassVPN/PacketTunnelProvider.swift
+++ b/GlassVPN/PacketTunnelProvider.swift
@@ -1,5 +1,6 @@
import NetworkExtension
+let swcdUserAgent: Data = "User-Agent: swcd".data(using: .ascii)!
fileprivate var hook : GlassVPNHook!
// MARK: ObserverFactory
@@ -15,8 +16,18 @@ class LDObserverFactory: ObserverFactory {
override func signal(_ event: ProxySocketEvent) {
switch event {
case .receivedRequest(let session, let socket):
+ if socket.isCancelled ||
+ (hook.forceDisconnectUnresolvable && session.ipAddress.isEmpty) {
+ hook.silentlyPrevented(session.host)
+ socket.forceDisconnect()
+ return
+ }
let kill = hook.processDNSRequest(session.host)
if kill { socket.forceDisconnect() }
+ case .readData(let data, on: let socket):
+ if hook.forceDisconnectSWCD, data.range(of: swcdUserAgent) != nil {
+ socket.disconnect()
+ }
default:
break
}
diff --git a/main/Common Classes/PrefsShared.swift b/main/Common Classes/PrefsShared.swift
index a86018c..5350a88 100644
--- a/main/Common Classes/PrefsShared.swift
+++ b/main/Common Classes/PrefsShared.swift
@@ -37,6 +37,14 @@ extension PrefsShared {
get { CurrentRecordingState(rawValue: Int("CurrentlyRecording")) ?? .Off }
set { Int("CurrentlyRecording", newValue.rawValue) }
}
+ static var ForceDisconnectUnresolvableDNS: Bool {
+ get { PrefsShared.Bool("ForceDisconnectUnresolvableDNS") }
+ set { PrefsShared.Bool("ForceDisconnectUnresolvableDNS", newValue) }
+ }
+ static var ForceDisconnectSWCD: Bool {
+ get { PrefsShared.Bool("ForceDisconnectSWCD") }
+ set { PrefsShared.Bool("ForceDisconnectSWCD", newValue) }
+ }
}
diff --git a/main/GUI/Base.lproj/Recordings.storyboard b/main/GUI/Base.lproj/Recordings.storyboard
index a546c42..6d22fe6 100644
--- a/main/GUI/Base.lproj/Recordings.storyboard
+++ b/main/GUI/Base.lproj/Recordings.storyboard
@@ -414,15 +414,15 @@
diff --git a/main/GUI/Base.lproj/Settings.storyboard b/main/GUI/Base.lproj/Settings.storyboard
index b9f6832..49a2dc4 100644
--- a/main/GUI/Base.lproj/Settings.storyboard
+++ b/main/GUI/Base.lproj/Settings.storyboard
@@ -1,5 +1,5 @@
-
+
@@ -243,25 +243,93 @@
-
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
@@ -282,6 +350,8 @@
+
+
@@ -364,7 +434,7 @@ If App Badge is enabled, display the letter "1" on the homescreen app icon, as l
-
+
@@ -391,7 +461,7 @@ If App Badge is enabled, display the letter "1" on the homescreen app icon, as l
-
+
@@ -418,7 +488,7 @@ If App Badge is enabled, display the letter "1" on the homescreen app icon, as l
-
+
@@ -476,7 +546,7 @@ If App Badge is enabled, display the letter "1" on the homescreen app icon, as l
-
+
@@ -602,7 +672,7 @@ If App Badge is enabled, display the letter "1" on the homescreen app icon, as l
-
+
@@ -832,6 +902,6 @@ If App Badge is enabled, display the letter "1" on the homescreen app icon, as l
-
+
diff --git a/main/GlassVPN.swift b/main/GlassVPN.swift
index e1acb49..ea6d504 100644
--- a/main/GlassVPN.swift
+++ b/main/GlassVPN.swift
@@ -161,4 +161,12 @@ struct VPNAppMessage {
static func isRecording(_ state: CurrentRecordingState) -> Self {
.init("recording-now:\(state.rawValue)")
}
+ /// Triggered whenever user taps on the switch in settings
+ static func disconnectUnresolvable(_ state: Bool) -> Self {
+ .init("disconnect-unresolvable:\(state ? 1 : 0)")
+ }
+ /// Triggered whenever user taps on the switch in settings
+ static func disconnectSWCD(_ state: Bool) -> Self {
+ .init("disconnect-swcd:\(state ? 1 : 0)")
+ }
}
diff --git a/main/GlassVPNHook.swift b/main/GlassVPNHook.swift
index 1023411..b692e62 100644
--- a/main/GlassVPNHook.swift
+++ b/main/GlassVPNHook.swift
@@ -10,6 +10,10 @@ class GlassVPNHook {
private var cachedNotify: CachedConnectionAlert!
private var currentlyRecording: Bool = false
+ public var forceDisconnectUnresolvable: Bool = false
+ public var forceDisconnectSWCD: Bool = false
+
+
init() { reset() }
/// Reload from stored settings and rebuilt binary search tree
@@ -18,6 +22,8 @@ class GlassVPNHook {
setAutoDelete(PrefsShared.AutoDeleteLogsDays)
cachedNotify = CachedConnectionAlert()
currentlyRecording = PrefsShared.CurrentlyRecording != .Off
+ forceDisconnectUnresolvable = PrefsShared.ForceDisconnectUnresolvableDNS
+ forceDisconnectSWCD = PrefsShared.ForceDisconnectSWCD
}
/// Invalidate auto-delete timer and release stored properties. You should nullify this instance afterwards.
@@ -28,6 +34,8 @@ class GlassVPNHook {
autoDeleteTimer?.invalidate()
cachedNotify = nil
currentlyRecording = false
+ forceDisconnectUnresolvable = false
+ forceDisconnectSWCD = false
}
/// Call this method from `PacketTunnelProvider.handleAppMessage(_:completionHandler:)`
@@ -50,6 +58,12 @@ class GlassVPNHook {
let newState = CurrentRecordingState(rawValue: Int(value) ?? 0)
currentlyRecording = newState != .Off
return
+ case "disconnect-unresolvable":
+ forceDisconnectUnresolvable = value == "1"
+ return
+ case "disconnect-swcd":
+ forceDisconnectSWCD = value == "1"
+ return
default: break
}
}
@@ -79,6 +93,11 @@ class GlassVPNHook {
return blockActive
}
+ func silentlyPrevented(_ domain: String) {
+ // TODO: persist in a separate db/table?
+ NSLog("[VPN.INFO] preventing connection to \(domain)")
+ }
+
/// Build binary tree for reverse DNS lookup
private func reloadDomainFilter() {
let tmp = AppDB?.loadFilters()?.map({
diff --git a/main/Settings/TVCSettings.swift b/main/Settings/TVCSettings.swift
index d7888c7..e0a9129 100644
--- a/main/Settings/TVCSettings.swift
+++ b/main/Settings/TVCSettings.swift
@@ -3,6 +3,8 @@ import UIKit
class TVCSettings: UITableViewController {
@IBOutlet var vpnToggle: UISwitch!
+ @IBOutlet var unresolvableToggle: UISwitch!
+ @IBOutlet var swcdToggle: UISwitch!
@IBOutlet var cellDomainsIgnored: UITableViewCell!
@IBOutlet var cellDomainsBlocked: UITableViewCell!
@IBOutlet var cellPrivacyAutoDelete: UITableViewCell!
@@ -14,6 +16,7 @@ class TVCSettings: UITableViewController {
reloadVPNState()
reloadLoggingFilterUI()
reloadPrivacyUI()
+ reloadAdvancedUI()
NotifyVPNStateChanged.observe(call: #selector(reloadVPNState), on: self)
NotifyDNSFilterChanged.observe(call: #selector(reloadLoggingFilterUI), on: self)
}
@@ -191,6 +194,21 @@ extension TVCSettings {
// MARK: - Advanced
extension TVCSettings {
+ private func reloadAdvancedUI() {
+ unresolvableToggle.isOn = PrefsShared.ForceDisconnectUnresolvableDNS
+ swcdToggle.isOn = PrefsShared.ForceDisconnectSWCD
+ }
+
+ @IBAction private func togglePreventUnresolvable(_ sender: UISwitch) {
+ PrefsShared.ForceDisconnectUnresolvableDNS = sender.isOn
+ GlassVPN.send(.disconnectUnresolvable(sender.isOn))
+ }
+
+ @IBAction private func togglePreventSWCD(_ sender: UISwitch) {
+ PrefsShared.ForceDisconnectSWCD = sender.isOn
+ GlassVPN.send(.disconnectSWCD(sender.isOn))
+ }
+
@IBAction private func exportDB() {
AppDB?.vacuum()
let sheet = UIActivityViewController(activityItems: [URL.internalDB()], applicationActivities: nil)