From 47c68e1df22a8d90bcac0159c1eb8ef4e34a3d5a Mon Sep 17 00:00:00 2001 From: mojganii Date: Wed, 26 Jun 2024 13:33:25 +0200 Subject: [PATCH] Apply multi-hop on tunnel partially --- ios/MullvadVPN.xcodeproj/project.pbxproj | 36 ++++--- ios/MullvadVPN/AppDelegate.swift | 9 +- .../TunnelManager/Tunnel+Settings.swift | 36 +++++++ .../TunnelManager/TunnelManager.swift | 40 +++---- .../TunnelManager/TunnelState+UI.swift | 12 +-- .../TunnelManager/TunnelState.swift | 25 ++++- .../Tunnel/TunnelControlView.swift | 9 +- .../Tunnel/TunnelControlViewModel.swift | 10 +- .../Tunnel/TunnelViewController.swift | 6 +- .../Relay/RelaySelectorWrapperTests.swift | 1 - .../TunnelSettingsStrategyTests.swift | 101 ++++++++++++++++++ .../Actor/PacketTunnelActor+PostQuantum.swift | 4 +- .../Actor/PacketTunnelActor.swift | 16 ++- .../Actor/PacketTunnelActorCommand.swift | 2 +- ios/PacketTunnelCore/Actor/StartOptions.swift | 3 +- .../Actor/State+Extensions.swift | 5 +- 16 files changed, 242 insertions(+), 73 deletions(-) create mode 100644 ios/MullvadVPN/TunnelManager/Tunnel+Settings.swift create mode 100644 ios/MullvadVPNTests/MullvadVPN/TunnelManager/TunnelSettingsStrategyTests.swift diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj index a56bd2155f76..a488777da938 100644 --- a/ios/MullvadVPN.xcodeproj/project.pbxproj +++ b/ios/MullvadVPN.xcodeproj/project.pbxproj @@ -485,8 +485,6 @@ 7A3353912AAA014400F0A71C /* SimulatorVPNConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A3353902AAA014400F0A71C /* SimulatorVPNConnection.swift */; }; 7A3353932AAA089000F0A71C /* SimulatorTunnelInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A3353922AAA089000F0A71C /* SimulatorTunnelInfo.swift */; }; 7A3353972AAA0F8600F0A71C /* OperationBlockObserverSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A3353962AAA0F8600F0A71C /* OperationBlockObserverSupport.swift */; }; - 7A3EFAAB2BDFDAE800318736 /* RelaySelection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A3EFAAA2BDFDAE800318736 /* RelaySelection.swift */; }; - 7A3AD5012C1068A800E9AD90 /* RelaySelectorPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A3AD5002C1068A800E9AD90 /* RelaySelectorPicker.swift */; }; 7A3AD5012C1068A800E9AD90 /* RelayPicking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A3AD5002C1068A800E9AD90 /* RelayPicking.swift */; }; 7A3FD1B52AD4465A0042BEA6 /* AppMessageHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A3FD1B42AD4465A0042BEA6 /* AppMessageHandlerTests.swift */; }; 7A3FD1B72AD54ABD0042BEA6 /* AnyTransport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58BDEB982A98F4ED00F578F2 /* AnyTransport.swift */; }; @@ -608,8 +606,6 @@ 7ADCB2D82B6A6EB300C88F89 /* AnyRelay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADCB2D72B6A6EB300C88F89 /* AnyRelay.swift */; }; 7ADCB2DA2B6A730400C88F89 /* IPOverrideRepositoryStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADCB2D92B6A730400C88F89 /* IPOverrideRepositoryStub.swift */; }; 7AE044BB2A935726003915D8 /* Routing.h in Headers */ = {isa = PBXBuildFile; fileRef = 7A88DCD02A8FABBE00D2FF0E /* Routing.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 7AE2414A2C20682B0076CE33 /* FormsheetPresentationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE241482C20682B0076CE33 /* FormsheetPresentationController.swift */; }; - 7AEBA52C2C22C65B0018BEC5 /* TimeInterval+Timeout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AEBA52B2C22C65B0018BEC5 /* TimeInterval+Timeout.swift */; }; 7AEBA52A2C2179F20018BEC5 /* RelaySelectorWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AEBA5292C2179F20018BEC5 /* RelaySelectorWrapper.swift */; }; 7AED35CC2BD13F60002A67D1 /* ApplicationConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58BFA5CB22A7CE1F00A6173D /* ApplicationConfiguration.swift */; }; 7AED35CD2BD13FC4002A67D1 /* ApplicationTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58C76A072A33850E00100D75 /* ApplicationTarget.swift */; }; @@ -862,12 +858,15 @@ F0164EBE2B4BFF940020268D /* ShadowsocksLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0164EBD2B4BFF940020268D /* ShadowsocksLoader.swift */; }; F0164EC32B4C49D30020268D /* ShadowsocksLoaderStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0164EC22B4C49D30020268D /* ShadowsocksLoaderStub.swift */; }; F0164ED12B4F2DCB0020268D /* AccessMethodIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0164ED02B4F2DCB0020268D /* AccessMethodIterator.swift */; }; + F01DAE332C2B032A00521E46 /* RelaySelection.swift in Sources */ = {isa = PBXBuildFile; fileRef = F01DAE322C2B032A00521E46 /* RelaySelection.swift */; }; F028A56A2A34D4E700C0CAA3 /* RedeemVoucherViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F028A5692A34D4E700C0CAA3 /* RedeemVoucherViewController.swift */; }; F028A56C2A34D8E600C0CAA3 /* AddCreditSucceededViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F028A56B2A34D8E600C0CAA3 /* AddCreditSucceededViewController.swift */; }; F02F41A02B9723AF00625A4F /* AddLocationsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F02F419A2B9723AE00625A4F /* AddLocationsViewController.swift */; }; F02F41A12B9723AF00625A4F /* AddLocationsDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = F02F419B2B9723AE00625A4F /* AddLocationsDataSource.swift */; }; F02F41A22B9723AF00625A4F /* AddLocationsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F02F419C2B9723AF00625A4F /* AddLocationsCoordinator.swift */; }; F03580252A13842C00E5DAFD /* IncreasedHitButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = F03580242A13842C00E5DAFD /* IncreasedHitButton.swift */; }; + F03A69F72C2AD2D6000E2E7E /* TimeInterval+Timeout.swift in Sources */ = {isa = PBXBuildFile; fileRef = F03A69F62C2AD2D5000E2E7E /* TimeInterval+Timeout.swift */; }; + F03A69F92C2AD414000E2E7E /* FormsheetPresentationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F03A69F82C2AD413000E2E7E /* FormsheetPresentationController.swift */; }; F04413612BA45CD70018A6EE /* CustomListLocationNodeBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F04413602BA45CD70018A6EE /* CustomListLocationNodeBuilder.swift */; }; F04413622BA45CE30018A6EE /* CustomListLocationNodeBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F04413602BA45CD70018A6EE /* CustomListLocationNodeBuilder.swift */; }; F04F95A12B21D24400431E08 /* shadowsocks.h in Headers */ = {isa = PBXBuildFile; fileRef = F04F95A02B21D24400431E08 /* shadowsocks.h */; settings = {ATTRIBUTES = (Private, ); }; }; @@ -908,6 +907,9 @@ F09D04BD2AEBB7C5003D4F89 /* OutgoingConnectionService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F09D04BC2AEBB7C5003D4F89 /* OutgoingConnectionService.swift */; }; F09D04C02AF39D63003D4F89 /* OutgoingConnectionServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F09D04BF2AF39D63003D4F89 /* OutgoingConnectionServiceTests.swift */; }; F09D04C12AF39EA2003D4F89 /* OutgoingConnectionService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F09D04BC2AEBB7C5003D4F89 /* OutgoingConnectionService.swift */; }; + F0A0868E2C22D60100BF83E7 /* Tunnel+Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0A0868D2C22D60100BF83E7 /* Tunnel+Settings.swift */; }; + F0A086902C22D6A700BF83E7 /* TunnelSettingsStrategyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0A0868F2C22D6A700BF83E7 /* TunnelSettingsStrategyTests.swift */; }; + F0A086922C22E0F100BF83E7 /* Tunnel+Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0A0868D2C22D60100BF83E7 /* Tunnel+Settings.swift */; }; F0ACE30D2BE4E478006D5333 /* MullvadMockData.h in Headers */ = {isa = PBXBuildFile; fileRef = F0ACE30A2BE4E478006D5333 /* MullvadMockData.h */; settings = {ATTRIBUTES = (Public, ); }; }; F0ACE3102BE4E478006D5333 /* MullvadMockData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F0ACE3082BE4E478006D5333 /* MullvadMockData.framework */; }; F0ACE3112BE4E478006D5333 /* MullvadMockData.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = F0ACE3082BE4E478006D5333 /* MullvadMockData.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -1894,8 +1896,6 @@ 7A3353902AAA014400F0A71C /* SimulatorVPNConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimulatorVPNConnection.swift; sourceTree = ""; }; 7A3353922AAA089000F0A71C /* SimulatorTunnelInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimulatorTunnelInfo.swift; sourceTree = ""; }; 7A3353962AAA0F8600F0A71C /* OperationBlockObserverSupport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OperationBlockObserverSupport.swift; sourceTree = ""; }; - 7A3EFAAA2BDFDAE800318736 /* RelaySelection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelaySelection.swift; sourceTree = ""; }; - 7A3AD5002C1068A800E9AD90 /* RelaySelectorPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelaySelectorPicker.swift; sourceTree = ""; }; 7A3AD5002C1068A800E9AD90 /* RelayPicking.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayPicking.swift; sourceTree = ""; }; 7A3FD1B42AD4465A0042BEA6 /* AppMessageHandlerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppMessageHandlerTests.swift; sourceTree = ""; }; 7A42DEC82A05164100B209BE /* SettingsInputCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsInputCell.swift; sourceTree = ""; }; @@ -1999,8 +1999,6 @@ 7AD0AA202AD6CB0000119E10 /* URLRequestProxyStub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLRequestProxyStub.swift; sourceTree = ""; }; 7ADCB2D72B6A6EB300C88F89 /* AnyRelay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyRelay.swift; sourceTree = ""; }; 7ADCB2D92B6A730400C88F89 /* IPOverrideRepositoryStub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPOverrideRepositoryStub.swift; sourceTree = ""; }; - 7AE241482C20682B0076CE33 /* FormsheetPresentationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FormsheetPresentationController.swift; sourceTree = ""; }; - 7AEBA52B2C22C65B0018BEC5 /* TimeInterval+Timeout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TimeInterval+Timeout.swift"; sourceTree = ""; }; 7AEBA5292C2179F20018BEC5 /* RelaySelectorWrapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RelaySelectorWrapper.swift; sourceTree = ""; }; 7AEF7F192AD00F52006FE45D /* AppMessageHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppMessageHandler.swift; sourceTree = ""; }; 7AF10EB12ADE859200C090B9 /* AlertViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlertViewController.swift; sourceTree = ""; }; @@ -2158,12 +2156,15 @@ F0164EBD2B4BFF940020268D /* ShadowsocksLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShadowsocksLoader.swift; sourceTree = ""; }; F0164EC22B4C49D30020268D /* ShadowsocksLoaderStub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShadowsocksLoaderStub.swift; sourceTree = ""; }; F0164ED02B4F2DCB0020268D /* AccessMethodIterator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessMethodIterator.swift; sourceTree = ""; }; + F01DAE322C2B032A00521E46 /* RelaySelection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RelaySelection.swift; sourceTree = ""; }; F028A5692A34D4E700C0CAA3 /* RedeemVoucherViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RedeemVoucherViewController.swift; sourceTree = ""; }; F028A56B2A34D8E600C0CAA3 /* AddCreditSucceededViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddCreditSucceededViewController.swift; sourceTree = ""; }; F02F419A2B9723AE00625A4F /* AddLocationsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddLocationsViewController.swift; sourceTree = ""; }; F02F419B2B9723AE00625A4F /* AddLocationsDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddLocationsDataSource.swift; sourceTree = ""; }; F02F419C2B9723AF00625A4F /* AddLocationsCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddLocationsCoordinator.swift; sourceTree = ""; }; F03580242A13842C00E5DAFD /* IncreasedHitButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IncreasedHitButton.swift; sourceTree = ""; }; + F03A69F62C2AD2D5000E2E7E /* TimeInterval+Timeout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TimeInterval+Timeout.swift"; sourceTree = ""; }; + F03A69F82C2AD413000E2E7E /* FormsheetPresentationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FormsheetPresentationController.swift; sourceTree = ""; }; F04413602BA45CD70018A6EE /* CustomListLocationNodeBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomListLocationNodeBuilder.swift; sourceTree = ""; }; F04DD3D72C130DF600E03E28 /* TunnelSettingsManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TunnelSettingsManager.swift; sourceTree = ""; }; F04F95A02B21D24400431E08 /* shadowsocks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = shadowsocks.h; sourceTree = ""; }; @@ -2196,6 +2197,8 @@ F09D04BA2AE95396003D4F89 /* URLSessionStub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLSessionStub.swift; sourceTree = ""; }; F09D04BC2AEBB7C5003D4F89 /* OutgoingConnectionService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutgoingConnectionService.swift; sourceTree = ""; }; F09D04BF2AF39D63003D4F89 /* OutgoingConnectionServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutgoingConnectionServiceTests.swift; sourceTree = ""; }; + F0A0868D2C22D60100BF83E7 /* Tunnel+Settings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Tunnel+Settings.swift"; sourceTree = ""; }; + F0A0868F2C22D6A700BF83E7 /* TunnelSettingsStrategyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelSettingsStrategyTests.swift; sourceTree = ""; }; F0ACE3082BE4E478006D5333 /* MullvadMockData.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MullvadMockData.framework; sourceTree = BUILT_PRODUCTS_DIR; }; F0ACE30A2BE4E478006D5333 /* MullvadMockData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MullvadMockData.h; sourceTree = ""; }; F0ACE32E2BE4EA8B006D5333 /* MockProxyFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockProxyFactory.swift; sourceTree = ""; }; @@ -2616,6 +2619,7 @@ 44DD7D262B6D18FB0005F67F /* MockTunnelInteractor.swift */, 44DD7D232B6CFFD70005F67F /* StartTunnelOperationTests.swift */, A9A5F9A12ACB003D0083449F /* TunnelManagerTests.swift */, + F0A0868F2C22D6A700BF83E7 /* TunnelSettingsStrategyTests.swift */, 44BB5F992BE529FE002520EB /* TunnelStateTests.swift */, A9E0317B2ACBFC7E0095D843 /* TunnelStore+Stubs.swift */, A9E031792ACB0AE70095D843 /* UIApplication+Stubs.swift */, @@ -2734,6 +2738,7 @@ 58F2E145276A2C9900A79513 /* StopTunnelOperation.swift */, 58E0A98727C8F46300FE6BDD /* Tunnel.swift */, 5875960926F371FC00BF6711 /* Tunnel+Messaging.swift */, + F0A0868D2C22D60100BF83E7 /* Tunnel+Settings.swift */, 5878A27229091D6D0096FC88 /* TunnelBlockObserver.swift */, 5803B4AF2940A47300C23744 /* TunnelConfiguration.swift */, 58968FAD28743E2000B799DC /* TunnelInteractor.swift */, @@ -2840,6 +2845,7 @@ 583FE01729C196F3006E85F9 /* SelectLocation */ = { isa = PBXGroup; children = ( + F01DAE322C2B032A00521E46 /* RelaySelection.swift */, F050AE5F2B73A41E003F4EDB /* AllLocationDataSource.swift */, F04413602BA45CD70018A6EE /* CustomListLocationNodeBuilder.swift */, F050AE612B74DBAC003F4EDB /* CustomListsDataSource.swift */, @@ -2853,7 +2859,6 @@ F0BE65362B9F136A005CC385 /* LocationSectionHeaderView.swift */, 5888AD86227B17950051EB06 /* LocationViewController.swift */, 7AB3BEB42BD7A6CB00E34384 /* LocationViewControllerWrapper.swift */, - 7A3EFAAA2BDFDAE800318736 /* RelaySelection.swift */, ); path = SelectLocation; sourceTree = ""; @@ -3954,7 +3959,7 @@ 7AE241492C20682B0076CE33 /* Presentation controllers */ = { isa = PBXGroup; children = ( - 7AE241482C20682B0076CE33 /* FormsheetPresentationController.swift */, + F03A69F82C2AD413000E2E7E /* FormsheetPresentationController.swift */, ); path = "Presentation controllers"; sourceTree = ""; @@ -3962,7 +3967,7 @@ 7AEBA52D2C2310D20018BEC5 /* Extensions */ = { isa = PBXGroup; children = ( - 7AEBA52B2C22C65B0018BEC5 /* TimeInterval+Timeout.swift */, + F03A69F62C2AD2D5000E2E7E /* TimeInterval+Timeout.swift */, ); path = Extensions; sourceTree = ""; @@ -5453,6 +5458,7 @@ A9A5F9EB2ACB05160083449F /* CharacterSet+IPAddress.swift in Sources */, F0D8825C2B04F70E00D3EF9A /* OutgoingConnectionData.swift in Sources */, 44B3C43D2C00CBBD0079782C /* PacketTunnelActorReducerTests.swift in Sources */, + F0A086902C22D6A700BF83E7 /* TunnelSettingsStrategyTests.swift in Sources */, A9A5F9EC2ACB05160083449F /* CodingErrors+CustomErrorDescription.swift in Sources */, A9A5F9ED2ACB05160083449F /* NSRegularExpression+IPAddress.swift in Sources */, A9A5F9EE2ACB05160083449F /* RESTCreateApplePaymentResponse+Localization.swift in Sources */, @@ -5474,6 +5480,7 @@ A9A5F9F62ACB05160083449F /* TunnelStatusNotificationProvider.swift in Sources */, A9A5F9F72ACB05160083449F /* NotificationProviderProtocol.swift in Sources */, A9A5F9F82ACB05160083449F /* NotificationProviderIdentifier.swift in Sources */, + F0A086922C22E0F100BF83E7 /* Tunnel+Settings.swift in Sources */, A9A5F9F92ACB05160083449F /* NotificationProvider.swift in Sources */, A9A5F9FA2ACB05160083449F /* InAppNotificationDescriptor.swift in Sources */, A9A5F9FB2ACB05160083449F /* InAppNotificationProvider.swift in Sources */, @@ -5713,6 +5720,7 @@ 5891BF5125E66B1E006D6FB0 /* UIBarButtonItem+KeyboardNavigation.swift in Sources */, 58E511E628DDDEAC00B0BCDE /* CodingErrors+CustomErrorDescription.swift in Sources */, 58C76A0B2A338E4300100D75 /* BackgroundTask.swift in Sources */, + F0A0868E2C22D60100BF83E7 /* Tunnel+Settings.swift in Sources */, 7A9CCCC32A96302800DD6A34 /* ApplicationCoordinator.swift in Sources */, 5864AF0729C78843005B0CD9 /* SettingsCellFactory.swift in Sources */, 587B75412668FD7800DEF7E9 /* AccountExpirySystemNotificationProvider.swift in Sources */, @@ -5752,6 +5760,7 @@ 7A9CCCB32A96302800DD6A34 /* WelcomeCoordinator.swift in Sources */, 587B753B2666467500DEF7E9 /* NotificationBannerView.swift in Sources */, 5827B0922B0CAB2800CCBBA1 /* MethodSettingsViewController.swift in Sources */, + F01DAE332C2B032A00521E46 /* RelaySelection.swift in Sources */, 58B993B12608A34500BA7811 /* LoginContentView.swift in Sources */, 7A516C2E2B6D357500BBD33D /* URL+Scoping.swift in Sources */, 5878A27529093A310096FC88 /* StorePaymentEvent.swift in Sources */, @@ -5800,6 +5809,7 @@ 58CEB30C2AFD586600E6E088 /* DynamicBackgroundConfiguration.swift in Sources */, 587B7536266528A200DEF7E9 /* NotificationManager.swift in Sources */, 5820EDA9288FE064006BF4E4 /* DeviceManagementInteractor.swift in Sources */, + F03A69F92C2AD414000E2E7E /* FormsheetPresentationController.swift in Sources */, 7A9CCCB92A96302800DD6A34 /* LocationCoordinator.swift in Sources */, 58FB865A26EA214400F188BC /* RelayCacheTrackerObserver.swift in Sources */, 581DFAEC2B1770C1005D6D1C /* AccessMethodViewModel+NavigationItem.swift in Sources */, @@ -5920,7 +5930,6 @@ 5807E2C02432038B00F5FF30 /* String+Split.swift in Sources */, 58B26E242943520C00D5980C /* NotificationProviderProtocol.swift in Sources */, 5877F94E2A0A59AA0052D9E9 /* NotificationResponse.swift in Sources */, - 7AE2414A2C20682B0076CE33 /* FormsheetPresentationController.swift in Sources */, 7A6389E52B7E4247008E77E1 /* EditCustomListCoordinator.swift in Sources */, 58677712290976FB006F721F /* SettingsInteractor.swift in Sources */, 58EF875D2B1638BF00C098B2 /* ProxyConfigurationTesterProtocol.swift in Sources */, @@ -5930,7 +5939,6 @@ 5878F50029CDA742003D4BE2 /* UIView+AutoLayoutBuilder.swift in Sources */, 7A28826D2BAAC9DE00FD9F20 /* IPOverrideHeaderView.swift in Sources */, A98502032B627B120061901E /* LocalNetworkProbe.swift in Sources */, - 7A3EFAAB2BDFDAE800318736 /* RelaySelection.swift in Sources */, 7A6F2FA92AFD0842006D0856 /* CustomDNSDataSource.swift in Sources */, 58EF580B25D69D7A00AEBA94 /* ProblemReportSubmissionOverlayView.swift in Sources */, 5892A45E265FABFF00890742 /* EmptyTableViewHeaderFooterView.swift in Sources */, @@ -6295,11 +6303,11 @@ F0ACE31E2BE4E4F2006D5333 /* AccountsProxy+Stubs.swift in Sources */, F0ACE3202BE4E4F2006D5333 /* AccessTokenManager+Stubs.swift in Sources */, F0ACE32C2BE4E77E006D5333 /* DeviceMock.swift in Sources */, - 7AEBA52C2C22C65B0018BEC5 /* TimeInterval+Timeout.swift in Sources */, F0ACE3222BE4E4F2006D5333 /* APIProxy+Stubs.swift in Sources */, F0ACE3332BE516F1006D5333 /* RESTRequestExecutor+Stubs.swift in Sources */, F0ACE32D2BE4E784006D5333 /* AccountMock.swift in Sources */, 7A52F96A2C1735AE00B133B9 /* RelaySelectorStub.swift in Sources */, + F03A69F72C2AD2D6000E2E7E /* TimeInterval+Timeout.swift in Sources */, F0ACE32F2BE4EA8B006D5333 /* MockProxyFactory.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/ios/MullvadVPN/AppDelegate.swift b/ios/MullvadVPN/AppDelegate.swift index f027b4dc59b4..33899bf807e2 100644 --- a/ios/MullvadVPN/AppDelegate.swift +++ b/ios/MullvadVPN/AppDelegate.swift @@ -78,6 +78,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD ipOverrideRepository: ipOverrideRepository ) + let constraintsUpdater = RelayConstraintsUpdater() + let multihopListener = MultihopStateListener() + let multihopUpdater = MultihopUpdater(listener: multihopListener) + relayCacheTracker = RelayCacheTracker( relayCache: ipOverrideWrapper, application: application, @@ -85,11 +89,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD ) addressCacheTracker = AddressCacheTracker(application: application, apiProxy: apiProxy, store: addressCache) - tunnelStore = TunnelStore(application: application) - let constraintsUpdater = RelayConstraintsUpdater() - let multihopListener = MultihopStateListener() - let multihopUpdater = MultihopUpdater(listener: multihopListener) + tunnelStore = TunnelStore(application: application) let relaySelector = RelaySelectorWrapper( relayCache: ipOverrideWrapper, diff --git a/ios/MullvadVPN/TunnelManager/Tunnel+Settings.swift b/ios/MullvadVPN/TunnelManager/Tunnel+Settings.swift new file mode 100644 index 000000000000..488fcc2ef76e --- /dev/null +++ b/ios/MullvadVPN/TunnelManager/Tunnel+Settings.swift @@ -0,0 +1,36 @@ +// +// Tunnel+Settings.swift +// MullvadVPN +// +// Created by Mojgan on 2024-06-19. +// Copyright © 2024 Mullvad VPN AB. All rights reserved. +// + +import MullvadLogging +import MullvadSettings + +protocol TunnelSettingsStrategyProtocol { + func shouldReconnectToNewRelay(oldSettings: LatestTunnelSettings, newSettings: LatestTunnelSettings) -> Bool +} + +struct TunnelSettingsStrategy: TunnelSettingsStrategyProtocol { + let logger: Logger? + + init(logger: Logger? = nil) { + self.logger = logger + } + + func shouldReconnectToNewRelay(oldSettings: LatestTunnelSettings, newSettings: LatestTunnelSettings) -> Bool { + switch (oldSettings, newSettings) { + case let (old, new) where old.relayConstraints != new.relayConstraints: + logger?.debug("Relay address changed from \(old.relayConstraints) to \(new.relayConstraints)") + return true + case let (old, new) where old.tunnelMultihopState != new.tunnelMultihopState: + logger? + .debug("Tunnel multi-hop state changed from \(old.tunnelMultihopState) to \(new.tunnelMultihopState)") + return true + default: + return false + } + } +} diff --git a/ios/MullvadVPN/TunnelManager/TunnelManager.swift b/ios/MullvadVPN/TunnelManager/TunnelManager.swift index d6d4be4d0616..877dbc1578d6 100644 --- a/ios/MullvadVPN/TunnelManager/TunnelManager.swift +++ b/ios/MullvadVPN/TunnelManager/TunnelManager.swift @@ -939,16 +939,16 @@ final class TunnelManager: StorePaymentObserver { let operation = AsyncBlockOperation(dispatchQueue: internalQueue) { let currentSettings = self._tunnelSettings var updatedSettings = self._tunnelSettings + let settingsStrategy = TunnelSettingsStrategy(logger: self.logger) modificationBlock(&updatedSettings) - // Select new relay only when relay constraints change. - let currentConstraints = currentSettings.relayConstraints - let updatedConstraints = updatedSettings.relayConstraints - let selectNewRelay = currentConstraints != updatedConstraints - self.setSettings(updatedSettings, persist: true) - self.reconnectTunnel(selectNewRelay: selectNewRelay, completionHandler: nil) + self.reconnectTunnel( + selectNewRelay: settingsStrategy + .shouldReconnectToNewRelay(oldSettings: currentSettings, newSettings: updatedSettings), + completionHandler: nil + ) } operation.completionBlock = { @@ -1146,27 +1146,27 @@ extension TunnelManager { ``` func delay(seconds: UInt) async throws { - try await Task.sleep(nanoseconds: UInt64(seconds) * 1_000_000_000) + try await Task.sleep(nanoseconds: UInt64(seconds) * 1_000_000_000) } Task { - print("Wait 5 seconds") - try await delay(seconds: 5) + print("Wait 5 seconds") + try await delay(seconds: 5) - print("Simulate active account") - self.tunnelManager.simulateAccountExpiration(option: .active) - try await delay(seconds: 5) + print("Simulate active account") + self.tunnelManager.simulateAccountExpiration(option: .active) + try await delay(seconds: 5) - print("Simulate close to expiry") - self.tunnelManager.simulateAccountExpiration(option: .closeToExpiry) - try await delay(seconds: 10) + print("Simulate close to expiry") + self.tunnelManager.simulateAccountExpiration(option: .closeToExpiry) + try await delay(seconds: 10) - print("Simulate expired account") - self.tunnelManager.simulateAccountExpiration(option: .expired) - try await delay(seconds: 5) + print("Simulate expired account") + self.tunnelManager.simulateAccountExpiration(option: .expired) + try await delay(seconds: 5) - print("Simulate active account") - self.tunnelManager.simulateAccountExpiration(option: .active) + print("Simulate active account") + self.tunnelManager.simulateAccountExpiration(option: .active) } ``` diff --git a/ios/MullvadVPN/TunnelManager/TunnelState+UI.swift b/ios/MullvadVPN/TunnelManager/TunnelState+UI.swift index 3422c8602d88..1f524f56e8ad 100644 --- a/ios/MullvadVPN/TunnelManager/TunnelState+UI.swift +++ b/ios/MullvadVPN/TunnelManager/TunnelState+UI.swift @@ -187,8 +187,8 @@ extension TunnelState { value: "Quantum secure connection. Connected to %@, %@", comment: "" ), - tunnelInfo.exit.location.city, // TODO: Multihop - tunnelInfo.exit.location.country // TODO: Multihop + tunnelInfo.exit.location.city, + tunnelInfo.exit.location.country ) } else { String( @@ -198,8 +198,8 @@ extension TunnelState { value: "Secure connection. Connected to %@, %@", comment: "" ), - tunnelInfo.exit.location.city, // TODO: Multihop - tunnelInfo.exit.location.country // TODO: Multihop + tunnelInfo.exit.location.city, + tunnelInfo.exit.location.country ) } @@ -219,8 +219,8 @@ extension TunnelState { value: "Reconnecting to %@, %@", comment: "" ), - tunnelInfo.exit.location.city, // TODO: Multihop - tunnelInfo.exit.location.country // TODO: Multihop + tunnelInfo.exit.location.city, + tunnelInfo.exit.location.country ) case .waitingForConnectivity(.noConnection), .error: diff --git a/ios/MullvadVPN/TunnelManager/TunnelState.swift b/ios/MullvadVPN/TunnelManager/TunnelState.swift index 2c5a6109b678..34177a5be21a 100644 --- a/ios/MullvadVPN/TunnelManager/TunnelState.swift +++ b/ios/MullvadVPN/TunnelManager/TunnelState.swift @@ -78,29 +78,44 @@ enum TunnelState: Equatable, CustomStringConvertible { case error(BlockedStateReason) var description: String { - switch self { + return switch self { case .pendingReconnect: "pending reconnect after disconnect" case let .connecting(tunnelRelays, isPostQuantum): if let tunnelRelays { - "connecting \(isPostQuantum ? "(PQ) " : "")to \(tunnelRelays.exit.hostname)" // TODO: Multihop + """ + connecting \(isPostQuantum ? "(PQ) " : "")\ + to \(tunnelRelays.exit.hostname)\ + \(tunnelRelays.entry.flatMap { " via \($0.hostname)" } ?? "") + """ } else { "connecting\(isPostQuantum ? " (PQ)" : ""), fetching relay" } case let .connected(tunnelRelays, isPostQuantum): - "connected \(isPostQuantum ? "(PQ) " : "")to \(tunnelRelays.exit.hostname)" // TODO: Multihop + """ + connected \(isPostQuantum ? "(PQ) " : "")\ + to \(tunnelRelays.exit.hostname)\ + \(tunnelRelays.entry.flatMap { " via \($0.hostname)" } ?? "") + """ case let .disconnecting(actionAfterDisconnect): "disconnecting and then \(actionAfterDisconnect)" case .disconnected: "disconnected" case let .reconnecting(tunnelRelays, isPostQuantum): - "reconnecting \(isPostQuantum ? "(PQ) " : "")to \(tunnelRelays.exit.hostname)" // TODO: Multihop + """ + reconnecting \(isPostQuantum ? "(PQ) " : "")\ + to \(tunnelRelays.exit.hostname)\ + \(tunnelRelays.entry.flatMap { " via \($0.hostname)" } ?? "") + """ case .waitingForConnectivity: "waiting for connectivity" case let .error(blockedStateReason): "error state: \(blockedStateReason)" case let .negotiatingPostQuantumKey(tunnelRelays, _): - "negotiating key with \(tunnelRelays.exit.hostname)" // TODO: Multihop + """ + negotiating key with exit relay: \(tunnelRelays.exit.hostname)\ + \(tunnelRelays.entry.flatMap { " via \($0.hostname)" } ?? "") + """ } } diff --git a/ios/MullvadVPN/View controllers/Tunnel/TunnelControlView.swift b/ios/MullvadVPN/View controllers/Tunnel/TunnelControlView.swift index c627f85fa360..8e7cac0676cb 100644 --- a/ios/MullvadVPN/View controllers/Tunnel/TunnelControlView.swift +++ b/ios/MullvadVPN/View controllers/Tunnel/TunnelControlView.swift @@ -139,7 +139,7 @@ final class TunnelControlView: UIView { connectButtonBlurView.isEnabled = model.enableButtons cityLabel.attributedText = attributedStringForLocation(string: model.city) countryLabel.attributedText = attributedStringForLocation(string: model.country) - connectionPanel.connectedRelayName = model.connectedRelayName + connectionPanel.connectedRelayName = model.connectedRelaysName connectionPanel.dataSource = model.connectionPanel updateSecureLabel(tunnelState: tunnelState) @@ -227,14 +227,15 @@ final class TunnelControlView: UIView { private func updateTunnelRelays(tunnelRelays: SelectedRelays?) { if let tunnelRelays { cityLabel.attributedText = attributedStringForLocation( - string: tunnelRelays.exit.location.city // TODO: Multihop + string: tunnelRelays.exit.location.city ) countryLabel.attributedText = attributedStringForLocation( - string: tunnelRelays.exit.location.country // TODO: Multihop + string: tunnelRelays.exit.location.country ) connectionPanel.isHidden = false - connectionPanel.connectedRelayName = tunnelRelays.exit.hostname // TODO: Multihop + connectionPanel.connectedRelayName = tunnelRelays.exit + .hostname + "\(tunnelRelays.entry.flatMap { " via \($0.hostname)" } ?? "")" } else { countryLabel.attributedText = attributedStringForLocation(string: " ") cityLabel.attributedText = attributedStringForLocation(string: " ") diff --git a/ios/MullvadVPN/View controllers/Tunnel/TunnelControlViewModel.swift b/ios/MullvadVPN/View controllers/Tunnel/TunnelControlViewModel.swift index c0df319d2505..d1ce7b3a7a93 100644 --- a/ios/MullvadVPN/View controllers/Tunnel/TunnelControlViewModel.swift +++ b/ios/MullvadVPN/View controllers/Tunnel/TunnelControlViewModel.swift @@ -14,7 +14,7 @@ struct TunnelControlViewModel { let enableButtons: Bool let city: String let country: String - let connectedRelayName: String + let connectedRelaysName: String let outgoingConnectionInfo: OutgoingConnectionInfo? var connectionPanel: ConnectionPanelData? { @@ -29,7 +29,7 @@ struct TunnelControlViewModel { } return ConnectionPanelData( - inAddress: "\(tunnelRelays.exit.endpoint.ipv4Relay.ip)\(portAndTransport)", // TODO: Multihop + inAddress: "\(tunnelRelays.entry?.endpoint.ipv4Relay.ip ?? tunnelRelays.exit.endpoint.ipv4Relay.ip)\(portAndTransport)", outAddress: outgoingConnectionInfo?.outAddress ) } @@ -41,7 +41,7 @@ struct TunnelControlViewModel { enableButtons: true, city: "", country: "", - connectedRelayName: "", + connectedRelaysName: "", outgoingConnectionInfo: nil ) } @@ -53,7 +53,7 @@ struct TunnelControlViewModel { enableButtons: enableButtons, city: city, country: country, - connectedRelayName: connectedRelayName, + connectedRelaysName: connectedRelaysName, outgoingConnectionInfo: nil ) } @@ -65,7 +65,7 @@ struct TunnelControlViewModel { enableButtons: enableButtons, city: city, country: country, - connectedRelayName: connectedRelayName, + connectedRelaysName: connectedRelaysName, outgoingConnectionInfo: outgoingConnectionInfo ) } diff --git a/ios/MullvadVPN/View controllers/Tunnel/TunnelViewController.swift b/ios/MullvadVPN/View controllers/Tunnel/TunnelViewController.swift index b5d0dfab64bf..95e9260d207f 100644 --- a/ios/MullvadVPN/View controllers/Tunnel/TunnelViewController.swift +++ b/ios/MullvadVPN/View controllers/Tunnel/TunnelViewController.swift @@ -150,15 +150,15 @@ class TunnelViewController: UIViewController, RootContainment { case let .connecting(tunnelRelays, _): mapViewController.removeLocationMarker() contentView.setAnimatingActivity(true) - mapViewController.setCenter(tunnelRelays?.exit.location.geoCoordinate, animated: animated) // TODO: Multihop + mapViewController.setCenter(tunnelRelays?.exit.location.geoCoordinate, animated: animated) case let .reconnecting(tunnelRelays, _), let .negotiatingPostQuantumKey(tunnelRelays, _): mapViewController.removeLocationMarker() contentView.setAnimatingActivity(true) - mapViewController.setCenter(tunnelRelays.exit.location.geoCoordinate, animated: animated) // TODO: Multihop + mapViewController.setCenter(tunnelRelays.exit.location.geoCoordinate, animated: animated) case let .connected(tunnelRelays, _): - let center = tunnelRelays.exit.location.geoCoordinate // TODO: Multihop + let center = tunnelRelays.exit.location.geoCoordinate mapViewController.setCenter(center, animated: animated) { self.contentView.setAnimatingActivity(false) diff --git a/ios/MullvadVPNTests/MullvadREST/Relay/RelaySelectorWrapperTests.swift b/ios/MullvadVPNTests/MullvadREST/Relay/RelaySelectorWrapperTests.swift index a1ecb02fdfc2..c7a3c968d635 100644 --- a/ios/MullvadVPNTests/MullvadREST/Relay/RelaySelectorWrapperTests.swift +++ b/ios/MullvadVPNTests/MullvadREST/Relay/RelaySelectorWrapperTests.swift @@ -34,7 +34,6 @@ class RelaySelectorWrapperTests: XCTestCase { relayCache: relayCache, multihopUpdater: multihopUpdater ) - multihopStateListener.onNewMultihop?(.off) let selectedRelays = try wrapper.selectRelays(with: RelayConstraints(), connectionAttemptCount: 0) diff --git a/ios/MullvadVPNTests/MullvadVPN/TunnelManager/TunnelSettingsStrategyTests.swift b/ios/MullvadVPNTests/MullvadVPN/TunnelManager/TunnelSettingsStrategyTests.swift new file mode 100644 index 000000000000..ec3727832d8b --- /dev/null +++ b/ios/MullvadVPNTests/MullvadVPN/TunnelManager/TunnelSettingsStrategyTests.swift @@ -0,0 +1,101 @@ +// +// TunnelSettingsStrategyTests.swift +// MullvadVPNTests +// +// Created by Mojgan on 2024-06-19. +// Copyright © 2024 Mullvad VPN AB. All rights reserved. +// +import MullvadSettings +import MullvadTypes +import XCTest + +final class TunnelSettingsStrategyTests: XCTestCase { + func connectToNewRelayOnMultihopChangesTest() { + var currentSettings = LatestTunnelSettings() + TunnelSettingsUpdate.multihop(.off).apply(to: ¤tSettings) + + var updatedSettings = currentSettings + TunnelSettingsUpdate.multihop(.on).apply(to: &updatedSettings) + + let tunnelSettingsStrategy = TunnelSettingsStrategy() + XCTAssertTrue(tunnelSettingsStrategy.shouldReconnectToNewRelay( + oldSettings: currentSettings, + newSettings: updatedSettings + )) + } + + func connectToNewRelayOnRelaysConstraintChangeTest() { + var currentSettings = LatestTunnelSettings() + TunnelSettingsUpdate.relayConstraints(RelayConstraints()).apply(to: ¤tSettings) + + var updatedSettings = currentSettings + TunnelSettingsUpdate.relayConstraints(RelayConstraints( + exitLocations: .only(UserSelectedRelays(locations: [.country("zz")])), + port: .only(9999), + filter: .only(.init(ownership: .rented, providers: .only(["foo", "bar"]))) + )).apply(to: &updatedSettings) + + let tunnelSettingsStrategy = TunnelSettingsStrategy() + XCTAssertTrue(tunnelSettingsStrategy.shouldReconnectToNewRelay( + oldSettings: currentSettings, + newSettings: updatedSettings + )) + } + + func connectToCurrentRelayOnDNSSettingsChangeTest() { + let currentSettings = LatestTunnelSettings() + + var updatedSettings = currentSettings + var dnsSettings = DNSSettings() + dnsSettings.blockingOptions = [.blockAdvertising, .blockTracking] + dnsSettings.enableCustomDNS = true + TunnelSettingsUpdate.dnsSettings(dnsSettings).apply(to: &updatedSettings) + + let tunnelSettingsStrategy = TunnelSettingsStrategy() + XCTAssertTrue(tunnelSettingsStrategy.shouldReconnectToNewRelay( + oldSettings: currentSettings, + newSettings: updatedSettings + )) + } + + func connectToCurrentRelayOnQuantumResistanceChangesTest() { + var currentSettings = LatestTunnelSettings() + TunnelSettingsUpdate.quantumResistance(.off).apply(to: ¤tSettings) + + var updatedSettings = currentSettings + TunnelSettingsUpdate.quantumResistance(.on).apply(to: &updatedSettings) + + let tunnelSettingsStrategy = TunnelSettingsStrategy() + XCTAssertTrue(tunnelSettingsStrategy.shouldReconnectToNewRelay( + oldSettings: currentSettings, + newSettings: updatedSettings + )) + } + + func connectToCurrentRelayOnWireGuardObfuscationChangeTest() { + var currentSettings = LatestTunnelSettings() + TunnelSettingsUpdate.obfuscation(WireGuardObfuscationSettings(state: .off, port: .port80)) + .apply(to: ¤tSettings) + + var updatedSettings = currentSettings + TunnelSettingsUpdate.obfuscation(WireGuardObfuscationSettings(state: .automatic, port: .automatic)) + .apply(to: &updatedSettings) + + let tunnelSettingsStrategy = TunnelSettingsStrategy() + XCTAssertTrue(tunnelSettingsStrategy.shouldReconnectToNewRelay( + oldSettings: currentSettings, + newSettings: updatedSettings + )) + } + + func connectToCurrentRelayWhenNothingChangeTest() { + let currentSettings = LatestTunnelSettings() + let updatedSettings = currentSettings + + let tunnelSettingsStrategy = TunnelSettingsStrategy() + XCTAssertFalse(tunnelSettingsStrategy.shouldReconnectToNewRelay( + oldSettings: currentSettings, + newSettings: updatedSettings + )) + } +} diff --git a/ios/PacketTunnelCore/Actor/PacketTunnelActor+PostQuantum.swift b/ios/PacketTunnelCore/Actor/PacketTunnelActor+PostQuantum.swift index 30348546f173..a03f0ae4f98f 100644 --- a/ios/PacketTunnelCore/Actor/PacketTunnelActor+PostQuantum.swift +++ b/ios/PacketTunnelCore/Actor/PacketTunnelActor+PostQuantum.swift @@ -75,8 +75,8 @@ extension PacketTunnelActor { state = .connecting(connectionState) try? await tunnelAdapter.start(configuration: configurationBuilder.makeConfiguration()) - // Resume tunnel monitoring and use IPv4 gateway as a probe address. - tunnelMonitor.start(probeAddress: connectionState.selectedRelays.exit.endpoint.ipv4Gateway) // TODO: Multihop + // Resume tunnel monitoring and use exit IPv4 gateway as a probe address. + tunnelMonitor.start(probeAddress: connectionState.selectedRelays.exit.endpoint.ipv4Gateway) // Restart default path observer and notify the observer with the current path that might have changed while // path observer was paused. startDefaultPathObserver(notifyObserverWithCurrentPath: false) diff --git a/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift b/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift index 715cfd840dc3..ebd88109dc9c 100644 --- a/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift +++ b/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift @@ -316,7 +316,7 @@ extension PacketTunnelActor { try await tunnelAdapter.start(configuration: configurationBuilder.makeConfiguration()) // Resume tunnel monitoring and use IPv4 gateway as a probe address. - tunnelMonitor.start(probeAddress: connectionState.selectedRelays.exit.endpoint.ipv4Gateway) // TODO: Multihop + tunnelMonitor.start(probeAddress: connectionState.selectedRelays.exit.endpoint.ipv4Gateway) } /** @@ -384,6 +384,8 @@ extension PacketTunnelActor { return nil } let selectedRelays = try callRelaySelector(nil, 0) + let selectedRelay = selectedRelays.entry ?? selectedRelays.exit + return State.ConnectionData( selectedRelays: selectedRelays, relayConstraints: settings.relayConstraints, @@ -392,9 +394,9 @@ extension PacketTunnelActor { networkReachability: networkReachability, connectionAttemptCount: 0, lastKeyRotation: lastKeyRotation, - connectedEndpoint: selectedRelays.exit.endpoint, // TODO: Multihop + connectedEndpoint: selectedRelay.endpoint, transportLayer: .udp, - remotePort: selectedRelays.exit.endpoint.ipv4Relay.port, // TODO: Multihop + remotePort: selectedRelay.endpoint.ipv4Relay.port, isPostQuantum: settings.quantumResistance.isEnabled ) } @@ -416,8 +418,14 @@ extension PacketTunnelActor { guard let connectionState = try makeConnectionState(nextRelays: nextRelays, settings: settings, reason: reason) else { return nil } + // + // Obfuscator will always be applied to the first hop, + // i.e., the entry in multi-hop or exit in single-hop. + // + let endpoint = connectionState.selectedRelays.entry?.endpoint ?? connectionState.selectedRelays.exit.endpoint + let obfuscatedEndpoint = protocolObfuscator.obfuscate( - connectionState.selectedRelays.exit.endpoint, // TODO: Multihop + connectionState.connectedEndpoint, settings: settings, retryAttempts: connectionState.selectedRelays.exit.retryAttempts // TODO: Multihop ) diff --git a/ios/PacketTunnelCore/Actor/PacketTunnelActorCommand.swift b/ios/PacketTunnelCore/Actor/PacketTunnelActorCommand.swift index fd731a32e150..2579a7de39d5 100644 --- a/ios/PacketTunnelCore/Actor/PacketTunnelActorCommand.swift +++ b/ios/PacketTunnelCore/Actor/PacketTunnelActorCommand.swift @@ -53,7 +53,7 @@ extension PacketTunnelActor { case .random: return "reconnect(random, \(stopTunnelMonitor))" case let .preSelected(selectedRelays): - return "reconnect(\(selectedRelays.exit.hostname), \(stopTunnelMonitor))" // TODO: Multihop + return "reconnect(\(selectedRelays.exit.hostname)\(selectedRelays.entry.flatMap { " via \($0.hostname)" } ?? ""), \(stopTunnelMonitor))" } case let .error(reason): return "error(\(reason))" diff --git a/ios/PacketTunnelCore/Actor/StartOptions.swift b/ios/PacketTunnelCore/Actor/StartOptions.swift index 4c8ad7587891..9af92fe34ce2 100644 --- a/ios/PacketTunnelCore/Actor/StartOptions.swift +++ b/ios/PacketTunnelCore/Actor/StartOptions.swift @@ -27,7 +27,8 @@ public struct StartOptions { public func logFormat() -> String { var s = "Start the tunnel via \(launchSource)" if let selectedRelays { - s += ", connect to \(selectedRelays.exit.hostname)" // TODO: Multihop + s += ", connect to \(selectedRelays.exit.hostname)" + s += selectedRelays.entry.flatMap { " via \($0.hostname)" } ?? "" } s += "." return s diff --git a/ios/PacketTunnelCore/Actor/State+Extensions.swift b/ios/PacketTunnelCore/Actor/State+Extensions.swift index 69d6579a9db9..fe26544204f3 100644 --- a/ios/PacketTunnelCore/Actor/State+Extensions.swift +++ b/ios/PacketTunnelCore/Actor/State+Extensions.swift @@ -47,10 +47,9 @@ extension State { func logFormat() -> String { switch self { case let .connecting(connState), let .connected(connState), let .reconnecting(connState): - let hostname = connState.selectedRelays.exit.hostname // TODO: Multihop - return """ - \(name) to \(hostname), \ + \(name) to \(connState.selectedRelays.entry.flatMap { "entry location: \($0.hostname) " } ?? ""),\ + exit location: \(connState.selectedRelays.exit.hostname), \ key: \(connState.keyPolicy.logFormat()), \ net: \(connState.networkReachability), \ attempt: \(connState.connectionAttemptCount)