summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--ios/MullvadVPN.xcodeproj/project.pbxproj37
-rw-r--r--ios/MullvadVPN/AutomaticKeyRotationManager.swift276
-rw-r--r--ios/MullvadVPN/NEProviderStopReason+Debug.swift42
-rw-r--r--ios/MullvadVPN/Optional+DispatchQueue.swift22
-rw-r--r--ios/PacketTunnel/PacketTunnelProvider.swift537
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))
}
}
+