diff options
| author | Andrej Mihajlov <and@mullvad.net> | 2023-06-19 17:52:05 +0200 |
|---|---|---|
| committer | Emīls <emils@mullvad.net> | 2023-07-11 17:17:41 +0200 |
| commit | c75db498ecbe738c8ebf435f4176fe057d57689a (patch) | |
| tree | e3e8b603b781a2ae70f98b41183566e93b8ebdcc | |
| parent | 473093688bb8f80d407fe7ce3de6d3533c064e57 (diff) | |
| download | mullvadvpn-c75db498ecbe738c8ebf435f4176fe057d57689a.tar.xz mullvadvpn-c75db498ecbe738c8ebf435f4176fe057d57689a.zip | |
Add bindings for tunnel obfuscator and a test
19 files changed, 1104 insertions, 1 deletions
diff --git a/Cargo.lock b/Cargo.lock index b237808e43..7f8d7a7007 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4362,6 +4362,18 @@ dependencies = [ ] [[package]] +name = "tunnel-obfuscator-proxy" +version = "0.0.0" +dependencies = [ + "cbindgen", + "libc", + "log", + "oslog", + "tokio", + "tunnel-obfuscation", +] + +[[package]] name = "typenum" version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/Cargo.toml b/Cargo.toml index 246e2a1e02..7e674b734a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ resolver = "2" members = [ "android/translations-converter", "ios/MullvadTransport/shadowsocks-proxy", + "ios/TunnelObfuscation/tunnel-obfuscator-proxy", "mullvad-daemon", "mullvad-cli", "mullvad-fs", diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj index e7dea2516a..0c93ba9efe 100644 --- a/ios/MullvadVPN.xcodeproj/project.pbxproj +++ b/ios/MullvadVPN.xcodeproj/project.pbxproj @@ -98,6 +98,8 @@ 583FE01029C0F532006E85F9 /* CustomSplitViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 583FE00F29C0F532006E85F9 /* CustomSplitViewController.swift */; }; 583FE01229C0F99A006E85F9 /* PresentationControllerDismissalInterceptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 583FE01129C0F99A006E85F9 /* PresentationControllerDismissalInterceptor.swift */; }; 583FE02429C1ACB3006E85F9 /* RESTCreateApplePaymentResponse+Localization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06FAE67828F83CA50033DD93 /* RESTCreateApplePaymentResponse+Localization.swift */; }; + 584023222A406BF5007B27AC /* TunnelObfuscator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 584023212A406BF5007B27AC /* TunnelObfuscator.swift */; }; + 584023292A407F5F007B27AC /* libtunnel_obfuscator_proxy.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 584023282A407F5F007B27AC /* libtunnel_obfuscator_proxy.a */; }; 58421030282D8A3C00F24E46 /* UpdateAccountDataOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5842102F282D8A3C00F24E46 /* UpdateAccountDataOperation.swift */; }; 58421032282E42B000F24E46 /* UpdateDeviceDataOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58421031282E42B000F24E46 /* UpdateDeviceDataOperation.swift */; }; 58435AC229CB2A350099C71B /* LocationCellFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58435AC129CB2A350099C71B /* LocationCellFactory.swift */; }; @@ -113,6 +115,9 @@ 584F99202902CBDD001F858D /* libRelaySelector.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5898D29829017DAC00EB5EBA /* libRelaySelector.a */; }; 5859A55329CD9B1300F66591 /* ChangeLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5859A55229CD9B1300F66591 /* ChangeLog.swift */; }; 5859A55529CD9DD900F66591 /* changes.txt in Resources */ = {isa = PBXBuildFile; fileRef = 5859A55429CD9DD800F66591 /* changes.txt */; }; + 585A02E92A4B283000C6CAFF /* TCPUnsafeListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = 585A02E82A4B283000C6CAFF /* TCPUnsafeListener.swift */; }; + 585A02EB2A4B285800C6CAFF /* UDPConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 585A02EA2A4B285800C6CAFF /* UDPConnection.swift */; }; + 585A02ED2A4B28F300C6CAFF /* TCPConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 585A02EC2A4B28F300C6CAFF /* TCPConnection.swift */; }; 585B4B8726D9098900555C4C /* TunnelStatusNotificationProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58A94AE326CFD945001CB97C /* TunnelStatusNotificationProvider.swift */; }; 585CA70F25F8C44600B47C62 /* UIMetrics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 585CA70E25F8C44600B47C62 /* UIMetrics.swift */; }; 585E820327F3285E00939F0E /* SendStoreReceiptOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 585E820227F3285E00939F0E /* SendStoreReceiptOperation.swift */; }; @@ -132,6 +137,8 @@ 5867771629097C5B006F721F /* ProductState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5867771529097C5B006F721F /* ProductState.swift */; }; 5868585524054096000B8131 /* AppButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5868585424054096000B8131 /* AppButton.swift */; }; 586891CD29D452E4002A8278 /* SafariCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 586891CC29D452E4002A8278 /* SafariCoordinator.swift */; }; + 58695AA02A4ADA9200328DB3 /* TunnelObfuscationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58695A9F2A4ADA9200328DB3 /* TunnelObfuscationTests.swift */; }; + 58695AA72A4B109F00328DB3 /* TunnelObfuscation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5840231F2A406BF5007B27AC /* TunnelObfuscation.framework */; }; 586A0DCB2A20E359006C731C /* MullvadTypes.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 58D223D5294C8E5E0029F5F8 /* MullvadTypes.framework */; }; 586A0DD12A20E371006C731C /* WireGuardKitTypes in Frameworks */ = {isa = PBXBuildFile; productRef = 586A0DD02A20E371006C731C /* WireGuardKitTypes */; }; 586A0DD42A20E4A9006C731C /* MullvadREST.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 06799ABC28F98E1D00ACD94E /* MullvadREST.framework */; }; @@ -222,6 +229,9 @@ 589A455C28E094BF00565204 /* OperationSmokeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58DF5B7E2852778600E92647 /* OperationSmokeTests.swift */; }; 589A455D28E094BF00565204 /* OperationObserverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 583E1E292848DF67004838B3 /* OperationObserverTests.swift */; }; 589A455F28E094BF00565204 /* OperationConditionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 580CBFB72848D503007878F0 /* OperationConditionTests.swift */; }; + 589C6A782A45AAB700DAD3EF /* tunnel_obfuscator_proxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 584023272A407679007B27AC /* tunnel_obfuscator_proxy.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 589C6A7C2A45AE0100DAD3EF /* TunnelObfuscation.h in Headers */ = {isa = PBXBuildFile; fileRef = 589C6A7B2A45AE0100DAD3EF /* TunnelObfuscation.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 589C6A7D2A45B06800DAD3EF /* TunnelObfuscation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5840231F2A406BF5007B27AC /* TunnelObfuscation.framework */; }; 58A1AA8C23F5584C009F7EA6 /* ConnectionPanelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58A1AA8B23F5584B009F7EA6 /* ConnectionPanelView.swift */; }; 58A3BDB028A1821A00C8C2C6 /* WgStats.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58A3BDAF28A1821A00C8C2C6 /* WgStats.swift */; }; 58A8EE5A2976BFBB009C0F8D /* SKError+Localized.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58A8EE592976BFBB009C0F8D /* SKError+Localized.swift */; }; @@ -407,6 +417,8 @@ A9D99BA52A1F808900DE27D3 /* RelayCache.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 063F02732902B63F001FA09F /* RelayCache.framework */; }; A9D99BA62A1F809C00DE27D3 /* libRelaySelector.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5898D29829017DAC00EB5EBA /* libRelaySelector.a */; }; A9D99BA92A1F81B700DE27D3 /* MullvadTransport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A97F1F412A1F4E1A00ECEFDE /* MullvadTransport.framework */; }; + A9EC20EF2A5D79ED0040D56E /* TunnelObfuscation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5840231F2A406BF5007B27AC /* TunnelObfuscation.framework */; }; + A9EC20F02A5D79ED0040D56E /* TunnelObfuscation.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 5840231F2A406BF5007B27AC /* TunnelObfuscation.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; E1187ABC289BBB850024E748 /* OutOfTimeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1187ABA289BBB850024E748 /* OutOfTimeViewController.swift */; }; E1187ABD289BBB850024E748 /* OutOfTimeContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1187ABB289BBB850024E748 /* OutOfTimeContentView.swift */; }; E158B360285381C60002F069 /* String+AccountFormatting.swift in Sources */ = {isa = PBXBuildFile; fileRef = E158B35F285381C60002F069 /* String+AccountFormatting.swift */; }; @@ -467,6 +479,13 @@ remoteGlobalIDString = 06799ABB28F98E1D00ACD94E; remoteInfo = MullvadREST; }; + 58695AA22A4ADA9200328DB3 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 58CE5E58224146200008646E /* Project object */; + proxyType = 1; + remoteGlobalIDString = 5840231E2A406BF5007B27AC; + remoteInfo = TunnelObfuscation; + }; 586A0DCD2A20E359006C731C /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 58CE5E58224146200008646E /* Project object */; @@ -691,6 +710,13 @@ remoteGlobalIDString = A97F1F402A1F4E1A00ECEFDE; remoteInfo = MullvadTransport; }; + A9EC20F12A5D79ED0040D56E /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 58CE5E58224146200008646E /* Project object */; + proxyType = 1; + remoteGlobalIDString = 5840231E2A406BF5007B27AC; + remoteInfo = TunnelObfuscation; + }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -702,6 +728,7 @@ files = ( 58D223E7294C8F120029F5F8 /* MullvadTypes.framework in Embed Frameworks */, 58D223FA294C8FF10029F5F8 /* MullvadLogging.framework in Embed Frameworks */, + A9EC20F02A5D79ED0040D56E /* TunnelObfuscation.framework in Embed Frameworks */, 06799AD228F98E1D00ACD94E /* MullvadREST.framework in Embed Frameworks */, 58D223CD294C8BCB0029F5F8 /* Operations.framework in Embed Frameworks */, A97F1F482A1F4E1A00ECEFDE /* MullvadTransport.framework in Embed Frameworks */, @@ -711,6 +738,15 @@ name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; + 5840231D2A406BF5007B27AC /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = "include/$(PRODUCT_NAME)"; + dstSubfolderSpec = 16; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 586A0DD32A20E371006C731C /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -877,6 +913,10 @@ 583FE00D29C0D586006E85F9 /* OutOfTimeCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OutOfTimeCoordinator.swift; sourceTree = "<group>"; }; 583FE00F29C0F532006E85F9 /* CustomSplitViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomSplitViewController.swift; sourceTree = "<group>"; }; 583FE01129C0F99A006E85F9 /* PresentationControllerDismissalInterceptor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PresentationControllerDismissalInterceptor.swift; sourceTree = "<group>"; }; + 5840231F2A406BF5007B27AC /* TunnelObfuscation.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TunnelObfuscation.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 584023212A406BF5007B27AC /* TunnelObfuscator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelObfuscator.swift; sourceTree = "<group>"; }; + 584023272A407679007B27AC /* tunnel_obfuscator_proxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = tunnel_obfuscator_proxy.h; path = "tunnel-obfuscator-proxy/include/tunnel_obfuscator_proxy.h"; sourceTree = "<group>"; }; + 584023282A407F5F007B27AC /* libtunnel_obfuscator_proxy.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libtunnel_obfuscator_proxy.a; path = "../target/x86_64-apple-ios/debug/libtunnel_obfuscator_proxy.a"; sourceTree = "<group>"; }; 5840250322B11AB700E4CFEC /* MullvadEndpoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MullvadEndpoint.swift; sourceTree = "<group>"; }; 5840BE34279EDB16002836BA /* OperationError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OperationError.swift; sourceTree = "<group>"; }; 5842102D282D3FC200F24E46 /* ResultBlockOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResultBlockOperation.swift; sourceTree = "<group>"; }; @@ -899,6 +939,9 @@ 58561C98239A5D1500BD6B5E /* IPv4Endpoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPv4Endpoint.swift; sourceTree = "<group>"; }; 5859A55229CD9B1300F66591 /* ChangeLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChangeLog.swift; sourceTree = "<group>"; }; 5859A55429CD9DD800F66591 /* changes.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = changes.txt; sourceTree = "<group>"; }; + 585A02E82A4B283000C6CAFF /* TCPUnsafeListener.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TCPUnsafeListener.swift; sourceTree = "<group>"; }; + 585A02EA2A4B285800C6CAFF /* UDPConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UDPConnection.swift; sourceTree = "<group>"; }; + 585A02EC2A4B28F300C6CAFF /* TCPConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TCPConnection.swift; sourceTree = "<group>"; }; 585CA70E25F8C44600B47C62 /* UIMetrics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIMetrics.swift; sourceTree = "<group>"; }; 585DA87626B024A600B8C587 /* CachedRelays.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CachedRelays.swift; sourceTree = "<group>"; }; 585DA89226B0323E00B8C587 /* TunnelProviderMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelProviderMessage.swift; sourceTree = "<group>"; }; @@ -921,6 +964,8 @@ 5867771529097C5B006F721F /* ProductState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductState.swift; sourceTree = "<group>"; }; 5868585424054096000B8131 /* AppButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppButton.swift; sourceTree = "<group>"; }; 586891CC29D452E4002A8278 /* SafariCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariCoordinator.swift; sourceTree = "<group>"; }; + 58695A9D2A4ADA9100328DB3 /* TunnelObfuscationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TunnelObfuscationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 58695A9F2A4ADA9200328DB3 /* TunnelObfuscationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelObfuscationTests.swift; sourceTree = "<group>"; }; 586A95112901321B007BAF2B /* IPv6Endpoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPv6Endpoint.swift; sourceTree = "<group>"; }; 586A951329013235007BAF2B /* AnyIPEndpoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyIPEndpoint.swift; sourceTree = "<group>"; }; 586E54FA27A2DF6D0029B88B /* SendTunnelProviderMessageOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendTunnelProviderMessageOperation.swift; sourceTree = "<group>"; }; @@ -994,6 +1039,8 @@ 5898D2B12902A6DE00EB5EBA /* RelayConstraint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayConstraint.swift; sourceTree = "<group>"; }; 5898D2B62902A9EA00EB5EBA /* PacketTunnelRelay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PacketTunnelRelay.swift; sourceTree = "<group>"; }; 589A455228E094B300565204 /* OperationsTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = OperationsTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 589C6A7A2A45ACCA00DAD3EF /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; + 589C6A7B2A45AE0100DAD3EF /* TunnelObfuscation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TunnelObfuscation.h; sourceTree = "<group>"; }; 589D28772846250500F9A7B3 /* OperationCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OperationCondition.swift; sourceTree = "<group>"; }; 589D28782846250500F9A7B3 /* AsyncOperationQueue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AsyncOperationQueue.swift; sourceTree = "<group>"; }; 589D28792846250500F9A7B3 /* OperationObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OperationObserver.swift; sourceTree = "<group>"; }; @@ -1172,6 +1219,22 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 5840231C2A406BF5007B27AC /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 584023292A407F5F007B27AC /* libtunnel_obfuscator_proxy.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 58695A9A2A4ADA9100328DB3 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 58695AA72A4B109F00328DB3 /* TunnelObfuscation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 5898D28629017BD300EB5EBA /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -1210,6 +1273,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + A9EC20EF2A5D79ED0040D56E /* TunnelObfuscation.framework in Frameworks */, 58F0974E2A20C31100DA2DAD /* WireGuardKitTypes in Frameworks */, 5898D2A92901844E00EB5EBA /* libRelaySelector.a in Frameworks */, 58D223F9294C8FF00029F5F8 /* MullvadLogging.framework in Frameworks */, @@ -1226,6 +1290,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 589C6A7D2A45B06800DAD3EF /* TunnelObfuscation.framework in Frameworks */, A9D99BA92A1F81B700DE27D3 /* MullvadTransport.framework in Frameworks */, 5898D2AB2901845400EB5EBA /* libRelaySelector.a in Frameworks */, 5898D2AC2901845400EB5EBA /* libTunnelProviderMessaging.a in Frameworks */, @@ -1715,6 +1780,17 @@ path = "Supporting Files"; sourceTree = "<group>"; }; + 584023202A406BF5007B27AC /* TunnelObfuscation */ = { + isa = PBXGroup; + children = ( + 589C6A7A2A45ACCA00DAD3EF /* Info.plist */, + 584023272A407679007B27AC /* tunnel_obfuscator_proxy.h */, + 589C6A7B2A45AE0100DAD3EF /* TunnelObfuscation.h */, + 584023212A406BF5007B27AC /* TunnelObfuscator.swift */, + ); + path = TunnelObfuscation; + sourceTree = "<group>"; + }; 5846226F26E229CD0035F7C2 /* StorePaymentManager */ = { isa = PBXGroup; children = ( @@ -1743,6 +1819,7 @@ 584F991F2902CBDD001F858D /* Frameworks */ = { isa = PBXGroup; children = ( + 584023282A407F5F007B27AC /* libtunnel_obfuscator_proxy.a */, 01F1FF1D29F0627D007083C3 /* libshadowsocks_proxy.a */, ); name = Frameworks; @@ -1775,6 +1852,17 @@ path = Protocols; sourceTree = "<group>"; }; + 58695A9E2A4ADA9200328DB3 /* TunnelObfuscationTests */ = { + isa = PBXGroup; + children = ( + 58695A9F2A4ADA9200328DB3 /* TunnelObfuscationTests.swift */, + 585A02E82A4B283000C6CAFF /* TCPUnsafeListener.swift */, + 585A02EA2A4B285800C6CAFF /* UDPConnection.swift */, + 585A02EC2A4B28F300C6CAFF /* TCPConnection.swift */, + ); + path = TunnelObfuscationTests; + sourceTree = "<group>"; + }; 586A950B2901250A007BAF2B /* Operations */ = { isa = PBXGroup; children = ( @@ -1975,6 +2063,8 @@ 589A455328E094B300565204 /* OperationsTests */, 58CE5E7A224146470008646E /* PacketTunnel */, A97F1F422A1F4E1A00ECEFDE /* MullvadTransport */, + 584023202A406BF5007B27AC /* TunnelObfuscation */, + 58695A9E2A4ADA9200328DB3 /* TunnelObfuscationTests */, 58CE5E61224146200008646E /* Products */, 584F991F2902CBDD001F858D /* Frameworks */, ); @@ -1997,6 +2087,8 @@ 58D223D5294C8E5E0029F5F8 /* MullvadTypes.framework */, 58D223F3294C8FF00029F5F8 /* MullvadLogging.framework */, A97F1F412A1F4E1A00ECEFDE /* MullvadTransport.framework */, + 5840231F2A406BF5007B27AC /* TunnelObfuscation.framework */, + 58695A9D2A4ADA9100328DB3 /* TunnelObfuscationTests.xctest */, ); name = Products; sourceTree = "<group>"; @@ -2206,6 +2298,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 5840232A2A4081BF007B27AC /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 589C6A7C2A45AE0100DAD3EF /* TunnelObfuscation.h in Headers */, + 589C6A782A45AAB700DAD3EF /* tunnel_obfuscator_proxy.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 58D223A0294C8A480029F5F8 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -2308,6 +2409,43 @@ productReference = 06799ABC28F98E1D00ACD94E /* MullvadREST.framework */; productType = "com.apple.product-type.framework"; }; + 5840231E2A406BF5007B27AC /* TunnelObfuscation */ = { + isa = PBXNativeTarget; + buildConfigurationList = 584023232A406BF5007B27AC /* Build configuration list for PBXNativeTarget "TunnelObfuscation" */; + buildPhases = ( + 584023262A406C01007B27AC /* ShellScript */, + 5840232A2A4081BF007B27AC /* Headers */, + 5840231B2A406BF5007B27AC /* Sources */, + 5840231C2A406BF5007B27AC /* Frameworks */, + 5840231D2A406BF5007B27AC /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = TunnelObfuscation; + productName = TunnelObfuscator; + productReference = 5840231F2A406BF5007B27AC /* TunnelObfuscation.framework */; + productType = "com.apple.product-type.framework"; + }; + 58695A9C2A4ADA9100328DB3 /* TunnelObfuscationTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 58695AA62A4ADA9200328DB3 /* Build configuration list for PBXNativeTarget "TunnelObfuscationTests" */; + buildPhases = ( + 58695A992A4ADA9100328DB3 /* Sources */, + 58695A9A2A4ADA9100328DB3 /* Frameworks */, + 58695A9B2A4ADA9100328DB3 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 58695AA32A4ADA9200328DB3 /* PBXTargetDependency */, + ); + name = TunnelObfuscationTests; + productName = TunnelObfuscationTests; + productReference = 58695A9D2A4ADA9100328DB3 /* TunnelObfuscationTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; 5898D28829017BD300EB5EBA /* TunnelProviderMessaging */ = { isa = PBXNativeTarget; buildConfigurationList = 5898D28F29017BD400EB5EBA /* Build configuration list for PBXNativeTarget "TunnelProviderMessaging" */; @@ -2408,6 +2546,7 @@ 063F02782902B63F001FA09F /* PBXTargetDependency */, 58CE5E80224146470008646E /* PBXTargetDependency */, A97F1F462A1F4E1A00ECEFDE /* PBXTargetDependency */, + A9EC20F22A5D79ED0040D56E /* PBXTargetDependency */, ); name = MullvadVPN; packageProductDependencies = ( @@ -2576,7 +2715,7 @@ 58CE5E58224146200008646E /* Project object */ = { isa = PBXProject; attributes = { - LastSwiftUpdateCheck = 1410; + LastSwiftUpdateCheck = 1430; LastUpgradeCheck = 1420; ORGANIZATIONNAME = "Mullvad VPN AB"; TargetAttributes = { @@ -2586,6 +2725,13 @@ 06799ABB28F98E1D00ACD94E = { CreatedOnToolsVersion = 14.0.1; }; + 5840231E2A406BF5007B27AC = { + CreatedOnToolsVersion = 14.3.1; + LastSwiftMigration = 1430; + }; + 58695A9C2A4ADA9100328DB3 = { + CreatedOnToolsVersion = 14.3.1; + }; 5898D28829017BD300EB5EBA = { CreatedOnToolsVersion = 14.1; }; @@ -2675,6 +2821,8 @@ 58D223D4294C8E5E0029F5F8 /* MullvadTypes */, 58D223F2294C8FF00029F5F8 /* MullvadLogging */, A97F1F402A1F4E1A00ECEFDE /* MullvadTransport */, + 5840231E2A406BF5007B27AC /* TunnelObfuscation */, + 58695A9C2A4ADA9100328DB3 /* TunnelObfuscationTests */, ); }; /* End PBXProject section */ @@ -2696,6 +2844,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 58695A9B2A4ADA9100328DB3 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 589A455028E094B300565204 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -2793,6 +2948,24 @@ shellPath = /bin/sh; shellScript = "exec > $PROJECT_DIR/relays-prebuild.log 2>&1\n\n$PROJECT_DIR/relays-prebuild.sh\n"; }; + 584023262A406C01007B27AC /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 12; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "CARGO_TARGET_DIR=${PROJECT_DIR}/../target bash ${PROJECT_DIR}/build-rust-library.sh tunnel-obfuscator-proxy\n"; + }; A95F86B92A1F54F800245DAC /* ShellScript */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -2857,6 +3030,25 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 5840231B2A406BF5007B27AC /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 584023222A406BF5007B27AC /* TunnelObfuscator.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 58695A992A4ADA9100328DB3 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 58695AA02A4ADA9200328DB3 /* TunnelObfuscationTests.swift in Sources */, + 585A02ED2A4B28F300C6CAFF /* TCPConnection.swift in Sources */, + 585A02E92A4B283000C6CAFF /* TCPUnsafeListener.swift in Sources */, + 585A02EB2A4B285800C6CAFF /* UDPConnection.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 5898D28529017BD300EB5EBA /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -3317,6 +3509,11 @@ target = 06799ABB28F98E1D00ACD94E /* MullvadREST */; targetProxy = 58153073294CBE8B00D1702E /* PBXContainerItemProxy */; }; + 58695AA32A4ADA9200328DB3 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 5840231E2A406BF5007B27AC /* TunnelObfuscation */; + targetProxy = 58695AA22A4ADA9200328DB3 /* PBXContainerItemProxy */; + }; 586A0DCE2A20E359006C731C /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 58D223D4294C8E5E0029F5F8 /* MullvadTypes */; @@ -3471,6 +3668,11 @@ target = A97F1F402A1F4E1A00ECEFDE /* MullvadTransport */; targetProxy = A9D99BA72A1F81B100DE27D3 /* PBXContainerItemProxy */; }; + A9EC20F22A5D79ED0040D56E /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 5840231E2A406BF5007B27AC /* TunnelObfuscation */; + targetProxy = A9EC20F12A5D79ED0040D56E /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ @@ -3615,6 +3817,121 @@ }; name = Release; }; + 584023242A406BF5007B27AC /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 5808273928487E3E006B77A4 /* Base.xcconfig */; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 4; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = TunnelObfuscation/Info.plist; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023 Mullvad VPN AB. All rights reserved."; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + "LIBRARY_SEARCH_PATHS[sdk=iphoneos*][arch=arm64]" = "$(PROJECT_DIR)/../target/aarch64-apple-ios/debug"; + "LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=arm64]" = "$(PROJECT_DIR)/../target/aarch64-apple-ios-sim/debug"; + "LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=x86_64]" = "$(PROJECT_DIR)/../target/x86_64-apple-ios/debug"; + MODULEMAP_PRIVATE_FILE = $PROJECT_DIR/TunnelObfuscation/module.private.modulemap; + PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).TunnelObfuscation"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 584023252A406BF5007B27AC /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 5808273928487E3E006B77A4 /* Base.xcconfig */; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 4; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = TunnelObfuscation/Info.plist; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023 Mullvad VPN AB. All rights reserved."; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + "LIBRARY_SEARCH_PATHS[sdk=iphoneos*][arch=arm64]" = "$(PROJECT_DIR)/../target/aarch64-apple-ios/release"; + "LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=arm64]" = "$(PROJECT_DIR)/../target/aarch64-apple-ios-sim/release"; + "LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=x86_64]" = "$(PROJECT_DIR)/../target/x86_64-apple-ios/release"; + MODULEMAP_PRIVATE_FILE = $PROJECT_DIR/TunnelObfuscation/module.private.modulemap; + PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).TunnelObfuscation"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 58695AA42A4ADA9200328DB3 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = CKG9MXH72F; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPN.TunnelObfuscationTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 58695AA52A4ADA9200328DB3 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = CKG9MXH72F; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPN.TunnelObfuscationTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; 5898D28D29017BD400EB5EBA /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -4333,6 +4650,24 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 584023232A406BF5007B27AC /* Build configuration list for PBXNativeTarget "TunnelObfuscation" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 584023242A406BF5007B27AC /* Debug */, + 584023252A406BF5007B27AC /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 58695AA62A4ADA9200328DB3 /* Build configuration list for PBXNativeTarget "TunnelObfuscationTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 58695AA42A4ADA9200328DB3 /* Debug */, + 58695AA52A4ADA9200328DB3 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 5898D28F29017BD400EB5EBA /* Build configuration list for PBXNativeTarget "TunnelProviderMessaging" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadVPN.xcscheme b/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadVPN.xcscheme index 29084e4730..44fd0a5d6a 100644 --- a/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadVPN.xcscheme +++ b/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadVPN.xcscheme @@ -194,6 +194,16 @@ ReferencedContainer = "container:MullvadVPN.xcodeproj"> </BuildableReference> </TestableReference> + <TestableReference + skipped = "NO"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "58695A9C2A4ADA9100328DB3" + BuildableName = "TunnelObfuscationTests.xctest" + BlueprintName = "TunnelObfuscationTests" + ReferencedContainer = "container:MullvadVPN.xcodeproj"> + </BuildableReference> + </TestableReference> </Testables> </TestAction> <LaunchAction @@ -216,6 +226,13 @@ ReferencedContainer = "container:MullvadVPN.xcodeproj"> </BuildableReference> </BuildableProductRunnable> + <EnvironmentVariables> + <EnvironmentVariable + key = "LIBDISPATCH_COOPERATIVE_POOL_STRICT" + value = "1" + isEnabled = "YES"> + </EnvironmentVariable> + </EnvironmentVariables> </LaunchAction> <ProfileAction buildConfiguration = "Release" diff --git a/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/TunnelObfuscationTests.xcscheme b/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/TunnelObfuscationTests.xcscheme new file mode 100644 index 0000000000..be288d68ea --- /dev/null +++ b/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/TunnelObfuscationTests.xcscheme @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + LastUpgradeVersion = "1430" + version = "1.7"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + </BuildAction> + <TestAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + shouldUseLaunchSchemeArgsEnv = "YES" + shouldAutocreateTestPlan = "YES"> + <Testables> + <TestableReference + skipped = "NO" + parallelizable = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "58695A9C2A4ADA9100328DB3" + BuildableName = "TunnelObfuscationTests.xctest" + BlueprintName = "TunnelObfuscationTests" + ReferencedContainer = "container:MullvadVPN.xcodeproj"> + </BuildableReference> + </TestableReference> + </Testables> + </TestAction> + <LaunchAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + debugServiceExtension = "internal" + allowLocationSimulation = "YES"> + </LaunchAction> + <ProfileAction + buildConfiguration = "Release" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + debugDocumentVersioning = "YES"> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/ios/TunnelObfuscation/Info.plist b/ios/TunnelObfuscation/Info.plist new file mode 100644 index 0000000000..0c67376eba --- /dev/null +++ b/ios/TunnelObfuscation/Info.plist @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict/> +</plist> diff --git a/ios/TunnelObfuscation/TunnelObfuscation.h b/ios/TunnelObfuscation/TunnelObfuscation.h new file mode 100644 index 0000000000..4d381512bb --- /dev/null +++ b/ios/TunnelObfuscation/TunnelObfuscation.h @@ -0,0 +1,19 @@ +// +// TunnelObfuscation.h +// TunnelObfuscation +// +// Created by pronebird on 2023-06-23. +// Copyright © 2022 Mullvad VPN AB. All rights reserved. +// + +#import <Foundation/Foundation.h> + +//! Project version number for TunnelObfuscation. +FOUNDATION_EXPORT double TunnelObfuscationVersionNumber; + +//! Project version string for TunnelObfuscation. +FOUNDATION_EXPORT const unsigned char TunnelObfuscationVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import <TunnelObfuscation/PublicHeader.h> + + diff --git a/ios/TunnelObfuscation/TunnelObfuscator.swift b/ios/TunnelObfuscation/TunnelObfuscator.swift new file mode 100644 index 0000000000..f84ea73067 --- /dev/null +++ b/ios/TunnelObfuscation/TunnelObfuscator.swift @@ -0,0 +1,72 @@ +// +// TunnelObfuscator.swift +// TunnelObfuscation +// +// Created by pronebird on 19/06/2023. +// Copyright © 2023 Mullvad VPN AB. All rights reserved. +// + +import Foundation +import Network +import TunnelObfuscatorProxy + +/// Class that implements UDP over TCP obfuscation by accepting traffic on a local UDP port and proxying it over TCP to the remote endpoint. +public final class TunnelObfuscator { + private let stateLock = NSLock() + private let remoteAddress: IPAddress + private let tcpPort: UInt16 + + private var proxyHandle = ProxyHandle(context: nil, port: 0) + private var isStarted = false + + /// Returns local UDP port used by proxy and bound to 127.0.0.1 (IPv4). + /// The returned value can be zero if obfuscator hasn't started yet. + public var localUdpPort: UInt16 { + return stateLock.withLock { proxyHandle.port } + } + + /// Initialize tunnel obfuscator with remote server address and TCP port where udp2tcp is running. + public init(remoteAddress: IPAddress, tcpPort: UInt16) { + self.remoteAddress = remoteAddress + self.tcpPort = tcpPort + } + + deinit { + stop() + } + + public func start() { + stateLock.withLock { + guard !isStarted else { return } + + let result = withUnsafeMutablePointer(to: &proxyHandle) { proxyHandlePointer in + let addressData = remoteAddress.rawValue + + return start_tunnel_obfuscator_proxy( + addressData.map { $0 }, + UInt(addressData.count), + tcpPort, + proxyHandlePointer + ) + } + + assert(result == 0) + + isStarted = true + } + } + + public func stop() { + stateLock.withLock { + guard isStarted else { return } + + let result = withUnsafeMutablePointer(to: &proxyHandle) { + stop_tunnel_obfuscator_proxy($0) + } + + assert(result == 0) + + isStarted = false + } + } +} diff --git a/ios/TunnelObfuscation/module.private.modulemap b/ios/TunnelObfuscation/module.private.modulemap new file mode 100644 index 0000000000..879cbd009d --- /dev/null +++ b/ios/TunnelObfuscation/module.private.modulemap @@ -0,0 +1,5 @@ +framework module TunnelObfuscatorProxy { + header "tunnel_obfuscator_proxy.h" + link "libtunnel_obfuscator_proxy" + export * +}
\ No newline at end of file diff --git a/ios/TunnelObfuscation/tunnel-obfuscator-proxy/Cargo.toml b/ios/TunnelObfuscation/tunnel-obfuscator-proxy/Cargo.toml new file mode 100644 index 0000000000..3a0e59fc15 --- /dev/null +++ b/ios/TunnelObfuscation/tunnel-obfuscator-proxy/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "tunnel-obfuscator-proxy" +version = "0.0.0" +edition = "2021" +license = "GPL-3.0" +publish = false + +[lib] +crate-type = [ "rlib", "staticlib" ] +bench = false + +[target.'cfg(target_os = "ios")'.dependencies] +tunnel-obfuscation = { path = "../../../tunnel-obfuscation" } +tokio = { version = "1", features = ["sync"] } +libc = "0.2" +log = "0.4" +oslog = "0.2" + +[target.'cfg(target_os = "ios")'.build-dependencies] +cbindgen = { version = "0.24.3", default-features = false } diff --git a/ios/TunnelObfuscation/tunnel-obfuscator-proxy/build.rs b/ios/TunnelObfuscation/tunnel-obfuscator-proxy/build.rs new file mode 100644 index 0000000000..475f26ba11 --- /dev/null +++ b/ios/TunnelObfuscation/tunnel-obfuscator-proxy/build.rs @@ -0,0 +1,14 @@ +#[cfg(target_os = "ios")] +fn main() { + let crate_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap(); + + cbindgen::Builder::new() + .with_crate(crate_dir) + .with_language(cbindgen::Language::C) + .generate() + .expect("failed to generate bindings") + .write_to_file("include/tunnel_obfuscator_proxy.h"); +} + +#[cfg(not(target_os = "ios"))] +fn main() {} diff --git a/ios/TunnelObfuscation/tunnel-obfuscator-proxy/include/tunnel_obfuscator_proxy.h b/ios/TunnelObfuscation/tunnel-obfuscator-proxy/include/tunnel_obfuscator_proxy.h new file mode 100644 index 0000000000..fdee41746f --- /dev/null +++ b/ios/TunnelObfuscation/tunnel-obfuscator-proxy/include/tunnel_obfuscator_proxy.h @@ -0,0 +1,16 @@ +#include <stdarg.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> + +typedef struct ProxyHandle { + void *context; + uint16_t port; +} ProxyHandle; + +int32_t start_tunnel_obfuscator_proxy(const uint8_t *peer_address, + uintptr_t peer_address_len, + uint16_t peer_port, + struct ProxyHandle *proxy_handle); + +int32_t stop_tunnel_obfuscator_proxy(struct ProxyHandle *proxy_handle); diff --git a/ios/TunnelObfuscation/tunnel-obfuscator-proxy/src/ffi.rs b/ios/TunnelObfuscation/tunnel-obfuscator-proxy/src/ffi.rs new file mode 100644 index 0000000000..a6ea98a86d --- /dev/null +++ b/ios/TunnelObfuscation/tunnel-obfuscator-proxy/src/ffi.rs @@ -0,0 +1,90 @@ +#![cfg(target_os = "ios")] + +use super::{TunnelObfuscatorHandle, TunnelObfuscatorRuntime}; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; +use std::sync::Once; + +static INIT_LOGGING: Once = Once::new(); + +#[repr(C)] +pub struct ProxyHandle { + pub context: *mut libc::c_void, + pub port: u16, +} + +#[no_mangle] +pub unsafe extern "C" fn start_tunnel_obfuscator_proxy( + peer_address: *const u8, + peer_address_len: usize, + peer_port: u16, + proxy_handle: *mut ProxyHandle, +) -> i32 { + INIT_LOGGING.call_once(|| { + let _ = oslog::OsLogger::new("net.mullvad.MullvadVPN.TunnelObfuscatorProxy") + .level_filter(log::LevelFilter::Info) + .init(); + }); + + let peer_sock_addr: SocketAddr = + if let Some(ip_address) = parse_ip_addr(peer_address, peer_address_len) { + SocketAddr::new(ip_address, peer_port) + } else { + return -1; + }; + + let result = TunnelObfuscatorRuntime::new(peer_sock_addr).and_then(|runtime| runtime.run()); + + match result { + Ok((local_endpoint, obfuscator_handle)) => { + let boxed_handle = Box::new(obfuscator_handle); + std::ptr::write( + proxy_handle, + ProxyHandle { + context: Box::into_raw(boxed_handle) as *mut _, + port: local_endpoint.port(), + }, + ); + 0 + } + Err(err) => { + log::error!("Failed to run tunnel obfuscator proxy {}", err); + err.raw_os_error().unwrap_or(-1) + } + } +} + +#[no_mangle] +pub unsafe extern "C" fn stop_tunnel_obfuscator_proxy(proxy_handle: *mut ProxyHandle) -> i32 { + let context_ptr = unsafe { (*proxy_handle).context }; + if context_ptr.is_null() { + return -1; + } + + let obfuscator_handle: Box<TunnelObfuscatorHandle> = + unsafe { Box::from_raw(context_ptr as *mut _) }; + obfuscator_handle.stop(); + unsafe { (*proxy_handle).context = std::ptr::null_mut() }; + 0 +} + +/// Constructs a new IP address from a pointer containing bytes representing an IP address. +/// +/// SAFETY: `addr` must be a pointer to at least `addr_len` bytes. +unsafe fn parse_ip_addr(addr: *const u8, addr_len: usize) -> Option<IpAddr> { + match addr_len { + 4 => { + // SAFETY: addr pointer must point to at least addr_len bytes + let bytes = unsafe { std::slice::from_raw_parts(addr, addr_len) }; + Some(Ipv4Addr::new(bytes[0], bytes[1], bytes[2], bytes[3]).into()) + } + 16 => { + // SAFETY: addr pointer must point to at least addr_len bytes + let bytes = unsafe { std::slice::from_raw_parts(addr, addr_len) }; + let mut addr_arr = [0u8; 16]; + addr_arr.as_mut_slice().copy_from_slice(bytes); + + Some(Ipv6Addr::from(addr_arr).into()) + } + _ => None, + } +} diff --git a/ios/TunnelObfuscation/tunnel-obfuscator-proxy/src/lib.rs b/ios/TunnelObfuscation/tunnel-obfuscator-proxy/src/lib.rs new file mode 100644 index 0000000000..09ac816bfa --- /dev/null +++ b/ios/TunnelObfuscation/tunnel-obfuscator-proxy/src/lib.rs @@ -0,0 +1,93 @@ +#![cfg(target_os = "ios")] + +use std::io; +use std::net::SocketAddr; +use tokio::sync::oneshot; +use tunnel_obfuscation::{create_obfuscator, Settings as ObfuscationSettings, Udp2TcpSettings}; + +mod ffi; +pub use ffi::{start_tunnel_obfuscator_proxy, stop_tunnel_obfuscator_proxy, ProxyHandle}; + +pub struct TunnelObfuscatorRuntime { + runtime: tokio::runtime::Runtime, + settings: ObfuscationSettings, +} + +impl TunnelObfuscatorRuntime { + pub fn new(peer: SocketAddr) -> io::Result<Self> { + let runtime = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build()?; + let settings = ObfuscationSettings::Udp2Tcp(Udp2TcpSettings { peer }); + + Ok(Self { runtime, settings }) + } + + pub fn run(self) -> io::Result<(SocketAddr, TunnelObfuscatorHandle)> { + let (tx, rx) = oneshot::channel(); + let (shutdown_tx, shutdown_rx) = oneshot::channel(); + let (startup_tx, startup_rx) = oneshot::channel(); + std::thread::spawn(move || { + self.run_service_inner(rx, startup_tx); + }); + + match startup_rx.blocking_recv() { + Ok(Ok(endpoint)) => Ok((endpoint, TunnelObfuscatorHandle { tx })), + Ok(Err(err)) => { + let _ = tx.send(shutdown_tx); + let _ = shutdown_rx.blocking_recv(); + Err(io::Error::new(io::ErrorKind::Other, err)) + } + Err(_) => { + let _ = tx.send(shutdown_tx); + let _ = shutdown_rx.blocking_recv(); + Err(io::Error::new( + io::ErrorKind::Other, + "Tokio runtime crashed", + )) + } + } + } + + fn run_service_inner( + self, + shutdown_rx: oneshot::Receiver<oneshot::Sender<()>>, + startup_done_tx: oneshot::Sender<io::Result<SocketAddr>>, + ) { + let Self { + settings, runtime, .. + } = self; + + std::thread::spawn(move || { + runtime.spawn(async move { + match create_obfuscator(&settings).await { + Ok(obfuscator) => { + let endpoint = obfuscator.endpoint(); + let _ = startup_done_tx.send(Ok(endpoint)); + let _ = obfuscator.run().await; + } + Err(err) => { + let _ = + startup_done_tx.send(Err(io::Error::new(io::ErrorKind::Other, err))); + } + } + }); + if let Ok(shutdown_tx) = runtime.block_on(shutdown_rx) { + std::mem::drop(runtime); + let _ = shutdown_tx.send(()); + } + }); + } +} + +pub struct TunnelObfuscatorHandle { + tx: oneshot::Sender<oneshot::Sender<()>>, +} + +impl TunnelObfuscatorHandle { + pub fn stop(self) { + let (shutdown_tx, shutdown_rx) = oneshot::channel(); + let _ = self.tx.send(shutdown_tx); + let _ = shutdown_rx.blocking_recv(); + } +} diff --git a/ios/TunnelObfuscationTests/TCPConnection.swift b/ios/TunnelObfuscationTests/TCPConnection.swift new file mode 100644 index 0000000000..85a26ff970 --- /dev/null +++ b/ios/TunnelObfuscationTests/TCPConnection.swift @@ -0,0 +1,73 @@ +// +// TCPConnection.swift +// TunnelObfuscationTests +// +// Created by pronebird on 27/06/2023. +// Copyright © 2023 Mullvad VPN AB. All rights reserved. +// + +import Foundation +import Network + +/// Minimal implementation of TCP connection capable of receiving data. +/// > Warning: Do not use this implementation in production code. See the warning in `start()`. +class TCPConnection { + private let dispatchQueue = DispatchQueue(label: "TCPConnection") + private let nwConnection: NWConnection + + init(nwConnection: NWConnection) { + self.nwConnection = nwConnection + } + + deinit { + cancel() + } + + /// Establishes the TCP connection. + /// + /// > Warning: This implementation is **not safe to use in production** + /// It will cancel the `listener.stateUpdateHandler` after it becomes ready and ignore future updates. + /// + /// Waits for the underlying connection to become ready before returning control to the caller, otherwise throws an + /// error if connection state indicates as such. + func start() async throws { + try await withCheckedThrowingContinuation { continuation in + nwConnection.stateUpdateHandler = { state in + switch state { + case .ready: + continuation.resume(returning: ()) + case let .failed(error): + continuation.resume(throwing: error) + case .cancelled: + continuation.resume(throwing: CancellationError()) + default: + return + } + // Reset state update handler after resuming continuation. + self.nwConnection.stateUpdateHandler = nil + } + nwConnection.start(queue: dispatchQueue) + } + } + + func cancel() { + nwConnection.cancel() + } + + func receiveData(minimumLength: Int, maximumLength: Int) async throws -> Data { + return try await withCheckedThrowingContinuation { continuation in + nwConnection.receive( + minimumIncompleteLength: minimumLength, + maximumLength: maximumLength + ) { content, contentContext, isComplete, error in + if let error { + continuation.resume(throwing: error) + } else if let content { + continuation.resume(returning: content) + } else if isComplete { + continuation.resume(returning: Data()) + } + } + } + } +} diff --git a/ios/TunnelObfuscationTests/TCPListener.swift b/ios/TunnelObfuscationTests/TCPListener.swift new file mode 100644 index 0000000000..edfcde84d9 --- /dev/null +++ b/ios/TunnelObfuscationTests/TCPListener.swift @@ -0,0 +1,75 @@ +// +// TCPListener.swift +// TunnelObfuscationTests +// +// Created by pronebird on 27/06/2023. +// Copyright © 2023 Mullvad VPN AB. All rights reserved. +// + +import Network + +/// Minimal implementation of a TCP listener. +class TCPOneShotListener { + private let dispatchQueue = DispatchQueue(label: "TCPListener") + private let listener: NWListener + + /// A stream of new TCP connections. + /// The caller may iterate over this stream to accept new TCP connections. + /// + /// `TCPConnection` objects are returned unopen, so the caller has to call `TCPConnection.start()` to accept the + /// connection before initiating the data exchange. + let newConnections: AsyncStream<TCPConnection> + + init() throws { + let listener = try NWListener(using: .tcp) + + newConnections = AsyncStream { continuation in + listener.newConnectionHandler = { nwConnection in + continuation.yield(TCPConnection(nwConnection: nwConnection)) + } + continuation.onTermination = { _ in + listener.newConnectionHandler = nil + } + } + + self.listener = listener + } + + deinit { + cancel() + } + + /// Local TCP port bound by listener on which it accepts new connections. + var listenPort: UInt16 { + return listener.port?.rawValue ?? 0 + } + + /// Start listening on a randomly assigned port which should be available via `listenPort` once this call returns + /// control back to the caller. + /// + /// Waits for the underlying connection to become ready before returning control to the caller, otherwise throws an + /// error if connection state indicates as such. + func start() async throws { + try await withCheckedThrowingContinuation { continuation in + listener.stateUpdateHandler = { state in + switch state { + case .ready: + continuation.resume(returning: ()) + case let .failed(error): + continuation.resume(throwing: error) + case .cancelled: + continuation.resume(throwing: CancellationError()) + default: + return + } + // Reset state update handler after resuming continuation. + self.listener.stateUpdateHandler = nil + } + listener.start(queue: dispatchQueue) + } + } + + func cancel() { + listener.cancel() + } +} diff --git a/ios/TunnelObfuscationTests/TCPUnsafeListener.swift b/ios/TunnelObfuscationTests/TCPUnsafeListener.swift new file mode 100644 index 0000000000..7d7b9ed949 --- /dev/null +++ b/ios/TunnelObfuscationTests/TCPUnsafeListener.swift @@ -0,0 +1,81 @@ +// +// TCPUnsafeListener.swift +// TunnelObfuscationTests +// +// Created by pronebird on 27/06/2023. +// Copyright © 2023 Mullvad VPN AB. All rights reserved. +// + +import Network + +/// Minimal implementation of a TCP listener. +/// > Warning: Do not use this implementation in production code. See the warning in `start()`. +class TCPUnsafeListener { + private let dispatchQueue = DispatchQueue(label: "TCPListener") + private let listener: NWListener + + /// A stream of new TCP connections. + /// The caller may iterate over this stream to accept new TCP connections. + /// + /// `TCPConnection` objects are returned unopen, so the caller has to call `TCPConnection.start()` to accept the + /// connection before initiating the data exchange. + let newConnections: AsyncStream<TCPConnection> + + init() throws { + let listener = try NWListener(using: .tcp) + + newConnections = AsyncStream { continuation in + listener.newConnectionHandler = { nwConnection in + continuation.yield(TCPConnection(nwConnection: nwConnection)) + } + continuation.onTermination = { @Sendable _ in + listener.newConnectionHandler = nil + } + } + + self.listener = listener + } + + deinit { + cancel() + } + + /// Local TCP port bound by listener on which it accepts new connections. + var listenPort: UInt16 { + return listener.port?.rawValue ?? 0 + } + + /// Start listening on a randomly assigned port which should be available via `listenPort` once this call returns + /// control back to the caller. + /// + /// > Warning: This implementation is **not safe to use in production** + /// It will cancel the `listener.stateUpdateHandler` after it becomes ready and ignore future updates. + /// + /// Waits for the underlying connection to become ready before returning control to the caller, otherwise throws an + /// error if connection state indicates as such. + func start() async throws { + try await withCheckedThrowingContinuation { continuation in + listener.stateUpdateHandler = { state in + switch state { + case .ready: + continuation.resume(returning: ()) + case let .failed(error): + continuation.resume(throwing: error) + case let .waiting(error): + continuation.resume(throwing: error) + case .cancelled: + continuation.resume(throwing: CancellationError()) + default: + return + } + // Reset state update handler after resuming continuation. + self.listener.stateUpdateHandler = nil + } + listener.start(queue: dispatchQueue) + } + } + + func cancel() { + listener.cancel() + } +} diff --git a/ios/TunnelObfuscationTests/TunnelObfuscationTests.swift b/ios/TunnelObfuscationTests/TunnelObfuscationTests.swift new file mode 100644 index 0000000000..0774ef9829 --- /dev/null +++ b/ios/TunnelObfuscationTests/TunnelObfuscationTests.swift @@ -0,0 +1,49 @@ +// +// TunnelObfuscationTests.swift +// TunnelObfuscationTests +// +// Created by pronebird on 27/06/2023. +// Copyright © 2023 Mullvad VPN AB. All rights reserved. +// + +import Network +import TunnelObfuscation +import XCTest + +final class TunnelObfuscationTests: XCTestCase { + func testRunningObfuscatorProxy() async throws { + // Each packet is prefixed with u16 that contains a payload length. + let preambleLength = MemoryLayout<UInt16>.size + let markerData = Data([109, 117, 108, 108, 118, 97, 100]) + let packetLength = markerData.count + preambleLength + + let tcpListener = try TCPUnsafeListener() + try await tcpListener.start() + + let obfuscator = TunnelObfuscator(remoteAddress: IPv4Address.loopback, tcpPort: tcpListener.listenPort) + obfuscator.start() + + // Accept incoming connections + let connectionDataTask = Task { + for await newConnection in tcpListener.newConnections { + try await newConnection.start() + + return try await newConnection.receiveData( + minimumLength: packetLength, + maximumLength: packetLength + ) + } + throw POSIXError(.ECANCELED) + } + + // Send marker data over UDP + let connection = UDPConnection(remote: IPv4Address.loopback, port: obfuscator.localUdpPort) + try await connection.start() + try await connection.sendData(markerData) + + // Validate the sent data + let receivedData = try await connectionDataTask.value + XCTAssert(receivedData.count == packetLength) + XCTAssertEqual(receivedData[preambleLength...], markerData) + } +} diff --git a/ios/TunnelObfuscationTests/UDPConnection.swift b/ios/TunnelObfuscationTests/UDPConnection.swift new file mode 100644 index 0000000000..8848643c05 --- /dev/null +++ b/ios/TunnelObfuscationTests/UDPConnection.swift @@ -0,0 +1,72 @@ +// +// UDPConnection.swift +// TunnelObfuscationTests +// +// Created by pronebird on 27/06/2023. +// Copyright © 2023 Mullvad VPN AB. All rights reserved. +// + +import Foundation +import Network + +/// Minimal implementation of UDP connection capable of sending data. +/// > Warning: Do not use this implementation in production code. See the warning in `start()`. +class UDPConnection { + private let dispatchQueue = DispatchQueue(label: "UDPConnection") + private let nwConnection: NWConnection + + init(remote: IPAddress, port: UInt16) { + nwConnection = NWConnection( + host: NWEndpoint.Host("\(remote)"), + port: NWEndpoint.Port(integerLiteral: port), + using: .udp + ) + } + + deinit { + cancel() + } + + /// Establishes the UDP connection. + /// + /// > Warning: This implementation is **not safe to use in production** + /// It will cancel the `listener.stateUpdateHandler` after it becomes ready and ignore future updates. + /// + /// Waits for the underlying connection to become ready before returning control to the caller, otherwise throws an + /// error if connection state indicates as such. + func start() async throws { + return try await withCheckedThrowingContinuation { continuation in + nwConnection.stateUpdateHandler = { state in + switch state { + case .ready: + continuation.resume(returning: ()) + case let .failed(error): + continuation.resume(throwing: error) + case .cancelled: + continuation.resume(throwing: CancellationError()) + default: + return + } + // Reset state update handler after resuming continuation. + self.nwConnection.stateUpdateHandler = nil + } + nwConnection.start(queue: dispatchQueue) + } + } + + func cancel() { + nwConnection.cancel() + } + + func sendData(_ data: Data) async throws { + return try await withCheckedThrowingContinuation { continuation in + nwConnection.send(content: data, completion: .contentProcessed { error in + if let error { + continuation.resume(throwing: error) + } else { + continuation.resume(returning: ()) + } + }) + } + } +} |
