summaryrefslogtreecommitdiffhomepage
path: root/ios
diff options
context:
space:
mode:
authorBug Magnet <marco.nikic@mullvad.net>2023-11-15 09:35:47 +0100
committerBug Magnet <marco.nikic@mullvad.net>2023-11-30 10:24:00 +0100
commit2f03f24a53e263959f84b2ce96ddb6ef010db855 (patch)
treedd1ee3f0c133265c3b11622d78d7dba0d5b403f8 /ios
parent5c8d672a199185fbe7ae7aebc98d7464d4a590dc (diff)
downloadmullvadvpn-2f03f24a53e263959f84b2ce96ddb6ef010db855.tar.xz
mullvadvpn-2f03f24a53e263959f84b2ce96ddb6ef010db855.zip
Have the PacketTunnel use UDP-over-TCP Obfuscation
Diffstat (limited to 'ios')
-rw-r--r--ios/MullvadSettings/WireGuardObfuscationSettings.swift10
-rw-r--r--ios/MullvadVPN.xcodeproj/project.pbxproj44
-rw-r--r--ios/MullvadVPN/SimulatorTunnelProvider/SimulatorTunnelProviderHost.swift3
-rw-r--r--ios/MullvadVPN/TunnelManager/TunnelManager.swift7
-rw-r--r--ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift5
-rw-r--r--ios/PacketTunnel/PacketTunnelProvider/RelaySelectorWrapper.swift3
-rw-r--r--ios/PacketTunnel/PacketTunnelProvider/SettingsReader.swift3
-rw-r--r--ios/PacketTunnelCore/Actor/PacketTunnelActor.swift19
-rw-r--r--ios/PacketTunnelCore/Actor/ProtocolObfuscator.swift67
-rw-r--r--ios/PacketTunnelCore/Actor/Protocols/RelaySelectorProtocol.swift12
-rw-r--r--ios/PacketTunnelCore/Actor/Protocols/SettingsReaderProtocol.swift8
-rw-r--r--ios/PacketTunnelCoreTests/AppMessageHandlerTests.swift3
-rw-r--r--ios/PacketTunnelCoreTests/Mocks/PacketTunnelActor+Mocks.swift3
-rw-r--r--ios/PacketTunnelCoreTests/Mocks/ProtocolObfuscationStub.swift17
-rw-r--r--ios/PacketTunnelCoreTests/Mocks/RelaySelectorStub.swift2
-rw-r--r--ios/PacketTunnelCoreTests/Mocks/SettingsReaderStub.swift4
-rw-r--r--ios/PacketTunnelCoreTests/Mocks/TunnelObfuscationStub.swift24
-rw-r--r--ios/PacketTunnelCoreTests/PacketTunnelActorTests.swift5
-rw-r--r--ios/PacketTunnelCoreTests/ProtocolObfuscatorTests.swift116
-rw-r--r--ios/TunnelObfuscation/UDPOverTCPObfuscator.swift (renamed from ios/TunnelObfuscation/TunnelObfuscator.swift)11
-rw-r--r--ios/TunnelObfuscationTests/TunnelObfuscationTests.swift2
21 files changed, 337 insertions, 31 deletions
diff --git a/ios/MullvadSettings/WireGuardObfuscationSettings.swift b/ios/MullvadSettings/WireGuardObfuscationSettings.swift
index ab2be2f96e..7259c97059 100644
--- a/ios/MullvadSettings/WireGuardObfuscationSettings.swift
+++ b/ios/MullvadSettings/WireGuardObfuscationSettings.swift
@@ -8,19 +8,27 @@
import Foundation
+/// Whether UDP-over-TCP obfuscation is enabled
+///
+/// `.automatic` means an algorithm will decide whether to use it or not.
public enum WireGuardObfuscationState: Codable {
case automatic
case on
case off
}
+/// The port to select when using UDP-over-TCP obfuscation
+///
+/// `.automatic` means an algorith will decide between using `port80` or `port5001`
public enum WireGuardObfuscationPort: UInt16, Codable {
case automatic = 0
case port80 = 80
case port5001 = 5001
+ /// The `UInt16` representation of the port.
+ /// - Returns: `0` if `.automatic`, `80` or `5001` otherwise.
public var portValue: UInt16 {
- rawValue
+ self == .automatic ? 0 : rawValue
}
public init?(rawValue: UInt16) {
diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj
index 2bec1aba13..820982df23 100644
--- a/ios/MullvadVPN.xcodeproj/project.pbxproj
+++ b/ios/MullvadVPN.xcodeproj/project.pbxproj
@@ -95,7 +95,7 @@
583DA21425FA4B5C00318683 /* LocationDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 583DA21325FA4B5C00318683 /* LocationDataSource.swift */; };
583FE01029C0F532006E85F9 /* CustomSplitViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 583FE00F29C0F532006E85F9 /* CustomSplitViewController.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 */; };
+ 584023222A406BF5007B27AC /* UDPOverTCPObfuscator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 584023212A406BF5007B27AC /* UDPOverTCPObfuscator.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 */; };
@@ -268,7 +268,7 @@
58C7A4582A863FB90060C66F /* TunnelMonitorProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58C7A42C2A85067A0060C66F /* TunnelMonitorProtocol.swift */; };
58C7A4592A863FB90060C66F /* WgStats.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58A3BDAF28A1821A00C8C2C6 /* WgStats.swift */; };
58C7A45B2A8640030060C66F /* PacketTunnelPathObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58225D272A84F23B0083D7F1 /* PacketTunnelPathObserver.swift */; };
- 58C7A45C2A8640490060C66F /* MullvadLogging.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 58D223F3294C8FF00029F5F8 /* MullvadLogging.framework */; platformFilter = ios; };
+ 58C7A45C2A8640490060C66F /* MullvadLogging.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 58D223F3294C8FF00029F5F8 /* MullvadLogging.framework */; };
58C7A4692A8643A90060C66F /* IPv4Header.h in Headers */ = {isa = PBXBuildFile; fileRef = 58218E1428B65058000C624F /* IPv4Header.h */; settings = {ATTRIBUTES = (Public, ); }; };
58C7A46A2A8643A90060C66F /* ICMPHeader.h in Headers */ = {isa = PBXBuildFile; fileRef = 58218E1628B65396000C624F /* ICMPHeader.h */; settings = {ATTRIBUTES = (Public, ); }; };
58C7A4702A8649ED0060C66F /* PingerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58C7A46F2A8649ED0060C66F /* PingerTests.swift */; };
@@ -506,11 +506,17 @@
A900E9BE2ACC654100C95F67 /* APIProxy+Stubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = A900E9BD2ACC654100C95F67 /* APIProxy+Stubs.swift */; };
A900E9C02ACC661900C95F67 /* AccessTokenManager+Stubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = A900E9BF2ACC661900C95F67 /* AccessTokenManager+Stubs.swift */; };
A917352129FAAA5200D5DCFD /* TransportStrategyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A917352029FAAA5200D5DCFD /* TransportStrategyTests.swift */; };
+ A91D78E32B03BDF200FCD5D3 /* TunnelObfuscation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5840231F2A406BF5007B27AC /* TunnelObfuscation.framework */; };
+ A91D78E42B03C01600FCD5D3 /* MullvadSettings.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 58B2FDD32AA71D2A003EB5C6 /* MullvadSettings.framework */; };
A93D13782A1F60A6001EB0B1 /* shadowsocks.h in Headers */ = {isa = PBXBuildFile; fileRef = 586F2BE129F6916F009E6924 /* shadowsocks.h */; settings = {ATTRIBUTES = (Private, ); }; };
A94D691A2ABAD66700413DD4 /* WireGuardKitTypes in Frameworks */ = {isa = PBXBuildFile; productRef = 58FE25E22AA72AE9003D1918 /* WireGuardKitTypes */; };
A94D691B2ABAD66700413DD4 /* WireGuardKitTypes in Frameworks */ = {isa = PBXBuildFile; productRef = 58FE25E72AA7399D003D1918 /* WireGuardKitTypes */; };
A95F86B72A1F53BA00245DAC /* URLSessionTransport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06FAE67C28F83CA50033DD93 /* URLSessionTransport.swift */; };
A95F86B82A1F547000245DAC /* ShadowsocksProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01F1FF1B29F06124007083C3 /* ShadowsocksProxy.swift */; };
+ A97D25AE2B0BB18100946B2D /* ProtocolObfuscator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A97D25AD2B0BB18100946B2D /* ProtocolObfuscator.swift */; };
+ A97D25B02B0BB5C400946B2D /* ProtocolObfuscationStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = A97D25AF2B0BB5C400946B2D /* ProtocolObfuscationStub.swift */; };
+ A97D25B22B0CB02D00946B2D /* ProtocolObfuscatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A97D25B12B0CB02D00946B2D /* ProtocolObfuscatorTests.swift */; };
+ A97D25B42B0CB59300946B2D /* TunnelObfuscationStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = A97D25B32B0CB59300946B2D /* TunnelObfuscationStub.swift */; };
A97D30172AE6B5E90045C0E4 /* StoredWgKeyData.swift in Sources */ = {isa = PBXBuildFile; fileRef = A97D30162AE6B5E90045C0E4 /* StoredWgKeyData.swift */; };
A97F1F442A1F4E1A00ECEFDE /* MullvadTransport.h in Headers */ = {isa = PBXBuildFile; fileRef = A97F1F432A1F4E1A00ECEFDE /* MullvadTransport.h */; settings = {ATTRIBUTES = (Public, ); }; };
A97F1F472A1F4E1A00ECEFDE /* MullvadTransport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A97F1F412A1F4E1A00ECEFDE /* MullvadTransport.framework */; };
@@ -981,6 +987,13 @@
remoteGlobalIDString = 7A88DCCD2A8FABBE00D2FF0E;
remoteInfo = Routing;
};
+ A91D78E12B03BDE500FCD5D3 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 58CE5E58224146200008646E /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 5840231E2A406BF5007B27AC;
+ remoteInfo = TunnelObfuscation;
+ };
A97F1F452A1F4E1A00ECEFDE /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 58CE5E58224146200008646E /* Project object */;
@@ -1265,7 +1278,7 @@
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>"; };
+ 584023212A406BF5007B27AC /* UDPOverTCPObfuscator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UDPOverTCPObfuscator.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>"; };
@@ -1617,6 +1630,10 @@
A9467E7E2A29DEFE000DC21F /* RelayCacheTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayCacheTests.swift; sourceTree = "<group>"; };
A9467E872A2DCD57000DC21F /* ShadowsocksConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShadowsocksConfiguration.swift; sourceTree = "<group>"; };
A9467E8A2A2E0317000DC21F /* ShadowsocksConfigurationCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShadowsocksConfigurationCache.swift; sourceTree = "<group>"; };
+ A97D25AD2B0BB18100946B2D /* ProtocolObfuscator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProtocolObfuscator.swift; sourceTree = "<group>"; };
+ A97D25AF2B0BB5C400946B2D /* ProtocolObfuscationStub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProtocolObfuscationStub.swift; sourceTree = "<group>"; };
+ A97D25B12B0CB02D00946B2D /* ProtocolObfuscatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProtocolObfuscatorTests.swift; sourceTree = "<group>"; };
+ A97D25B32B0CB59300946B2D /* TunnelObfuscationStub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelObfuscationStub.swift; sourceTree = "<group>"; };
A97D30162AE6B5E90045C0E4 /* StoredWgKeyData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoredWgKeyData.swift; sourceTree = "<group>"; };
A97F1F412A1F4E1A00ECEFDE /* MullvadTransport.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MullvadTransport.framework; sourceTree = BUILT_PRODUCTS_DIR; };
A97F1F432A1F4E1A00ECEFDE /* MullvadTransport.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MullvadTransport.h; sourceTree = "<group>"; };
@@ -1761,6 +1778,8 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
+ A91D78E42B03C01600FCD5D3 /* MullvadSettings.framework in Frameworks */,
+ A91D78E32B03BDF200FCD5D3 /* TunnelObfuscation.framework in Frameworks */,
58238CB92AD57EC700768310 /* MullvadREST.framework in Frameworks */,
A94D691A2ABAD66700413DD4 /* WireGuardKitTypes in Frameworks */,
58FE65952AB1D90600E53CB5 /* MullvadTypes.framework in Frameworks */,
@@ -2324,7 +2343,7 @@
589C6A7A2A45ACCA00DAD3EF /* Info.plist */,
584023272A407679007B27AC /* tunnel_obfuscator_proxy.h */,
589C6A7B2A45AE0100DAD3EF /* TunnelObfuscation.h */,
- 584023212A406BF5007B27AC /* TunnelObfuscator.swift */,
+ 584023212A406BF5007B27AC /* UDPOverTCPObfuscator.swift */,
);
path = TunnelObfuscation;
sourceTree = "<group>";
@@ -2402,6 +2421,7 @@
586C14592AC4735F00245C01 /* PacketTunnelActor+Public.swift */,
583832262AC3193600EA2071 /* PacketTunnelActor+SleepCycle.swift */,
7AD0AA192AD69B6E00119E10 /* PacketTunnelActorProtocol.swift */,
+ A97D25AD2B0BB18100946B2D /* ProtocolObfuscator.swift */,
58E7A0312AA0715100C57861 /* Protocols */,
58ED3A132A7C199C0085CE65 /* StartOptions.swift */,
5824030C2A811B0000163DE8 /* State.swift */,
@@ -2653,6 +2673,7 @@
58C7A46F2A8649ED0060C66F /* PingerTests.swift */,
5838321C2AC1C54600EA2071 /* TaskSleepTests.swift */,
58092E532A8B832E00C3CC72 /* TunnelMonitorTests.swift */,
+ A97D25B12B0CB02D00946B2D /* ProtocolObfuscatorTests.swift */,
);
path = PacketTunnelCoreTests;
sourceTree = "<group>";
@@ -2915,6 +2936,8 @@
5838321A2AC1B18400EA2071 /* PacketTunnelActor+Mocks.swift */,
7AD0AA1B2AD6A63F00119E10 /* PacketTunnelActorStub.swift */,
7AD0AA202AD6CB0000119E10 /* URLRequestProxyStub.swift */,
+ A97D25AF2B0BB5C400946B2D /* ProtocolObfuscationStub.swift */,
+ A97D25B32B0CB59300946B2D /* TunnelObfuscationStub.swift */,
);
path = Mocks;
sourceTree = "<group>";
@@ -3400,6 +3423,7 @@
buildRules = (
);
dependencies = (
+ A91D78E22B03BDE500FCD5D3 /* PBXTargetDependency */,
58C7A45F2A8640490060C66F /* PBXTargetDependency */,
58FE65982AB1D90600E53CB5 /* PBXTargetDependency */,
58238CBC2AD57EC800768310 /* PBXTargetDependency */,
@@ -4075,7 +4099,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- 584023222A406BF5007B27AC /* TunnelObfuscator.swift in Sources */,
+ 584023222A406BF5007B27AC /* UDPOverTCPObfuscator.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -4298,6 +4322,7 @@
7AD0AA1F2AD6C8B900119E10 /* URLRequestProxyProtocol.swift in Sources */,
7A6B4F592AB8412E00123853 /* TunnelMonitorTimings.swift in Sources */,
58FE25DB2AA72A8F003D1918 /* StartOptions.swift in Sources */,
+ A97D25AE2B0BB18100946B2D /* ProtocolObfuscator.swift in Sources */,
583832212AC3174700EA2071 /* PacketTunnelActor+NetworkReachability.swift in Sources */,
58FE25D82AA72A8F003D1918 /* ConfigurationBuilder.swift in Sources */,
7AEF7F1A2AD00F52006FE45D /* AppMessageHandler.swift in Sources */,
@@ -4335,12 +4360,15 @@
58FE25F02AA77664003D1918 /* RelaySelectorStub.swift in Sources */,
581F23AF2A8CF94D00788AB6 /* PingerMock.swift in Sources */,
7A3FD1B62AD542110042BEA6 /* ServerRelaysResponse+Stubs.swift in Sources */,
+ A97D25B42B0CB59300946B2D /* TunnelObfuscationStub.swift in Sources */,
+ A97D25B02B0BB5C400946B2D /* ProtocolObfuscationStub.swift in Sources */,
7A3FD1B72AD54ABD0042BEA6 /* AnyTransport.swift in Sources */,
58FE25F22AA77674003D1918 /* SettingsReaderStub.swift in Sources */,
58F7753D2AB8473200425B47 /* BlockedStateErrorMapperStub.swift in Sources */,
58FE25D42AA729B5003D1918 /* PacketTunnelActorTests.swift in Sources */,
7A3FD1B52AD4465A0042BEA6 /* AppMessageHandlerTests.swift in Sources */,
58C7A4702A8649ED0060C66F /* PingerTests.swift in Sources */,
+ A97D25B22B0CB02D00946B2D /* ProtocolObfuscatorTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -4831,7 +4859,6 @@
};
58C7A45F2A8640490060C66F /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
- platformFilter = ios;
target = 58D223F2294C8FF00029F5F8 /* MullvadLogging */;
targetProxy = 58C7A45E2A8640490060C66F /* PBXContainerItemProxy */;
};
@@ -4976,6 +5003,11 @@
target = 7A88DCCD2A8FABBE00D2FF0E /* Routing */;
targetProxy = 7ABCA5B52A9349F20044A708 /* PBXContainerItemProxy */;
};
+ A91D78E22B03BDE500FCD5D3 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 5840231E2A406BF5007B27AC /* TunnelObfuscation */;
+ targetProxy = A91D78E12B03BDE500FCD5D3 /* PBXContainerItemProxy */;
+ };
A97F1F462A1F4E1A00ECEFDE /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = A97F1F402A1F4E1A00ECEFDE /* MullvadTransport */;
diff --git a/ios/MullvadVPN/SimulatorTunnelProvider/SimulatorTunnelProviderHost.swift b/ios/MullvadVPN/SimulatorTunnelProvider/SimulatorTunnelProviderHost.swift
index bac002e436..ecd2082d95 100644
--- a/ios/MullvadVPN/SimulatorTunnelProvider/SimulatorTunnelProviderHost.swift
+++ b/ios/MullvadVPN/SimulatorTunnelProvider/SimulatorTunnelProviderHost.swift
@@ -170,7 +170,8 @@ final class SimulatorTunnelProviderHost: SimulatorTunnelProviderDelegate {
return SelectedRelay(
endpoint: selectorResult.endpoint,
hostname: selectorResult.relay.hostname,
- location: selectorResult.location
+ location: selectorResult.location,
+ retryAttempts: 0
)
}
diff --git a/ios/MullvadVPN/TunnelManager/TunnelManager.swift b/ios/MullvadVPN/TunnelManager/TunnelManager.swift
index 77e1eb24cd..bd1583b11f 100644
--- a/ios/MullvadVPN/TunnelManager/TunnelManager.swift
+++ b/ios/MullvadVPN/TunnelManager/TunnelManager.swift
@@ -823,16 +823,18 @@ final class TunnelManager: StorePaymentObserver {
fileprivate func selectRelay() throws -> SelectedRelay {
let cachedRelays = try relayCacheTracker.getCachedRelays()
+ let retryAttempts = tunnelStatus.observedState.connectionState?.connectionAttemptCount ?? 0
let selectorResult = try RelaySelector.evaluate(
relays: cachedRelays.relays,
constraints: settings.relayConstraints,
- numberOfFailedAttempts: tunnelStatus.observedState.connectionState?.connectionAttemptCount ?? 0
+ numberOfFailedAttempts: retryAttempts
)
return SelectedRelay(
endpoint: selectorResult.endpoint,
hostname: selectorResult.relay.hostname,
- location: selectorResult.location
+ location: selectorResult.location,
+ retryAttempts: retryAttempts
)
}
@@ -992,7 +994,6 @@ final class TunnelManager: StorePaymentObserver {
let updatedConstraints = updatedSettings.relayConstraints
let selectNewRelay = currentConstraints != updatedConstraints
- // TODO: Handle using an obfuscator here
self.setSettings(updatedSettings, persist: true)
self.reconnectTunnel(selectNewRelay: selectNewRelay, completionHandler: nil)
}
diff --git a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift
index 76e7eede9d..25f37358e2 100644
--- a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift
+++ b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift
@@ -14,6 +14,7 @@ import MullvadTypes
import NetworkExtension
import PacketTunnelCore
import RelayCache
+import TunnelObfuscation
class PacketTunnelProvider: NEPacketTunnelProvider {
private let internalQueue = DispatchQueue(label: "PacketTunnel-internalQueue")
@@ -72,6 +73,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
let devicesProxy = proxyFactory.createDevicesProxy()
deviceChecker = DeviceChecker(accountsProxy: accountsProxy, devicesProxy: devicesProxy)
+ let obfuscator = ProtocolObfuscator<UDPOverTCPObfuscator>()
actor = PacketTunnelActor(
timings: PacketTunnelActorTimings(),
@@ -80,7 +82,8 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
defaultPathObserver: PacketTunnelPathObserver(packetTunnelProvider: self, eventQueue: internalQueue),
blockedStateErrorMapper: BlockedStateErrorMapper(),
relaySelector: RelaySelectorWrapper(relayCache: relayCache),
- settingsReader: SettingsReader()
+ settingsReader: SettingsReader(),
+ protocolObfuscator: obfuscator
)
let urlRequestProxy = URLRequestProxy(dispatchQueue: internalQueue, transportProvider: transportProvider)
diff --git a/ios/PacketTunnel/PacketTunnelProvider/RelaySelectorWrapper.swift b/ios/PacketTunnel/PacketTunnelProvider/RelaySelectorWrapper.swift
index eb5bd6dcee..cedeb1ea7b 100644
--- a/ios/PacketTunnel/PacketTunnelProvider/RelaySelectorWrapper.swift
+++ b/ios/PacketTunnel/PacketTunnelProvider/RelaySelectorWrapper.swift
@@ -28,7 +28,8 @@ struct RelaySelectorWrapper: RelaySelectorProtocol {
return SelectedRelay(
endpoint: selectorResult.endpoint,
hostname: selectorResult.relay.hostname,
- location: selectorResult.location
+ location: selectorResult.location,
+ retryAttempts: connectionAttemptFailureCount
)
}
}
diff --git a/ios/PacketTunnel/PacketTunnelProvider/SettingsReader.swift b/ios/PacketTunnel/PacketTunnelProvider/SettingsReader.swift
index 48874253a5..10282b5ff3 100644
--- a/ios/PacketTunnel/PacketTunnelProvider/SettingsReader.swift
+++ b/ios/PacketTunnel/PacketTunnelProvider/SettingsReader.swift
@@ -20,7 +20,8 @@ struct SettingsReader: SettingsReaderProtocol {
privateKey: deviceData.wgKeyData.privateKey,
interfaceAddresses: [deviceData.ipv4Address, deviceData.ipv6Address],
relayConstraints: settings.relayConstraints,
- dnsServers: settings.dnsSettings.selectedDNSServers
+ dnsServers: settings.dnsSettings.selectedDNSServers,
+ obfuscation: settings.wireGuardObfuscation
)
}
}
diff --git a/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift b/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift
index 9e3598db65..b624b29cd2 100644
--- a/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift
+++ b/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift
@@ -10,6 +10,7 @@ import Foundation
import MullvadLogging
import MullvadTypes
import NetworkExtension
+import TunnelObfuscation
import WireGuardKitTypes
/**
@@ -43,6 +44,7 @@ public actor PacketTunnelActor {
let blockedStateErrorMapper: BlockedStateErrorMapperProtocol
let relaySelector: RelaySelectorProtocol
let settingsReader: SettingsReaderProtocol
+ let protocolObfuscator: ProtocolObfuscation
nonisolated let commandChannel = CommandChannel()
@@ -53,7 +55,8 @@ public actor PacketTunnelActor {
defaultPathObserver: DefaultPathObserverProtocol,
blockedStateErrorMapper: BlockedStateErrorMapperProtocol,
relaySelector: RelaySelectorProtocol,
- settingsReader: SettingsReaderProtocol
+ settingsReader: SettingsReaderProtocol,
+ protocolObfuscator: ProtocolObfuscation
) {
self.timings = timings
self.tunnelAdapter = tunnelAdapter
@@ -62,6 +65,7 @@ public actor PacketTunnelActor {
self.blockedStateErrorMapper = blockedStateErrorMapper
self.relaySelector = relaySelector
self.settingsReader = settingsReader
+ self.protocolObfuscator = protocolObfuscator
consumeCommands(channel: commandChannel)
}
@@ -218,7 +222,10 @@ extension PacketTunnelActor {
- nextRelay: which relay should be selected next.
- reason: reason for reconnect
*/
- private func tryStart(nextRelay: NextRelay = .random, reason: ReconnectReason = .userInitiated) async throws {
+ private func tryStart(
+ nextRelay: NextRelay = .random,
+ reason: ReconnectReason = .userInitiated
+ ) async throws {
let settings: Settings = try settingsReader.read()
guard let connectionState = try makeConnectionState(nextRelay: nextRelay, settings: settings, reason: reason),
@@ -239,7 +246,13 @@ extension PacketTunnelActor {
state = .reconnecting(connectionState)
}
- let endpoint = connectionState.selectedRelay.endpoint
+ var endpoint = connectionState.selectedRelay.endpoint
+ endpoint = protocolObfuscator.obfuscate(
+ endpoint,
+ settings: settings,
+ retryAttempts: connectionState.selectedRelay.retryAttempts
+ )
+
let configurationBuilder = ConfigurationBuilder(
privateKey: activeKey,
interfaceAddresses: settings.interfaceAddresses,
diff --git a/ios/PacketTunnelCore/Actor/ProtocolObfuscator.swift b/ios/PacketTunnelCore/Actor/ProtocolObfuscator.swift
new file mode 100644
index 0000000000..467dd4c9df
--- /dev/null
+++ b/ios/PacketTunnelCore/Actor/ProtocolObfuscator.swift
@@ -0,0 +1,67 @@
+//
+// ProtocolObfuscator.swift
+// PacketTunnelCore
+//
+// Created by Marco Nikic on 2023-11-20.
+// Copyright © 2023 Mullvad VPN AB. All rights reserved.
+//
+
+import Foundation
+import MullvadTypes
+import TunnelObfuscation
+
+public protocol ProtocolObfuscation {
+ func obfuscate(_ endpoint: MullvadEndpoint, settings: Settings, retryAttempts: UInt) -> MullvadEndpoint
+}
+
+public class ProtocolObfuscator<Obfuscator: TunnelObfuscation>: ProtocolObfuscation {
+ var tunnelObfuscator: TunnelObfuscation?
+
+ public init() {}
+
+ /// Obfuscates a Mullvad endpoint based on a number of retry attempts.
+ ///
+ /// This retry logic used is explained at the following link
+ /// https://github.com/mullvad/mullvadvpn-app/blob/main/docs/relay-selector.md#default-constraints-for-tunnel-endpoints
+ /// - Parameters:
+ /// - endpoint: The endpoint to obfuscate.
+ /// - settings: Whether obfuscation should be used or not.
+ /// - retryAttempts: The number of times a connection was attempted to `endpoint`
+ /// - Returns: `endpoint` if obfuscation is disabled, or an obfuscated endpoint otherwise.
+ public func obfuscate(_ endpoint: MullvadEndpoint, settings: Settings, retryAttempts: UInt = 0) -> MullvadEndpoint {
+ var obfuscatedEndpoint = endpoint
+ let shouldObfuscate = switch settings.obfuscation.state {
+ case .automatic:
+ retryAttempts % 4 == 2 || retryAttempts % 4 == 3
+ case .on:
+ true
+ case .off:
+ false
+ }
+
+ guard shouldObfuscate else {
+ tunnelObfuscator = nil
+ return endpoint
+ }
+ var tcpPort = settings.obfuscation.port
+ if tcpPort == .automatic {
+ tcpPort = retryAttempts % 2 == 0 ? .port80 : .port5001
+ }
+ let obfuscator = Obfuscator(
+ remoteAddress: obfuscatedEndpoint.ipv4Relay.ip,
+ tcpPort: tcpPort.portValue
+ )
+ obfuscator.start()
+ tunnelObfuscator = obfuscator
+
+ let localObfuscatorEndpoint = IPv4Endpoint(ip: .loopback, port: obfuscator.localUdpPort)
+ obfuscatedEndpoint = MullvadEndpoint(
+ ipv4Relay: localObfuscatorEndpoint,
+ ipv4Gateway: obfuscatedEndpoint.ipv4Gateway,
+ ipv6Gateway: obfuscatedEndpoint.ipv6Gateway,
+ publicKey: obfuscatedEndpoint.publicKey
+ )
+
+ return obfuscatedEndpoint
+ }
+}
diff --git a/ios/PacketTunnelCore/Actor/Protocols/RelaySelectorProtocol.swift b/ios/PacketTunnelCore/Actor/Protocols/RelaySelectorProtocol.swift
index 7cd3003537..4224233f70 100644
--- a/ios/PacketTunnelCore/Actor/Protocols/RelaySelectorProtocol.swift
+++ b/ios/PacketTunnelCore/Actor/Protocols/RelaySelectorProtocol.swift
@@ -17,18 +17,22 @@ public protocol RelaySelectorProtocol {
/// Struct describing the selected relay.
public struct SelectedRelay: Equatable, Codable {
/// Selected relay endpoint.
- public var endpoint: MullvadEndpoint
+ public let endpoint: MullvadEndpoint
/// Relay hostname.
- public var hostname: String
+ public let hostname: String
/// Relay geo location.
- public var location: Location
+ public let location: Location
+
+ /// Number of retried attempts to connect to a relay.
+ public let retryAttempts: UInt
/// Designated initializer.
- public init(endpoint: MullvadEndpoint, hostname: String, location: Location) {
+ public init(endpoint: MullvadEndpoint, hostname: String, location: Location, retryAttempts: UInt) {
self.endpoint = endpoint
self.hostname = hostname
self.location = location
+ self.retryAttempts = retryAttempts
}
}
diff --git a/ios/PacketTunnelCore/Actor/Protocols/SettingsReaderProtocol.swift b/ios/PacketTunnelCore/Actor/Protocols/SettingsReaderProtocol.swift
index dc3bfbcd0f..9d75149b45 100644
--- a/ios/PacketTunnelCore/Actor/Protocols/SettingsReaderProtocol.swift
+++ b/ios/PacketTunnelCore/Actor/Protocols/SettingsReaderProtocol.swift
@@ -7,6 +7,7 @@
//
import Foundation
+import MullvadSettings
import MullvadTypes
import Network
import WireGuardKitTypes
@@ -36,16 +37,21 @@ public struct Settings {
/// DNS servers selected by user.
public var dnsServers: SelectedDNSServers
+ /// Obfuscation settings
+ public var obfuscation: WireGuardObfuscationSettings
+
public init(
privateKey: PrivateKey,
interfaceAddresses: [IPAddressRange],
relayConstraints: RelayConstraints,
- dnsServers: SelectedDNSServers
+ dnsServers: SelectedDNSServers,
+ obfuscation: WireGuardObfuscationSettings
) {
self.privateKey = privateKey
self.interfaceAddresses = interfaceAddresses
self.relayConstraints = relayConstraints
self.dnsServers = dnsServers
+ self.obfuscation = obfuscation
}
}
diff --git a/ios/PacketTunnelCoreTests/AppMessageHandlerTests.swift b/ios/PacketTunnelCoreTests/AppMessageHandlerTests.swift
index d11dd47802..79562eec28 100644
--- a/ios/PacketTunnelCoreTests/AppMessageHandlerTests.swift
+++ b/ios/PacketTunnelCoreTests/AppMessageHandlerTests.swift
@@ -88,7 +88,8 @@ final class AppMessageHandlerTests: XCTestCase {
let selectedRelay = SelectedRelay(
endpoint: selectorResult.endpoint,
hostname: selectorResult.relay.hostname,
- location: selectorResult.location
+ location: selectorResult.location,
+ retryAttempts: 0
)
_ = try? await appMessageHandler.handleAppMessage(
diff --git a/ios/PacketTunnelCoreTests/Mocks/PacketTunnelActor+Mocks.swift b/ios/PacketTunnelCoreTests/Mocks/PacketTunnelActor+Mocks.swift
index 3e01bec31c..c33f20457d 100644
--- a/ios/PacketTunnelCoreTests/Mocks/PacketTunnelActor+Mocks.swift
+++ b/ios/PacketTunnelCoreTests/Mocks/PacketTunnelActor+Mocks.swift
@@ -34,7 +34,8 @@ extension PacketTunnelActor {
defaultPathObserver: defaultPathObserver,
blockedStateErrorMapper: blockedStateErrorMapper,
relaySelector: relaySelector,
- settingsReader: settingsReader
+ settingsReader: settingsReader,
+ protocolObfuscator: ProtocolObfuscationStub()
)
}
}
diff --git a/ios/PacketTunnelCoreTests/Mocks/ProtocolObfuscationStub.swift b/ios/PacketTunnelCoreTests/Mocks/ProtocolObfuscationStub.swift
new file mode 100644
index 0000000000..7426075b61
--- /dev/null
+++ b/ios/PacketTunnelCoreTests/Mocks/ProtocolObfuscationStub.swift
@@ -0,0 +1,17 @@
+//
+// ProtocolObfuscationStub.swift
+// PacketTunnelCoreTests
+//
+// Created by Marco Nikic on 2023-11-20.
+// Copyright © 2023 Mullvad VPN AB. All rights reserved.
+//
+
+import Foundation
+@testable import MullvadTypes
+@testable import PacketTunnelCore
+
+struct ProtocolObfuscationStub: ProtocolObfuscation {
+ func obfuscate(_ endpoint: MullvadEndpoint, settings: Settings, retryAttempts: UInt) -> MullvadEndpoint {
+ endpoint
+ }
+}
diff --git a/ios/PacketTunnelCoreTests/Mocks/RelaySelectorStub.swift b/ios/PacketTunnelCoreTests/Mocks/RelaySelectorStub.swift
index c601c860d4..4922c080c5 100644
--- a/ios/PacketTunnelCoreTests/Mocks/RelaySelectorStub.swift
+++ b/ios/PacketTunnelCoreTests/Mocks/RelaySelectorStub.swift
@@ -44,7 +44,7 @@ extension RelaySelectorStub {
cityCode: "got",
latitude: 0,
longitude: 0
- )
+ ), retryAttempts: 0
)
}
}
diff --git a/ios/PacketTunnelCoreTests/Mocks/SettingsReaderStub.swift b/ios/PacketTunnelCoreTests/Mocks/SettingsReaderStub.swift
index 1893f4363d..c95133b091 100644
--- a/ios/PacketTunnelCoreTests/Mocks/SettingsReaderStub.swift
+++ b/ios/PacketTunnelCoreTests/Mocks/SettingsReaderStub.swift
@@ -7,6 +7,7 @@
//
import Foundation
+@testable import MullvadSettings
import MullvadTypes
import PacketTunnelCore
import WireGuardKitTypes
@@ -27,7 +28,8 @@ extension SettingsReaderStub {
privateKey: PrivateKey(),
interfaceAddresses: [IPAddressRange(from: "127.0.0.1/32")!],
relayConstraints: RelayConstraints(),
- dnsServers: .gateway
+ dnsServers: .gateway,
+ obfuscation: WireGuardObfuscationSettings(state: .off, port: .automatic)
)
return SettingsReaderStub {
diff --git a/ios/PacketTunnelCoreTests/Mocks/TunnelObfuscationStub.swift b/ios/PacketTunnelCoreTests/Mocks/TunnelObfuscationStub.swift
new file mode 100644
index 0000000000..e070115986
--- /dev/null
+++ b/ios/PacketTunnelCoreTests/Mocks/TunnelObfuscationStub.swift
@@ -0,0 +1,24 @@
+//
+// TunnelObfuscationStub.swift
+// PacketTunnelCoreTests
+//
+// Created by Marco Nikic on 2023-11-21.
+// Copyright © 2023 Mullvad VPN AB. All rights reserved.
+//
+
+import Foundation
+import Network
+@testable import TunnelObfuscation
+
+struct TunnelObfuscationStub: TunnelObfuscation {
+ let remotePort: UInt16
+ init(remoteAddress: IPAddress, tcpPort: UInt16) {
+ remotePort = tcpPort
+ }
+
+ func start() {}
+
+ func stop() {}
+
+ var localUdpPort: UInt16 { 42 }
+}
diff --git a/ios/PacketTunnelCoreTests/PacketTunnelActorTests.swift b/ios/PacketTunnelCoreTests/PacketTunnelActorTests.swift
index 65c0fbcf09..dccadc3442 100644
--- a/ios/PacketTunnelCoreTests/PacketTunnelActorTests.swift
+++ b/ios/PacketTunnelCoreTests/PacketTunnelActorTests.swift
@@ -7,7 +7,7 @@
//
import Combine
-import MullvadSettings
+@testable import MullvadSettings
import MullvadTypes
import Network
@testable import PacketTunnelCore
@@ -206,7 +206,8 @@ final class PacketTunnelActorTests: XCTestCase {
privateKey: PrivateKey(),
interfaceAddresses: [IPAddressRange(from: "127.0.0.1/32")!],
relayConstraints: RelayConstraints(),
- dnsServers: .gateway
+ dnsServers: .gateway,
+ obfuscation: WireGuardObfuscationSettings(state: .off, port: .automatic)
)
}
}
diff --git a/ios/PacketTunnelCoreTests/ProtocolObfuscatorTests.swift b/ios/PacketTunnelCoreTests/ProtocolObfuscatorTests.swift
new file mode 100644
index 0000000000..dd644a74e1
--- /dev/null
+++ b/ios/PacketTunnelCoreTests/ProtocolObfuscatorTests.swift
@@ -0,0 +1,116 @@
+//
+// ProtocolObfuscatorTests.swift
+// PacketTunnelCoreTests
+//
+// Created by Marco Nikic on 2023-11-21.
+// Copyright © 2023 Mullvad VPN AB. All rights reserved.
+//
+
+@testable import MullvadSettings
+@testable import MullvadTypes
+import Network
+@testable import PacketTunnelCore
+@testable import WireGuardKitTypes
+import XCTest
+
+final class ProtocolObfuscatorTests: XCTestCase {
+ var obfuscator: ProtocolObfuscator<TunnelObfuscationStub>!
+ var address: IPv4Address!
+ var gateway: IPv4Address!
+ var v4Endpoint: IPv4Endpoint!
+ var endpoint: MullvadEndpoint!
+
+ override func setUpWithError() throws {
+ obfuscator = ProtocolObfuscator<TunnelObfuscationStub>()
+ address = try XCTUnwrap(IPv4Address("1.2.3.4"))
+ gateway = try XCTUnwrap(IPv4Address("5.6.7.8"))
+ v4Endpoint = IPv4Endpoint(ip: address, port: 56)
+ endpoint = MullvadEndpoint(
+ ipv4Relay: v4Endpoint,
+ ipv4Gateway: gateway,
+ ipv6Gateway: .any,
+ publicKey: Data()
+ )
+ }
+
+ func testObfuscateOffDoesNotChangeEndpoint() {
+ let settings = settings(.off, obfuscationPort: .automatic)
+ let nonObfuscatedEndpoint = obfuscator.obfuscate(endpoint, settings: settings)
+
+ XCTAssertEqual(endpoint, nonObfuscatedEndpoint)
+ }
+
+ func testObfuscateOnPort80() throws {
+ let settings = settings(.on, obfuscationPort: .port80)
+ let obfuscatedEndpoint = obfuscator.obfuscate(endpoint, settings: settings)
+ let obfuscationProtocol = try XCTUnwrap(obfuscator.tunnelObfuscator as? TunnelObfuscationStub)
+
+ validate(obfuscatedEndpoint, against: obfuscationProtocol, expect: .port80)
+ }
+
+ func testObfuscateOnPort5001() throws {
+ let settings = settings(.on, obfuscationPort: .port5001)
+ let obfuscatedEndpoint = obfuscator.obfuscate(endpoint, settings: settings)
+ let obfuscationProtocol = try XCTUnwrap(obfuscator.tunnelObfuscator as? TunnelObfuscationStub)
+
+ validate(obfuscatedEndpoint, against: obfuscationProtocol, expect: .port5001)
+ }
+
+ func testObfuscateOnPortAutomaticIsPort80OnEvenRetryAttempts() throws {
+ let settings = settings(.on, obfuscationPort: .automatic)
+ let obfuscatedEndpoint = obfuscator.obfuscate(endpoint, settings: settings, retryAttempts: 2)
+ let obfuscationProtocol = try XCTUnwrap(obfuscator.tunnelObfuscator as? TunnelObfuscationStub)
+
+ validate(obfuscatedEndpoint, against: obfuscationProtocol, expect: .port80)
+ }
+
+ func testObfuscateOnPortAutomaticIsPort5001OnOddRetryAttempts() throws {
+ let settings = settings(.on, obfuscationPort: .automatic)
+ let obfuscatedEndpoint = obfuscator.obfuscate(endpoint, settings: settings, retryAttempts: 3)
+ let obfuscationProtocol = try XCTUnwrap(obfuscator.tunnelObfuscator as? TunnelObfuscationStub)
+
+ validate(obfuscatedEndpoint, against: obfuscationProtocol, expect: .port5001)
+ }
+
+ func testObfuscateAutomaticIsPort80EveryThirdAttempts() throws {
+ let settings = settings(.automatic, obfuscationPort: .automatic)
+ let obfuscatedEndpoint = obfuscator.obfuscate(endpoint, settings: settings, retryAttempts: 6)
+ let obfuscationProtocol = try XCTUnwrap(obfuscator.tunnelObfuscator as? TunnelObfuscationStub)
+
+ validate(obfuscatedEndpoint, against: obfuscationProtocol, expect: .port80)
+ }
+
+ func testObfuscateAutomaticIsPort5001EveryFourthAttempts() throws {
+ let settings = settings(.automatic, obfuscationPort: .automatic)
+ let obfuscatedEndpoint = obfuscator.obfuscate(endpoint, settings: settings, retryAttempts: 7)
+ let obfuscationProtocol = try XCTUnwrap(obfuscator.tunnelObfuscator as? TunnelObfuscationStub)
+
+ validate(obfuscatedEndpoint, against: obfuscationProtocol, expect: .port5001)
+ }
+
+ private func validate(
+ _ obfuscatedEndpoint: MullvadEndpoint,
+ against obfuscationProtocol: TunnelObfuscationStub,
+ expect port: WireGuardObfuscationPort
+ ) {
+ XCTAssertEqual(obfuscatedEndpoint.ipv4Relay.ip, .loopback)
+ XCTAssertEqual(obfuscatedEndpoint.ipv4Relay.port, obfuscationProtocol.localUdpPort)
+ XCTAssertEqual(obfuscationProtocol.remotePort, port.portValue)
+ }
+
+ private func settings(
+ _ obfuscationState: WireGuardObfuscationState,
+ obfuscationPort: WireGuardObfuscationPort
+ ) -> Settings {
+ Settings(
+ privateKey: PrivateKey(),
+ interfaceAddresses: [IPAddressRange(from: "127.0.0.1/32")!],
+ relayConstraints: RelayConstraints(),
+ dnsServers: .gateway,
+ obfuscation: WireGuardObfuscationSettings(
+ state: obfuscationState,
+ port: obfuscationPort
+ )
+ )
+ }
+}
diff --git a/ios/TunnelObfuscation/TunnelObfuscator.swift b/ios/TunnelObfuscation/UDPOverTCPObfuscator.swift
index f84ea73067..9af8410561 100644
--- a/ios/TunnelObfuscation/TunnelObfuscator.swift
+++ b/ios/TunnelObfuscation/UDPOverTCPObfuscator.swift
@@ -10,11 +10,18 @@ import Foundation
import Network
import TunnelObfuscatorProxy
+public protocol TunnelObfuscation {
+ init(remoteAddress: IPAddress, tcpPort: UInt16)
+ func start()
+ func stop()
+ var localUdpPort: UInt16 { get }
+}
+
/// 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 {
+public final class UDPOverTCPObfuscator: TunnelObfuscation {
private let stateLock = NSLock()
private let remoteAddress: IPAddress
- private let tcpPort: UInt16
+ internal let tcpPort: UInt16
private var proxyHandle = ProxyHandle(context: nil, port: 0)
private var isStarted = false
diff --git a/ios/TunnelObfuscationTests/TunnelObfuscationTests.swift b/ios/TunnelObfuscationTests/TunnelObfuscationTests.swift
index 0774ef9829..7edbc6d238 100644
--- a/ios/TunnelObfuscationTests/TunnelObfuscationTests.swift
+++ b/ios/TunnelObfuscationTests/TunnelObfuscationTests.swift
@@ -20,7 +20,7 @@ final class TunnelObfuscationTests: XCTestCase {
let tcpListener = try TCPUnsafeListener()
try await tcpListener.start()
- let obfuscator = TunnelObfuscator(remoteAddress: IPv4Address.loopback, tcpPort: tcpListener.listenPort)
+ let obfuscator = UDPOverTCPObfuscator(remoteAddress: IPv4Address.loopback, tcpPort: tcpListener.listenPort)
obfuscator.start()
// Accept incoming connections