From 017aa891ec216bb466f86a627c80e62e1ade1b39 Mon Sep 17 00:00:00 2001 From: relikd Date: Fri, 7 Feb 2020 16:08:57 +0100 Subject: [PATCH] Initial Commit --- AppCheck.xcodeproj/project.pbxproj | 611 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/swiftpm/Package.resolved | 16 + .../xcshareddata/xcschemes/AppCheck.xcscheme | 77 +++ .../xcshareddata/xcschemes/GlassDNS.xcscheme | 96 +++ GlassDNS/DNSProxyProvider.swift | 210 ++++++ GlassDNS/GlassDNS.entitlements | 18 + GlassDNS/Info.plist | 31 + GlassTunnel-old/GlassTunnel.entitlements | 14 + GlassTunnel-old/Info.plist | 31 + GlassTunnel-old/PacketTunnelProvider.swift | 57 ++ SQDB.swift | 173 +++++ main/AppDelegate.swift | 261 ++++++++ main/AppInfoType.swift | 129 ++++ .../AppIcon.appiconset/Artwork.png | Bin 0 -> 8266 bytes .../AppIcon.appiconset/Contents.json | 116 ++++ .../AppIcon.appiconset/icon_120x120.png | Bin 0 -> 1148 bytes .../AppIcon.appiconset/icon_152x152.png | Bin 0 -> 1517 bytes .../AppIcon.appiconset/icon_167x167.png | Bin 0 -> 1623 bytes .../AppIcon.appiconset/icon_180x180.png | Bin 0 -> 1730 bytes .../AppIcon.appiconset/icon_20x20.png | Bin 0 -> 258 bytes .../AppIcon.appiconset/icon_29x29.png | Bin 0 -> 369 bytes .../AppIcon.appiconset/icon_40x40.png | Bin 0 -> 452 bytes .../AppIcon.appiconset/icon_58x58.png | Bin 0 -> 593 bytes .../AppIcon.appiconset/icon_60x60.png | Bin 0 -> 636 bytes .../AppIcon.appiconset/icon_76x76.png | Bin 0 -> 779 bytes .../AppIcon.appiconset/icon_80x80.png | Bin 0 -> 774 bytes .../AppIcon.appiconset/icon_87x87.png | Bin 0 -> 837 bytes main/Assets.xcassets/Contents.json | 6 + main/Base.lproj/LaunchScreen.storyboard | 36 ++ main/Base.lproj/Main.storyboard | 204 ++++++ main/BundleIcon.swift | 78 +++ main/Info.plist | 45 ++ main/LaunchIcon.png | Bin 0 -> 1398 bytes main/ProxyState.swift | 23 + main/Settings.bundle/Root.plist | 31 + main/Settings.bundle/en.lproj/Root.strings | Bin 0 -> 472 bytes main/TVCApps.swift | 139 ++++ main/TVCRequestDetails.swift | 24 + main/TVCRequests.swift | 64 ++ main/main.entitlements | 18 + 42 files changed, 2523 insertions(+) create mode 100644 AppCheck.xcodeproj/project.pbxproj create mode 100644 AppCheck.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 AppCheck.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 AppCheck.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved create mode 100644 AppCheck.xcodeproj/xcshareddata/xcschemes/AppCheck.xcscheme create mode 100644 AppCheck.xcodeproj/xcshareddata/xcschemes/GlassDNS.xcscheme create mode 100644 GlassDNS/DNSProxyProvider.swift create mode 100644 GlassDNS/GlassDNS.entitlements create mode 100644 GlassDNS/Info.plist create mode 100644 GlassTunnel-old/GlassTunnel.entitlements create mode 100644 GlassTunnel-old/Info.plist create mode 100644 GlassTunnel-old/PacketTunnelProvider.swift create mode 100644 SQDB.swift create mode 100644 main/AppDelegate.swift create mode 100644 main/AppInfoType.swift create mode 100644 main/Assets.xcassets/AppIcon.appiconset/Artwork.png create mode 100644 main/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 main/Assets.xcassets/AppIcon.appiconset/icon_120x120.png create mode 100644 main/Assets.xcassets/AppIcon.appiconset/icon_152x152.png create mode 100644 main/Assets.xcassets/AppIcon.appiconset/icon_167x167.png create mode 100644 main/Assets.xcassets/AppIcon.appiconset/icon_180x180.png create mode 100644 main/Assets.xcassets/AppIcon.appiconset/icon_20x20.png create mode 100644 main/Assets.xcassets/AppIcon.appiconset/icon_29x29.png create mode 100644 main/Assets.xcassets/AppIcon.appiconset/icon_40x40.png create mode 100644 main/Assets.xcassets/AppIcon.appiconset/icon_58x58.png create mode 100644 main/Assets.xcassets/AppIcon.appiconset/icon_60x60.png create mode 100644 main/Assets.xcassets/AppIcon.appiconset/icon_76x76.png create mode 100644 main/Assets.xcassets/AppIcon.appiconset/icon_80x80.png create mode 100644 main/Assets.xcassets/AppIcon.appiconset/icon_87x87.png create mode 100644 main/Assets.xcassets/Contents.json create mode 100644 main/Base.lproj/LaunchScreen.storyboard create mode 100644 main/Base.lproj/Main.storyboard create mode 100644 main/BundleIcon.swift create mode 100644 main/Info.plist create mode 100644 main/LaunchIcon.png create mode 100644 main/ProxyState.swift create mode 100644 main/Settings.bundle/Root.plist create mode 100644 main/Settings.bundle/en.lproj/Root.strings create mode 100644 main/TVCApps.swift create mode 100644 main/TVCRequestDetails.swift create mode 100644 main/TVCRequests.swift create mode 100644 main/main.entitlements diff --git a/AppCheck.xcodeproj/project.pbxproj b/AppCheck.xcodeproj/project.pbxproj new file mode 100644 index 0000000..fe95774 --- /dev/null +++ b/AppCheck.xcodeproj/project.pbxproj @@ -0,0 +1,611 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 52; + objects = { + +/* Begin PBXBuildFile section */ + 541A957623E602DF00C09C19 /* LaunchIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = 541A957523E602DF00C09C19 /* LaunchIcon.png */; }; + 541AC5D82399498A00A769D7 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 541AC5D72399498A00A769D7 /* AppDelegate.swift */; }; + 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, ); }; }; + 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 */; }; + 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 */ = { + isa = PBXContainerItemProxy; + containerPortal = 541AC5CC2399498A00A769D7 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 548B1F8723D3374F005B047C; + remoteInfo = DNS; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 541AC5FD239949BE00A769D7 /* Embed App Extensions */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 13; + files = ( + 548B1F9023D3374F005B047C /* GlassDNS.appex in Embed App Extensions */, + ); + name = "Embed App Extensions"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 541A957523E602DF00C09C19 /* LaunchIcon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = LaunchIcon.png; sourceTree = ""; }; + 541AC5D42399498A00A769D7 /* AppCheck.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AppCheck.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 541AC5D72399498A00A769D7 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 541AC5DC2399498A00A769D7 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 541AC5DE2399498B00A769D7 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 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 = ""; }; + 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 = ""; }; + 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 = ""; }; + 54C056DC23E9EEF700214A3F /* BundleIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BundleIcon.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 541AC5D12399498A00A769D7 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 54B7560123D4EFDC008F0C41 /* DNS in Frameworks */, + 541AC5EB2399499A00A769D7 /* NetworkExtension.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 548B1F8523D3374F005B047C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 54B7560323D4F5BC008F0C41 /* DNS in Frameworks */, + 5433233423ECD3F5007C8052 /* NetworkExtension.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 541AC5CB2399498A00A769D7 = { + isa = PBXGroup; + children = ( + 54B7562223D7B2DC008F0C41 /* SQDB.swift */, + 541AC5D62399498A00A769D7 /* main */, + 548B1F8923D3374F005B047C /* GlassDNS */, + 541AC5D52399498A00A769D7 /* Products */, + 541AC5E92399499A00A769D7 /* Frameworks */, + ); + sourceTree = ""; + }; + 541AC5D52399498A00A769D7 /* Products */ = { + isa = PBXGroup; + children = ( + 541AC5D42399498A00A769D7 /* AppCheck.app */, + 548B1F8823D3374F005B047C /* GlassDNS.appex */, + ); + name = Products; + sourceTree = ""; + }; + 541AC5D62399498A00A769D7 /* main */ = { + isa = PBXGroup; + children = ( + 548B1F9423D338EC005B047C /* main.entitlements */, + 541AC5D72399498A00A769D7 /* AppDelegate.swift */, + 54C056DC23E9EEF700214A3F /* BundleIcon.swift */, + 54C056DA23E9E36E00214A3F /* AppInfoType.swift */, + 54953E5E23DEBE840054345C /* TVCApps.swift */, + 54953E6023E0D69A0054345C /* TVCRequests.swift */, + 54953E6E23E44CD00054345C /* TVCRequestDetails.swift */, + 541AC5DB2399498A00A769D7 /* Main.storyboard */, + 541A957523E602DF00C09C19 /* LaunchIcon.png */, + 541AC5E02399498B00A769D7 /* LaunchScreen.storyboard */, + 541AC5DE2399498B00A769D7 /* Assets.xcassets */, + 541AC5E32399498B00A769D7 /* Info.plist */, + 54953E7023E473F10054345C /* Settings.bundle */, + ); + path = main; + sourceTree = ""; + }; + 541AC5E92399499A00A769D7 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 541AC5EA2399499A00A769D7 /* NetworkExtension.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 548B1F8923D3374F005B047C /* GlassDNS */ = { + isa = PBXGroup; + children = ( + 548B1F8A23D3374F005B047C /* DNSProxyProvider.swift */, + 548B1F8C23D3374F005B047C /* Info.plist */, + 548B1F8D23D3374F005B047C /* GlassDNS.entitlements */, + ); + path = GlassDNS; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 541AC5D32399498A00A769D7 /* AppCheck */ = { + isa = PBXNativeTarget; + buildConfigurationList = 541AC5E62399498B00A769D7 /* Build configuration list for PBXNativeTarget "AppCheck" */; + buildPhases = ( + 541AC5D02399498A00A769D7 /* Sources */, + 541AC5D12399498A00A769D7 /* Frameworks */, + 541AC5D22399498A00A769D7 /* Resources */, + 541AC5FD239949BE00A769D7 /* Embed App Extensions */, + 54C056E023EAFBDF00214A3F /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + 548B1F8F23D3374F005B047C /* PBXTargetDependency */, + ); + name = AppCheck; + packageProductDependencies = ( + 54B7560023D4EFDC008F0C41 /* DNS */, + ); + productName = PrivacyScore; + productReference = 541AC5D42399498A00A769D7 /* AppCheck.app */; + productType = "com.apple.product-type.application"; + }; + 548B1F8723D3374F005B047C /* GlassDNS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 548B1F9123D3374F005B047C /* Build configuration list for PBXNativeTarget "GlassDNS" */; + buildPhases = ( + 548B1F8423D3374F005B047C /* Sources */, + 548B1F8523D3374F005B047C /* Frameworks */, + 548B1F8623D3374F005B047C /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = GlassDNS; + packageProductDependencies = ( + 54B7560223D4F5BC008F0C41 /* DNS */, + ); + productName = DNS; + productReference = 548B1F8823D3374F005B047C /* GlassDNS.appex */; + productType = "com.apple.product-type.app-extension"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 541AC5CC2399498A00A769D7 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1130; + LastUpgradeCheck = 1010; + ORGANIZATIONNAME = relikd; + TargetAttributes = { + 541AC5D32399498A00A769D7 = { + CreatedOnToolsVersion = 10.1; + LastSwiftMigration = 1130; + SystemCapabilities = { + com.apple.NetworkExtensions.iOS = { + enabled = 1; + }; + }; + }; + 548B1F8723D3374F005B047C = { + CreatedOnToolsVersion = 11.3.1; + }; + }; + }; + buildConfigurationList = 541AC5CF2399498A00A769D7 /* Build configuration list for PBXProject "AppCheck" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 541AC5CB2399498A00A769D7; + packageReferences = ( + 54B755FF23D4EFDC008F0C41 /* XCRemoteSwiftPackageReference "DNS" */, + ); + productRefGroup = 541AC5D52399498A00A769D7 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 541AC5D32399498A00A769D7 /* AppCheck */, + 548B1F8723D3374F005B047C /* GlassDNS */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 541AC5D22399498A00A769D7 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 54953E7123E473F10054345C /* Settings.bundle in Resources */, + 541AC5E22399498B00A769D7 /* LaunchScreen.storyboard in Resources */, + 541AC5DF2399498B00A769D7 /* Assets.xcassets in Resources */, + 541AC5DD2399498A00A769D7 /* Main.storyboard in Resources */, + 541A957623E602DF00C09C19 /* LaunchIcon.png in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 548B1F8623D3374F005B047C /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 54C056E023EAFBDF00214A3F /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "#agvtool next-version -all\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 541AC5D02399498A00A769D7 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 54953E3323DC752E0054345C /* SQDB.swift in Sources */, + 54953E6123E0D69A0054345C /* TVCRequests.swift in Sources */, + 54953E5F23DEBE840054345C /* TVCApps.swift in Sources */, + 54C056DB23E9E36E00214A3F /* AppInfoType.swift in Sources */, + 54953E6F23E44CD00054345C /* TVCRequestDetails.swift in Sources */, + 54C056DD23E9EEF700214A3F /* BundleIcon.swift in Sources */, + 541AC5D82399498A00A769D7 /* AppDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 548B1F8423D3374F005B047C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 548B1F8B23D3374F005B047C /* DNSProxyProvider.swift in Sources */, + 54953E3423DC752F0054345C /* SQDB.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 548B1F8F23D3374F005B047C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 548B1F8723D3374F005B047C /* GlassDNS */; + targetProxy = 548B1F8E23D3374F005B047C /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 541AC5DB2399498A00A769D7 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 541AC5DC2399498A00A769D7 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 541AC5E02399498B00A769D7 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 541AC5E12399498B00A769D7 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 541AC5E42399498B00A769D7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = MY7CB4555Z; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 541AC5E52399498B00A769D7 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = MY7CB4555Z; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 541AC5E72399498B00A769D7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = main/main.entitlements; + CURRENT_PROJECT_VERSION = 7; + INFOPLIST_FILE = main/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0.0; + PRODUCT_BUNDLE_IDENTIFIER = "de.uni-bamberg.psi.AppCheck"; + PRODUCT_NAME = AppCheck; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; + }; + name = Debug; + }; + 541AC5E82399498B00A769D7 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = main/main.entitlements; + CURRENT_PROJECT_VERSION = 7; + INFOPLIST_FILE = main/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0.0; + PRODUCT_BUNDLE_IDENTIFIER = "de.uni-bamberg.psi.AppCheck"; + PRODUCT_NAME = AppCheck; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; + }; + name = Release; + }; + 548B1F9223D3374F005B047C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = GlassDNS/GlassDNS.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 7; + DEVELOPMENT_TEAM = MY7CB4555Z; + INFOPLIST_FILE = GlassDNS/Info.plist; + 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 = ""; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; + }; + name = Debug; + }; + 548B1F9323D3374F005B047C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = GlassDNS/GlassDNS.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 7; + DEVELOPMENT_TEAM = MY7CB4555Z; + INFOPLIST_FILE = GlassDNS/Info.plist; + 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 = ""; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 541AC5CF2399498A00A769D7 /* Build configuration list for PBXProject "AppCheck" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 541AC5E42399498B00A769D7 /* Debug */, + 541AC5E52399498B00A769D7 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 541AC5E62399498B00A769D7 /* Build configuration list for PBXNativeTarget "AppCheck" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 541AC5E72399498B00A769D7 /* Debug */, + 541AC5E82399498B00A769D7 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 548B1F9123D3374F005B047C /* Build configuration list for PBXNativeTarget "GlassDNS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 548B1F9223D3374F005B047C /* Debug */, + 548B1F9323D3374F005B047C /* 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/contents.xcworkspacedata b/AppCheck.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/AppCheck.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/AppCheck.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/AppCheck.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/AppCheck.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/AppCheck.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/AppCheck.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..6fe03c5 --- /dev/null +++ b/AppCheck.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,16 @@ +{ + "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/AppCheck.xcscheme b/AppCheck.xcodeproj/xcshareddata/xcschemes/AppCheck.xcscheme new file mode 100644 index 0000000..2dd6982 --- /dev/null +++ b/AppCheck.xcodeproj/xcshareddata/xcschemes/AppCheck.xcscheme @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AppCheck.xcodeproj/xcshareddata/xcschemes/GlassDNS.xcscheme b/AppCheck.xcodeproj/xcshareddata/xcschemes/GlassDNS.xcscheme new file mode 100644 index 0000000..ba7377a --- /dev/null +++ b/AppCheck.xcodeproj/xcshareddata/xcschemes/GlassDNS.xcscheme @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/GlassDNS/DNSProxyProvider.swift b/GlassDNS/DNSProxyProvider.swift new file mode 100644 index 0000000..59747f5 --- /dev/null +++ b/GlassDNS/DNSProxyProvider.swift @@ -0,0 +1,210 @@ +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/GlassDNS/GlassDNS.entitlements new file mode 100644 index 0000000..4a8655d --- /dev/null +++ b/GlassDNS/GlassDNS.entitlements @@ -0,0 +1,18 @@ + + + + + com.apple.developer.networking.networkextension + + dns-proxy + + com.apple.developer.networking.vpn.api + + allow-vpn + + com.apple.security.application-groups + + group.de.uni-bamberg.psi.AppCheck + + + diff --git a/GlassDNS/Info.plist b/GlassDNS/Info.plist new file mode 100644 index 0000000..d15f797 --- /dev/null +++ b/GlassDNS/Info.plist @@ -0,0 +1,31 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + GlassDNS + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + $(MARKETING_VERSION) + CFBundleVersion + 7 + NSExtension + + NSExtensionPointIdentifier + com.apple.networkextension.dns-proxy + NSExtensionPrincipalClass + $(PRODUCT_MODULE_NAME).DNSProxyProvider + + + diff --git a/GlassTunnel-old/GlassTunnel.entitlements b/GlassTunnel-old/GlassTunnel.entitlements new file mode 100644 index 0000000..2ddf77d --- /dev/null +++ b/GlassTunnel-old/GlassTunnel.entitlements @@ -0,0 +1,14 @@ + + + + + com.apple.developer.networking.networkextension + + packet-tunnel-provider + + com.apple.security.application-groups + + group.it.h4q.psi.PrivacyScore + + + diff --git a/GlassTunnel-old/Info.plist b/GlassTunnel-old/Info.plist new file mode 100644 index 0000000..2280eac --- /dev/null +++ b/GlassTunnel-old/Info.plist @@ -0,0 +1,31 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Tunnel + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + NSExtension + + NSExtensionPointIdentifier + com.apple.networkextension.packet-tunnel + NSExtensionPrincipalClass + $(PRODUCT_MODULE_NAME).PacketTunnelProvider + + + diff --git a/GlassTunnel-old/PacketTunnelProvider.swift b/GlassTunnel-old/PacketTunnelProvider.swift new file mode 100644 index 0000000..0d78264 --- /dev/null +++ b/GlassTunnel-old/PacketTunnelProvider.swift @@ -0,0 +1,57 @@ +import NetworkExtension + +class PacketTunnelProvider: NEPacketTunnelProvider { + + override func startTunnel(options: [String : NSObject]?, completionHandler: @escaping (Error?) -> Void) { + NSLog("TUN: startTunnel") +// let endpoint = NWHostEndpoint(hostname:"127.0.0.1", port:"4000") +// self.createTCPConnection(to: endpoint, enableTLS: false, tlsParameters: nil, delegate: nil) + completionHandler(nil) + /* + let settings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "127.0.0.1") + let ip4set = NEIPv4Settings(addresses: ["127.0.0.1"], subnetMasks: ["255.255.255.0"]) + let defaultRoute = NEIPv4Route.default() + let localRoute = NEIPv4Route(destinationAddress: "192.168.2.1", subnetMask: "255.255.255.0") + ip4set.includedRoutes = [defaultRoute, localRoute] + ip4set.excludedRoutes = [] + settings.ipv4Settings = ip4set +// settings.mtu = 1500 + settings.dnsSettings = NEDNSSettings(servers: ["8.8.8.8"]) + settings.tunnelOverheadBytes = 150 + + self.setTunnelNetworkSettings(settings) { error in + guard error == nil else { + NSLog("setTunnelNetworkSettings error: \(String(describing: error))") + return + } + NSLog("setTunnelNetworkSettings success") + completionHandler(nil) + }*/ + } + + override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) { + NSLog("TUN: stopTunnel") + completionHandler() + } + + override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)?) { + NSLog("TUN: handleAppMessage") + if let handler = completionHandler { + handler(messageData) + } + } + + override func sleep(completionHandler: @escaping () -> Void) { + NSLog("TUN: sleep") + completionHandler() + } + + override func wake() { + NSLog("TUN: wake") + } + + override func createUDPSessionThroughTunnel(to remoteEndpoint: NWEndpoint, from localEndpoint: NWHostEndpoint?) -> NWUDPSession { + NSLog("TUN: createUDP") + return createUDPSession(to: remoteEndpoint, from: localEndpoint) + } +} diff --git a/SQDB.swift b/SQDB.swift new file mode 100644 index 0000000..6c5e21a --- /dev/null +++ b/SQDB.swift @@ -0,0 +1,173 @@ +import Foundation +import SQLite3 + +//let basePath = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true) +let basePath = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.de.uni-bamberg.psi.AppCheck") +public let DB_PATH = basePath!.appendingPathComponent("dnslog.sqlite").relativePath + +enum SQLiteError: Error { + case OpenDatabase(message: String) + case Prepare(message: String) + case Step(message: String) + case Bind(message: String) +} + +//: ## The Database Connection +class SQLiteDatabase { + private let dbPointer: OpaquePointer? + private init(dbPointer: OpaquePointer?) { + self.dbPointer = dbPointer + } + + fileprivate var errorMessage: String { + if let errorPointer = sqlite3_errmsg(dbPointer) { + let errorMessage = String(cString: errorPointer) + return errorMessage + } else { + return "No error message provided from sqlite." + } + } + + deinit { + sqlite3_close(dbPointer) +// SQLiteDatabase.destroyDatabase(path: DB_PATH) + } + + static func destroyDatabase(path: String) { + do { + if FileManager.default.fileExists(atPath: path) { + try FileManager.default.removeItem(atPath: path) + } + } catch { + print("Could not destroy database file: \(path)") + } + } + + func destroyContent() throws { + let deleteStatement = try prepareStatement(sql: "DELETE FROM req;") + defer { + sqlite3_finalize(deleteStatement) + } + guard sqlite3_step(deleteStatement) == SQLITE_DONE else { + throw SQLiteError.Step(message: errorMessage) + } + } + + static func open(path: String) throws -> SQLiteDatabase { + var db: OpaquePointer? + //sqlite3_open_v2(path, &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_SHAREDCACHE, nil) + if sqlite3_open(path, &db) == SQLITE_OK { + return SQLiteDatabase(dbPointer: db) + } else { + defer { + if db != nil { + sqlite3_close(db) + } + } + if let errorPointer = sqlite3_errmsg(db) { + let message = String(cString: errorPointer) + throw SQLiteError.OpenDatabase(message: message) + } else { + throw SQLiteError.OpenDatabase(message: "No error message provided from sqlite.") + } + } + } + + func prepareStatement(sql: String) throws -> OpaquePointer? { + var statement: OpaquePointer? + guard sqlite3_prepare_v2(dbPointer, sql, -1, &statement, nil) == SQLITE_OK else { + throw SQLiteError.Prepare(message: errorMessage) + } + return statement + } + + func createTable(table: SQLTable.Type) throws { + let createTableStatement = try prepareStatement(sql: table.createStatement) + defer { + sqlite3_finalize(createTableStatement) + } + guard sqlite3_step(createTableStatement) == SQLITE_DONE else { + throw SQLiteError.Step(message: errorMessage) + } + } +} + +protocol SQLTable { + static var createStatement: String { get } +} + +struct DNSQuery: SQLTable { + let app: String + let dns: String + let ts: Int64 + static var createStatement: String { + return """ + CREATE TABLE IF NOT EXISTS req( + app VARCHAR(255), + dns VARCHAR(2047), + ts BIGINT DEFAULT (strftime('%s','now')) + ); + """ + } +} + +extension SQLiteDatabase { + + func insertDNSQuery(appId: NSString, dnsQuery: NSString) throws { + let insertSql = "INSERT INTO req (app, dns) 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 + else { + throw SQLiteError.Bind(message: errorMessage) + } + guard sqlite3_step(insertStatement) == SQLITE_DONE else { + throw SQLiteError.Step(message: errorMessage) + } + } + + 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;" + guard let queryStatement = try? prepareStatement(sql: querySql) else { + print("Error preparing statement for insert") + return [] + } + defer { + sqlite3_finalize(queryStatement) + } + var res: [String] = [] + while (sqlite3_step(queryStatement) == SQLITE_ROW) { + let appId = sqlite3_column_text(queryStatement, 0) + res.append(String(cString: appId!)) + } + return res + } +} diff --git a/main/AppDelegate.swift b/main/AppDelegate.swift new file mode 100644 index 0000000..8a963e8 --- /dev/null +++ b/main/AppDelegate.swift @@ -0,0 +1,261 @@ +import UIKit +import NetworkExtension + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + + 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") + SQLiteDatabase.destroyDatabase(path: DB_PATH) + } + do { + let db = try SQLiteDatabase.open(path: DB_PATH) + try db.createTable(table: DNSQuery.self) + } catch {} + +// loadVPN { self.startVPN() } + 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() + } + + func setProxyEnabled(_ newState: Bool) { + // DNS: + if newState != managerDNS.isEnabled { + newState ? enableDNS() : disableDNS() + } + // 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 } + } + } + } + + private func postDNSState() { + managerDNS.loadFromPreferences {_ in + NotificationCenter.default.post(name: .init("ChangedStateGlassDNS"), object: self.managerDNS.isEnabled) + } + } + + + // MARK: VPN + + /*var managerVPN: NETunnelProviderManager? + + private func loadVPN(_ finally: @escaping () -> 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 { + return + } + body() + self.managerVPN?.saveToPreferences { (error) in + guard error == nil else { + NSLog("VPN: save error: \(String(describing: error))") + 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() + } + } + + /// 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) + } + 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 + } + + /// 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) + } + } +} +*/ diff --git a/main/AppInfoType.swift b/main/AppInfoType.swift new file mode 100644 index 0000000..10b0a66 --- /dev/null +++ b/main/AppInfoType.swift @@ -0,0 +1,129 @@ +import Foundation +import UIKit + +private let fm = FileManager.default +private let documentsDir = try! fm.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true) +private let bundleInfoDir = documentsDir.appendingPathComponent("bundleInfo", isDirectory:true) + + +struct AppInfoType : Decodable { + var id: String + var name: String? + var seller: String? + var imageURL: URL? + private var remoteImgURL: String? + private var cache: Bool? + private let localJSON: URL + private let localImgURL: URL + + static func initWorkingDir() { + try? fm.createDirectory(at: bundleInfoDir, withIntermediateDirectories: true, attributes: nil) +// print("init dir: \(bundleInfoDir)") + } + + init(id: String) { + self.id = id + if id == "" { + name = "–?–" + cache = true + localJSON = URL(fileURLWithPath: "") + localImgURL = localJSON + } else { + localJSON = bundleInfoDir.appendingPathComponent("\(id).json") + localImgURL = bundleInfoDir.appendingPathComponent("\(id).img") + reload() + } + } + + mutating func reload() { + if fm.fileExists(atPath: localImgURL.path) { + imageURL = localImgURL + } + guard name == nil, seller == nil, + fm.fileExists(atPath: localJSON.path), + let attr = try? fm.attributesOfItem(atPath: localJSON.path), + attr[FileAttributeKey.size] as! UInt64 > 0 else + { + // process json only if attributes not set yet, + // OR json doesn't exist, OR json is empty + return + } + (name, seller, remoteImgURL) = parseJSON(localJSON) + + if remoteImgURL == nil || imageURL != nil { + cache = true + } + } + + func getImage() -> UIImage? { + if let img = imageURL, let data = try? Data(contentsOf: img) { + return UIImage(data: data, scale: 2.0) + } else if id.hasPrefix("com.apple.") { + return appIconApple + } else { + return appIconUnknown + } + } + + private func parseJSON(_ location: URL) -> (name: String?, seller: String?, image: String?) { + do { + let data = try Data.init(contentsOf: location) + if + let json = try JSONSerialization.jsonObject(with: data, options: .fragmentsAllowed) as? [String: Any], + let resAll = json["results"] as? [Any], + let res = resAll.first as? [String: Any] + { + let name = res["trackName"] as? String // trackCensoredName + let seller = res["sellerName"] as? String // artistName + let image = res["artworkUrl60"] as? String // artworkUrl100 + return (name, seller, image) + } else if id.hasPrefix("com.apple.") { + return (String(id.dropFirst(10)), "Apple Inc.", nil) + } + } catch {} + return (nil, nil, nil) + } + + mutating func updateIfNeeded(_ updateClosure: () -> Void) { + guard cache == nil, + let safeId = id.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else { + return + } + cache = false // meaning: hasn't downloaded yet, but is about to do +// print("downloading \(id)") + _ = downloadURL("https://itunes.apple.com/lookup?bundleId=\(safeId)", toFile: localJSON).flatMap{ +// print("downloading \(id) done.") + reload() + updateClosure() + return downloadURL(remoteImgURL, toFile: localImgURL) + }.map{ +// print("downloading \(id) image done.") + reload() + updateClosure() + } + } + + enum NetworkError: Error { + case url + } + + private func downloadURL(_ urlStr: String?, toFile: URL) -> Result { + guard let urlStr = urlStr, let url = URL(string: urlStr) else { + return .failure(NetworkError.url) + } + var result: Result! + let semaphore = DispatchSemaphore(value: 0) + URLSession.shared.downloadTask(with: url) { location, response, error in + if let loc = location { + try? fm.removeItem(at: toFile) + try? fm.moveItem(at: loc, to: toFile) + result = .success(()) + } else { + result = .failure(error!) + } + semaphore.signal() + }.resume() + _ = semaphore.wait(wallTimeout: .distantFuture) + return result + } +} diff --git a/main/Assets.xcassets/AppIcon.appiconset/Artwork.png b/main/Assets.xcassets/AppIcon.appiconset/Artwork.png new file mode 100644 index 0000000000000000000000000000000000000000..8aa3185a126b2ff59a60df2b2f08032ebe6eb41f GIT binary patch literal 8266 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 literal 0 HcmV?d00001 diff --git a/main/Assets.xcassets/AppIcon.appiconset/Contents.json b/main/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..5051a06 --- /dev/null +++ b/main/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,116 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "icon_40x40.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "icon_60x60.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "icon_58x58.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "icon_87x87.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "icon_80x80.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "icon_120x120.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "icon_120x120.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "icon_180x180.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "icon_20x20.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "icon_40x40.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "icon_29x29.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "icon_58x58.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "icon_40x40.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "icon_80x80.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "icon_76x76.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "icon_152x152.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "icon_167x167.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Artwork.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/main/Assets.xcassets/AppIcon.appiconset/icon_120x120.png b/main/Assets.xcassets/AppIcon.appiconset/icon_120x120.png new file mode 100644 index 0000000000000000000000000000000000000000..ff478146dbee046686261644fb13e9a2b48f5757 GIT binary patch literal 1148 zcmeAS@N?(olHy`uVBq!ia0y~yV5k6L4h9AW1{wZLJ_ZIBXHOT$kP5~(GnpsE+RL&|~lvR}4uo4WqDMp5@<7Uw{2-@Aqd%#JSQp9k;ns;<&Z)n(CAvMUESP@7-&5y1~@{ zV^Z7a+qqL2=YL4*T7CRPgS^sH5&QoEUOE2%x_gCcV%}TEdv@kKW0_}1kAF(4K3Mej9+>v-Y=_{+u;Qfo z7g)PhZhf6PM?%m%OUZHXd}mRfSF6+SFFoRTq+I2;CvWz_2by1Vq`i9L_IxXtc=hZw zwOY5+(O0f-zFJ|cd9Fk7W6KVKon?yLc8hLVM;P*V zH^yp<+bMR{EZSE;TOeDI-BdYo_P58iuA=%Y=I<*Mdo37#{fW>o1;}+&u?zI!jRN&aq-Zqr|oC{hEMCTxbN3=HvZb)6^B#`*Dk(sse5x{V5t(>I=Nmb+k^K<2KkWyMS6H~VgN=LtG)ccZ2|(s;k&c6Xka zhrcdcGo8Wmx18&Y;*GIfw=%_d&CI;>u=`&3+n^08H&jjgX8RS0N(y z@rdvL(rHc0ZpB^o?CDr~`SsWRJdQ_PmLB_hewT5}<{#Bjr{XIVlfqWTe&jvBt5eWW z_3QpCdpaloihumi&Ew$KIPvQj<@YBoxy|Q}M*WHxzwT-BRvpO2NVnx~;XZI|204axU*hkw!1*4XC4x#`(QPj33! z`_R)V>5-59<|#chY`V|+O%)ejb<0C>_)VsSs^Ge)GO{H&HPDwtKE}g!X zXZ~uE__DV_3~S~oD1O^?cxl92&hu*mA}!0}H}y!&4^qB&XYHmBp{1IJcD?WJt?8)L z{gIS(rLI7K=GwQ1#Dtgp4Nuv&(|ut_Y*nhHdvm$s-|oDec#fp2+gC3Y&%A1NHi%JM zN@ULsS*_-!;;-DSMeRZuFP{?I67hBIsr1U|y8>T0ML+a~A4n=+D$oMIP2i`Ukm3g zO6qB9WMu!i;=Zu!Q3+P&?V1Iv)<`T)=(uTOlUFGjXlPz{(BZsu#gnCrl6c>L{=#~D zk>-qf%bORLNqTPVe5I+MchF(&gM5qlpf`d`=Y4sk#&buc&zn&o;?l6<5g9k`39$m zEl*hRJg_IV`*eq5l0o<7!w!ddo)+)uogksaW_;G-%#`d8Q?yf$2pAf5#~EB;YVMJb z5j1q^Ig_Ny^Y=n}da2;XT^$lLUH?yK+5Xsbv1HP(j)OWy2OaEfUdIUk} zMMpxTef`q;vD+kEb5i4@CR9I|w}O>9UVFap)0;^m8~x{mX^RSOOn4Bs#;7c*JMf5J zdC_c1u`{xgKBdPWb*gi9C?*}>Wo^(MWl;X%dRA1^;iET_L=24aJ7ud--ZLAj1eko?>`E+>L<|{?Prj6P z?a4nj|HCWgW9EM-3%#K+msD0mjyGVuAakbCgXL21scZ9yEz8h|(x#2y}Tenws$K14B zb8=tWEM0dsjO)#xzxOU~owQ{8yu-?E3s&3QoTwOj`E2p#Ivw{OrL;Mk9)Fij7P7hb zVS`?h=%J-$|H|IaxD&KM?Y%>XUDAmw;R}PlztD2EX=PuhcUo|9$IO&0bt6%?e&;E? zeGvj1KVPtMHY$~0;^g_0y+1y%P1!B>P4D?b#;FReil!E46FV*z%vWD($@Rls$e%}$ zJA2--phfNK>Td6Y75?mad6+Nwjo`oI%dHpeUceK4W_H>w8{P%VXW4&S_!zqQ+_yP9 z(VoTb;)Y782|t!R+4$4_%}A}EI{SBr>mdKI;Vst03v143IG5A literal 0 HcmV?d00001 diff --git a/main/Assets.xcassets/AppIcon.appiconset/icon_167x167.png b/main/Assets.xcassets/AppIcon.appiconset/icon_167x167.png new file mode 100644 index 0000000000000000000000000000000000000000..eeebc835d6a85ff2fd82af410f3661bb2d845fd1 GIT binary patch literal 1623 zcmeAS@N?(olHy`uVBq!ia0y~yU|0^q91IK$41u-61`G^ra-J@ZAr*{o4l?pGFfcG0 zynK4?@IQ72h6dreenIRq_qSJi<`>S`DZ-t5XD6S-9!mz#NiBgE+0xcw=jY_!-Udcy z&gsRux3``BKSf{pxl*;&?oVH@iAVW-t@-%s?@iZ-Mr)7%4u2D|@9eQl{mIThE1QozIxcf^vj1V`V%>{o+Ny7FmN@P$+8k`{{iXj`i*5cS z#p_QBS5(x@l-Re@nwO7!2wSaUn=dr6-dB2R_uRFrufC<&2rhqC@igUd z9Md$JxpSWTPIwWx?#`OK!3SD+CwD4e(M)5fxTM*CFGd5MM7R_@6$3p;T3WW}?n zKH)dXVSg;61#Ny7Hg&W7newb!=oaTafdamRljYS{yw;v2XxXH0@3-N+(#1Q6KRh|k zIZy2D6;tm=JAJisZaC)c$dJ=+O~3N-k!lxAy&mkm)<2NBweb#R82bC=y zHbP?R7CAfbC8WsS{~Xwu@y5dORr%(&OSX&6m@ZDR*qCopza^=9otd+ORQ+3)n|W1} zkKW#B%in%hS+TIG`|R?Kw(_DMJRGbYFT3o$c%W!q`jxPpi#H0J!oP-{{{F0Lot-n7 zTf4!;_T1MaElwr|exYU#`PYq|zq;5eN?H4Tm~t=gqL`2W?s)>e;_5f}t^WlrTORA* z!FF_I*0Bd($`^&dZmG0T@cV7fw^E^M(L8SP8=cpVcM8X}tzV*BF>l$t{qlz1JmvRg z1eV{D`!4DCxN=sN-7>z-F5>J*R%UH5p7)?~R+Ze!oQoEPc@u+Yz5jac{%4!O^D|d# zPhU|#Cwi~p`?#f*Jk23~pN?!??y=j$(Zp{~^j^#Glt)_st$k;0HamTJ)$*o{kA-iqS7zEb6^tD9rB zQ#*eh-#K;T7Utf9sy{l1F9)7~wrKh9=%(MZ?H_;UJbXFOzeCCIye*$t{l4yB)58n2 zCq8<)V!3Wr)x~MMHP1Hxns4PQ;_q&&*k!R-clw`X&b%xCZ@jMsrf8QsD`mb90_wT>SN)^AZ z8vp&$l%(_XxObrR`YN~M=Ra;|IrI4Z)tc9t@(=a@=Y)q!6x-ArAH2M3`TiH5eotk; z@>x0fyy=wvEAGE#EZ(=%eZ%stRc@;^F1Cj{o;&jMx0T|&zN?exXnd~f$hTFrx*b2I zs`g}@?a5A~_1ABHP3bmTUw>=0di0l~M_*IQZ7qZUTYmhtC)U4c(bglsWxs7yk1nZy z^jCTJyp+I0e|I!gHPtQ`6ViQt=RVJh-+BVipK)8h|5oz8 zhChqeKfI8&zQF%~)uE4~7SDvA+Mje5r}mJ!l+nYi!rI{q0=szn{C_CHu!8f1h`M`NhXaIzIeazI&eJnNsh*?F;*V zW=g0%^O*M_^HpWb|Cr}f*!>IV`@7jLU;B2V^0Qmbd&N4R#kqbk`8wzA!y7C1tbBdA zwodM=&IR{1w)gg&tCeT`+GA)u?|u27Im;^SP8FBz*WbJ~slqL{>G`fB@8uWHV=R9A z(eC>B$iC--3yb$Q-RrNu@_oi3`FFqDB5V(*r@8Lgzw;K)s^pXR+HLz@MeO?C?Z0dG z^B>bMe9x8_kh%MxFXMS%Cj&zwLr3KSt*VbrEq=29^aLmU^*?a_!hgMI_DlY6yizy& wpZJsbs_#Dz{zopr0Qe41o&W#< literal 0 HcmV?d00001 diff --git a/main/Assets.xcassets/AppIcon.appiconset/icon_180x180.png b/main/Assets.xcassets/AppIcon.appiconset/icon_180x180.png new file mode 100644 index 0000000000000000000000000000000000000000..e0265d0e5360e603a697a391e04c79fcf3f8df6e GIT binary patch literal 1730 zcmeAS@N?(olHy`uVBq!ia0y~yVAuk}91IK$3=+#C-!U+-b$YruhEy=VIoJpiY*_Gj zrT9uF1_p+-XEF5lu}c2)e{=5bZL+;H({FFfz0I8rB45mTxNl|L+{-85-uv!b z?;Uvc*R^x+g4gLhT46kMPSKl#yTd0x*b=Jtu~hq6|NGDHZ@WH!tIL?VI5FbF>*hPm zhrB;W-ubsr`LOrr$cwxF={;Qf)2HsKz`l|SvD$))ikA$x*1tVDo$ve-w~Y*!4)03! z`(AfgT|Xi_!`!}0zjmTPN9d}J*Dp>tsa^hdOOkI=onY)Qp~Ov>Bl)*SEn%qH&K*DN z%)j2~_19c}$seD-toTp#W|PL7;h&cNeDn88N$;jjab@1lzk8obB_5u8QZlAQ|!Mh~Z&3q{y!cx5pa^pge?>Ag$VxJN4P`~twqW9-!w%xq} z2C4j;OzblXo^i3h-?;RrW3#*H8kUSqF5r^Gq-dV`=z#+nD`3ohN)SR?7!9U|G18YV02wd)f{tEp zbho96zFbjHOt&!SPTZ!=`}$1tG%n_ifBu$anlF9KdF|M-3+?=|D_>sX{m8ETE>?b{o{jn21&(%M z2D^-+e?|W}cs^*0qpY@in$#AVeO#}trfA8o{j!!RdqU)eph}UjLet1wzAwKX?>@2C z^V&W~`OkNanf@Kql2i2Ft!cY$6~`t&>lexUBGz$#J6e7|N%sDvrTaBkZkdub_x18U zGs8dD>Rr3os*oEZb?u&`bLGsuRJ%oXlFS)SrmMbOTzARheOsJzM*kGXMfb87<|Uu3 zW64~7>BKGT7s(6f2$Vj(Xkl9>8CmacLz@lH(08z8Y*>-wdHrls&#FN&5TgasDIjCc69yx!{_6cc-)@aXQpy} zu|z=K4bAR5?eq8fPuRA+Ync0UL+HLQCs8RCYV^PWB zAZ=c~_4D!!CvAJYUvTOEuO5e-XX@sL&ss0z_Df^&rIqb>jG`^KY_VO+soBhQi2dTc zODU-rI_y8$XxFY;n^UM?|M-#C-ghiT(|^t`toEuFW1cNEoByV@k6iGsSI4y{ncr9$ z{OtX#Utw0y@9oGpbdqI}c^mk$W%1od7gNQRKNe&^KFN~#U$Zv*h*jPEDeW&B-|pkd z{I4myHSVrM&|#a_2l1JU6<_OQCu`pe^|*A;T5fw)w6TA;WbmwfUmM2EkXiX>ZLVgA z%(|cb%(L|IhY9&-_tYf%WbQNl=rl1fbD!zwNpnEd|3}MXGjH+iJbd@=>0M`X!e>n@ z<^FT_z0)MW)t46BGrGNNO0@c{2}>@oGP&{pm19=)=lr0LH>R7l2y> zrq4C7$}wL2lzFP^L?zZzj!SM!ds|K`-g)%#b2(>y^P1XYhu>{byz}SdXN8=*U4Nzt zEa~<7t$s&_vwi#I}RJ_!Bynz8-*e?^td#~%cOmHxIIJSTjD-$-Jr-(%LL%jXxD#4f!qqs9I~h;6Es)(`iLOL<=Ue>T`0Iu>DE>2h$DaK)wj z8V^F1-Yl+NVX|51nDdn2?+-i|U)s~sdpz_tm;Wxsc#A*H2k(DsWwvYT^$NDmv3nDz z;F1|~>t6I{^&i=+QX8}%?l8T>EwfKGX6XCulnX&)VP}G zSRdY(v3yca)^k2i28Kq44%KD*S=*UQ*Kbp>zI7?%B&e1A tcl-9H#HpWsH-CLn%d-AXKO+MJgLM3^r(5gUJv>46kf*Dk%Q~loCIFCOQ!4-f literal 0 HcmV?d00001 diff --git a/main/Assets.xcassets/AppIcon.appiconset/icon_20x20.png b/main/Assets.xcassets/AppIcon.appiconset/icon_20x20.png new file mode 100644 index 0000000000000000000000000000000000000000..e6c49f776b62e8d5e0ae5cda2bd72f5e758d455a GIT binary patch literal 258 zcmeAS@N?(olHy`uVBq!ia0y~yU=RUe4h9AWh82&TE;2AMob+^Y45?sDR^UAGLb|E3 z(Pq+vNw3``#4NuOhUjHU*!^AbI`1j!rtgxQ z|5tP{dT?H(BvMVF_;PHVKlUsKZo}Ho2$KR_EgD+YfTMn-rxCeA9L}~YK336Qys7E|No}{KmYW!%!RE}dv5>l zU0(mEevj2@0U7I7jfXlnzdl#=VKeSqa>n$bP0x?N@zZBana1ZSo7%C((IPf+nWq&4 Y!{g9pu8UM0>Of)X>FVdQ&MBb@05iO!&Hw-a literal 0 HcmV?d00001 diff --git a/main/Assets.xcassets/AppIcon.appiconset/icon_40x40.png b/main/Assets.xcassets/AppIcon.appiconset/icon_40x40.png new file mode 100644 index 0000000000000000000000000000000000000000..dac331c53b04bd1c6ef162e58a17fbd98af184d3 GIT binary patch literal 452 zcmeAS@N?(olHy`uVBq!ia0y~yV9)?z4h9AWhLuyFZ)9L#?DlkV45?tebD6m(CQyX! zfpJIH?Xt&L0&{l<=4!j;CgEa7Vpew7}6>rDb2XpAl#d%sjNg$Z_)m0r@+kjgw~! z*f;mB5Kpn}u(0GW=VAWHo%~g7iKlNYcjXVEZG|f&ujpE&D*b$@z~8)~y2@$`x8J__ zue0MG8?vi!-*$fKPRr2bt7~Km!efr7^SyIe)RXZsPnY}l>II(-9zXEjqb|lPysYz( zf=p0-qA6RGr1GwV`Dgo1Y_gGlI*Y&ZaI9Oc%e0*@JA%FcRo)OZohhXJXX2IS?0%0= zM~}~G>~Vb*uOU;&=d8Yec`5T@Q^ws#yOY=#nR1lg>o@bCqP2OthQFD^qt@z%Lp&2h zwBJb2&gxh)D_gv`r+3GjTVfIdEi4JF44JN651q1hFfoMPvHIk6@0c|xdOThIT-G@y GGywpY{K4@6 literal 0 HcmV?d00001 diff --git a/main/Assets.xcassets/AppIcon.appiconset/icon_58x58.png b/main/Assets.xcassets/AppIcon.appiconset/icon_58x58.png new file mode 100644 index 0000000000000000000000000000000000000000..7aa1253812d6d7e92b56a05330fe03e69dace1b7 GIT binary patch literal 593 zcmeAS@N?(olHy`uVBq!ia0y~yV6XyV4h9AWh9eh5zc4T`NqD+AhEy=VoyL4Y#!;Zv zdRy-8ZMnDglp*xCT$ZJd~d!_J;Q&_(EUqob)}|w$~%TE-q$ek zyt&x*{}=`%2L0IHS3f5#5jI>= zrCTO&phMwb;PiJIi}&%F3pwQ1uc+Mrz|kbAgl*sTPrG*sy?FS~fTLoDN5Ixa8}H0_ z%3(@7owY(ED$wVc>A;vO3YeYl8u!?nMDbMlB37*pVnI(U+=PQ#9*}+q0GHemN zG9i6oOm^V13*~P(lEQ-PTTb`Rj@9>ixbAVP&E{JiH)?n-HaTmb(qZtL8QK@6_&1he z+M>iL6SXYnEvBnvzUB&@dRJ6k(aqccxXwq5-N$5=%-0Nqr0LziI(&pr{SnRX(Q=n{ z_W9PnSNGAwU4j}Xqu&^=lG)0(^Mu4{rM5@>ALAZu@C*$Uou#olGvrEq%Xd-FT?yqO zp+&q6`s{ahfLItD>tL=hy7qvO>kMu$8rKao@EqJJ%Yu zm&RRN_OQD5$2Y0TUJ*W)ueNVKUwXEZ^Z5$3vxSc{a<0g1Uwty1XVzmiNtHqiqkuOJ vTTHFxIhg8sIR89U|KP6oy!l@JKb1O$9kuhlXT@JX3rZ`Vu6{1-oD!MlhfAoIPC}Ln;`PB&rfb7&Nrm z6?QIC=qk&(y)F0lwzncl)~z?TviQIEHy3jq4laLZQ_}h??TXgy z<5O!iZ}zl&xX*um%PFms;fj+!C1vFm|K$4cccu&TO8OleV8a@K=su zl6m^%l;W;4+h0!qv`hW_{IgCoH#4kzTHt=}Cuh9WhtE@29>^f+7;pX1Dt6~< zot9t9qPquliYrYw^q$Bn-Vnt2e$p0!8wq;$&yHpYZg2^9E)P5JGr6Jmm{GW%yV)!8 zi&wHzQdJ7h_&ht+v+UHSRcBJJg-ojdHhIf${o92M93fhq^)ptTDfz%G)IP~6Mr-e; z+`a!zR9ZK9?%0%8y+JqMKQ8s)G(NYB3@c9gB-?L0s+_syRO>CBU1D4ZR`2|5B$1$# zJ(YV~WXWxAzDN1b9%rA}yr6T9miVTwc{*ht-;ni2o>g3?Q&05JemqqO6%{p@Kb9LfkCBNL;+j2kW`&T_rQ!n2A{GHi~eHJroem>v7?EjzV z{%ZBQjRjMaZsw%M+H==beg5#}R^{2^+b4D%?Av_0=9|&X=_f;TQrIU4*Lf8kdv({` zj`7UllaVPmeKIFx`QQ1#cjlJRq8z!CSG;P@ZWG=YKS5N}i?7st(_1fvy2w*sGhIC< z<{nIEcz)`b_UFA!ec~$Zf_Lsrizz%6#IPh?>Ffl4hOi^*pZBuGU0uMjjPb_>B|S-Y zZzXYU#y95`PajF}IXRobE&9~4>*nm2dl(C>w$IHuzjlG)47J~i49sg&j> z%yQtEkJ+ZahUCwCKd~i$ieb9l_)vtcH~ty4zpassw*Pv|+gY3kFR90{{@ZgcJV9$3F^ zW>f0Rt-oFJ79ERk3<*3ob?!O89AlQ#mx`?$3|&&^G0%3D44>w9#EkD*pyBjSLGQE= ztXwmxM_bzS)U=eKhlifTOnE6#pgdn8c&eevhd%pF+U}+s8-Be@G<001_1p2P&%Kbz z`&Q5RdSLRTCZEu>Rk6z_rOrELGShaYPJM9rvXhr@PwslixI>eNA-t?h`?5*Z`DOac zW3?tNZ`-+vF`scymRsHv&+Jt`Yg@e?ixhR$^HXmv`ySW3=fYQ=$+<5-i7nBdob&0~ zi?u$NziQrY`gVKUPNQWZ&sn!G%$>&GRX%NF#>;KT1MXxe)L(gdYD

x6HkHCtf7f za_rX9zM8r+dWzAV>1TgcEznxMtnRF?RG@OSkGObfq^0ANn3cx@N+iR-eOmbQdeXi( zx^7=w3}cJWe0P-|(Ri;Ebgy#h ro@n3GeD+VB1*h;&;!!u?a${s*G40JeKh0(XD3yD<`njxgN@xNAW{7iW literal 0 HcmV?d00001 diff --git a/main/Assets.xcassets/AppIcon.appiconset/icon_80x80.png b/main/Assets.xcassets/AppIcon.appiconset/icon_80x80.png new file mode 100644 index 0000000000000000000000000000000000000000..a60bb4beb5cd6cba60e90fa86b1bc3b3a17cd387 GIT binary patch literal 774 zcmeAS@N?(olHy`uVBq!ia0y~yUfLzx)`6Q%(aHIGq+8waI(8D#gfR`9>Ue5^>w$;-OoHsYy1xhimB=vp3<#hW#{)Y zYFXOpeCT05!+|>&OcRgH5ph^m%JAUk5!J-%^IKV&jwM`>lVivcjTLa)!}{aOVuq|) z412u3|2oa*7oNqe09uO})(YcesM^eJc&B1ZXYlg7R ztsA|wnS?^WG1`1CXiYtH^(v#PV;MulIZ?jmrMs*aEa@;$@H!{*uGv6~$*W0@>B4mN zp084eR&Z%#ay=+1n6^Pgp~q-J*Nydz)m769Bv_ukGFs5}mEqRLmfsAGOTAV!G;rNw zia03H#dZJ7W~Lt-j3zWpxyrpm%u8QHbl#FGGma^Ylg*31{N^xFP2^~2oZ%F5pKk@T zn&fx3ogFV28V>P1v}3sXqWj`Ak$_im%n`yZ8Hx>VQ43dm2u>1m6AW0$JYi}EM}u9} z!W9M*KPB5&mWoHah$M48Hg$?zxZvX69bU1Y3m$!EICGIRwYBNU{R^B~j1enDPjRVw z$+~N3r@W}Bj||h`oaD1Ixo(PrpD0(5P2j5;huuPEoUlw(mS)i5^Yr_`wr!V_Vfwug zRrQEq|F9JXQL84m?6o+-W$O0qld5)zdi0e>Y8Uf!sY@KLt@Fg@o;fhFLc+p`x&2!$&&-M0 zu4~MBW=`z(XKg!k;i`^zVQ)`;rAgi1zr5lFkNsKp{}OEef98|5TvJT_s-nloc>B+D XZsxvfGAm*MD1m#r`njxgN@xNAMV?2E literal 0 HcmV?d00001 diff --git a/main/Assets.xcassets/AppIcon.appiconset/icon_87x87.png b/main/Assets.xcassets/AppIcon.appiconset/icon_87x87.png new file mode 100644 index 0000000000000000000000000000000000000000..c2ffad2ab63341f8fd8198bf339c3c639e11a06d GIT binary patch literal 837 zcmeAS@N?(olHy`uVBq!ia0y~yUJ5Z$U zea`J|xwq2{K;*XE>VrAAzuhYTuURxnfA8I$FZbA8?EiN1WOeNndHcG5hE;L`8|r^| zeSGV6#QdXP9Iv`TM@HBHI6jsBrojccI(2{xIb4W4$R7{6ePlt#8dYwp$JT z-J3tZH#DF0BRMjD@6UY~i|Yv;1lHZ!L`y1Q&jw9VbcQ*TIztUSrEt$R;j zMYY!U8$o)}gC*mIrhk`Z*I%9jlMn6w1JIb zUAtg~Wt>~G>Z|TO(<=6=B?Ran*WM)c^z=ugK&i|%i+&#Xm>0?tEXbD=z9#JCpR31T z$5n%X?dTe&H`^MTpk5tDE_8E^;4W~sMIrO*Zv1rHB-}=Y31y7&np0_usaKYZD z&%VO?!QW-Ok3Qe17N-{XQ}O(Pq@RBsrS&b{WvUp>wUuYyE4PU2o&2L{@3NPFdmdl4 z)Yr|Kq&Ved?WD(|mYsJl{a&wpFONz6{&jngcdxft-pHCRYSF3o=sWAt?0V$_seSEc z4i8Lf_XqAVQr~a>y+xoxGp_OSpV~XVPgIg-?0L@I8?RI_>$^M?>;IpBg(~ZxKmWeC xuI~Sv=ih&y`90Zw|NLK{fA6pR^W!J`54OE&-*@R*-g*nlAD*s$F6*2UngGyFopS&H literal 0 HcmV?d00001 diff --git a/main/Assets.xcassets/Contents.json b/main/Assets.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/main/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/main/Base.lproj/LaunchScreen.storyboard b/main/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..9a22346 --- /dev/null +++ b/main/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/main/Base.lproj/Main.storyboard b/main/Base.lproj/Main.storyboard new file mode 100644 index 0000000..be23ef3 --- /dev/null +++ b/main/Base.lproj/Main.storyboard @@ -0,0 +1,204 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + 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`. + +⒈ 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. +⒊ Use your apps as usual. +⒋ Come back to AppCheck to see the results. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/main/BundleIcon.swift b/main/BundleIcon.swift new file mode 100644 index 0000000..d05298d --- /dev/null +++ b/main/BundleIcon.swift @@ -0,0 +1,78 @@ +import UIKit + +let appIconApple = generateAppleIcon() +let appIconUnknown = generateUnknownIcon() + +func generateAppleIcon() -> UIImage? { + let rect = CGRect(x: 0, y: 0, width: 30, height: 30) + UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0) + +// #colorLiteral(red: 0.5843137503, green: 0.8235294223, blue: 0.4196078479, alpha: 1).setFill() +// UIBezierPath(roundedRect: rect, cornerRadius: 0).fill() +// print("drawing") + let fs = 36 as CGFloat + let hFont = UIFont.systemFont(ofSize: fs) + var attrib = [ + NSAttributedString.Key.font: hFont, + NSAttributedString.Key.foregroundColor: UIColor.gray + ] + + let str = "" as NSString + let actualHeight = str.size(withAttributes: attrib).height + attrib[NSAttributedString.Key.font] = hFont.withSize(fs * fs / actualHeight) + + let strW = str.size(withAttributes: attrib).width + str.draw(at: CGPoint(x: (rect.size.width - strW) / 2.0, y: -3), withAttributes: attrib) + + let img = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + return img +} + +func generateUnknownIcon() -> UIImage? { + let rect = CGRect(x: 0, y: 0, width: 30, height: 30) + UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0) + + let context = UIGraphicsGetCurrentContext()! + let lineWidth: CGFloat = 0.5 + let corner: CGFloat = 6.75 + let c = corner / CGFloat.pi + lineWidth/2 + let sz: CGFloat = rect.height + let m = sz / 2 + let r1 = 0.2 * sz, r2 = sqrt(2 * r1 * r1) + + // diagonal + context.lineFromTo(x1: c, y1: c, x2: sz-c, y2: sz-c) + context.lineFromTo(x1: c, y1: sz-c, x2: sz-c, y2: c) + // horizontal + context.lineFromTo(x1: 0, y1: m, x2: sz, y2: m) + context.lineFromTo(x1: 0, y1: m + r1, x2: sz, y2: m + r1) + context.lineFromTo(x1: 0, y1: m - r1, x2: sz, y2: m - r1) + // vertical + context.lineFromTo(x1: m, y1: 0, x2: m, y2: sz) + context.lineFromTo(x1: m + r1, y1: 0, x2: m + r1, y2: sz) + context.lineFromTo(x1: m - r1, y1: 0, x2: m - r1, y2: sz) + // circles + context.addEllipse(in: CGRect(x: m - r1, y: m - r1, width: 2*r1, height: 2*r1)) + context.addEllipse(in: CGRect(x: m - r2, y: m - r2, width: 2*r2, height: 2*r2)) + let r3 = CGRect(x: c, y: c, width: sz - 2*c, height: sz - 2*c) + context.addEllipse(in: r3) + context.addRect(r3) + + UIColor.clear.setFill() + UIColor.gray.setStroke() + let rounded = UIBezierPath(roundedRect: rect.insetBy(dx: lineWidth/2, dy: lineWidth/2), cornerRadius: corner) + rounded.lineWidth = lineWidth + rounded.stroke() + + let img = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + return img +} + +extension CGContext { + func lineFromTo(x1: CGFloat, y1: CGFloat, x2: CGFloat, y2: CGFloat) { + self.move(to: CGPoint(x: x1, y: y1)) + self.addLine(to: CGPoint(x: x2, y: y2)) + } +} diff --git a/main/Info.plist b/main/Info.plist new file mode 100644 index 0000000..63ce9be --- /dev/null +++ b/main/Info.plist @@ -0,0 +1,45 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(MARKETING_VERSION) + CFBundleVersion + 7 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/main/LaunchIcon.png b/main/LaunchIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..2f56c6feb0303a50aac09fa8dceef8ead07b07cd GIT binary patch literal 1398 zcmeAS@N?(olHy`uVBq!ia0y~yU}ykg4h9AWhG(I6;tUL|ww^AIAr*{oW--tCVkL6a z_|KoUZ)a9+%e{?2f7vYe)=rLBpZ)FL-0#8XkKavu{-?Nl)-umf-lJWj+F@%VHYTkt z`YI|DTX(PXckaF`U)Fl9Y3^qF`{mtScKwS>U2g5yxY{nh{*o5+gx71G)uuC^_B{qDaD-ShuNntZu4sitB1CaqOQ!qsUppJ%Y|*ZsHLzHiO} z){3e%k2GU%e7QEiYu~#w#f--^w+UUA{k_uv!?BnghR!1QdUuw1oqzQVEW*a0bzMHZ zo%EG`#^;5+$Jl3-{g}7zIO}WP2|E`3VSddj@T~f*@{f#NR?KJqt=?A@&ZF|MuK&Tz zZ@ZXO*0r3CYgEqNUdb{&^)+V$lgRyCZn>M1&*=bLhK5#Sm z{_QkZ>3arZuKi)%m#@WKAb!A#(~6DZed%>}g~Hao3;~ZfXB2iwC|pd^KVY4EYSqyK zp?zmO88?~hCi`EV8fPo3X^Z!9+B#SN=N(W0=nL!igT!8D6SvxfRDz z5c1Szmg$VcQyDV2raX9QsMPR9*~+VX25W&T!!8H&-`R?S4t$r-zLGlgMD)N<1rzya zr(~5831)0nod=3*SgF1F!KVAJ5z(y4)*c=w*O^82@l&{ItArG1q^sRNSTi_6?j395wem#jno&_4@nm zJKwJ8d~NvmUs8s_m}y0$+Kt)bS6?W~uMjTFXwUw>{K~fdcQ)KVIa_y5 zueyrd0WLQd4fg8xmwW9aE#vARPgG%VP)g8oE}wJz@f+JWtKAnZHSN+1E zreGR#z=1X298A}U?bvtg-&Nh&FB@bMR9zA_a(s)H{B|k0fafkp#NO%c2ECSV=dRqa zm;c_Ulc}Sj`$F^v-Btsv^n{VeoJeN!2Kl+a2%U$1h|Mw(p6~EhasXtOV z;hyyWh_jcRDy;QtyafvcYqoW4$`ZTQazvJ~xb4%@)&_gkVs{490^twpOVe0(u$buj zx3O(-X>e4iHE?9<>ejSl{D1LG55xS1{{>lY4gM2$-rp#j$-00;X2qRZ6^HkmG5up| ztrk&OT0PzFw|q2j*};gt7R(HSKek*>-1kQCdoAbZD3- zpFH!NEaMFM%KPC*qpEU^emX6+Wip6=X3+gJ{JZ=vA3g1uRa^U+4DA2h(_j0H`J`af z8ooXzjyYHV>|3z2VCOug1FtM!I{j;7bxpX~Re66?`~Bx;+w*Tev1I5t&~9rhQ@rA% zJt6J{Hjypbu3<8Y{eloZ3JbQNS?cHvh z8TuZ6zjJ5LziAsQdpRV2UAp^u;@y4vHOVvdg-^!(V`6q?c;*~ + + + + StringsTable + Root + PreferenceSpecifiers + + + Type + PSToggleSwitchSpecifier + Title + Kill proxy on startup + Key + kill_proxy + DefaultValue + + + + Type + PSToggleSwitchSpecifier + Title + Delete DB on startup + Key + kill_db + DefaultValue + + + + + diff --git a/main/Settings.bundle/en.lproj/Root.strings b/main/Settings.bundle/en.lproj/Root.strings new file mode 100644 index 0000000000000000000000000000000000000000..847a9c05a0c170f77081498e54267cc0a62fa1a6 GIT binary patch literal 472 zcmezWPoF`HL4m=ML4l!|A(J7GA)O(IAr;IkVJL!%6f-C=q%mZIWpo%67|Izk81fm4 z!RksFG8sz1G71ct3?P-o3orVc>zNXLn1>Z15C68YIiBv zryy6SG30|?pURNP0CJ}SLq0v(hh0@{ literal 0 HcmV?d00001 diff --git a/main/TVCApps.swift b/main/TVCApps.swift new file mode 100644 index 0000000..a0926ed --- /dev/null +++ b/main/TVCApps.swift @@ -0,0 +1,139 @@ +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/TVCRequestDetails.swift b/main/TVCRequestDetails.swift new file mode 100644 index 0000000..1bb3838 --- /dev/null +++ b/main/TVCRequestDetails.swift @@ -0,0 +1,24 @@ +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 new file mode 100644 index 0000000..e307260 --- /dev/null +++ b/main/TVCRequests.swift @@ -0,0 +1,64 @@ +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 new file mode 100644 index 0000000..4a8655d --- /dev/null +++ b/main/main.entitlements @@ -0,0 +1,18 @@ + + + + + com.apple.developer.networking.networkextension + + dns-proxy + + com.apple.developer.networking.vpn.api + + allow-vpn + + com.apple.security.application-groups + + group.de.uni-bamberg.psi.AppCheck + + +