diff options
| author | Andrej Mihajlov <and@mullvad.net> | 2021-09-17 13:43:00 +0200 |
|---|---|---|
| committer | Andrej Mihajlov <and@mullvad.net> | 2021-09-21 10:43:58 +0200 |
| commit | 8e2d145de80eeddc9f11083613b76acfea80f8e2 (patch) | |
| tree | 41f735256db38d073ba58657c287884d81813498 | |
| parent | 466f2af2f6b817b70bde13de923f6abb131b5aa1 (diff) | |
| download | mullvadvpn-8e2d145de80eeddc9f11083613b76acfea80f8e2.tar.xz mullvadvpn-8e2d145de80eeddc9f11083613b76acfea80f8e2.zip | |
PacketTunnel: refactor and remove networking from the tunnel
| -rw-r--r-- | ios/MullvadVPN.xcodeproj/project.pbxproj | 37 | ||||
| -rw-r--r-- | ios/MullvadVPN/AutomaticKeyRotationManager.swift | 276 | ||||
| -rw-r--r-- | ios/MullvadVPN/NEProviderStopReason+Debug.swift | 42 | ||||
| -rw-r--r-- | ios/MullvadVPN/Optional+DispatchQueue.swift | 22 | ||||
| -rw-r--r-- | ios/PacketTunnel/PacketTunnelProvider.swift | 537 |
5 files changed, 222 insertions, 692 deletions
diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj index f8e3a2e0f2..b92fc6925c 100644 --- a/ios/MullvadVPN.xcodeproj/project.pbxproj +++ b/ios/MullvadVPN.xcodeproj/project.pbxproj @@ -11,7 +11,6 @@ 5807E2C2243203D000F5FF30 /* StringTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5807E2C1243203D000F5FF30 /* StringTests.swift */; }; 5807E2C3243203E700F5FF30 /* String+Split.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5807E2BF2432038B00F5FF30 /* String+Split.swift */; }; 580EE20624B3222200F9D8A1 /* ExclusivityController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 580EE20524B3222200F9D8A1 /* ExclusivityController.swift */; }; - 580EE20724B3222400F9D8A1 /* ExclusivityController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 580EE20524B3222200F9D8A1 /* ExclusivityController.swift */; }; 580EE22424B3243100F9D8A1 /* AsyncBlockOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 580EE22324B3243100F9D8A1 /* AsyncBlockOperation.swift */; }; 5811DE50239014550011EB53 /* NEVPNStatus+Debug.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5811DE4F239014550011EB53 /* NEVPNStatus+Debug.swift */; }; 5815039724D6ECAE00C9C50E /* CustomFormatLogHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5815039624D6ECAE00C9C50E /* CustomFormatLogHandler.swift */; }; @@ -27,7 +26,6 @@ 581CBCEE229826FD00727D7F /* StaticTableViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 581CBCED229826FD00727D7F /* StaticTableViewDataSource.swift */; }; 581FC4FA2695ACE100AA97BA /* Account.strings in Resources */ = {isa = PBXBuildFile; fileRef = 581FC4F82695ACE100AA97BA /* Account.strings */; }; 5820674926E63EC900655B05 /* Promise+BackgroundTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5820674826E63EC800655B05 /* Promise+BackgroundTask.swift */; }; - 5823FA5026CA690600283BF8 /* OSLogHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5823FA4F26CA690600283BF8 /* OSLogHandler.swift */; }; 5820674E26E6510200655B05 /* REST.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5820674D26E6510200655B05 /* REST.swift */; }; 5820675026E6514100655B05 /* HTTP.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5820674F26E6514100655B05 /* HTTP.swift */; }; 5820675526E6528200655B05 /* RelayCacheError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 585DA87926B024F900B8C587 /* RelayCacheError.swift */; }; @@ -39,6 +37,7 @@ 5820675C26E6576800655B05 /* RelayCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5820675A26E6576800655B05 /* RelayCache.swift */; }; 5820675E26E6839900655B05 /* PresentAlertOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5820675D26E6839900655B05 /* PresentAlertOperation.swift */; }; 5820676226E75D8500655B05 /* REST.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5820674D26E6510200655B05 /* REST.swift */; }; + 5823FA5126CA690600283BF8 /* OSLogHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5823FA4F26CA690600283BF8 /* OSLogHandler.swift */; }; 58293FAE2510CA58005D0BB5 /* ProblemReportViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58293FAC2510CA58005D0BB5 /* ProblemReportViewController.swift */; }; 58293FB125124117005D0BB5 /* CustomTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58293FB025124117005D0BB5 /* CustomTextField.swift */; }; 58293FB3251241B4005D0BB5 /* CustomTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58293FB2251241B3005D0BB5 /* CustomTextView.swift */; }; @@ -52,9 +51,9 @@ 582BB1B52295780F0055B6EF /* AccountExpiry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 582BB1B42295780F0055B6EF /* AccountExpiry.swift */; }; 582CFEE726945FC30072883A /* AppStoreSubscriptions.strings in Resources */ = {isa = PBXBuildFile; fileRef = 582CFEE526945FC30072883A /* AppStoreSubscriptions.strings */; }; 582CFEEA269463B80072883A /* Settings.strings in Resources */ = {isa = PBXBuildFile; fileRef = 582CFEE8269463B80072883A /* Settings.strings */; }; + 582E021A26F49D2E0031BCD8 /* AnyRelayCacheObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FB865726EA213300F188BC /* AnyRelayCacheObserver.swift */; }; + 582E021B26F49D3B0031BCD8 /* RelayCacheObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FB865926EA214400F188BC /* RelayCacheObserver.swift */; }; 5835B7CC233B76CB0096D79F /* TunnelManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5835B7CB233B76CB0096D79F /* TunnelManager.swift */; }; - 583BC70724FE4DC500C9DE04 /* Optional+DispatchQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 583BC70624FE4DC400C9DE04 /* Optional+DispatchQueue.swift */; }; - 583BC70824FE4DC500C9DE04 /* Optional+DispatchQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 583BC70624FE4DC400C9DE04 /* Optional+DispatchQueue.swift */; }; 583DA21425FA4B5C00318683 /* LocationDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 583DA21325FA4B5C00318683 /* LocationDataSource.swift */; }; 5840250122B1124600E4CFEC /* IPAddress+Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5840250022B1124600E4CFEC /* IPAddress+Codable.swift */; }; 5840250222B1124600E4CFEC /* IPAddress+Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5840250022B1124600E4CFEC /* IPAddress+Codable.swift */; }; @@ -69,6 +68,7 @@ 5846227326E22A160035F7C2 /* AppStorePaymentObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5846227226E22A160035F7C2 /* AppStorePaymentObserver.swift */; }; 5846227526E22A350035F7C2 /* AnyAppStorePaymentObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5846227426E22A350035F7C2 /* AnyAppStorePaymentObserver.swift */; }; 5846227726E22A7C0035F7C2 /* AppStorePaymentManagerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5846227626E22A7C0035F7C2 /* AppStorePaymentManagerDelegate.swift */; }; + 5846227A26E24F1F0035F7C2 /* ExclusivityController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 580EE20524B3222200F9D8A1 /* ExclusivityController.swift */; }; 584789B8264D4A2A000E45FB /* old_le_root_cert.cer in Resources */ = {isa = PBXBuildFile; fileRef = 584789B4264D4A2A000E45FB /* old_le_root_cert.cer */; }; 584789B9264D4A2A000E45FB /* old_le_root_cert.cer in Resources */ = {isa = PBXBuildFile; fileRef = 584789B4264D4A2A000E45FB /* old_le_root_cert.cer */; }; 584789BE264D4A2A000E45FB /* new_le_root_cert.cer in Resources */ = {isa = PBXBuildFile; fileRef = 584789B7264D4A2A000E45FB /* new_le_root_cert.cer */; }; @@ -133,6 +133,7 @@ 58781CC922AE7CA8009B9D8E /* RelayConstraints.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58781CC822AE7CA8009B9D8E /* RelayConstraints.swift */; }; 58781CCE22AE8918009B9D8E /* RelayConstraints.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58781CC822AE7CA8009B9D8E /* RelayConstraints.swift */; }; 58781CD522AFBA39009B9D8E /* RelaySelector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58781CD422AFBA39009B9D8E /* RelaySelector.swift */; }; + 5878BA1426DD0B01004147D7 /* OSLogHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5823FA4F26CA690600283BF8 /* OSLogHandler.swift */; }; 587A01FC23F1F0BE00B68763 /* SimulatorTunnelProviderHost.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587A01FB23F1F0BE00B68763 /* SimulatorTunnelProviderHost.swift */; }; 587AD7C623421D7000E93A53 /* TunnelSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587AD7C523421D7000E93A53 /* TunnelSettings.swift */; }; 587AD7C723421D8600E93A53 /* TunnelSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587AD7C523421D7000E93A53 /* TunnelSettings.swift */; }; @@ -146,7 +147,6 @@ 587C575426D2615F005EF767 /* PacketTunnelOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587C575226D2615F005EF767 /* PacketTunnelOptions.swift */; }; 587CBFE322807F530028DED3 /* UIColor+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587CBFE222807F530028DED3 /* UIColor+Helpers.swift */; }; 5883A09E266A5AF7003EFFCB /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 587B7543266922BF00DEF7E9 /* Localizable.strings */; }; - 588534BF246193D90018B744 /* AutomaticKeyRotationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 588534BD246193C00018B744 /* AutomaticKeyRotationManager.swift */; }; 58871D1E25D535A3002297FA /* WireGuardKit in Frameworks */ = {isa = PBXBuildFile; productRef = 58871D1D25D535A3002297FA /* WireGuardKit */; }; 58871D2325D535D2002297FA /* IPAddressRange+Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5850366725A47AC700A43E93 /* IPAddressRange+Codable.swift */; }; 5888AD83227B11080051EB06 /* SelectLocationCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5888AD82227B11080051EB06 /* SelectLocationCell.swift */; }; @@ -196,9 +196,7 @@ 58B9EB132488ED2100095626 /* AlertPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58B9EB122488ED2100095626 /* AlertPresenter.swift */; }; 58B9EB152489139B00095626 /* DisplayChainedError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58B9EB142489139B00095626 /* DisplayChainedError.swift */; }; 58BA692E23E99EFF009DC256 /* Locking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58BA692D23E99EFF009DC256 /* Locking.swift */; }; - 58BA692F23E99F5B009DC256 /* Locking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58BA692D23E99EFF009DC256 /* Locking.swift */; }; 58BA693123EADA6A009DC256 /* SimulatorTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58BA693023EADA6A009DC256 /* SimulatorTunnelProvider.swift */; }; - 58BA693223EAE1AE009DC256 /* SimulatorTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58BA693023EADA6A009DC256 /* SimulatorTunnelProvider.swift */; }; 58BA791B2578F092006FAEA0 /* WireGuardKit in Frameworks */ = {isa = PBXBuildFile; productRef = 58BA791A2578F092006FAEA0 /* WireGuardKit */; }; 58BA7947257901A5006FAEA0 /* WireGuardKit in Frameworks */ = {isa = PBXBuildFile; productRef = 58BA7946257901A5006FAEA0 /* WireGuardKit */; }; 58BF345E26F09F3C002A6CAA /* ExclusivityController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 580EE20524B3222200F9D8A1 /* ExclusivityController.swift */; }; @@ -210,7 +208,6 @@ 58CAF4EF26025954007C5886 /* SimulatorTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58BA693023EADA6A009DC256 /* SimulatorTunnelProvider.swift */; }; 58CB0EE024B86751001EF0D8 /* RESTClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58CB0EDF24B86751001EF0D8 /* RESTClient.swift */; }; 58CC40EF24A601900019D96E /* ObserverList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58CC40EE24A601900019D96E /* ObserverList.swift */; }; - 58CC40F024A602780019D96E /* ObserverList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58CC40EE24A601900019D96E /* ObserverList.swift */; }; 58CCA010224249A1004F3011 /* ConnectViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58CCA00F224249A1004F3011 /* ConnectViewController.swift */; }; 58CCA01222424D11004F3011 /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58CCA01122424D11004F3011 /* SettingsViewController.swift */; }; 58CCA0162242560B004F3011 /* UIColor+Palette.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58CCA0152242560B004F3011 /* UIColor+Palette.swift */; }; @@ -288,8 +285,6 @@ 58FB865526E8BF3100F188BC /* AppStorePaymentManagerError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FB865426E8BF3100F188BC /* AppStorePaymentManagerError.swift */; }; 58FB865E26EA284E00F188BC /* LogFormatting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FB865D26EA284E00F188BC /* LogFormatting.swift */; }; 58FB865F26EA2E6D00F188BC /* LogFormatting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FB865D26EA284E00F188BC /* LogFormatting.swift */; }; - 58FB865826EA213300F188BC /* AnyRelayCacheObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FB865726EA213300F188BC /* AnyRelayCacheObserver.swift */; }; - 58FB865A26EA214400F188BC /* RelayCacheObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FB865926EA214400F188BC /* RelayCacheObserver.swift */; }; 58FD5BE724192A2C00112C88 /* AppStoreReceipt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FD5BE624192A2B00112C88 /* AppStoreReceipt.swift */; }; 58FD5BF024238EB300112C88 /* SKProduct+Formatting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FD5BEF24238EB300112C88 /* SKProduct+Formatting.swift */; }; 58FD5BF22424F7D700112C88 /* UserInterfaceInteractionRestriction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FD5BF12424F7D700112C88 /* UserInterfaceInteractionRestriction.swift */; }; @@ -375,7 +370,6 @@ 582CFEE626945FC30072883A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/AppStoreSubscriptions.strings; sourceTree = "<group>"; }; 582CFEE9269463B80072883A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Settings.strings; sourceTree = "<group>"; }; 5835B7CB233B76CB0096D79F /* TunnelManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelManager.swift; sourceTree = "<group>"; }; - 583BC70624FE4DC400C9DE04 /* Optional+DispatchQueue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Optional+DispatchQueue.swift"; sourceTree = "<group>"; }; 583DA21325FA4B5C00318683 /* LocationDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationDataSource.swift; sourceTree = "<group>"; }; 5840250022B1124600E4CFEC /* IPAddress+Codable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IPAddress+Codable.swift"; sourceTree = "<group>"; }; 5840250322B11AB700E4CFEC /* MullvadEndpoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MullvadEndpoint.swift; sourceTree = "<group>"; }; @@ -436,7 +430,6 @@ 587B7544266922BF00DEF7E9 /* en */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; }; 587C575226D2615F005EF767 /* PacketTunnelOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PacketTunnelOptions.swift; sourceTree = "<group>"; }; 587CBFE222807F530028DED3 /* UIColor+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Helpers.swift"; sourceTree = "<group>"; }; - 588534BD246193C00018B744 /* AutomaticKeyRotationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutomaticKeyRotationManager.swift; sourceTree = "<group>"; }; 5888AD82227B11080051EB06 /* SelectLocationCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectLocationCell.swift; sourceTree = "<group>"; }; 5888AD86227B17950051EB06 /* SelectLocationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectLocationViewController.swift; sourceTree = "<group>"; }; 58906DDF2445C7A5002F0673 /* NEProviderStopReason+Debug.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NEProviderStopReason+Debug.swift"; sourceTree = "<group>"; }; @@ -534,9 +527,9 @@ 58FAEDFE24533A7000CB0F5B /* KeychainReturn.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainReturn.swift; sourceTree = "<group>"; }; 58FAEE0024533A9C00CB0F5B /* KeychainClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainClass.swift; sourceTree = "<group>"; }; 58FB865426E8BF3100F188BC /* AppStorePaymentManagerError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppStorePaymentManagerError.swift; sourceTree = "<group>"; }; - 58FB865D26EA284E00F188BC /* LogFormatting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogFormatting.swift; sourceTree = "<group>"; }; 58FB865726EA213300F188BC /* AnyRelayCacheObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyRelayCacheObserver.swift; sourceTree = "<group>"; }; 58FB865926EA214400F188BC /* RelayCacheObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayCacheObserver.swift; sourceTree = "<group>"; }; + 58FB865D26EA284E00F188BC /* LogFormatting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogFormatting.swift; sourceTree = "<group>"; }; 58FD5BE624192A2B00112C88 /* AppStoreReceipt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppStoreReceipt.swift; sourceTree = "<group>"; }; 58FD5BEF24238EB300112C88 /* SKProduct+Formatting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SKProduct+Formatting.swift"; sourceTree = "<group>"; }; 58FD5BF12424F7D700112C88 /* UserInterfaceInteractionRestriction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserInterfaceInteractionRestriction.swift; sourceTree = "<group>"; }; @@ -784,7 +777,6 @@ 58FD5BE624192A2B00112C88 /* AppStoreReceipt.swift */, 58CE5E6A224146210008646E /* Assets.xcassets */, 58FEEB57260B662E00A621A8 /* AutomaticKeyboardResponder.swift */, - 588534BD246193C00018B744 /* AutomaticKeyRotationManager.swift */, 589AB4F6227B64450039131E /* BasicTableViewCell.swift */, 5891BF1B25E3E3EB006D6FB0 /* Bundle+ProductVersion.swift */, 58F840B12464491D0044E708 /* ChainedError.swift */, @@ -834,7 +826,6 @@ 58CC40EE24A601900019D96E /* ObserverList.swift */, 58E1338C26D2BFB500CC316B /* Promise */, 580EE1FF24B3218800F9D8A1 /* Operations */, - 583BC70624FE4DC400C9DE04 /* Optional+DispatchQueue.swift */, 585DA88D26B031D100B8C587 /* TunnelIPC */, 58ACF6482655365700ACE4B7 /* PreferencesViewController.swift */, 58C6B35322BB87C4003C19AD /* PrivateKeyWithMetadata.swift */, @@ -1227,6 +1218,7 @@ 58E1337B26D2BEDD00CC316B /* Promise+ReceiveOn.swift in Sources */, 58A8BE81239FBE62006B74AC /* IPEndpoint.swift in Sources */, 5896AE7F246ACE76005B36CB /* Keychain.swift in Sources */, + 5846227A26E24F1F0035F7C2 /* ExclusivityController.swift in Sources */, 58E1336B26D2BE3700CC316B /* PromiseObserver.swift in Sources */, 58871D2325D535D2002297FA /* IPAddressRange+Codable.swift in Sources */, ); @@ -1256,6 +1248,7 @@ 58E6771F24ADFE7800AA26E7 /* SettingsNavigationController.swift in Sources */, 58A1AA8C23F5584C009F7EA6 /* ConnectionPanelView.swift in Sources */, 582BB1B52295780F0055B6EF /* AccountExpiry.swift in Sources */, + 582E021A26F49D2E0031BCD8 /* AnyRelayCacheObserver.swift in Sources */, 582BB1B3229574F40055B6EF /* SettingsAccountCell.swift in Sources */, 585DA88426B0270700B8C587 /* ServerRelaysResponse.swift in Sources */, 5875960726F36B3A00BF6711 /* TunnelIPCError.swift in Sources */, @@ -1272,7 +1265,6 @@ 585DA89126B0322700B8C587 /* TunnelIPC.swift in Sources */, 5877153023981F7B001F8237 /* WireguardKeysViewController.swift in Sources */, 587B7536266528A200DEF7E9 /* NotificationManager.swift in Sources */, - 58FB865A26EA214400F188BC /* RelayCacheObserver.swift in Sources */, 58ACF64D26567A5000ACE4B7 /* CustomSwitch.swift in Sources */, 5850367F25A481D800A43E93 /* IPAddressRange+Codable.swift in Sources */, 5871FB96254ADE4E0051A0A4 /* ConsolidatedApplicationLog.swift in Sources */, @@ -1319,9 +1311,9 @@ 58B67B482602079E008EF58E /* RelaySelector.swift in Sources */, 58DF28A52417CB4B00E836B0 /* AppStorePaymentManager.swift in Sources */, 583DA21425FA4B5C00318683 /* LocationDataSource.swift in Sources */, + 5878BA1426DD0B01004147D7 /* OSLogHandler.swift in Sources */, 582BB1AF229566420055B6EF /* SettingsCell.swift in Sources */, 5873884D239E6D7E00E96C4E /* EmbeddedViewContainerView.swift in Sources */, - 583BC70724FE4DC500C9DE04 /* Optional+DispatchQueue.swift in Sources */, 58F3C0A4249CB069003E76BE /* HeaderBarView.swift in Sources */, 5820674926E63EC900655B05 /* Promise+BackgroundTask.swift in Sources */, 58B9EB132488ED2100095626 /* AlertPresenter.swift in Sources */, @@ -1348,6 +1340,7 @@ 5857F24324C8662600CF6F47 /* SelectLocationHeaderView.swift in Sources */, 58AEEF652344A36000C9BBD5 /* KeychainError.swift in Sources */, 581503A624D6F4AE00C9C50E /* Logging.swift in Sources */, + 582E021B26F49D3B0031BCD8 /* RelayCacheObserver.swift in Sources */, 58CCA01222424D11004F3011 /* SettingsViewController.swift in Sources */, 58FB865526E8BF3100F188BC /* AppStorePaymentManagerError.swift in Sources */, 58FD5BF42428C67600112C88 /* InAppPurchaseButton.swift in Sources */, @@ -1368,7 +1361,6 @@ 58293FB725138B88005D0BB5 /* CustomNavigationController.swift in Sources */, 587425C12299833500CA2045 /* RootContainerViewController.swift in Sources */, 585DA87726B024A600B8C587 /* CachedRelays.swift in Sources */, - 58FB865826EA213300F188BC /* AnyRelayCacheObserver.swift in Sources */, 585DA89B26B146B300B8C587 /* TunnelIPCCoding.swift in Sources */, 5896AE84246D5889005B36CB /* CustomDateComponentsFormatting.swift in Sources */, 585DA89626B0328000B8C587 /* TunnelIPCResponse.swift in Sources */, @@ -1378,7 +1370,6 @@ 58B43C1925F77DB60002C8C3 /* ConnectMainContentView.swift in Sources */, 58561C99239A5D1500BD6B5E /* IPEndpoint.swift in Sources */, 58FD5BF22424F7D700112C88 /* UserInterfaceInteractionRestriction.swift in Sources */, - 5823FA5026CA690600283BF8 /* OSLogHandler.swift in Sources */, 5811DE50239014550011EB53 /* NEVPNStatus+Debug.swift in Sources */, 58C3A4B222456F1B00340BDB /* AccountInputGroupView.swift in Sources */, 58F840B22464491D0044E708 /* ChainedError.swift in Sources */, @@ -1405,7 +1396,6 @@ 58FAEE0324533ABE00CB0F5B /* KeychainReturn.swift in Sources */, 58BFA5CD22A7CE1F00A6173D /* ApplicationConfiguration.swift in Sources */, 5850368D25A49E2200A43E93 /* PrivateKeyWithMetadata.swift in Sources */, - 580EE20724B3222400F9D8A1 /* ExclusivityController.swift in Sources */, 58F840B02464382C0044E708 /* KeychainItemRevision.swift in Sources */, 5820675826E652AF00655B05 /* RelayCacheIO.swift in Sources */, 5820675726E652A600655B05 /* REST.swift in Sources */, @@ -1419,13 +1409,11 @@ 5875960B26F3723000BF6711 /* TunnelIPC.swift in Sources */, 58AEEF662344A37400C9BBD5 /* KeychainError.swift in Sources */, 5840250222B1124600E4CFEC /* IPAddress+Codable.swift in Sources */, - 58BA693223EAE1AE009DC256 /* SimulatorTunnelProvider.swift in Sources */, + 58E1337226D2BE9C00CC316B /* AnyOptional.swift in Sources */, 5820675C26E6576800655B05 /* RelayCache.swift in Sources */, 58CE5E7C224146470008646E /* PacketTunnelProvider.swift in Sources */, 58FAEDF1245069CA00CB0F5B /* KeychainAttributes.swift in Sources */, 586AA296234B696B00502875 /* WireguardAssociatedAddresses.swift in Sources */, - 58BA692F23E99F5B009DC256 /* Locking.swift in Sources */, - 58CC40F024A602780019D96E /* ObserverList.swift in Sources */, 58E1337226D2BE9C00CC316B /* AnyOptional.swift in Sources */, 58E1338226D2BF5C00CC316B /* Promise+Result.swift in Sources */, 585DA88526B0270700B8C587 /* ServerRelaysResponse.swift in Sources */, @@ -1437,7 +1425,6 @@ 58E1336A26D2BE3700CC316B /* PromiseObserver.swift in Sources */, 5815039824D6ECAE00C9C50E /* CustomFormatLogHandler.swift in Sources */, 5840250522B11AB700E4CFEC /* MullvadEndpoint.swift in Sources */, - 583BC70824FE4DC500C9DE04 /* Optional+DispatchQueue.swift in Sources */, 58906DE02445C7A5002F0673 /* NEProviderStopReason+Debug.swift in Sources */, 5815039E24D6ECE600C9C50E /* TextFileOutputStream.swift in Sources */, 585DA87826B024A900B8C587 /* CachedRelays.swift in Sources */, @@ -1449,11 +1436,11 @@ 5820675626E6528A00655B05 /* RESTError.swift in Sources */, 5820675526E6528200655B05 /* RelayCacheError.swift in Sources */, 58561C9A239A5D1500BD6B5E /* IPEndpoint.swift in Sources */, - 588534BF246193D90018B744 /* AutomaticKeyRotationManager.swift in Sources */, 58E1337626D2BEC400CC316B /* Promise+Optional.swift in Sources */, 58781CCE22AE8918009B9D8E /* RelayConstraints.swift in Sources */, 581503A024D6F01E00C9C50E /* LogRotation.swift in Sources */, 58781CD522AFBA39009B9D8E /* RelaySelector.swift in Sources */, + 5823FA5126CA690600283BF8 /* OSLogHandler.swift in Sources */, 5820675926E652BE00655B05 /* RESTCoding.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/ios/MullvadVPN/AutomaticKeyRotationManager.swift b/ios/MullvadVPN/AutomaticKeyRotationManager.swift deleted file mode 100644 index 6a16d55a87..0000000000 --- a/ios/MullvadVPN/AutomaticKeyRotationManager.swift +++ /dev/null @@ -1,276 +0,0 @@ -// -// AutomaticKeyRotationManager.swift -// MullvadVPN -// -// Created by pronebird on 05/05/2020. -// Copyright © 2020 Mullvad VPN AB. All rights reserved. -// - -import Foundation -import Logging -import WireGuardKit - -/// A private key rotation retry interval on failure (in seconds) -private let kRetryIntervalOnFailure = 300 - -/// A private key rotation interval (in days) -private let kRotationInterval = 4 - -/// A struct describing the key rotation result -struct KeyRotationResult { - var isNew: Bool - var publicKeyWithMetadata: PublicKeyWithMetadata -} - -class AutomaticKeyRotationManager { - - enum Error: ChainedError { - /// REST error - case rest(RestError) - - /// A failure to read the tunnel settings - case readTunnelSettings(TunnelSettingsManager.Error) - - /// A failure to update tunnel settings - case updateTunnelSettings(TunnelSettingsManager.Error) - - var errorDescription: String? { - switch self { - case .rest: - return "REST error" - case .readTunnelSettings: - return "Read tunnel settings error" - case .updateTunnelSettings: - return "Update tunnel settings error" - } - } - } - - private let logger = Logger(label: "AutomaticKeyRotationManager") - - private let rest = MullvadRest() - private let persistentKeychainReference: Data - - /// A dispatch queue used for synchronization - private let dispatchQueue = DispatchQueue(label: "net.mullvad.vpn.key-manager", qos: .utility) - - /// A timer source used to schedule a delayed key rotation - private var timerSource: DispatchSourceTimer? - - /// Internal lock used for access synchronization to public members of this class - private let stateLock = NSLock() - - /// Internal variable indicating that the key rotation has already started - private var isAutomaticRotationEnabled = false - - /// A REST request for replacing the key on server - private var dataTask: URLSessionTask? - - /// A variable backing the `eventHandler` public property - private var _eventHandler: ((KeyRotationResult) -> Void)? - - /// A dispatch queue used for broadcasting events - private let eventQueue: DispatchQueue? - - /// An event handler that's invoked when key rotation occurred - var eventHandler: ((KeyRotationResult) -> Void)? { - get { - stateLock.withCriticalBlock { - self._eventHandler - } - } - set { - stateLock.withCriticalBlock { - self._eventHandler = newValue - } - } - } - - init(persistentKeychainReference: Data, eventQueue: DispatchQueue?) { - self.persistentKeychainReference = persistentKeychainReference - self.eventQueue = eventQueue - } - - func startAutomaticRotation(queue: DispatchQueue?, completionHandler: @escaping () -> Void) { - dispatchQueue.async { - if !self.isAutomaticRotationEnabled { - self.logger.info("Start automatic key rotation") - - self.isAutomaticRotationEnabled = true - self.performKeyRotation() - } - - queue.performOnWrappedOrCurrentQueue(block: completionHandler) - } - } - - func stopAutomaticRotation(queue: DispatchQueue?, completionHandler: @escaping () -> Void) { - dispatchQueue.async { - if self.isAutomaticRotationEnabled { - self.logger.info("Stop automatic key rotation") - - self.isAutomaticRotationEnabled = false - - self.dataTask?.cancel() - self.dataTask = nil - - self.timerSource?.cancel() - } - - queue.performOnWrappedOrCurrentQueue(block: completionHandler) - } - } - - private func performKeyRotation() { - let result = TunnelSettingsManager.load(searchTerm: .persistentReference(persistentKeychainReference)) - - switch result { - case .success(let keychainEntry): - let currentPrivateKey = keychainEntry.tunnelSettings.interface.privateKey - - if Self.shouldRotateKey(creationDate: currentPrivateKey.creationDate) { - let result = makeReplaceKeyTask(accountToken: keychainEntry.accountToken, oldPublicKey: currentPrivateKey.privateKey.publicKey) { (result) in - let result = result.map { (tunnelSettings) -> KeyRotationResult in - let newPrivateKey = tunnelSettings.interface.privateKey - - return KeyRotationResult( - isNew: true, - publicKeyWithMetadata: newPrivateKey.publicKeyWithMetadata - ) - } - - self.didCompleteKeyRotation(result: result) - } - - switch result { - case .success(let newTask): - self.dataTask = newTask - newTask.resume() - - case .failure(let error): - self.dataTask = nil - self.didCompleteKeyRotation(result: .failure(.rest(error))) - } - } else { - let event = KeyRotationResult( - isNew: false, - publicKeyWithMetadata: currentPrivateKey.publicKeyWithMetadata - ) - - self.didCompleteKeyRotation(result: .success(event)) - } - - case .failure(let error): - self.didCompleteKeyRotation(result: .failure(.readTunnelSettings(error))) - } - } - - private func makeReplaceKeyTask( - accountToken: String, - oldPublicKey: PublicKey, - completionHandler: @escaping (Result<TunnelSettings, Error>) -> Void) -> Result<URLSessionDataTask, RestError> - { - let newPrivateKeyWithMetadata = PrivateKeyWithMetadata() - let payload = TokenPayload( - token: accountToken, - payload: ReplaceWireguardKeyRequest( - old: oldPublicKey.rawValue, - new: newPrivateKeyWithMetadata.privateKey.publicKey.rawValue - ) - ) - - return rest.replaceWireguardKey().dataTask(payload: payload) { (result) in - self.dispatchQueue.async { - let updateResult = result.mapError { (error) -> Error in - return .rest(error) - }.flatMap { (response) -> Result<TunnelSettings, Error> in - let addresses = WireguardAssociatedAddresses( - ipv4Address: response.ipv4Address, - ipv6Address: response.ipv6Address - ) - - return self.updateTunnelSettings(privateKeyWithMetadata: newPrivateKeyWithMetadata, addresses: addresses) - } - completionHandler(updateResult) - } - } - } - - private func updateTunnelSettings(privateKeyWithMetadata: PrivateKeyWithMetadata, addresses: WireguardAssociatedAddresses) -> Result<TunnelSettings, Error> { - let updateResult = TunnelSettingsManager.update(searchTerm: .persistentReference(self.persistentKeychainReference)) - { (tunnelSettings) in - tunnelSettings.interface.privateKey = privateKeyWithMetadata - tunnelSettings.interface.addresses = [ - addresses.ipv4Address, - addresses.ipv6Address - ] - } - - return updateResult.mapError { .updateTunnelSettings($0) } - } - - private func didCompleteKeyRotation(result: Result<KeyRotationResult, Error>) { - var nextRotationTime: DispatchWallTime? - - switch result { - case .success(let event): - if event.isNew { - logger.info("Finished private key rotation") - - eventQueue.performOnWrappedOrCurrentQueue { - self.eventHandler?(event) - } - } - - if let rotationDate = Self.nextRotation(creationDate: event.publicKeyWithMetadata.creationDate) { - let interval = rotationDate.timeIntervalSinceNow - - logger.info("Next private key rotation on \(rotationDate)") - - nextRotationTime = .now() + .seconds(Int(interval)) - } else { - logger.error("Failed to compute the next private rotation date. Retry in \(kRetryIntervalOnFailure) seconds.") - - nextRotationTime = .now() + .seconds(kRetryIntervalOnFailure) - } - - case .failure(.rest(.network(URLError.cancelled))): - logger.info("Key rotation was cancelled") - - case .failure(let error): - logger.error("Failed to rotate the private key. Retry in \(kRetryIntervalOnFailure) seconds. Error: \(error.displayChain())") - - nextRotationTime = .now() + .seconds(kRetryIntervalOnFailure) - } - - if let nextRotationTime = nextRotationTime, isAutomaticRotationEnabled { - scheduleRetry(wallDeadline: nextRotationTime) - } - } - - private func scheduleRetry(wallDeadline: DispatchWallTime) { - let timerSource = DispatchSource.makeTimerSource(queue: dispatchQueue) - timerSource.setEventHandler { [weak self] in - guard let self = self else { return } - - if self.isAutomaticRotationEnabled { - self.performKeyRotation() - } - } - - timerSource.schedule(wallDeadline: wallDeadline) - timerSource.activate() - - self.timerSource = timerSource - } - - private class func nextRotation(creationDate: Date) -> Date? { - return Calendar.current.date(byAdding: .day, value: kRotationInterval, to: creationDate) - } - - private class func shouldRotateKey(creationDate: Date) -> Bool { - return nextRotation(creationDate: creationDate) - .map { $0 <= Date() } ?? false - } - -} diff --git a/ios/MullvadVPN/NEProviderStopReason+Debug.swift b/ios/MullvadVPN/NEProviderStopReason+Debug.swift index 2162212eff..eab9028dbe 100644 --- a/ios/MullvadVPN/NEProviderStopReason+Debug.swift +++ b/ios/MullvadVPN/NEProviderStopReason+Debug.swift @@ -9,48 +9,46 @@ import Foundation import NetworkExtension -extension NEProviderStopReason: CustomDebugStringConvertible { - public var debugDescription: String { - var output = "NEProviderStopReason." +extension NEProviderStopReason: CustomStringConvertible { + public var description: String { switch self { case .none: - output += "none" + return "none" case .userInitiated: - output += "userInitiated" + return "user initiated" case .providerFailed: - output += "providerFailed" + return "provider failed" case .noNetworkAvailable: - output += "noNetworkAvailable" + return "no network available" case .unrecoverableNetworkChange: - output += "unrecoverableNetworkChange" + return "unrecoverable network change" case .providerDisabled: - output += "providerDisabled" + return "provider disabled" case .authenticationCanceled: - output += "authenticationCanceled" + return "authentication cancelled" case .configurationFailed: - output += "configurationFailed" + return "configuration failed" case .idleTimeout: - output += "idleTimeout" + return "idle timeout" case .configurationDisabled: - output += "configurationDisabled" + return "configuration disabled" case .configurationRemoved: - output += "configurationRemoved" + return "configuration removed" case .superceded: - output += "superceded" + return "superceded" case .userLogout: - output += "userLogout" + return "user logout" case .userSwitch: - output += "userSwitch" + return "user switch" case .connectionFailed: - output += "connectionFailed" + return "connection failed" case .sleep: - output += "sleep" + return "sleep" case .appUpdate: - output += "appUpdate" + return "app update" @unknown default: - output += "\(self.rawValue)" + return "unknown value (\(self.rawValue))" } - return output } } diff --git a/ios/MullvadVPN/Optional+DispatchQueue.swift b/ios/MullvadVPN/Optional+DispatchQueue.swift deleted file mode 100644 index 34402977ec..0000000000 --- a/ios/MullvadVPN/Optional+DispatchQueue.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// Optional+DispatchQueue.swift -// MullvadVPN -// -// Created by pronebird on 01/09/2020. -// Copyright © 2020 Mullvad VPN AB. All rights reserved. -// - -import Foundation - -extension Optional where Wrapped == DispatchQueue { - /// Unwrap the `DispatchQueue` and perform the block on it, otherwise call the block - /// synchronously on the current queue when `Optional` is `.none`. - func performOnWrappedOrCurrentQueue(block: @escaping () -> Void) { - switch self { - case .some(let queue): - queue.async(execute: block) - case .none: - block() - } - } -} diff --git a/ios/PacketTunnel/PacketTunnelProvider.swift b/ios/PacketTunnel/PacketTunnelProvider.swift index bee420458f..8bd55358b8 100644 --- a/ios/PacketTunnel/PacketTunnelProvider.swift +++ b/ios/PacketTunnel/PacketTunnelProvider.swift @@ -14,116 +14,144 @@ import WireGuardKit class PacketTunnelProvider: NEPacketTunnelProvider { - enum OperationCategory { - case exclusive - } - /// Tunnel provider logger - private let logger: Logger + private let providerLogger: Logger /// WireGuard adapter logger - private let wgAdapterLogger: Logger - - /// Current tunnel state - private var tunnelState: PacketTunnelState = .disconnected { - didSet { - logger.info("New tunnel state: \(String(reflecting: self.tunnelState))") - } - } + private let tunnelLogger: Logger /// Internal queue - private let dispatchQueue = DispatchQueue(label: "net.mullvad.MullvadVPN.PacketTunnel", qos: .utility) - - private lazy var operationQueue: OperationQueue = { - let operationQueue = OperationQueue() - operationQueue.underlyingQueue = self.dispatchQueue - return operationQueue - }() + private let dispatchQueue = DispatchQueue(label: "PacketTunnel", qos: .utility) - private lazy var wgAdapter: WireGuardAdapter = { + /// WireGuard adapter + private lazy var adapter: WireGuardAdapter = { return WireGuardAdapter(with: self, logHandler: { [weak self] (logLevel, message) in - self?.wgAdapterLogger.log(level: logLevel.loggerLevel, "\(message)") + self?.dispatchQueue.async { + self?.tunnelLogger.log(level: logLevel.loggerLevel, "\(message)") + } }) }() - private lazy var exclusivityController: ExclusivityController<OperationCategory> = { - return ExclusivityController(operationQueue: self.operationQueue) - }() + /// Tunnel connection info + private var tunnelConnectionInfo: TunnelConnectionInfo? { + didSet { + if let tunnelConnectionInfo = tunnelConnectionInfo { + self.providerLogger.debug("Set tunnel relay to \(tunnelConnectionInfo.hostname)") + } else { + self.providerLogger.debug("Unset tunnel relay") + } + } + } override init() { initLoggingSystem(bundleIdentifier: Bundle.main.bundleIdentifier!) - logger = Logger(label: "PacketTunnelProvider") - wgAdapterLogger = Logger(label: "WireGuard") - } + var providerLogger = Logger(label: "PacketTunnelProvider") + var tunnelLogger = Logger(label: "WireGuard") - // MARK: - Subclass + let pid = ProcessInfo.processInfo.processIdentifier + providerLogger[metadataKey: "pid"] = .stringConvertible(pid) + tunnelLogger[metadataKey: "pid"] = .stringConvertible(pid) - override func startTunnel(options: [String : NSObject]?, completionHandler: @escaping (Error?) -> Void) { - logger.info("Start the tunnel") + self.providerLogger = providerLogger + self.tunnelLogger = tunnelLogger + } - let operation = AsyncBlockOperation { (finish) in - self.doStartTunnel { (result) in - switch result { - case .success: - self.logger.info("Started the tunnel") - completionHandler(nil) + override func startTunnel(options: [String : NSObject]?, completionHandler: @escaping (Error?) -> Void) { + let tunnelOptions = PacketTunnelOptions(rawOptions: options ?? [:]) + let appSelectorResult: Result<RelaySelectorResult?, Error> = Result { try tunnelOptions.getSelectorResult() } - case .failure(let error): - self.logger.error(chainedError: error, message: "Failed to start the tunnel") - completionHandler(error) - } + switch appSelectorResult { + case .success(.some(let selectorResult)): + providerLogger.debug("Start the tunnel via app, connect to \(selectorResult.tunnelConnectionInfo.hostname)") - finish() + case .success(nil): + if tunnelOptions.isOnDemand() { + providerLogger.debug("Start the tunnel via on-demand rule") + } else { + providerLogger.debug("Start the tunnel via system") } + + case .failure(let error): + providerLogger.debug("Start the tunnel via app") + providerLogger.error( + chainedError: AnyChainedError(error), + message: "Failed to decode relay selector result passed from the app. Will continue by picking new relay." + ) } - exclusivityController.addOperation(operation, categories: [.exclusive]) + makeConfiguration(appSelectorResult.flattenValue) + .asPromise() + .receive(on: dispatchQueue) + .mapThen { tunnelConfiguration in + let tunnelConnectionInfo = tunnelConfiguration.selectorResult.tunnelConnectionInfo + self.tunnelConnectionInfo = tunnelConnectionInfo + + return self.adapter.start(tunnelConfiguration: tunnelConfiguration.wgTunnelConfig) + .mapError { error in + return PacketTunnelProviderError.startWireguardAdapter(error) + } + .receive(on: self.dispatchQueue) + } + .onSuccess { + self.providerLogger.debug("Started the tunnel") + } + .onFailure { error in + self.providerLogger.error(chainedError: error, message: "Failed to start the tunnel") + } + .observe { completion in + completionHandler(completion.unwrappedValue?.error) + } } override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) { - logger.info("Stop the tunnel. Reason: \(reason)") + providerLogger.debug("Stop the tunnel: \(reason)") - let operation = AsyncBlockOperation { (finish) in - self.doStopTunnel { (result) in - switch result { - case .success: - self.logger.info("Stopped the tunnel") - case .failure(let error): - self.logger.error(chainedError: error, message: "Failed to stop the tunnel") - } - finish() + adapter.stop() + .receive(on: self.dispatchQueue) + .mapError { error in + return PacketTunnelProviderError.stopWireguardAdapter(error) + } + .onFailure { error in + self.providerLogger.error(chainedError: error, message: "Failed to stop the tunnel gracefully") + } + .observe { _ in + self.providerLogger.debug("Stopped the tunnel") + completionHandler() } - } - - operation.addDidFinishBlockObserver { (op) in - completionHandler() - } - - exclusivityController.addOperation(operation, categories: [.exclusive]) } override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)?) { - dispatchQueue.async { - let decodeResult = PacketTunnelIpcHandler.decodeRequest(messageData: messageData) - .mapError { PacketTunnelProviderError.ipcHandler($0) } + Result { try TunnelIPC.Coding.decodeRequest(messageData) } + .mapError { PacketTunnelProviderError.ipcHandler($0) } + .asPromise() + .onFailure { error in + self.providerLogger.error(chainedError: error, message: "Failed to decode the app message request") + } + .receive(on: dispatchQueue) + .mapThen { request -> Result<Data?, PacketTunnelProviderError>.Promise in + self.providerLogger.debug("handleAppMessage: \(request)") - switch decodeResult { - case .success(let request): switch request { case .reloadTunnelSettings: - self.reloadTunnelSettings { (result) in - self.replyAppMessage(result.map { true }, completionHandler: completionHandler) - } + self.reloadTunnelSettings().observe { _ in } + return .success(nil) - case .tunnelInformation: - self.replyAppMessage(.success(self.tunnelState.tunnelConnectionInfo), completionHandler: completionHandler) + case .tunnelConnectionInfo: + return Result { try TunnelIPC.Coding.encodeResponse(self.tunnelConnectionInfo) } + .mapError { PacketTunnelProviderError.ipcHandler($0) } + .map { data -> Data? in + return .some(data) + } + .flatMapError { error in + self.providerLogger.error(chainedError: error, message: "Failed to encode the app message response for \(request)") + return .success(nil) + } + .asPromise() } - - case .failure(let error): - self.replyAppMessage(Result<String, PacketTunnelProviderError>.failure(error), completionHandler: completionHandler) + }.observe { completion in + completionHandler?(completion.unwrappedValue?.value ?? nil) } - } } override func sleep(completionHandler: @escaping () -> Void) { @@ -135,225 +163,78 @@ class PacketTunnelProvider: NEPacketTunnelProvider { // Add code here to wake up. } - // MARK: - Tunnel management - - private func doStartTunnel(completionHandler: @escaping (Result<(), PacketTunnelProviderError>) -> Void) { - self.tunnelState = .connecting(nil) - - makePacketTunnelConfig { (result) in - guard case .success(let packetTunnelConfig) = result else { - self.tunnelState = .disconnected - - completionHandler(result.map { _ in () }) - return - } - - self.tunnelState = .connecting(packetTunnelConfig.selectorResult.tunnelConnectionInfo) - - self.wgAdapter.start(tunnelConfiguration: packetTunnelConfig.wgTunnelConfig) { (error) in - self.dispatchQueue.async { - if let error = error { - self.tunnelState = .disconnected - completionHandler(.failure(.startWireguardAdapter(error))) - return - } - - let persistentKeychainReference = packetTunnelConfig.persistentKeychainReference - let keyRotationManager = AutomaticKeyRotationManager(persistentKeychainReference: persistentKeychainReference, eventQueue: self.dispatchQueue) - keyRotationManager.eventHandler = { [weak self] (keyRotationEvent) in - guard let self = self else { return } - - self.reloadTunnelSettings { (result) in - switch result { - case .success: - break - - case .failure(let error): - self.logger.error(chainedError: error, message: "Failed to reload tunnel settings") - } - } - } + // MARK: - Private - RelayCache.shared.startPeriodicUpdates(queue: self.dispatchQueue) { - keyRotationManager.startAutomaticRotation(queue: self.dispatchQueue) { - let context = PacketTunnelContext( - wgAdapter: self.wgAdapter, - keyRotationManager: keyRotationManager + private func makeConfiguration(_ appSelectorResult: RelaySelectorResult? = nil) -> Result<PacketTunnelConfiguration, PacketTunnelProviderError> { + return protocolConfiguration.passwordReference.map { data in + return Self.readTunnelSettings(keychainReference: data) + .flatMap { tunnelSettings in + return (appSelectorResult.map { .success($0) } ?? Self.selectRelayEndpoint(relayConstraints: tunnelSettings.relayConstraints)) + .map { (selectorResult) -> PacketTunnelConfiguration in + return PacketTunnelConfiguration( + tunnelSettings: tunnelSettings, + selectorResult: selectorResult ) - - self.tunnelState = .connected(packetTunnelConfig.selectorResult.tunnelConnectionInfo, context) - - completionHandler(.success(())) } - } } - } - } + } ?? .failure(.missingKeychainConfigurationReference) } - private func doStopTunnel(completionHandler: @escaping (Result<(), PacketTunnelProviderError>) -> Void) { - guard let context = self.tunnelState.context else { - logger.warning("Cannot stop the tunnel in such state: \(self.tunnelState)") - completionHandler(.failure(.invalidTunnelState)) - return - } + private func reloadTunnelSettings() -> Result<(), PacketTunnelProviderError>.Promise { + providerLogger.debug("Reload tunnel settings") - self.tunnelState = .disconnecting + return makeConfiguration() + .asPromise() + .mapThen { packetTunnelConfig in + let tunnelConnectionInfo = packetTunnelConfig.selectorResult.tunnelConnectionInfo + let oldTunnelConnectionInfo = self.tunnelConnectionInfo + self.tunnelConnectionInfo = tunnelConnectionInfo - RelayCache.shared.stopPeriodicUpdates(queue: self.dispatchQueue) { - context.keyRotationManager.stopAutomaticRotation(queue: self.dispatchQueue) { - context.wgAdapter.stop { (error) in - self.dispatchQueue.async { - self.tunnelState = .disconnected - - if let error = error { - completionHandler(.failure(.stopWireguardAdapter(error))) - } else { - completionHandler(.success(())) - } + return self.adapter.update(tunnelConfiguration: packetTunnelConfig.wgTunnelConfig) + .receive(on: self.dispatchQueue) + .mapError { error in + return PacketTunnelProviderError.updateWireguardConfiguration(error) } - } - } - } - } - - private func doReloadTunnelSettings(completionHandler: @escaping (Result<(), PacketTunnelProviderError>) -> Void) { - guard let context = self.tunnelState.context else { - logger.warning("Cannot reload tunnel settings in such state: \(self.tunnelState)") - completionHandler(.failure(.invalidTunnelState)) - return - } - - logger.info("Reload tunnel settings") - - let priorTunnelState = self.tunnelState - self.tunnelState = .reconnecting(nil, context) - - makePacketTunnelConfig { (result) in - guard case .success(let packetTunnelConfig) = result else { - self.tunnelState = priorTunnelState - - completionHandler(result.map { _ in () }) - return - } - - self.tunnelState = .reconnecting(packetTunnelConfig.selectorResult.tunnelConnectionInfo, context) - - context.wgAdapter.update(tunnelConfiguration: packetTunnelConfig.wgTunnelConfig) { (error) in - self.dispatchQueue.async { - if let error = error { - self.tunnelState = priorTunnelState - completionHandler(.failure(.updateWireguardConfiguration(error))) - } else { - self.tunnelState = .connected(packetTunnelConfig.selectorResult.tunnelConnectionInfo, context) - completionHandler(.success(())) + .onSuccess { _ in + self.providerLogger.debug("Updated WireGuard configuration") + } + .onFailure { error in + self.tunnelConnectionInfo = oldTunnelConnectionInfo + self.providerLogger.error(chainedError: error, message: "Failed to update WireGuard configuration") } - } - } - } - } - - // MARK: - Private - - private func replyAppMessage<T: Codable>( - _ result: Result<T, PacketTunnelProviderError>, - completionHandler: ((Data?) -> Void)?) { - let result = result.flatMap { (response) -> Result<Data, PacketTunnelProviderError> in - return PacketTunnelIpcHandler.encodeResponse(response: response) - .mapError { PacketTunnelProviderError.ipcHandler($0) } - } - - switch result { - case .success(let data): - completionHandler?(data) - - case .failure(let error): - self.logger.error(chainedError: error) - completionHandler?(nil) - } - } - - private func makePacketTunnelConfig(completionHandler: @escaping (Result<PacketTunnelConfiguration, PacketTunnelProviderError>) -> Void) { - guard let keychainReference = protocolConfiguration.passwordReference else { - completionHandler(.failure(.missingKeychainConfigurationReference)) - return - } - - Self.makePacketTunnelConfig(keychainReference: keychainReference, queue: self.dispatchQueue) { (result) in - completionHandler(result) - } - } - - private func reloadTunnelSettings(completionHandler: @escaping (Result<(), PacketTunnelProviderError>) -> Void) { - let operation = AsyncBlockOperation { (finish) in - self.doReloadTunnelSettings { (result) in - completionHandler(result) - finish() - } - } - - exclusivityController.addOperation(operation, categories: [.exclusive]) - } - - /// Returns a `PacketTunnelConfig` that contains the tunnel settings and selected relay - private class func makePacketTunnelConfig(keychainReference: Data, queue: DispatchQueue?, completionHandler: @escaping (Result<PacketTunnelConfiguration, PacketTunnelProviderError>) -> Void) { - switch Self.readTunnelSettings(keychainReference: keychainReference) { - case .success(let tunnelSettings): - Self.selectRelayEndpoint(relayConstraints: tunnelSettings.relayConstraints) { (result) in - let result = result.map { (selectorResult) -> PacketTunnelConfiguration in - return PacketTunnelConfiguration( - persistentKeychainReference: keychainReference, - tunnelSettings: tunnelSettings, - selectorResult: selectorResult - ) - } - - queue.performOnWrappedOrCurrentQueue { - completionHandler(result) - } - } - - case .failure(let error): - queue.performOnWrappedOrCurrentQueue { - completionHandler(.failure(error)) } - } } /// Read tunnel settings from Keychain private class func readTunnelSettings(keychainReference: Data) -> Result<TunnelSettings, PacketTunnelProviderError> { - TunnelSettingsManager.load(searchTerm: .persistentReference(keychainReference)) + return TunnelSettingsManager.load(searchTerm: .persistentReference(keychainReference)) .mapError { PacketTunnelProviderError.cannotReadTunnelSettings($0) } .map { $0.tunnelSettings } } /// Load relay cache with potential networking to refresh the cache and pick the relay for the /// given relay constraints. - private class func selectRelayEndpoint(relayConstraints: RelayConstraints, completionHandler: @escaping (Result<RelaySelectorResult, PacketTunnelProviderError>) -> Void) { - RelayCache.shared.read { (result) in - switch result { - case .success(let cachedRelayList): - let relaySelector = RelaySelector(relays: cachedRelayList.relays) + private class func selectRelayEndpoint(relayConstraints: RelayConstraints) -> Result<RelaySelectorResult, PacketTunnelProviderError> { + let cacheFileURL = RelayCache.IO.defaultCacheFileURL(forSecurityApplicationGroupIdentifier: ApplicationConfiguration.securityGroupIdentifier)! + let prebundledRelaysURL = RelayCache.IO.preBundledRelaysFileURL! - if let selectorResult = relaySelector.evaluate(with: relayConstraints) { - completionHandler(.success(selectorResult)) + return RelayCache.IO.readWithFallback(cacheFileURL: cacheFileURL, preBundledRelaysFileURL: prebundledRelaysURL) + .mapError { relayCacheError -> PacketTunnelProviderError in + return .readRelayCache(relayCacheError) + } + .flatMap { cachedRelayList -> Result<RelaySelectorResult, PacketTunnelProviderError> in + if let selectorResult = RelaySelector.evaluate(relays: cachedRelayList.relays, constraints: relayConstraints) { + return .success(selectorResult) } else { - completionHandler(.failure(.noRelaySatisfyingConstraint)) + return .failure(.noRelaySatisfyingConstraint) } - - case .failure(let error): - completionHandler(.failure(.readRelayCache(error))) } - } } } enum PacketTunnelProviderError: ChainedError { - /// Failure to perform operation in such state - case invalidTunnelState - /// Failure to read the relay cache - case readRelayCache(RelayCacheError) + case readRelayCache(RelayCache.Error) /// Failure to satisfy the relay constraint case noRelaySatisfyingConstraint @@ -374,13 +255,10 @@ enum PacketTunnelProviderError: ChainedError { case updateWireguardConfiguration(WireGuardAdapterError) /// IPC handler failure - case ipcHandler(PacketTunnelIpcHandler.Error) + case ipcHandler(Error) var errorDescription: String? { switch self { - case .invalidTunnelState: - return "Failure to handle request in such tunnel state" - case .readRelayCache: return "Failure to read the relay cache" @@ -409,7 +287,6 @@ enum PacketTunnelProviderError: ChainedError { } struct PacketTunnelConfiguration { - var persistentKeychainReference: Data var tunnelSettings: TunnelSettings var selectorResult: RelaySelectorResult } @@ -459,102 +336,67 @@ extension PacketTunnelConfiguration { } } -struct PacketTunnelContext { - let wgAdapter: WireGuardAdapter - let keyRotationManager: AutomaticKeyRotationManager -} - -enum PacketTunnelState { - case connecting(TunnelConnectionInfo?) - case connected(TunnelConnectionInfo, PacketTunnelContext) - case disconnecting - case disconnected - case reconnecting(TunnelConnectionInfo?, PacketTunnelContext) - - var tunnelConnectionInfo: TunnelConnectionInfo? { +extension WireGuardLogLevel { + var loggerLevel: Logger.Level { switch self { - case .connecting(let connectionInfo): - return connectionInfo - case .connected(let connectionInfo, _): - return connectionInfo - case .disconnecting: - return nil - case .disconnected: - return nil - case .reconnecting(let connectionInfo, _): - return connectionInfo + case .verbose: + return .debug + case .error: + return .error } } +} - var context: PacketTunnelContext? { - switch self { - case .connecting: - return nil - case .connected(_, let context): - return context - case .disconnecting: - return nil - case .disconnected: - return nil - case .reconnecting(_, let context): - return context +extension WireGuardAdapter { + func start(tunnelConfiguration: TunnelConfiguration) -> Result<(), WireGuardAdapterError>.Promise { + return Result<(), WireGuardAdapterError>.Promise { resolver in + self.start(tunnelConfiguration: tunnelConfiguration) { error in + resolver.resolve(value: error.map { .failure($0) } ?? .success(())) + } } } -} -extension PacketTunnelState: CustomStringConvertible, CustomDebugStringConvertible { - var description: String { - switch self { - case .connecting: - return "connecting" - case .connected: - return "connected" - case .disconnecting: - return "disconnecting" - case .disconnected: - return "disconnected" - case .reconnecting: - return "reconnecting" + func stop() -> Result<(), WireGuardAdapterError>.Promise { + return Result<(), WireGuardAdapterError>.Promise { resolver in + self.stop { error in + resolver.resolve(value: error.map { .failure($0) } ?? .success(())) + } } } - var debugDescription: String { - var output = "PacketTunnelState." + func update(tunnelConfiguration: TunnelConfiguration) -> Result<(), WireGuardAdapterError>.Promise { + return Result<(), WireGuardAdapterError>.Promise { resolver in + self.update(tunnelConfiguration: tunnelConfiguration) { error in + resolver.resolve(value: error.map { .failure($0) } ?? .success(())) + } + } + } +} +extension WireGuardAdapterError: LocalizedError { + public var errorDescription: String? { switch self { - case .connecting(let connectionInfo): - output.append("connecting(") - output.append(String(reflecting: connectionInfo)) - output.append(")") - - case .connected(let connectionInfo, _): - output.append("connected(") - output.append(String(reflecting: connectionInfo)) - output.append(")") + case .cannotLocateTunnelFileDescriptor: + return "Failure to locate tunnel file descriptor." - case .disconnecting: - output.append("disconnecting") + case .invalidState: + return "Failure to perform an operation in such state." - case .disconnected: - output.append("disconnected") + case .dnsResolution(let resolutionErrors): + let detailedErrorDescription = resolutionErrors + .enumerated() + .map { index, dnsResolutionError in + return "\(index): \(dnsResolutionError.address) \(dnsResolutionError.errorDescription ?? "???")" + } + .joined(separator: "\n") - case .reconnecting(let connectionInfo, _): - output.append("reconnecting(") - output.append(String(reflecting: connectionInfo)) - output.append(")") - } + return "Failure to resolve endpoints:\n\(detailedErrorDescription)" - return output - } -} + case .setNetworkSettings: + return "Failure to set network settings" -extension WireGuardLogLevel { - var loggerLevel: Logger.Level { - switch self { - case .verbose: - return .debug - case .error: - return .error + case .startWireGuardBackend(let code): + return "Failure to start WireGuard backend (error code: \(code))" } } } @@ -570,3 +412,4 @@ extension MullvadEndpoint { return Endpoint(host: .ipv6(ipv6Relay.ip), port: .init(integerLiteral: ipv6Relay.port)) } } + |
