From 188a130825c7cebed12afa348a6cb63ddfbba337 Mon Sep 17 00:00:00 2001 From: relikd Date: Tue, 25 Feb 2020 00:08:37 +0100 Subject: [PATCH] VPN initial --- .gitignore | 7 + AppCheck.xcodeproj/project.pbxproj | 256 +++++++++------- .../xcshareddata/swiftpm/Package.resolved | 16 - .../{GlassDNS.xcscheme => GlassVPN.xcscheme} | 26 +- Cartfile | 1 + Cartfile.resolved | 8 + GlassDNS/DNSProxyProvider.swift | 210 ------------- .../GlassVPN.entitlements | 6 +- {GlassDNS => GlassVPN}/Info.plist | 10 +- GlassVPN/PacketTunnelProvider.swift | 127 ++++++++ SQDB.swift | 122 +++++--- main/AppDelegate.swift | 279 +++++------------- main/Base.lproj/Main.storyboard | 67 ++--- main/TVCApps.swift | 139 --------- main/TVCDomains.swift | 138 +++++++++ main/TVCHostDetails.swift | 44 +++ main/TVCHosts.swift | 80 +++++ main/TVCRequestDetails.swift | 24 -- main/TVCRequests.swift | 64 ---- main/main.entitlements | 2 +- 20 files changed, 753 insertions(+), 873 deletions(-) create mode 100644 .gitignore delete mode 100644 AppCheck.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved rename AppCheck.xcodeproj/xcshareddata/xcschemes/{GlassDNS.xcscheme => GlassVPN.xcscheme} (80%) create mode 100644 Cartfile create mode 100644 Cartfile.resolved delete mode 100644 GlassDNS/DNSProxyProvider.swift rename GlassDNS/GlassDNS.entitlements => GlassVPN/GlassVPN.entitlements (75%) rename {GlassDNS => GlassVPN}/Info.plist (80%) create mode 100644 GlassVPN/PacketTunnelProvider.swift delete mode 100644 main/TVCApps.swift create mode 100644 main/TVCDomains.swift create mode 100644 main/TVCHostDetails.swift create mode 100644 main/TVCHosts.swift delete mode 100644 main/TVCRequestDetails.swift delete mode 100644 main/TVCRequests.swift diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d6f92d8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +xcuserdata/ +.build/ +build/ +DerivedData/ + +Carthage/Checkouts +Carthage/Build \ No newline at end of file diff --git a/AppCheck.xcodeproj/project.pbxproj b/AppCheck.xcodeproj/project.pbxproj index fe95774..13350cf 100644 --- a/AppCheck.xcodeproj/project.pbxproj +++ b/AppCheck.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 52; + objectVersion = 50; objects = { /* Begin PBXBuildFile section */ @@ -12,29 +12,45 @@ 541AC5DD2399498A00A769D7 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 541AC5DB2399498A00A769D7 /* Main.storyboard */; }; 541AC5DF2399498B00A769D7 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 541AC5DE2399498B00A769D7 /* Assets.xcassets */; }; 541AC5E22399498B00A769D7 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 541AC5E02399498B00A769D7 /* LaunchScreen.storyboard */; }; - 541AC5EB2399499A00A769D7 /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 541AC5EA2399499A00A769D7 /* NetworkExtension.framework */; }; - 5433233423ECD3F5007C8052 /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 541AC5EA2399499A00A769D7 /* NetworkExtension.framework */; }; - 548B1F8B23D3374F005B047C /* DNSProxyProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 548B1F8A23D3374F005B047C /* DNSProxyProvider.swift */; }; - 548B1F9023D3374F005B047C /* GlassDNS.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 548B1F8823D3374F005B047C /* GlassDNS.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 543CDB2023EEE61900B7F323 /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 543CDB1F23EEE61900B7F323 /* PacketTunnelProvider.swift */; }; + 543CDB2523EEE61900B7F323 /* GlassVPN.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 543CDB1D23EEE61900B7F323 /* GlassVPN.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 546063D023FC2565008F505A /* CocoaAsyncSocket.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 546063B723FC254C008F505A /* CocoaAsyncSocket.framework */; }; + 546063D123FC2565008F505A /* CocoaAsyncSocket.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 546063B723FC254C008F505A /* CocoaAsyncSocket.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 546063D223FC2565008F505A /* CocoaLumberjack.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 546063B523FC254B008F505A /* CocoaLumberjack.framework */; }; + 546063D323FC2565008F505A /* CocoaLumberjack.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 546063B523FC254B008F505A /* CocoaLumberjack.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 546063D423FC2565008F505A /* CocoaLumberjackSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 546063B423FC254B008F505A /* CocoaLumberjackSwift.framework */; }; + 546063D523FC2565008F505A /* CocoaLumberjackSwift.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 546063B423FC254B008F505A /* CocoaLumberjackSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 546063D623FC2565008F505A /* lwip.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 546063B623FC254B008F505A /* lwip.framework */; }; + 546063D723FC2565008F505A /* lwip.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 546063B623FC254B008F505A /* lwip.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 546063D823FC2565008F505A /* MMDB.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 546063B923FC254C008F505A /* MMDB.framework */; }; + 546063D923FC2565008F505A /* MMDB.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 546063B923FC254C008F505A /* MMDB.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 546063DA23FC2565008F505A /* NEKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 546063B223FC254B008F505A /* NEKit.framework */; }; + 546063DB23FC2565008F505A /* NEKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 546063B223FC254B008F505A /* NEKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 546063DC23FC2565008F505A /* Resolver.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 546063BA23FC254C008F505A /* Resolver.framework */; }; + 546063DD23FC2565008F505A /* Resolver.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 546063BA23FC254C008F505A /* Resolver.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 546063DE23FC2565008F505A /* Sodium.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 546063B123FC254B008F505A /* Sodium.framework */; }; + 546063DF23FC2565008F505A /* Sodium.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 546063B123FC254B008F505A /* Sodium.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 546063E023FC2565008F505A /* tun2socks.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 546063B323FC254B008F505A /* tun2socks.framework */; }; + 546063E123FC2565008F505A /* tun2socks.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 546063B323FC254B008F505A /* tun2socks.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 546063E223FC2565008F505A /* Yaml.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 546063B823FC254C008F505A /* Yaml.framework */; }; + 546063E323FC2565008F505A /* Yaml.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 546063B823FC254C008F505A /* Yaml.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 546063E523FEFAFE008F505A /* SQDB.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B7562223D7B2DC008F0C41 /* SQDB.swift */; }; 54953E3323DC752E0054345C /* SQDB.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B7562223D7B2DC008F0C41 /* SQDB.swift */; }; - 54953E3423DC752F0054345C /* SQDB.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B7562223D7B2DC008F0C41 /* SQDB.swift */; }; - 54953E5F23DEBE840054345C /* TVCApps.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54953E5E23DEBE840054345C /* TVCApps.swift */; }; - 54953E6123E0D69A0054345C /* TVCRequests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54953E6023E0D69A0054345C /* TVCRequests.swift */; }; - 54953E6F23E44CD00054345C /* TVCRequestDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54953E6E23E44CD00054345C /* TVCRequestDetails.swift */; }; + 54953E5F23DEBE840054345C /* TVCDomains.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54953E5E23DEBE840054345C /* TVCDomains.swift */; }; + 54953E6123E0D69A0054345C /* TVCHosts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54953E6023E0D69A0054345C /* TVCHosts.swift */; }; + 54953E6F23E44CD00054345C /* TVCHostDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54953E6E23E44CD00054345C /* TVCHostDetails.swift */; }; 54953E7123E473F10054345C /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 54953E7023E473F10054345C /* Settings.bundle */; }; - 54B7560123D4EFDC008F0C41 /* DNS in Frameworks */ = {isa = PBXBuildFile; productRef = 54B7560023D4EFDC008F0C41 /* DNS */; }; - 54B7560323D4F5BC008F0C41 /* DNS in Frameworks */ = {isa = PBXBuildFile; productRef = 54B7560223D4F5BC008F0C41 /* DNS */; }; 54C056DB23E9E36E00214A3F /* AppInfoType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54C056DA23E9E36E00214A3F /* AppInfoType.swift */; }; 54C056DD23E9EEF700214A3F /* BundleIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54C056DC23E9EEF700214A3F /* BundleIcon.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ - 548B1F8E23D3374F005B047C /* PBXContainerItemProxy */ = { + 543CDB2323EEE61900B7F323 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 541AC5CC2399498A00A769D7 /* Project object */; proxyType = 1; - remoteGlobalIDString = 548B1F8723D3374F005B047C; - remoteInfo = DNS; + remoteGlobalIDString = 543CDB1C23EEE61900B7F323; + remoteInfo = GlassVPN; }; /* End PBXContainerItemProxy section */ @@ -45,11 +61,31 @@ dstPath = ""; dstSubfolderSpec = 13; files = ( - 548B1F9023D3374F005B047C /* GlassDNS.appex in Embed App Extensions */, + 543CDB2523EEE61900B7F323 /* GlassVPN.appex in Embed App Extensions */, ); name = "Embed App Extensions"; runOnlyForDeploymentPostprocessing = 0; }; + 546063E423FC2565008F505A /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 546063D923FC2565008F505A /* MMDB.framework in Embed Frameworks */, + 546063D323FC2565008F505A /* CocoaLumberjack.framework in Embed Frameworks */, + 546063D123FC2565008F505A /* CocoaAsyncSocket.framework in Embed Frameworks */, + 546063D723FC2565008F505A /* lwip.framework in Embed Frameworks */, + 546063E123FC2565008F505A /* tun2socks.framework in Embed Frameworks */, + 546063E323FC2565008F505A /* Yaml.framework in Embed Frameworks */, + 546063DD23FC2565008F505A /* Resolver.framework in Embed Frameworks */, + 546063DF23FC2565008F505A /* Sodium.framework in Embed Frameworks */, + 546063DB23FC2565008F505A /* NEKit.framework in Embed Frameworks */, + 546063D523FC2565008F505A /* CocoaLumberjackSwift.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ @@ -61,14 +97,24 @@ 541AC5E12399498B00A769D7 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 541AC5E32399498B00A769D7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 541AC5EA2399499A00A769D7 /* NetworkExtension.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NetworkExtension.framework; path = System/Library/Frameworks/NetworkExtension.framework; sourceTree = SDKROOT; }; - 548B1F8823D3374F005B047C /* GlassDNS.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = GlassDNS.appex; sourceTree = BUILT_PRODUCTS_DIR; }; - 548B1F8A23D3374F005B047C /* DNSProxyProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DNSProxyProvider.swift; sourceTree = ""; }; - 548B1F8C23D3374F005B047C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 548B1F8D23D3374F005B047C /* GlassDNS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = GlassDNS.entitlements; sourceTree = ""; }; + 543CDB1D23EEE61900B7F323 /* GlassVPN.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = GlassVPN.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + 543CDB1F23EEE61900B7F323 /* PacketTunnelProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PacketTunnelProvider.swift; sourceTree = ""; }; + 543CDB2123EEE61900B7F323 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 543CDB2223EEE61900B7F323 /* GlassVPN.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = GlassVPN.entitlements; sourceTree = ""; }; + 546063B123FC254B008F505A /* Sodium.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sodium.framework; path = Carthage/Build/iOS/Sodium.framework; sourceTree = ""; }; + 546063B223FC254B008F505A /* NEKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NEKit.framework; path = Carthage/Build/iOS/NEKit.framework; sourceTree = ""; }; + 546063B323FC254B008F505A /* tun2socks.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = tun2socks.framework; path = Carthage/Build/iOS/tun2socks.framework; sourceTree = ""; }; + 546063B423FC254B008F505A /* CocoaLumberjackSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CocoaLumberjackSwift.framework; path = Carthage/Build/iOS/CocoaLumberjackSwift.framework; sourceTree = ""; }; + 546063B523FC254B008F505A /* CocoaLumberjack.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CocoaLumberjack.framework; path = Carthage/Build/iOS/CocoaLumberjack.framework; sourceTree = ""; }; + 546063B623FC254B008F505A /* lwip.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = lwip.framework; path = Carthage/Build/iOS/lwip.framework; sourceTree = ""; }; + 546063B723FC254C008F505A /* CocoaAsyncSocket.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CocoaAsyncSocket.framework; path = Carthage/Build/iOS/CocoaAsyncSocket.framework; sourceTree = ""; }; + 546063B823FC254C008F505A /* Yaml.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Yaml.framework; path = Carthage/Build/iOS/Yaml.framework; sourceTree = ""; }; + 546063B923FC254C008F505A /* MMDB.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MMDB.framework; path = Carthage/Build/iOS/MMDB.framework; sourceTree = ""; }; + 546063BA23FC254C008F505A /* Resolver.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Resolver.framework; path = Carthage/Build/iOS/Resolver.framework; sourceTree = ""; }; 548B1F9423D338EC005B047C /* main.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = main.entitlements; sourceTree = ""; }; - 54953E5E23DEBE840054345C /* TVCApps.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TVCApps.swift; sourceTree = ""; }; - 54953E6023E0D69A0054345C /* TVCRequests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TVCRequests.swift; sourceTree = ""; }; - 54953E6E23E44CD00054345C /* TVCRequestDetails.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TVCRequestDetails.swift; sourceTree = ""; }; + 54953E5E23DEBE840054345C /* TVCDomains.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TVCDomains.swift; sourceTree = ""; }; + 54953E6023E0D69A0054345C /* TVCHosts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TVCHosts.swift; sourceTree = ""; }; + 54953E6E23E44CD00054345C /* TVCHostDetails.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TVCHostDetails.swift; sourceTree = ""; }; 54953E7023E473F10054345C /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = ""; }; 54B7562223D7B2DC008F0C41 /* SQDB.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SQDB.swift; sourceTree = ""; }; 54C056DA23E9E36E00214A3F /* AppInfoType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppInfoType.swift; sourceTree = ""; }; @@ -80,17 +126,23 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 54B7560123D4EFDC008F0C41 /* DNS in Frameworks */, - 541AC5EB2399499A00A769D7 /* NetworkExtension.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; - 548B1F8523D3374F005B047C /* Frameworks */ = { + 543CDB1A23EEE61900B7F323 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 54B7560323D4F5BC008F0C41 /* DNS in Frameworks */, - 5433233423ECD3F5007C8052 /* NetworkExtension.framework in Frameworks */, + 546063DC23FC2565008F505A /* Resolver.framework in Frameworks */, + 546063D423FC2565008F505A /* CocoaLumberjackSwift.framework in Frameworks */, + 546063D023FC2565008F505A /* CocoaAsyncSocket.framework in Frameworks */, + 546063D223FC2565008F505A /* CocoaLumberjack.framework in Frameworks */, + 546063DE23FC2565008F505A /* Sodium.framework in Frameworks */, + 546063D623FC2565008F505A /* lwip.framework in Frameworks */, + 546063D823FC2565008F505A /* MMDB.framework in Frameworks */, + 546063E223FC2565008F505A /* Yaml.framework in Frameworks */, + 546063E023FC2565008F505A /* tun2socks.framework in Frameworks */, + 546063DA23FC2565008F505A /* NEKit.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -102,7 +154,7 @@ children = ( 54B7562223D7B2DC008F0C41 /* SQDB.swift */, 541AC5D62399498A00A769D7 /* main */, - 548B1F8923D3374F005B047C /* GlassDNS */, + 543CDB1E23EEE61900B7F323 /* GlassVPN */, 541AC5D52399498A00A769D7 /* Products */, 541AC5E92399499A00A769D7 /* Frameworks */, ); @@ -112,7 +164,7 @@ isa = PBXGroup; children = ( 541AC5D42399498A00A769D7 /* AppCheck.app */, - 548B1F8823D3374F005B047C /* GlassDNS.appex */, + 543CDB1D23EEE61900B7F323 /* GlassVPN.appex */, ); name = Products; sourceTree = ""; @@ -124,9 +176,9 @@ 541AC5D72399498A00A769D7 /* AppDelegate.swift */, 54C056DC23E9EEF700214A3F /* BundleIcon.swift */, 54C056DA23E9E36E00214A3F /* AppInfoType.swift */, - 54953E5E23DEBE840054345C /* TVCApps.swift */, - 54953E6023E0D69A0054345C /* TVCRequests.swift */, - 54953E6E23E44CD00054345C /* TVCRequestDetails.swift */, + 54953E5E23DEBE840054345C /* TVCDomains.swift */, + 54953E6023E0D69A0054345C /* TVCHosts.swift */, + 54953E6E23E44CD00054345C /* TVCHostDetails.swift */, 541AC5DB2399498A00A769D7 /* Main.storyboard */, 541A957523E602DF00C09C19 /* LaunchIcon.png */, 541AC5E02399498B00A769D7 /* LaunchScreen.storyboard */, @@ -140,19 +192,29 @@ 541AC5E92399499A00A769D7 /* Frameworks */ = { isa = PBXGroup; children = ( + 546063B723FC254C008F505A /* CocoaAsyncSocket.framework */, + 546063B523FC254B008F505A /* CocoaLumberjack.framework */, + 546063B423FC254B008F505A /* CocoaLumberjackSwift.framework */, + 546063B623FC254B008F505A /* lwip.framework */, + 546063B923FC254C008F505A /* MMDB.framework */, + 546063B223FC254B008F505A /* NEKit.framework */, + 546063BA23FC254C008F505A /* Resolver.framework */, + 546063B123FC254B008F505A /* Sodium.framework */, + 546063B323FC254B008F505A /* tun2socks.framework */, + 546063B823FC254C008F505A /* Yaml.framework */, 541AC5EA2399499A00A769D7 /* NetworkExtension.framework */, ); name = Frameworks; sourceTree = ""; }; - 548B1F8923D3374F005B047C /* GlassDNS */ = { + 543CDB1E23EEE61900B7F323 /* GlassVPN */ = { isa = PBXGroup; children = ( - 548B1F8A23D3374F005B047C /* DNSProxyProvider.swift */, - 548B1F8C23D3374F005B047C /* Info.plist */, - 548B1F8D23D3374F005B047C /* GlassDNS.entitlements */, + 543CDB1F23EEE61900B7F323 /* PacketTunnelProvider.swift */, + 543CDB2123EEE61900B7F323 /* Info.plist */, + 543CDB2223EEE61900B7F323 /* GlassVPN.entitlements */, ); - path = GlassDNS; + path = GlassVPN; sourceTree = ""; }; /* End PBXGroup section */ @@ -171,34 +233,33 @@ buildRules = ( ); dependencies = ( - 548B1F8F23D3374F005B047C /* PBXTargetDependency */, + 543CDB2423EEE61900B7F323 /* PBXTargetDependency */, ); name = AppCheck; packageProductDependencies = ( - 54B7560023D4EFDC008F0C41 /* DNS */, ); productName = PrivacyScore; productReference = 541AC5D42399498A00A769D7 /* AppCheck.app */; productType = "com.apple.product-type.application"; }; - 548B1F8723D3374F005B047C /* GlassDNS */ = { + 543CDB1C23EEE61900B7F323 /* GlassVPN */ = { isa = PBXNativeTarget; - buildConfigurationList = 548B1F9123D3374F005B047C /* Build configuration list for PBXNativeTarget "GlassDNS" */; + buildConfigurationList = 543CDB2623EEE61A00B7F323 /* Build configuration list for PBXNativeTarget "GlassVPN" */; buildPhases = ( - 548B1F8423D3374F005B047C /* Sources */, - 548B1F8523D3374F005B047C /* Frameworks */, - 548B1F8623D3374F005B047C /* Resources */, + 543CDB1923EEE61900B7F323 /* Sources */, + 543CDB1A23EEE61900B7F323 /* Frameworks */, + 543CDB1B23EEE61900B7F323 /* Resources */, + 546063E423FC2565008F505A /* Embed Frameworks */, ); buildRules = ( ); dependencies = ( ); - name = GlassDNS; + name = GlassVPN; packageProductDependencies = ( - 54B7560223D4F5BC008F0C41 /* DNS */, ); - productName = DNS; - productReference = 548B1F8823D3374F005B047C /* GlassDNS.appex */; + productName = GlassVPN; + productReference = 543CDB1D23EEE61900B7F323 /* GlassVPN.appex */; productType = "com.apple.product-type.app-extension"; }; /* End PBXNativeTarget section */ @@ -220,7 +281,7 @@ }; }; }; - 548B1F8723D3374F005B047C = { + 543CDB1C23EEE61900B7F323 = { CreatedOnToolsVersion = 11.3.1; }; }; @@ -235,14 +296,13 @@ ); mainGroup = 541AC5CB2399498A00A769D7; packageReferences = ( - 54B755FF23D4EFDC008F0C41 /* XCRemoteSwiftPackageReference "DNS" */, ); productRefGroup = 541AC5D52399498A00A769D7 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 541AC5D32399498A00A769D7 /* AppCheck */, - 548B1F8723D3374F005B047C /* GlassDNS */, + 543CDB1C23EEE61900B7F323 /* GlassVPN */, ); }; /* End PBXProject section */ @@ -260,7 +320,7 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 548B1F8623D3374F005B047C /* Resources */ = { + 543CDB1B23EEE61900B7F323 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( @@ -295,31 +355,31 @@ buildActionMask = 2147483647; files = ( 54953E3323DC752E0054345C /* SQDB.swift in Sources */, - 54953E6123E0D69A0054345C /* TVCRequests.swift in Sources */, - 54953E5F23DEBE840054345C /* TVCApps.swift in Sources */, + 54953E6123E0D69A0054345C /* TVCHosts.swift in Sources */, + 54953E5F23DEBE840054345C /* TVCDomains.swift in Sources */, 54C056DB23E9E36E00214A3F /* AppInfoType.swift in Sources */, - 54953E6F23E44CD00054345C /* TVCRequestDetails.swift in Sources */, + 54953E6F23E44CD00054345C /* TVCHostDetails.swift in Sources */, 54C056DD23E9EEF700214A3F /* BundleIcon.swift in Sources */, 541AC5D82399498A00A769D7 /* AppDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; - 548B1F8423D3374F005B047C /* Sources */ = { + 543CDB1923EEE61900B7F323 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 548B1F8B23D3374F005B047C /* DNSProxyProvider.swift in Sources */, - 54953E3423DC752F0054345C /* SQDB.swift in Sources */, + 543CDB2023EEE61900B7F323 /* PacketTunnelProvider.swift in Sources */, + 546063E523FEFAFE008F505A /* SQDB.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ - 548B1F8F23D3374F005B047C /* PBXTargetDependency */ = { + 543CDB2423EEE61900B7F323 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = 548B1F8723D3374F005B047C /* GlassDNS */; - targetProxy = 548B1F8E23D3374F005B047C /* PBXContainerItemProxy */; + target = 543CDB1C23EEE61900B7F323 /* GlassVPN */; + targetProxy = 543CDB2323EEE61900B7F323 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ @@ -503,51 +563,51 @@ }; name = Release; }; - 548B1F9223D3374F005B047C /* Debug */ = { + 543CDB2723EEE61A00B7F323 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - CODE_SIGN_ENTITLEMENTS = GlassDNS/GlassDNS.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_ENTITLEMENTS = GlassVPN/GlassVPN.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 7; - DEVELOPMENT_TEAM = MY7CB4555Z; - INFOPLIST_FILE = GlassDNS/Info.plist; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/iOS", + ); + INFOPLIST_FILE = GlassVPN/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.2; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.0.0; - PRODUCT_BUNDLE_IDENTIFIER = "de.uni-bamberg.psi.AppCheck.DNS"; - PRODUCT_NAME = GlassDNS; - PROVISIONING_PROFILE_SPECIFIER = ""; + PRODUCT_BUNDLE_IDENTIFIER = "de.uni-bamberg.psi.AppCheck.VPN"; + PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; - 548B1F9323D3374F005B047C /* Release */ = { + 543CDB2823EEE61A00B7F323 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - CODE_SIGN_ENTITLEMENTS = GlassDNS/GlassDNS.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_ENTITLEMENTS = GlassVPN/GlassVPN.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 7; - DEVELOPMENT_TEAM = MY7CB4555Z; - INFOPLIST_FILE = GlassDNS/Info.plist; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/iOS", + ); + INFOPLIST_FILE = GlassVPN/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.2; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.0.0; - PRODUCT_BUNDLE_IDENTIFIER = "de.uni-bamberg.psi.AppCheck.DNS"; - PRODUCT_NAME = GlassDNS; - PROVISIONING_PROFILE_SPECIFIER = ""; + PRODUCT_BUNDLE_IDENTIFIER = "de.uni-bamberg.psi.AppCheck.VPN"; + PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; @@ -572,40 +632,16 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 548B1F9123D3374F005B047C /* Build configuration list for PBXNativeTarget "GlassDNS" */ = { + 543CDB2623EEE61A00B7F323 /* Build configuration list for PBXNativeTarget "GlassVPN" */ = { isa = XCConfigurationList; buildConfigurations = ( - 548B1F9223D3374F005B047C /* Debug */, - 548B1F9323D3374F005B047C /* Release */, + 543CDB2723EEE61A00B7F323 /* Debug */, + 543CDB2823EEE61A00B7F323 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ - -/* Begin XCRemoteSwiftPackageReference section */ - 54B755FF23D4EFDC008F0C41 /* XCRemoteSwiftPackageReference "DNS" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/Bouke/DNS"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 1.2.0; - }; - }; -/* End XCRemoteSwiftPackageReference section */ - -/* Begin XCSwiftPackageProductDependency section */ - 54B7560023D4EFDC008F0C41 /* DNS */ = { - isa = XCSwiftPackageProductDependency; - package = 54B755FF23D4EFDC008F0C41 /* XCRemoteSwiftPackageReference "DNS" */; - productName = DNS; - }; - 54B7560223D4F5BC008F0C41 /* DNS */ = { - isa = XCSwiftPackageProductDependency; - package = 54B755FF23D4EFDC008F0C41 /* XCRemoteSwiftPackageReference "DNS" */; - productName = DNS; - }; -/* End XCSwiftPackageProductDependency section */ }; rootObject = 541AC5CC2399498A00A769D7 /* Project object */; } diff --git a/AppCheck.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/AppCheck.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved deleted file mode 100644 index 6fe03c5..0000000 --- a/AppCheck.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ /dev/null @@ -1,16 +0,0 @@ -{ - "object": { - "pins": [ - { - "package": "DNS", - "repositoryURL": "https://github.com/Bouke/DNS", - "state": { - "branch": null, - "revision": "78bbd1589890a90b202d11d5f9e1297050cf0eb2", - "version": "1.2.0" - } - } - ] - }, - "version": 1 -} diff --git a/AppCheck.xcodeproj/xcshareddata/xcschemes/GlassDNS.xcscheme b/AppCheck.xcodeproj/xcshareddata/xcschemes/GlassVPN.xcscheme similarity index 80% rename from AppCheck.xcodeproj/xcshareddata/xcschemes/GlassDNS.xcscheme rename to AppCheck.xcodeproj/xcshareddata/xcschemes/GlassVPN.xcscheme index ba7377a..25a3acb 100644 --- a/AppCheck.xcodeproj/xcshareddata/xcschemes/GlassDNS.xcscheme +++ b/AppCheck.xcodeproj/xcshareddata/xcschemes/GlassVPN.xcscheme @@ -15,10 +15,10 @@ buildForAnalyzing = "YES"> + BlueprintIdentifier = "543CDB1C23EEE61900B7F323" + BuildableName = "GlassVPN.appex" + BlueprintName = "GlassVPN" + ReferencedContainer = "container:AppCheck.xcodeproj"> + BuildableName = "AppCheck.app" + BlueprintName = "AppCheck" + ReferencedContainer = "container:AppCheck.xcodeproj"> @@ -62,9 +62,9 @@ + BuildableName = "AppCheck.app" + BlueprintName = "AppCheck" + ReferencedContainer = "container:AppCheck.xcodeproj"> @@ -80,9 +80,9 @@ + BuildableName = "AppCheck.app" + BlueprintName = "AppCheck" + ReferencedContainer = "container:AppCheck.xcodeproj"> diff --git a/Cartfile b/Cartfile new file mode 100644 index 0000000..1a40700 --- /dev/null +++ b/Cartfile @@ -0,0 +1 @@ +github "zhuhaow/NEKit" \ No newline at end of file diff --git a/Cartfile.resolved b/Cartfile.resolved new file mode 100644 index 0000000..6f05e5f --- /dev/null +++ b/Cartfile.resolved @@ -0,0 +1,8 @@ +github "CocoaLumberjack/CocoaLumberjack" "3.6.1" +github "behrang/YamlSwift" "3.4.3" +github "lexrus/MMDB-Swift" "0.5.0" +github "robbiehanson/CocoaAsyncSocket" "7.6.3" +github "zhuhaow/NEKit" "0.15.0" +github "zhuhaow/Resolver" "0.2.0" +github "zhuhaow/Sodium-framework" "v1.0.10.1" +github "zhuhaow/tun2socks" "0.7.0" diff --git a/GlassDNS/DNSProxyProvider.swift b/GlassDNS/DNSProxyProvider.swift deleted file mode 100644 index 59747f5..0000000 --- a/GlassDNS/DNSProxyProvider.swift +++ /dev/null @@ -1,210 +0,0 @@ -import NetworkExtension -import DNS - -fileprivate var db: SQLiteDatabase? - -class DNSProxyProvider: NEDNSProxyProvider { - - override func startProxy(options:[String: Any]? = nil, completionHandler: @escaping (Error?) -> Void) { - // Add code here to start the DNS proxy. - simpleTunnelLog("startProxy") - do { - db = try SQLiteDatabase.open(path: DB_PATH) - try db!.createTable(table: DNSQuery.self) - } catch { - simpleTunnelLog("Error: \(error)") - completionHandler(error) - return - } - completionHandler(nil) - } - - override func stopProxy(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) { - // Add code here to stop the DNS proxy. - simpleTunnelLog("stopProxy") - db = nil - completionHandler() - } - - override func sleep(completionHandler: @escaping () -> Void) { - // Add code here to get ready to sleep. - simpleTunnelLog("sleep") - completionHandler() - } - - override func wake() { - // Add code here to wake up. - simpleTunnelLog("wake") - } - - override func handleNewUDPFlow(_ flow: NEAppProxyUDPFlow, initialRemoteEndpoint remoteEndpoint: NWEndpoint) -> Bool { - simpleTunnelLog("handleUDPFlow \(flow.metaData.sourceAppSigningIdentifier)") - simpleTunnelLog("handleUDPFlow \((remoteEndpoint as! NWHostEndpoint).hostname)") - - let con = createUDPSession(to: remoteEndpoint, from: (flow.localEndpoint as! NWHostEndpoint)) - let newConnection = ClientDNSProxy(newUDPFlow: flow) - newConnection.open(con) - return true - } - - - /*override func handleNewFlow(_ flow: NEAppProxyFlow) -> Bool { - // Add code here to handle the incoming flow. - NSLog("PSI: handleFlow %@", flow.metaData.sourceAppSigningIdentifier) - - var newConnection: ClientAppProxyConnection? - - guard let clientTunnel = tunnel else { return false } - - - if let tcpFlow = flow as? NEAppProxyTCPFlow { - let remoteHost = (tcpFlow.remoteEndpoint as! NWHostEndpoint).hostname - let remotePort = (tcpFlow.remoteEndpoint as! NWHostEndpoint).port - NSLog("PSI: TCP HOST : \(remoteHost)") - NSLog("PSI: TCP PORT : \(remotePort)") - newConnection = ClientAppProxyTCPConnection(tunnel: clientTunnel, newTCPFlow: tcpFlow) - } else if let udpFlow = flow as? NEAppProxyUDPFlow { - let localHost = (udpFlow.localEndpoint as! NWHostEndpoint).hostname - let localPort = (udpFlow.localEndpoint as! NWHostEndpoint).port - NSLog("PSI: UDP HOST : \(localHost)") - NSLog("PSI: UDP PORT : \(localPort)") - newConnection = ClientAppProxyUDPConnection(tunnel: clientTunnel, newUDPFlow: udpFlow) - } - - guard newConnection != nil else { return false } - - newConnection!.open() - - return true - // return super.handleNewFlow(flow) - }*/ -} - -class ClientDNSProxy : NSObject { - public let identifier: Int - let appProxyFlow: NEAppProxyFlow - var datagramsOutstanding = 0 - var conn: NWUDPSession? - - /// The NEAppProxyUDPFlow object corresponding to this connection. - var UDPFlow: NEAppProxyUDPFlow { - return (appProxyFlow as! NEAppProxyUDPFlow) - } - - init(newUDPFlow: NEAppProxyUDPFlow) { - appProxyFlow = newUDPFlow - identifier = newUDPFlow.hash - super.init() - } - - /// Send an "Open" message to the SimpleTunnel server, to begin the process of establishing a flow of data in the SimpleTunnel protocol. - func open(_ connection: NWUDPSession) { -// open([ -// TunnelMessageKey.TunnelType.rawValue: TunnelLayer.app.rawValue as AnyObject, -// TunnelMessageKey.AppProxyFlowType.rawValue: AppProxyFlowKind.udp.rawValue as AnyObject -// ]) - connection.setReadHandler({ datas, error in - guard let datagrams = datas, error == nil else { - simpleTunnelLog("Failed to read UDP connection: \(String(describing: error))") - return - } - - self.UDPFlow.writeDatagrams(datagrams, sentBy: [connection.endpoint]) { error in - if let error = error { - simpleTunnelLog("Failed to write datagrams to the UDP Flow: \(error)") -// self.tunnel?.sendCloseType(.read, forConnection: self.identifier) - self.UDPFlow.closeWriteWithError(nil) - } - } - }, maxDatagrams: 32) - self.conn = connection - UDPFlow.open(withLocalEndpoint: (UDPFlow.localEndpoint as! NWHostEndpoint)) { (e: Error?) in - self.handleSendResult(nil) - } - } - - /// Handle the result of sending a "Data" message to the SimpleTunnel server. - func handleSendResult(_ error: NSError?) { - - if let sendError = error { - simpleTunnelLog("Failed to send message to Tunnel Server. error = \(sendError)") -// handleErrorCondition(.hostUnreachable) - return - } - - if datagramsOutstanding > 0 { - datagramsOutstanding -= 1 - } - - // Only read more datagrams from the source application if all outstanding datagrams have been sent on the network. - guard datagramsOutstanding == 0 else { return } - - // Read a new set of datagrams from the source application. - UDPFlow.readDatagrams { datagrams, remoteEndPoints, readError in - - guard let readDatagrams = datagrams, - let readEndpoints = remoteEndPoints - , readError == nil else - { - simpleTunnelLog("Failed to read data from the UDP flow. error = \(String(describing: readError))") -// self.handleErrorCondition(.peerReset) - return - } - - guard !readDatagrams.isEmpty && readEndpoints.count == readDatagrams.count else { - simpleTunnelLog("\(self.identifier): Received EOF on the UDP flow. Closing the flow...") -// self.tunnel?.sendCloseType(.write, forConnection: self.identifier) - self.UDPFlow.closeReadWithError(nil) - return - } - - self.datagramsOutstanding = readDatagrams.count - - for (index, datagram) in readDatagrams.enumerated() { - guard let endpoint = readEndpoints[index] as? NWHostEndpoint else { continue } - - let response = try! Message.init(deserialize: datagram) - for q in response.questions { - simpleTunnelLog("got name \(q.name)") - try? db?.insertDNSQuery(appId: self.UDPFlow.metaData.sourceAppSigningIdentifier as NSString, - dnsQuery: q.name as NSString) - } - - simpleTunnelLog("(\(self.identifier)): Sending a \(datagram.count)-byte datagram to \(endpoint.hostname):\(endpoint.port)") - // Send a data message to the SimpleTunnel server. -// self.sendDataMessage(datagram, extraProperties:[ -// TunnelMessageKey.Host.rawValue: endpoint.hostname as AnyObject, -// TunnelMessageKey.Port.rawValue: Int(endpoint.port)! as AnyObject -// ]) - self.conn?.writeDatagram(datagram, completionHandler: { conError in - simpleTunnelLog("write con err: \(String(describing: conError))") - }) - } - -// self.UDPFlow.writeDatagrams(readDatagrams, sentBy: readEndpoints) { writeError in -// simpleTunnelLog("write error \(String(describing: writeError))") -// } - } - } - - /// Send a datagram received from the SimpleTunnel server to the destination application. - func sendDataWithEndPoint(_ data: Data, host: String, port: Int) { - let datagrams = [ data ] - let endpoints = [ NWHostEndpoint(hostname: host, port: String(port)) ] - - // Send the datagram to the destination application. - UDPFlow.writeDatagrams(datagrams, sentBy: endpoints) { error in - if let error = error { - simpleTunnelLog("Failed to write datagrams to the UDP Flow: \(error)") -// self.tunnel?.sendCloseType(.read, forConnection: self.identifier) - self.UDPFlow.closeWriteWithError(nil) - } - } - } -} - - -public func simpleTunnelLog(_ message: String) { -// NSLog("PSI: \(message)") -} - diff --git a/GlassDNS/GlassDNS.entitlements b/GlassVPN/GlassVPN.entitlements similarity index 75% rename from GlassDNS/GlassDNS.entitlements rename to GlassVPN/GlassVPN.entitlements index 4a8655d..e26d0f5 100644 --- a/GlassDNS/GlassDNS.entitlements +++ b/GlassVPN/GlassVPN.entitlements @@ -4,11 +4,7 @@ com.apple.developer.networking.networkextension - dns-proxy - - com.apple.developer.networking.vpn.api - - allow-vpn + packet-tunnel-provider com.apple.security.application-groups diff --git a/GlassDNS/Info.plist b/GlassVPN/Info.plist similarity index 80% rename from GlassDNS/Info.plist rename to GlassVPN/Info.plist index d15f797..a725338 100644 --- a/GlassDNS/Info.plist +++ b/GlassVPN/Info.plist @@ -5,7 +5,7 @@ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName - GlassDNS + GlassVPN CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -17,15 +17,15 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - $(MARKETING_VERSION) + 1.0 CFBundleVersion - 7 + 1 NSExtension NSExtensionPointIdentifier - com.apple.networkextension.dns-proxy + com.apple.networkextension.packet-tunnel NSExtensionPrincipalClass - $(PRODUCT_MODULE_NAME).DNSProxyProvider + $(PRODUCT_MODULE_NAME).PacketTunnelProvider diff --git a/GlassVPN/PacketTunnelProvider.swift b/GlassVPN/PacketTunnelProvider.swift new file mode 100644 index 0000000..449af8e --- /dev/null +++ b/GlassVPN/PacketTunnelProvider.swift @@ -0,0 +1,127 @@ +import NetworkExtension +import NEKit + +fileprivate var db: SQLiteDatabase? +fileprivate var blockedDomains: [String] = [] +fileprivate var ignoredDomains: [String] = [] + +// MARK: ObserverFactory + +class LDObserverFactory: ObserverFactory { + + override func getObserverForProxySocket(_ socket: ProxySocket) -> Observer? { + return LDProxySocketObserver() + } + + class LDProxySocketObserver: Observer { + override func signal(_ event: ProxySocketEvent) { + switch event { + case .receivedRequest(let session, let socket): + QLog("DNS: \(session.host)") + if ignoredDomains.allSatisfy({ session.host != $0 && !session.host.hasSuffix("." + $0) }) { + try? db?.insertDNSQuery(session.host) + } else { + QLog("ignored") + } + for domain in blockedDomains { + if (session.host == domain || session.host.hasSuffix("." + domain)) { + QLog("blocked") + socket.forceDisconnect() + return + } + } + default: + break + } + } + + } +} + +// MARK: NEPacketTunnelProvider + +class PacketTunnelProvider: NEPacketTunnelProvider { + + let proxyServerPort: UInt16 = 9090 + let proxyServerAddress = "127.0.0.1" + var proxyServer: GCDHTTPProxyServer! + + override func startTunnel(options: [String : NSObject]?, completionHandler: @escaping (Error?) -> Void) { + QLog("startTunnel") + ignoredDomains = ["signal.org", "whispersystems.org"] + // TODO: init blocked & ignored + do { + db = try SQLiteDatabase.open(path: DB_PATH) + try db!.createTable(table: DNSQuery.self) + } catch { + completionHandler(error) + return + } + if proxyServer != nil { + proxyServer.stop() + } + proxyServer = nil + + let settings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: proxyServerAddress) + settings.mtu = NSNumber(value: 1500) + + let proxySettings = NEProxySettings() + proxySettings.httpEnabled = true; + proxySettings.httpServer = NEProxyServer(address: proxyServerAddress, port: Int(proxyServerPort)) + proxySettings.httpsEnabled = true; + proxySettings.httpsServer = NEProxyServer(address: proxyServerAddress, port: Int(proxyServerPort)) + proxySettings.excludeSimpleHostnames = false; + proxySettings.exceptionList = [] + proxySettings.matchDomains = [""] + + settings.dnsSettings = NEDNSSettings(servers: ["127.0.0.1"]) + settings.proxySettings = proxySettings; + RawSocketFactory.TunnelProvider = self + ObserverFactory.currentFactory = LDObserverFactory() + + self.setTunnelNetworkSettings(settings) { error in + guard error == nil else { + QLog("setTunnelNetworkSettings error: \(String(describing: error))") + completionHandler(error) + return + } + QLog("setTunnelNetworkSettings success \(self.packetFlow)") + completionHandler(nil) + + self.proxyServer = GCDHTTPProxyServer(address: IPAddress(fromString: self.proxyServerAddress), port: Port(port: self.proxyServerPort)) + do { + try self.proxyServer.start() + completionHandler(nil) + } + catch let proxyError { + QLog("Error starting proxy server \(proxyError)") + completionHandler(proxyError) + } + } + } + + + override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) { + QLog("stopTunnel") + db = nil + DNSServer.currentServer = nil + RawSocketFactory.TunnelProvider = nil + ObserverFactory.currentFactory = nil + proxyServer.stop() + proxyServer = nil + QLog("error on stopping: \(reason)") + completionHandler() + exit(EXIT_SUCCESS) + } + + override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)?) { + QLog("handleAppMessage") + if let handler = completionHandler { + handler(messageData) + } + } +} + +public func QLog(_ message: String) { + NSLog("TUN: \(message)") +} diff --git a/SQDB.swift b/SQDB.swift index 6c5e21a..2e498bb 100644 --- a/SQDB.swift +++ b/SQDB.swift @@ -97,15 +97,15 @@ protocol SQLTable { } struct DNSQuery: SQLTable { - let app: String - let dns: String let ts: Int64 + let domain: String + let host: String? static var createStatement: String { return """ CREATE TABLE IF NOT EXISTS req( - app VARCHAR(255), - dns VARCHAR(2047), - ts BIGINT DEFAULT (strftime('%s','now')) + ts BIGINT DEFAULT (strftime('%s','now')), + domain VARCHAR(255) NOT NULL, + host VARCHAR(2047) ); """ } @@ -113,15 +113,30 @@ struct DNSQuery: SQLTable { extension SQLiteDatabase { - func insertDNSQuery(appId: NSString, dnsQuery: NSString) throws { - let insertSql = "INSERT INTO req (app, dns) VALUES (?, ?);" + func insertDNSQuery(_ dnsQuery: String) throws { + // Split dns query into subdomain part + var domain: String = dnsQuery + var host: String? = nil + let lastChr = dnsQuery.last?.asciiValue ?? 0 + if lastChr > UInt8(ascii: "9") || lastChr < UInt8(ascii: "0") { // if not IP address + guard let last1 = dnsQuery.lastIndex(of: ".") else { + return + } + let last2 = dnsQuery[...dnsQuery.index(before: last1)].lastIndex(of: ".") + if let idx = last2 { + domain = String(dnsQuery.suffix(from: dnsQuery.index(after: idx))) + host = String(dnsQuery.prefix(upTo: idx)) + } + } + // perform query + let insertSql = "INSERT INTO req (domain, host) VALUES (?, ?);" let insertStatement = try prepareStatement(sql: insertSql) defer { sqlite3_finalize(insertStatement) } guard - sqlite3_bind_text(insertStatement, 1, appId.utf8String, -1, nil) == SQLITE_OK && - sqlite3_bind_text(insertStatement, 2, dnsQuery.utf8String, -1, nil) == SQLITE_OK + sqlite3_bind_text(insertStatement, 1, (domain as NSString).utf8String, -1, nil) == SQLITE_OK && + sqlite3_bind_text(insertStatement, 2, (host as NSString?)?.utf8String, -1, nil) == SQLITE_OK else { throw SQLiteError.Bind(message: errorMessage) } @@ -130,32 +145,9 @@ extension SQLiteDatabase { } } - func dnsQueriesForApp(appIdentifier: NSString, _ body: @escaping (DNSQuery) -> Void) { - let querySql = "SELECT * FROM req WHERE app = ?;" - guard let queryStatement = try? prepareStatement(sql: querySql) else { - print("Error preparing statement for insert") - return - } - defer { - sqlite3_finalize(queryStatement) - } - guard sqlite3_bind_text(queryStatement, 1, appIdentifier.utf8String, -1, nil) == SQLITE_OK else { - print("Error binding insert key") - return - } - while (sqlite3_step(queryStatement) == SQLITE_ROW) { - let appId = sqlite3_column_text(queryStatement, 0) - let dnsQ = sqlite3_column_text(queryStatement, 1) - let ts = sqlite3_column_int64(queryStatement, 2) - let res = DNSQuery(app: String(cString: appId!), - dns: String(cString: dnsQ!), - ts: ts) - body(res) - } - } - - func appList() -> [String] { - let querySql = "SELECT DISTINCT app FROM req;" + func domainList() -> [GroupedDomain] { +// let querySql = "SELECT DISTINCT domain FROM req;" + let querySql = "SELECT domain, COUNT(*), MAX(ts) FROM req GROUP BY domain ORDER BY 3 DESC;" guard let queryStatement = try? prepareStatement(sql: querySql) else { print("Error preparing statement for insert") return [] @@ -163,11 +155,65 @@ extension SQLiteDatabase { defer { sqlite3_finalize(queryStatement) } - var res: [String] = [] + var res: [GroupedDomain] = [] while (sqlite3_step(queryStatement) == SQLITE_ROW) { - let appId = sqlite3_column_text(queryStatement, 0) - res.append(String(cString: appId!)) + let d = sqlite3_column_text(queryStatement, 0) + let c = sqlite3_column_int64(queryStatement, 1) + let l = sqlite3_column_int64(queryStatement, 2) + res.append(GroupedDomain(label: String(cString: d!), count: c, lastModified: l)) + } + return res + } + + func hostsForDomain(_ domain: NSString) -> [GroupedDomain] { + let querySql = "SELECT host, COUNT(*), MAX(ts) FROM req WHERE domain = ? GROUP BY host ORDER BY 1 ASC;" + guard let queryStatement = try? prepareStatement(sql: querySql) else { + print("Error preparing statement for insert") + return [] + } + defer { + sqlite3_finalize(queryStatement) + } + guard sqlite3_bind_text(queryStatement, 1, domain.utf8String, -1, nil) == SQLITE_OK else { + print("Error binding insert key") + return [] + } + var res: [GroupedDomain] = [] + while (sqlite3_step(queryStatement) == SQLITE_ROW) { + let h = sqlite3_column_text(queryStatement, 0) + let c = sqlite3_column_int64(queryStatement, 1) + let l = sqlite3_column_int64(queryStatement, 2) + res.append(GroupedDomain(label: h != nil ? String(cString: h!) : "", count: c, lastModified: l)) + } + return res + } + + func timesForDomain(_ domain: String, host: String?) -> [Timestamp] { + let querySql = "SELECT ts FROM req WHERE domain = ? AND host = ?;" + guard let queryStatement = try? prepareStatement(sql: querySql) else { + print("Error preparing statement for insert") + return [] + } + defer { + sqlite3_finalize(queryStatement) + } + guard + sqlite3_bind_text(queryStatement, 1, (domain as NSString).utf8String, -1, nil) == SQLITE_OK && + sqlite3_bind_text(queryStatement, 2, (host as NSString?)?.utf8String, -1, nil) == SQLITE_OK + else { + print("Error binding insert key") + return [] + } + var res: [Timestamp] = [] + while (sqlite3_step(queryStatement) == SQLITE_ROW) { + let ts = sqlite3_column_int64(queryStatement, 0) + res.append(ts) } return res } } + +typealias Timestamp = Int64 +struct GroupedDomain { + let label: String, count: Int64, lastModified: Timestamp +} diff --git a/main/AppDelegate.swift b/main/AppDelegate.swift index 8a963e8..8a5507a 100644 --- a/main/AppDelegate.swift +++ b/main/AppDelegate.swift @@ -1,19 +1,24 @@ import UIKit import NetworkExtension +let VPNConfigBundleIdentifier = "de.uni-bamberg.psi.AppCheck.VPN" +let dateFormatter = DateFormatter() + @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? + var managerVPN: NETunnelProviderManager? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss" - if UserDefaults.standard.bool(forKey: "kill_proxy") { - UserDefaults.standard.set(false, forKey: "kill_proxy") - disableDNS() - } else { - postDNSState() - } +// if UserDefaults.standard.bool(forKey: "kill_proxy") { +// UserDefaults.standard.set(false, forKey: "kill_proxy") +// disableDNS() +// } else { +// postDNSState() +// } if UserDefaults.standard.bool(forKey: "kill_db") { UserDefaults.standard.set(false, forKey: "kill_db") @@ -24,238 +29,92 @@ class AppDelegate: UIResponder, UIApplicationDelegate { try db.createTable(table: DNSQuery.self) } catch {} -// loadVPN { self.startVPN() } + self.postVPNState(.invalid) + loadVPN { mgr in + self.managerVPN = mgr + self.postVPNState() + } return true } func applicationDidBecomeActive(_ application: UIApplication) { // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. - postDNSState() +// postVPNState() } func setProxyEnabled(_ newState: Bool) { - // DNS: - if newState != managerDNS.isEnabled { - newState ? enableDNS() : disableDNS() + guard let mgr = self.managerVPN else { + self.createNewVPN { manager in + self.managerVPN = manager + self.setProxyEnabled(newState) + } + return } - // VPN: -// let con = self.managerVPN?.connection -// if newState != (con?.status == NEVPNStatus.connected) { -// self.updateVPN { -// self.managerVPN?.isEnabled = newState -// newState ? try? con?.startVPNTunnel() : con?.stopVPNTunnel() -// } -// } - } - - - // MARK: DNS - - let managerDNS = NEDNSProxyManager.shared() - - private func enableDNS() { - updateDNS { - self.managerDNS.localizedDescription = "GlassDNS" - let proto = NEDNSProxyProviderProtocol() - proto.providerBundleIdentifier = "de.uni-bamberg.psi.AppCheck.DNS" - self.managerDNS.providerProtocol = proto - self.managerDNS.isEnabled = true - } - } - - private func disableDNS() { - updateDNS { - self.managerDNS.isEnabled = false - } - } - - private func updateDNS(_ body: @escaping () -> Void) { - managerDNS.loadFromPreferences { (error) in - guard error == nil else { return } - body() - self.managerDNS.saveToPreferences { (error) in - self.postDNSState() - guard error == nil else { return } + let state = mgr.isEnabled && (mgr.connection.status == NEVPNStatus.connected) + if state != newState { + self.updateVPN({ mgr.isEnabled = true }) { + newState ? try? mgr.connection.startVPNTunnel() : mgr.connection.stopVPNTunnel() } } } - private func postDNSState() { - managerDNS.loadFromPreferences {_ in - NotificationCenter.default.post(name: .init("ChangedStateGlassDNS"), object: self.managerDNS.isEnabled) - } - } - - // MARK: VPN - /*var managerVPN: NETunnelProviderManager? + private func createNewVPN(_ success: @escaping (_ manager: NETunnelProviderManager) -> Void) { + let mgr = NETunnelProviderManager() + mgr.localizedDescription = "AppCheck Monitor" + let proto = NETunnelProviderProtocol() + proto.providerBundleIdentifier = VPNConfigBundleIdentifier + proto.serverAddress = "127.0.0.1" + mgr.protocolConfiguration = proto + mgr.isEnabled = true + mgr.saveToPreferences { error in + guard error == nil else { return } + success(mgr) + } + } - private func loadVPN(_ finally: @escaping () -> Void) { + private func loadVPN(_ finally: @escaping (_ manager: NETunnelProviderManager?) -> Void) { NETunnelProviderManager.loadAllFromPreferences { managers, error in - if managers?.count ?? 0 > 0 { - managers?.forEach({ mgr in - if let proto = (mgr.protocolConfiguration as? NETunnelProviderProtocol) { - if proto.providerBundleIdentifier == "de.uni-bamberg.psi.AppCheck.Tunnel" { -// self.managerVPN = mgr - mgr.removeFromPreferences() - } - } - }) - } - if self.managerVPN != nil { - finally() - } else { - let mgr = NETunnelProviderManager() - mgr.localizedDescription = "GlassTunnel" - let proto = NETunnelProviderProtocol() - proto.providerBundleIdentifier = "de.uni-bamberg.psi.AppCheck.Tunnel" - proto.serverAddress = "127.0.0.1" -// proto.username = "none" -// proto.proxySettings = NEProxySettings() -// proto.proxySettings?.httpEnabled = true -// proto.proxySettings?.httpsEnabled = true -// proto.authenticationMethod = .sharedSecret -// proto.sharedSecretReference = try! VPNKeychain.persistentReferenceFor(service: "GlassTunnel", account:"none", password: "none".data(using: String.Encoding.utf8)!) - mgr.protocolConfiguration = proto - mgr.isEnabled = true - self.managerVPN = mgr - mgr.saveToPreferences { (error) in - guard error == nil else { - NSLog("VPN: save error: \(String(describing: error))") - return - } - finally() - } - } - } - } - - private func startVPN() { - updateVPN { - do { - try self.managerVPN?.connection.startVPNTunnel() - } catch { - print("VPN: start error: \(error.localizedDescription)") - } - } - } - - private func updateVPN(_ body: @escaping () -> Void) { - self.managerVPN?.loadFromPreferences { (error) in - guard error == nil else { + guard let mgrs = managers, mgrs.count > 0 else { + finally(nil) return } - body() - self.managerVPN?.saveToPreferences { (error) in - guard error == nil else { - NSLog("VPN: save error: \(String(describing: error))") - return + for mgr in mgrs { + if let proto = (mgr.protocolConfiguration as? NETunnelProviderProtocol) { + if proto.providerBundleIdentifier == VPNConfigBundleIdentifier { + finally(mgr) + return + } } } - } - }*/ -} - -// MARK: VPNKeychain -/* -/// Utility routines for working with the keychain. - -enum VPNKeychain { - /// Returns a persistent reference for a generic password keychain item, adding it to - /// (or updating it in) the keychain if necessary. - /// - /// This delegates the work to two helper routines depending on whether the item already - /// exists in the keychain or not. - /// - /// - Parameters: - /// - service: The service name for the item. - /// - account: The account for the item. - /// - password: The desired password. - /// - Returns: A persistent reference to the item. - /// - Throws: Any error returned by the Security framework. - - static func persistentReferenceFor(service: String, account: String, password: Data) throws -> Data { - var copyResult: CFTypeRef? = nil - let err = SecItemCopyMatching([ - kSecClass: kSecClassGenericPassword, - kSecAttrService: service, - kSecAttrAccount: account, - kSecReturnPersistentRef: true, - kSecReturnData: true - ] as NSDictionary, ©Result) - switch err { - case errSecSuccess: - return try self.persistentReferenceByUpdating(copyResult: copyResult!, service: service, account: account, password: password) - case errSecItemNotFound: - return try self.persistentReferenceByAdding(service: service, account:account, password: password) - default: - try throwOSStatus(err) - // `throwOSStatus(_:)` only returns in the `errSecSuccess` case. We know we're - // not in that case but the compiler can't figure that out, alas. - fatalError() + finally(nil) } } - /// Returns a persistent reference for a generic password keychain item by updating it - /// in the keychain if necessary. - /// - /// - Parameters: - /// - copyResult: The result from the `SecItemCopyMatching` done by `persistentReferenceFor(service:account:password:)`. - /// - service: The service name for the item. - /// - account: The account for the item. - /// - password: The desired password. - /// - Returns: A persistent reference to the item. - /// - Throws: Any error returned by the Security framework. - - private static func persistentReferenceByUpdating(copyResult: CFTypeRef, service: String, account: String, password: Data) throws -> Data { - let copyResult = copyResult as! [String:Any] - let persistentRef = copyResult[kSecValuePersistentRef as String] as! NSData as Data - let currentPassword = copyResult[kSecValueData as String] as! NSData as Data - if password != currentPassword { - let err = SecItemUpdate([ - kSecClass: kSecClassGenericPassword, - kSecAttrService: service, - kSecAttrAccount: account, - ] as NSDictionary, [ - kSecValueData: password - ] as NSDictionary) - try throwOSStatus(err) + private func updateVPN(_ body: @escaping () -> Void, _ onSuccess: @escaping () -> Void) { + self.managerVPN?.loadFromPreferences { error in + guard error == nil else { return } + body() + self.managerVPN?.saveToPreferences { error in + guard error == nil else { return } + onSuccess() + } } - return persistentRef } - /// Returns a persistent reference for a generic password keychain item by adding it to - /// the keychain. - /// - /// - Parameters: - /// - service: The service name for the item. - /// - account: The account for the item. - /// - password: The desired password. - /// - Returns: A persistent reference to the item. - /// - Throws: Any error returned by the Security framework. - - private static func persistentReferenceByAdding(service: String, account: String, password: Data) throws -> Data { - var addResult: CFTypeRef? = nil - let err = SecItemAdd([ - kSecClass: kSecClassGenericPassword, - kSecAttrService: service, - kSecAttrAccount: account, - kSecValueData: password, - kSecReturnPersistentRef: true, - ] as NSDictionary, &addResult) - try throwOSStatus(err) - return addResult! as! NSData as Data + private func postVPNState() { + guard let mgr = self.managerVPN else { + self.postVPNState(.invalid) + return + } + mgr.loadFromPreferences { _ in + self.postVPNState(mgr.connection.status) + } } - /// Throws an error if a Security framework call has failed. - /// - /// - Parameter err: The error to check. - - private static func throwOSStatus(_ err: OSStatus) throws { - guard err == errSecSuccess else { - throw NSError(domain: NSOSStatusErrorDomain, code: Int(err), userInfo: nil) - } + private func postVPNState(_ state: NEVPNStatus) { + NotificationCenter.default.post(name: .init("ChangedStateGlassVPN"), object: state) } } -*/ + diff --git a/main/Base.lproj/Main.storyboard b/main/Base.lproj/Main.storyboard index be23ef3..67fe63e 100644 --- a/main/Base.lproj/Main.storyboard +++ b/main/Base.lproj/Main.storyboard @@ -7,17 +7,16 @@ - + - - + + - - + @@ -26,11 +25,7 @@ AppCheck helps you identify which applications communicate with third parties. It does so by logging DNS network requests. AppCheck learns only the destination addresses, not the actual data that is exchanged. -For improved readability, AppCheck shows domain names in reversed order (org.example instead of example.org) - -Requests to the same domain in quick succession cannot be monitored due to DNS caching (counters may be inaccurate). - -Your data belongs to you. Therefore, monitoring and analysis take place on your device only. The app does not share any data with us or any other third-party. There is one exception to this rule: To display app icons, AppCheck sends the Bundle ID (e.g., com.apple.Music) of all monitored apps to `itunes.apple.com`. +Your data belongs to you. Therefore, monitoring and analysis take place on your device only. The app does not share any data with us or any other third-party. ⒈ Tap the red button in the upper right corner to start the DNS proxy. The proxy is only accessible locally to apps on this device. ⒉ The proxy monitors DNS requests in the background. @@ -41,35 +36,31 @@ Your data belongs to you. Therefore, monitoring and analysis take place on your - - + + - - + + - - + @@ -78,7 +69,7 @@ Your data belongs to you. Therefore, monitoring and analysis take place on your - + @@ -98,34 +89,34 @@ Your data belongs to you. Therefore, monitoring and analysis take place on your - + - + - - + + - + - @@ -140,7 +131,7 @@ Your data belongs to you. Therefore, monitoring and analysis take place on your - + @@ -149,13 +140,13 @@ Your data belongs to you. Therefore, monitoring and analysis take place on your - + - + diff --git a/main/TVCApps.swift b/main/TVCApps.swift deleted file mode 100644 index a0926ed..0000000 --- a/main/TVCApps.swift +++ /dev/null @@ -1,139 +0,0 @@ -import UIKit - - - -class TVCApps: UITableViewController { - - private let appDelegate = UIApplication.shared.delegate as! AppDelegate - private var dataSource: [AppInfoType] = [] - @IBOutlet private var welcomeMessage: UITextView! - - override func viewDidLoad() { - super.viewDidLoad() - self.welcomeMessage.frame.size.height = 0 - AppInfoType.initWorkingDir() - - NotificationCenter.default.addObserver(forName: .init("ChangedStateGlassDNS"), object: nil, queue: OperationQueue.main) { [weak self] notification in -// let stateView = self.navigationItem.rightBarButtonItem?.customView as? ProxyStateView -// stateView?.status = (notification.object as! Bool ? .running : .stopped) -// let active = notification.object as! Bool - self?.changeState(notification.object as! Bool) - } - // pull-to-refresh -// tableView.refreshControl = UIRefreshControl() -// tableView.refreshControl?.addTarget(self, action: #selector(reloadDataSource(_:)), for: .valueChanged) -// performSelector(inBackground: #selector(reloadDataSource(_:)), with: nil) - NotificationCenter.default.addObserver(forName: UIApplication.didBecomeActiveNotification, object: nil, queue: OperationQueue.main) { [weak self] _ in - self?.reloadDataSource(nil) - } -// navigationItem.leftBarButtonItem?.title = "\u{2699}\u{0000FE0E}☰" -// navigationItem.leftBarButtonItem?.setTitleTextAttributes([NSAttributedString.Key.font : UIFont.systemFont(ofSize: 32)], for: .normal) - } - - @IBAction func clickToolbarLeft(_ sender: Any) { - let alert = UIAlertController(title: "Clear results?", - message: "You are about to delete all results that have been logged in the past. Continue?", preferredStyle: .alert) - alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil)) - alert.addAction(UIAlertAction(title: "Delete", style: .destructive, handler: { [weak self] _ in - try? SQLiteDatabase.open(path: DB_PATH).destroyContent() - self?.reloadDataSource(nil) - })) - self.present(alert, animated: true, completion: nil) - } - - @IBAction func clickToolbarRight(_ sender: Any) { - let inactive = (self.navigationItem.rightBarButtonItem?.tag == 0) - let alert = UIAlertController(title: "\(inactive ? "En" : "Dis")able Proxy?", - message: "The DNS proxy is currently \(inactive ? "dis" : "en")abled, do you want to proceed and \(inactive ? "en" : "dis")able logging?", preferredStyle: .alert) - alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil)) - alert.addAction(UIAlertAction(title: inactive ? "Enable" : "Disable", style: .default, handler: { [weak self] _ in - self?.changeState(inactive) - self?.appDelegate.setProxyEnabled(inactive) - })) - self.present(alert, animated: true, completion: nil) - } - - func changeState(_ active: Bool) { - let stateView = self.navigationItem.rightBarButtonItem - if stateView?.tag == 0 && !active, - stateView?.tag == 1 && active { - return // don't need to change, already correct state - } - stateView?.tag = (active ? 1 : 0) - stateView?.title = (active ? "Active" : "Inactive") - stateView?.tintColor = (active ? .systemGreen : .systemRed) -// let newButton = UIBarButtonItem(barButtonSystemItem: (active ? .pause : .play), target: self, action: #selector(clickToolbarRight(_:))) -// newButton.tintColor = (active ? .systemRed : .systemGreen) -// newButton.tag = (active ? 1 : 0) -// self.navigationItem.setRightBarButton(newButton, animated: true) - } - - private func updateCellAt(_ index: Int) { - DispatchQueue.main.async { - guard index >= 0 else { - self.welcomeMessage.frame.size.height = (self.dataSource.count == 0 ? self.view.frame.size.height : 0) - self.tableView.reloadData() - return - } - if let idx = self.tableView.indexPathsForVisibleRows?.first(where: { indexPath -> Bool in - indexPath.row == index - }) { - self.tableView.reloadRows(at: [idx], with: .automatic) - } - } - } - - override func prepare(for segue: UIStoryboardSegue, sender: Any?) { - if let index = tableView.indexPathForSelectedRow?.row { - let info = dataSource[index] - segue.destination.navigationItem.prompt = info.name ?? info.id - (segue.destination as? TVCRequests)?.appBundleId = info.id - } - } - - - // MARK: - Table View Delegate - - override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return dataSource.count - } - - override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = tableView.dequeueReusableCell(withIdentifier: "AppBundleCell")! - let appBundle = dataSource[indexPath.row] - cell.textLabel?.text = appBundle.name ?? appBundle.id - cell.detailTextLabel?.text = appBundle.seller - - cell.imageView?.image = appBundle.getImage() - cell.imageView?.layer.cornerRadius = 6.75 - cell.imageView?.layer.masksToBounds = true - return cell - } - - - // MARK: - Data Source - - @objc private func reloadDataSource(_ sender : Any?) { - DispatchQueue.global().async { - self.dataSource = self.sqliteAppList().map { AppInfoType(id: $0) } // will load from HD - self.updateCellAt(-1) - for i in self.dataSource.indices { - self.dataSource[i].updateIfNeeded { [weak self] in - self?.updateCellAt(i) - } - } - if let refreshControl = sender as? UIRefreshControl { - refreshControl.endRefreshing() - } - } - } - - private func sqliteAppList() -> [String] { -// return ["unkown", "com.apple.Fitness", "com.apple.AppStore", "com.apple.store.Jolly", "com.apple.supportapp", "com.apple.TVRemote", "com.apple.Bridge", "com.apple.calculator", "com.apple.mobilecal", "com.apple.camera", "com.apple.classroom", "com.apple.clips", "com.apple.mobiletimer", "com.apple.compass", "com.apple.MobileAddressBook", "com.apple.facetime", "com.apple.appleseed.FeedbackAssistant", "com.apple.mobileme.fmf1", "com.apple.mobileme.fmip1", "com.apple.findmy", "com.apple.DocumentsApp", "com.apple.gamecenter", "com.apple.mobilegarageband", "com.apple.Health", "com.apple.Antimony", "com.apple.Home", "com.apple.iBooks", "com.apple.iCloudDriveApp", "com.apple.iMovie", "com.apple.itunesconnect.mobile", "com.apple.MobileStore", "com.apple.itunesu", "com.apple.Keynote", "com.apple.musicapps.remote", "com.apple.mobilemail", "com.apple.Maps", "com.apple.measure", "com.apple.MobileSMS", "com.apple.Music", "com.apple.musicmemos", "com.apple.news", "com.apple.mobilenotes", "com.apple.Numbers", "com.apple.Pages", "com.apple.mobilephone", "com.apple.Photo-Booth", "com.apple.mobileslideshow", "com.apple.Playgrounds", "com.apple.podcasts", "com.apple.reminders", "com.apple.Remote", "com.apple.mobilesafari", "com.apple.Preferences", "is.workflow.my.app", "com.apple.shortcuts", "com.apple.SiriViewService", "com.apple.stocks", "com.apple.tips", "com.apple.movietrailers", "com.apple.tv", "com.apple.videos", "com.apple.VoiceMemos", "com.apple.Passbook", "com.apple.weather", "com.apple.wwdc"] -// return ["com.apple.backupd", "com.apple.searchd", "com.apple.SafariBookmarksSyncAgent", "com.apple.AppStore", "com.apple.mobilemail", "com.apple.iBooks", "com.apple.icloud.searchpartyd", "com.apple.ap.adprivacyd", "com.apple.bluetoothd", "com.apple.commcentermobilehelper", "com.apple", "com.apple.coreidv.coreidvd", "com.apple.online-auth-agent", "com.apple.tipsd", "com.apple.wifid", "com.apple.captiveagent", "com.apple.pipelined", "com.apple.bird", "com.apple.amfid", "com.apple.nsurlsessiond", "com.apple.Preferences", "com.apple.sharingd", "com.apple.UserEventAgent", "com.apple.healthappd"] - guard let db = try? SQLiteDatabase.open(path: DB_PATH) else { - return [] - } - return db.appList() - } -} diff --git a/main/TVCDomains.swift b/main/TVCDomains.swift new file mode 100644 index 0000000..b59df2d --- /dev/null +++ b/main/TVCDomains.swift @@ -0,0 +1,138 @@ +import UIKit +import NetworkExtension + + +class TVCDomains: UITableViewController { + + private let appDelegate = UIApplication.shared.delegate as! AppDelegate + private var dataSource: [GroupedDomain] = [] + @IBOutlet private var welcomeMessage: UITextView! + + override func viewDidLoad() { + super.viewDidLoad() + self.welcomeMessage.frame.size.height = 0 +// AppInfoType.initWorkingDir() + + NotificationCenter.default.addObserver(forName: .NEVPNStatusDidChange, object: nil, queue: OperationQueue.main) { [weak self] notification in + self?.changeState((notification.object as? NETunnelProviderSession)?.status ?? .invalid) + } + NotificationCenter.default.addObserver(forName: .init("ChangedStateGlassVPN"), object: nil, queue: OperationQueue.main) { [weak self] notification in + self?.changeState((notification.object as? NEVPNStatus) ?? .invalid) + } + + // pull-to-refresh + tableView.refreshControl = UIRefreshControl() + tableView.refreshControl?.addTarget(self, action: #selector(reloadDataSource(_:)), for: .valueChanged) + performSelector(inBackground: #selector(reloadDataSource(_:)), with: nil) + NotificationCenter.default.addObserver(forName: UIApplication.didBecomeActiveNotification, object: nil, queue: OperationQueue.main) { [weak self] _ in + self?.reloadDataSource(nil) + } +// navigationItem.leftBarButtonItem?.title = "\u{2699}\u{0000FE0E}☰" +// navigationItem.leftBarButtonItem?.setTitleTextAttributes([NSAttributedString.Key.font : UIFont.systemFont(ofSize: 32)], for: .normal) + } + + @IBAction func clickToolbarLeft(_ sender: Any) { + let alert = UIAlertController(title: "Clear results?", + message: "You are about to delete all results that have been logged in the past. Continue?", preferredStyle: .alert) + alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil)) + alert.addAction(UIAlertAction(title: "Delete", style: .destructive, handler: { [weak self] _ in + try? SQLiteDatabase.open(path: DB_PATH).destroyContent() + self?.reloadDataSource(nil) + })) + self.present(alert, animated: true, completion: nil) + } + + @IBAction func clickToolbarRight(_ sender: Any) { + let active = (self.navigationItem.rightBarButtonItem?.tag == NEVPNStatus.connected.rawValue) + let alert = UIAlertController(title: "\(active ? "Dis" : "En")able Proxy?", + message: "The VPN proxy is currently \(active ? "en" : "dis")abled, do you want to proceed and \(active ? "dis" : "en")able logging?", preferredStyle: .alert) + alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil)) + alert.addAction(UIAlertAction(title: active ? "Disable" : "Enable", style: .default, handler: { [weak self] _ in + self?.appDelegate.setProxyEnabled(!active) + })) + self.present(alert, animated: true, completion: nil) + } + + func changeState(_ newState: NEVPNStatus) { + let stateView = self.navigationItem.rightBarButtonItem + if stateView?.tag == newState.rawValue { + return // don't need to change, already correct state + } + stateView?.tag = newState.rawValue + switch newState { + case .connected: + stateView?.title = "Active" + stateView?.tintColor = .systemGreen + case .connecting, .disconnecting, .reasserting: + stateView?.title = "Updating" + stateView?.tintColor = .systemYellow + case .invalid, .disconnected: + fallthrough + @unknown default: + stateView?.title = "Inactive" + stateView?.tintColor = .systemRed + } +// let newButton = UIBarButtonItem(barButtonSystemItem: (active ? .pause : .play), target: self, action: #selector(clickToolbarRight(_:))) +// newButton.tintColor = (active ? .systemRed : .systemGreen) +// newButton.tag = (active ? 1 : 0) +// self.navigationItem.setRightBarButton(newButton, animated: true) + } + + private func updateCellAt(_ index: Int) { + DispatchQueue.main.async { + guard index >= 0 else { + self.welcomeMessage.frame.size.height = (self.dataSource.count == 0 ? self.view.frame.size.height : 0) + self.tableView.reloadData() + return + } + if let idx = self.tableView.indexPathsForVisibleRows?.first(where: { indexPath -> Bool in + indexPath.row == index + }) { + self.tableView.reloadRows(at: [idx], with: .automatic) + } + } + } + + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + if let index = tableView.indexPathForSelectedRow?.row { + let dom = dataSource[index].label + segue.destination.navigationItem.prompt = dom + (segue.destination as? TVCHosts)?.domain = dom + } + } + + + // MARK: - Table View Delegate + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return dataSource.count + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "DomainCell")! + let entry = dataSource[indexPath.row] + let last = Date.init(timeIntervalSince1970: Double(entry.lastModified)) + + cell.textLabel?.text = entry.label + cell.detailTextLabel?.text = "\(dateFormatter.string(from: last)) — \(entry.count)" + return cell + } + + + // MARK: - Data Source + + @objc private func reloadDataSource(_ sender : Any?) { + self.dataSource = self.sqliteAppList() + if let refreshControl = sender as? UIRefreshControl { + DispatchQueue.main.async { refreshControl.endRefreshing() } + } + self.updateCellAt(-1) + } + + private func sqliteAppList() -> [GroupedDomain] { + guard let db = try? SQLiteDatabase.open(path: DB_PATH) else { + return [] + } + return db.domainList() + } +} diff --git a/main/TVCHostDetails.swift b/main/TVCHostDetails.swift new file mode 100644 index 0000000..1a5360b --- /dev/null +++ b/main/TVCHostDetails.swift @@ -0,0 +1,44 @@ +import UIKit + +class TVCHostDetails: UITableViewController { + + public var domain: String? + public var host: String? + private var dataSource: [Timestamp] = [] + + override func viewDidLoad() { + super.viewDidLoad() + // pull-to-refresh + tableView.refreshControl = UIRefreshControl() + tableView.refreshControl?.addTarget(self, action: #selector(reloadDataSource(_:)), for: .valueChanged) + performSelector(inBackground: #selector(reloadDataSource(_:)), with: nil) + NotificationCenter.default.addObserver(forName: UIApplication.didBecomeActiveNotification, object: nil, queue: OperationQueue.main) { [weak self] _ in + self?.reloadDataSource(nil) + } + } + + @objc private func reloadDataSource(_ sender : Any?) { + dataSource = [] + guard let dom = domain, let db = try? SQLiteDatabase.open(path: DB_PATH) else { + return + } + dataSource = db.timesForDomain(dom, host: host) + DispatchQueue.main.async { + if let refreshControl = sender as? UIRefreshControl { + refreshControl.endRefreshing() + } + self.tableView.reloadData() + } + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return dataSource.count + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "HostDetailCell")! + let date = Date.init(timeIntervalSince1970: Double(dataSource[indexPath.row])) + cell.textLabel?.text = dateFormatter.string(from: date) + return cell + } +} diff --git a/main/TVCHosts.swift b/main/TVCHosts.swift new file mode 100644 index 0000000..284cf54 --- /dev/null +++ b/main/TVCHosts.swift @@ -0,0 +1,80 @@ +import UIKit + +class TVCHosts: UITableViewController { + + private var attributedDomain: NSAttributedString = NSAttributedString(string: "") + public var domain: String? { + willSet { + attributedDomain = NSAttributedString(string: ".\(newValue ?? "")", + attributes: [.foregroundColor : UIColor.darkGray]) + } + } + private var dataSource: [GroupedDomain] = [] + + override func viewDidLoad() { + super.viewDidLoad() + // pull-to-refresh + tableView.refreshControl = UIRefreshControl() + tableView.refreshControl?.addTarget(self, action: #selector(reloadDataSource(_:)), for: .valueChanged) + performSelector(inBackground: #selector(reloadDataSource(_:)), with: nil) + NotificationCenter.default.addObserver(forName: UIApplication.didBecomeActiveNotification, object: nil, queue: OperationQueue.main) { [weak self] _ in + self?.reloadDataSource(nil) + } + } + + @objc private func reloadDataSource(_ sender : Any?) { +// dataSource = [("hi", [1, 2]), ("there", [2, 4, 8, 1580472632]), ("dude", [1, 2, 3])] +// return () + dataSource = [] + guard let dom = domain, let db = try? SQLiteDatabase.open(path: DB_PATH) else { + return + } + dataSource = db.hostsForDomain(dom as NSString) + +// var list: [String: [Int64]] = [:] +// db.subdomainsForDomain(appIdentifier: dom as NSString) { query in +//// let x = query.dns.split(separator: ".").reversed().joined(separator: ".") +// let x = query.host ?? "" +// if list[x] == nil { +// list[x] = [] +// } +// list[x]?.append(query.ts) +// } +// dataSource = list.sorted{ $0.0 < $1.0 } + DispatchQueue.main.async { + if let refreshControl = sender as? UIRefreshControl { + refreshControl.endRefreshing() + } + self.tableView.reloadData() + } + } + + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + if let index = tableView.indexPathForSelectedRow?.row { + + let entry = dataSource[index] + segue.destination.navigationItem.prompt = "\(entry.label).\(domain ?? "")" + let vc = (segue.destination as? TVCHostDetails) + vc?.domain = domain + vc?.host = entry.label + } + } + + // MARK: - Data Source + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return dataSource.count + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "HostCell")! + let entry = dataSource[indexPath.row] + let last = Date.init(timeIntervalSince1970: Double(entry.lastModified)) + let x = NSMutableAttributedString(string: entry.label) + x.append(attributedDomain) + cell.textLabel?.attributedText = x +// cell.textLabel?.text = "\(entry.label).\(domain ?? "")" + cell.detailTextLabel?.text = "\(dateFormatter.string(from: last)) — \(entry.count)" + return cell + } +} diff --git a/main/TVCRequestDetails.swift b/main/TVCRequestDetails.swift deleted file mode 100644 index 1bb3838..0000000 --- a/main/TVCRequestDetails.swift +++ /dev/null @@ -1,24 +0,0 @@ -import UIKit - -class TVCRequestDetails: UITableViewController { - - public var dataSource: [Int64] = [] - private let dateFormatter = DateFormatter() - - override func viewDidLoad() { - super.viewDidLoad() - dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss" - } - - override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return dataSource.count - } - - override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = tableView.dequeueReusableCell(withIdentifier: "RequestDetailCell")! - let intVal = dataSource[indexPath.row] - let date = Date.init(timeIntervalSince1970: Double(intVal)) - cell.textLabel?.text = dateFormatter.string(from: date) - return cell - } -} diff --git a/main/TVCRequests.swift b/main/TVCRequests.swift deleted file mode 100644 index e307260..0000000 --- a/main/TVCRequests.swift +++ /dev/null @@ -1,64 +0,0 @@ -import UIKit - -class TVCRequests: UITableViewController { - - public var appBundleId: String? = nil - private var dataSource: [(String, [Int64])] = [] - - override func viewDidLoad() { - super.viewDidLoad() - // pull-to-refresh - tableView.refreshControl = UIRefreshControl() - tableView.refreshControl?.addTarget(self, action: #selector(reloadDataSource(_:)), for: .valueChanged) - performSelector(inBackground: #selector(reloadDataSource(_:)), with: nil) - NotificationCenter.default.addObserver(forName: UIApplication.didBecomeActiveNotification, object: nil, queue: OperationQueue.main) { [weak self] _ in - self?.reloadDataSource(nil) - } - } - - @objc private func reloadDataSource(_ sender : Any?) { -// dataSource = [("hi", [1, 2]), ("there", [2, 4, 8, 1580472632]), ("dude", [1, 2, 3])] -// return () - dataSource = [] - guard let bundleId = appBundleId, let db = try? SQLiteDatabase.open(path: DB_PATH) else { - return - } - var list: [String: [Int64]] = [:] - db.dnsQueriesForApp(appIdentifier: bundleId as NSString) { query in - let x = query.dns.split(separator: ".").reversed().joined(separator: ".") - if list[x] == nil { - list[x] = [] - } - list[x]?.append(query.ts) - } - dataSource = list.sorted{ $0.0 < $1.0 } - DispatchQueue.main.async { - self.tableView.reloadData() - if let refreshControl = sender as? UIRefreshControl { - refreshControl.endRefreshing() - } - } - } - - override func prepare(for segue: UIStoryboardSegue, sender: Any?) { - if let index = tableView.indexPathForSelectedRow?.row { - let info = dataSource[index] - segue.destination.navigationItem.prompt = info.0 - (segue.destination as? TVCRequestDetails)?.dataSource = info.1 - } - } - - // MARK: - Data Source - - override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return dataSource.count - } - - override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = tableView.dequeueReusableCell(withIdentifier: "RequestCell")! - let info = dataSource[indexPath.row] - cell.textLabel?.text = info.0 - cell.detailTextLabel?.text = "\(info.1.count)" - return cell - } -} diff --git a/main/main.entitlements b/main/main.entitlements index 4a8655d..91ecf65 100644 --- a/main/main.entitlements +++ b/main/main.entitlements @@ -4,7 +4,7 @@ com.apple.developer.networking.networkextension - dns-proxy + packet-tunnel-provider com.apple.developer.networking.vpn.api