diff options
| author | Bug Magnet <marco.nikic@mullvad.net> | 2024-10-30 17:10:43 +0100 |
|---|---|---|
| committer | Bug Magnet <marco.nikic@mullvad.net> | 2024-11-13 15:41:48 +0100 |
| commit | 26b75ba407b6099ae815775ac921024c6708d055 (patch) | |
| tree | 3cf4c06914d66baa6acce0f8298fb77fdcd21c85 | |
| parent | e763bf54a1d49f1452d6328acb2b2aaea26beae9 (diff) | |
| download | mullvadvpn-26b75ba407b6099ae815775ac921024c6708d055.tar.xz mullvadvpn-26b75ba407b6099ae815775ac921024c6708d055.zip | |
Add shadowsocks obfuscation as an option
| -rw-r--r-- | ios/MullvadRustRuntime/TunnelObfuscator.swift (renamed from ios/MullvadRustRuntime/UDPOverTCPObfuscator.swift) | 33 | ||||
| -rw-r--r-- | ios/MullvadRustRuntime/include/mullvad_rust_runtime.h | 10 | ||||
| -rw-r--r-- | ios/MullvadRustRuntimeTests/TCPConnection.swift | 6 | ||||
| -rw-r--r-- | ios/MullvadRustRuntimeTests/TunnelObfuscationTests.swift | 48 | ||||
| -rw-r--r-- | ios/MullvadRustRuntimeTests/UDPConnection.swift | 35 | ||||
| -rw-r--r-- | ios/MullvadRustRuntimeTests/UnsafeListener.swift (renamed from ios/MullvadRustRuntimeTests/TCPUnsafeListener.swift) | 21 | ||||
| -rw-r--r-- | ios/MullvadVPN.xcodeproj/project.pbxproj | 18 | ||||
| -rw-r--r-- | ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift | 2 | ||||
| -rw-r--r-- | ios/PacketTunnelCore/Actor/ProtocolObfuscator.swift | 4 | ||||
| -rw-r--r-- | ios/PacketTunnelCoreTests/Mocks/TunnelObfuscationStub.swift | 2 | ||||
| -rw-r--r-- | mullvad-ios/src/lib.rs | 2 | ||||
| -rw-r--r-- | mullvad-ios/src/tunnel_obfuscator_proxy/ffi.rs | 10 | ||||
| -rw-r--r-- | mullvad-ios/src/tunnel_obfuscator_proxy/mod.rs | 26 |
13 files changed, 173 insertions, 44 deletions
diff --git a/ios/MullvadRustRuntime/UDPOverTCPObfuscator.swift b/ios/MullvadRustRuntime/TunnelObfuscator.swift index 8d5d874c84..549794b3af 100644 --- a/ios/MullvadRustRuntime/UDPOverTCPObfuscator.swift +++ b/ios/MullvadRustRuntime/TunnelObfuscator.swift @@ -11,8 +11,13 @@ import MullvadRustRuntimeProxy import MullvadTypes import Network +public enum TunnelObfuscationProtocol { + case udpOverTcp + case shadowsocks +} + public protocol TunnelObfuscation { - init(remoteAddress: IPAddress, tcpPort: UInt16) + init(remoteAddress: IPAddress, tcpPort: UInt16, obfuscationProtocol: TunnelObfuscationProtocol) func start() func stop() var localUdpPort: UInt16 { get } @@ -21,11 +26,15 @@ public protocol TunnelObfuscation { var transportLayer: TransportLayer { 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 UDPOverTCPObfuscator: TunnelObfuscation { +/// Class that implements obfuscation by accepting traffic on a local port and proxying it to the remote endpoint. +/// +/// The obfuscation happens either by wrapping UDP traffic into TCP traffic, or by using a local shadowsocks server +/// to encrypt the UDP traffic sent. +public final class TunnelObfuscator: TunnelObfuscation { private let stateLock = NSLock() private let remoteAddress: IPAddress internal let tcpPort: UInt16 + internal let obfuscationProtocol: TunnelObfuscationProtocol private var proxyHandle = ProxyHandle(context: nil, port: 0) private var isStarted = false @@ -38,12 +47,20 @@ public final class UDPOverTCPObfuscator: TunnelObfuscation { public var remotePort: UInt16 { tcpPort } - public var transportLayer: TransportLayer { .tcp } + public var transportLayer: TransportLayer { + switch obfuscationProtocol { + case .udpOverTcp: + .tcp + case .shadowsocks: + .udp + } + } /// Initialize tunnel obfuscator with remote server address and TCP port where udp2tcp is running. - public init(remoteAddress: IPAddress, tcpPort: UInt16) { + public init(remoteAddress: IPAddress, tcpPort: UInt16, obfuscationProtocol: TunnelObfuscationProtocol) { self.remoteAddress = remoteAddress self.tcpPort = tcpPort + self.obfuscationProtocol = obfuscationProtocol } deinit { @@ -54,6 +71,11 @@ public final class UDPOverTCPObfuscator: TunnelObfuscation { stateLock.withLock { guard !isStarted else { return } + let obfuscationProtocol = switch obfuscationProtocol { + case .udpOverTcp: TunnelObfuscatorProtocol(0) + case .shadowsocks: TunnelObfuscatorProtocol(1) + } + let result = withUnsafeMutablePointer(to: &proxyHandle) { proxyHandlePointer in let addressData = remoteAddress.rawValue @@ -61,6 +83,7 @@ public final class UDPOverTCPObfuscator: TunnelObfuscation { addressData.map { $0 }, UInt(addressData.count), tcpPort, + obfuscationProtocol, proxyHandlePointer ) } diff --git a/ios/MullvadRustRuntime/include/mullvad_rust_runtime.h b/ios/MullvadRustRuntime/include/mullvad_rust_runtime.h index a110313c72..26904b89df 100644 --- a/ios/MullvadRustRuntime/include/mullvad_rust_runtime.h +++ b/ios/MullvadRustRuntime/include/mullvad_rust_runtime.h @@ -6,6 +6,15 @@ #include <stdlib.h> /** + * SAFETY: `TunnelObfuscatorProtocol` values must either be `0` or `1` + */ +enum TunnelObfuscatorProtocol { + UdpOverTcp = 0, + Shadowsocks, +}; +typedef uint8_t TunnelObfuscatorProtocol; + +/** * A thin wrapper around [`mullvad_encrypted_dns_proxy::state::EncryptedDnsProxyState`] that * can start a local forwarder (see [`Self::start`]). */ @@ -179,6 +188,7 @@ int32_t stop_shadowsocks_proxy(struct ProxyHandle *proxy_config); int32_t start_tunnel_obfuscator_proxy(const uint8_t *peer_address, uintptr_t peer_address_len, uint16_t peer_port, + TunnelObfuscatorProtocol obfuscation_protocol, struct ProxyHandle *proxy_handle); int32_t stop_tunnel_obfuscator_proxy(struct ProxyHandle *proxy_handle); diff --git a/ios/MullvadRustRuntimeTests/TCPConnection.swift b/ios/MullvadRustRuntimeTests/TCPConnection.swift index c8543b4879..dd9b58c0d0 100644 --- a/ios/MullvadRustRuntimeTests/TCPConnection.swift +++ b/ios/MullvadRustRuntimeTests/TCPConnection.swift @@ -11,14 +11,16 @@ 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 { +class TCPConnection: Connection { private let dispatchQueue = DispatchQueue(label: "TCPConnection") private let nwConnection: NWConnection - init(nwConnection: NWConnection) { + required init(nwConnection: NWConnection) { self.nwConnection = nwConnection } + static var connectionParameters: NWParameters { .tcp } + deinit { cancel() } diff --git a/ios/MullvadRustRuntimeTests/TunnelObfuscationTests.swift b/ios/MullvadRustRuntimeTests/TunnelObfuscationTests.swift index 7372842c8d..84c9ca8f4f 100644 --- a/ios/MullvadRustRuntimeTests/TunnelObfuscationTests.swift +++ b/ios/MullvadRustRuntimeTests/TunnelObfuscationTests.swift @@ -11,16 +11,20 @@ import Network import XCTest final class TunnelObfuscationTests: XCTestCase { - func testRunningObfuscatorProxy() async throws { + func testRunningUdpOverTcpObfuscatorProxy() 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() + let tcpListener = try UnsafeListener<TCPConnection>() try await tcpListener.start() - let obfuscator = UDPOverTCPObfuscator(remoteAddress: IPv4Address.loopback, tcpPort: tcpListener.listenPort) + let obfuscator = TunnelObfuscator( + remoteAddress: IPv4Address.loopback, + tcpPort: tcpListener.listenPort, + obfuscationProtocol: .udpOverTcp + ) obfuscator.start() // Accept incoming connections @@ -46,4 +50,42 @@ final class TunnelObfuscationTests: XCTestCase { XCTAssert(receivedData.count == packetLength) XCTAssertEqual(receivedData[preambleLength...], markerData) } + + func testRunningShadowsocksObfuscatorProxy() async throws { + let markerData = Data([109, 117, 108, 108, 118, 97, 100]) + + let localUdpListener = try UnsafeListener<UDPConnection>() + try await localUdpListener.start() + + let localObfuscator = TunnelObfuscator( + remoteAddress: IPv4Address.loopback, + tcpPort: localUdpListener.listenPort, + obfuscationProtocol: .shadowsocks + ) + localObfuscator.start() + + // Accept incoming connections + let localConnectionDataTask = Task { + for await obfuscatedConnection in localUdpListener.newConnections { + try await obfuscatedConnection.start() + + let readDatagram = try await obfuscatedConnection.readSingleDatagram() + // Write into the connection the unencrypted payload that was just read + try await obfuscatedConnection.sendData(readDatagram) + return readDatagram + } + throw POSIXError(.ECANCELED) + } + + // Send marker data over UDP + let connection = UDPConnection(remote: IPv4Address.loopback, port: localObfuscator.localUdpPort) + try await connection.start() + try await connection.sendData(markerData) + let readDataFromObfuscator = try await connection.readSingleDatagram() + + // As the connection data is encrypted and this test does not run a shadowsocks server to decrypt the payload + // The connection from the local UDP listener writes back what it read from the obfuscator, unencrypted + _ = try await localConnectionDataTask.value + XCTAssertEqual(readDataFromObfuscator, markerData) + } } diff --git a/ios/MullvadRustRuntimeTests/UDPConnection.swift b/ios/MullvadRustRuntimeTests/UDPConnection.swift index f0886e5fb5..563f3e6c4c 100644 --- a/ios/MullvadRustRuntimeTests/UDPConnection.swift +++ b/ios/MullvadRustRuntimeTests/UDPConnection.swift @@ -9,20 +9,31 @@ import Foundation import Network +protocol Connection { + init(nwConnection: NWConnection) + static var connectionParameters: NWParameters { get } +} + /// 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 { +class UDPConnection: Connection { private let dispatchQueue = DispatchQueue(label: "UDPConnection") private let nwConnection: NWConnection - init(remote: IPAddress, port: UInt16) { - nwConnection = NWConnection( + convenience init(remote: IPAddress, port: UInt16) { + self.init(nwConnection: NWConnection( host: NWEndpoint.Host("\(remote)"), port: NWEndpoint.Port(integerLiteral: port), using: .udp - ) + )) + } + + required init(nwConnection: NWConnection) { + self.nwConnection = nwConnection } + static var connectionParameters: NWParameters { .udp } + deinit { cancel() } @@ -58,6 +69,22 @@ class UDPConnection { nwConnection.cancel() } + func readSingleDatagram() async throws -> Data { + return try await withCheckedThrowingContinuation { continuation in + nwConnection.receiveMessage { data, _, _, error in + guard let data else { + continuation.resume(throwing: POSIXError(.EIO)) + return + } + if let error { + continuation.resume(throwing: error) + return + } + continuation.resume(with: .success(data)) + } + } + } + func sendData(_ data: Data) async throws { return try await withCheckedThrowingContinuation { continuation in nwConnection.send(content: data, completion: .contentProcessed { error in diff --git a/ios/MullvadRustRuntimeTests/TCPUnsafeListener.swift b/ios/MullvadRustRuntimeTests/UnsafeListener.swift index d01fc29dbb..2ccf0c1aaf 100644 --- a/ios/MullvadRustRuntimeTests/TCPUnsafeListener.swift +++ b/ios/MullvadRustRuntimeTests/UnsafeListener.swift @@ -1,5 +1,5 @@ // -// TCPUnsafeListener.swift +// UnsafeListener.swift // MullvadRustRuntimeTests // // Created by pronebird on 27/06/2023. @@ -8,25 +8,24 @@ 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") +class UnsafeListener<T: Connection> { + private let dispatchQueue = DispatchQueue(label: "com.test.unsafeListener") private let listener: NWListener - /// A stream of new TCP connections. - /// The caller may iterate over this stream to accept new TCP connections. + /// A stream of new connections. + /// The caller may iterate over this stream to accept new connections. /// - /// `TCPConnection` objects are returned unopen, so the caller has to call `TCPConnection.start()` to accept the + /// `Connection` objects are returned unopen, so the caller has to call `Connection.start()` to accept the /// connection before initiating the data exchange. - let newConnections: AsyncStream<TCPConnection> + let newConnections: AsyncStream<T> init() throws { - let listener = try NWListener(using: .tcp) + let listener = try NWListener(using: T.connectionParameters) newConnections = AsyncStream { continuation in listener.newConnectionHandler = { nwConnection in - continuation.yield(TCPConnection(nwConnection: nwConnection)) + continuation.yield(T(nwConnection: nwConnection)) } continuation.onTermination = { @Sendable _ in listener.newConnectionHandler = nil @@ -40,7 +39,7 @@ class TCPUnsafeListener { cancel() } - /// Local TCP port bound by listener on which it accepts new connections. + /// Local port bound by listener on which it accepts new connections. var listenPort: UInt16 { return listener.port?.rawValue ?? 0 } diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj index 7770f47b10..1b1c87beaf 100644 --- a/ios/MullvadVPN.xcodeproj/project.pbxproj +++ b/ios/MullvadVPN.xcodeproj/project.pbxproj @@ -841,10 +841,10 @@ A9D99B9A2A1F7C3200DE27D3 /* RESTTransport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06FAE67D28F83CA50033DD93 /* RESTTransport.swift */; }; A9D9A4AE2C36CFE9004088DD /* WireGuardKitTypes in Frameworks */ = {isa = PBXBuildFile; productRef = A9D9A4AD2C36CFE9004088DD /* WireGuardKitTypes */; }; A9D9A4B12C36D10E004088DD /* ShadowSocksProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0DDE40F2B220458006B57A7 /* ShadowSocksProxy.swift */; }; - A9D9A4B22C36D12D004088DD /* UDPOverTCPObfuscator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 584023212A406BF5007B27AC /* UDPOverTCPObfuscator.swift */; }; + A9D9A4B22C36D12D004088DD /* TunnelObfuscator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 584023212A406BF5007B27AC /* TunnelObfuscator.swift */; }; A9D9A4BB2C36D397004088DD /* EphemeralPeerNegotiator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9EB4F9C2B7FAB21002A2D7A /* EphemeralPeerNegotiator.swift */; }; A9D9A4C42C36D53C004088DD /* MullvadRustRuntime.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A992DA1D2C24709F00DE7CE5 /* MullvadRustRuntime.framework */; }; - A9D9A4CC2C36D54E004088DD /* TCPUnsafeListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = 585A02E82A4B283000C6CAFF /* TCPUnsafeListener.swift */; }; + A9D9A4CC2C36D54E004088DD /* UnsafeListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = 585A02E82A4B283000C6CAFF /* UnsafeListener.swift */; }; A9D9A4CD2C36D54E004088DD /* UDPConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 585A02EA2A4B285800C6CAFF /* UDPConnection.swift */; }; A9D9A4CE2C36D54E004088DD /* TunnelObfuscationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58695A9F2A4ADA9200328DB3 /* TunnelObfuscationTests.swift */; }; A9D9A4CF2C36D54E004088DD /* TCPConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 585A02EC2A4B28F300C6CAFF /* TCPConnection.swift */; }; @@ -1506,7 +1506,7 @@ 583E60952A9F6D0800DC61EF /* ConfigurationBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationBuilder.swift; sourceTree = "<group>"; }; 583FE00B29C0C7FD006E85F9 /* ModalPresentationConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalPresentationConfiguration.swift; sourceTree = "<group>"; }; 583FE01129C0F99A006E85F9 /* PresentationControllerDismissalInterceptor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PresentationControllerDismissalInterceptor.swift; sourceTree = "<group>"; }; - 584023212A406BF5007B27AC /* UDPOverTCPObfuscator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UDPOverTCPObfuscator.swift; sourceTree = "<group>"; }; + 584023212A406BF5007B27AC /* TunnelObfuscator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelObfuscator.swift; sourceTree = "<group>"; }; 584023282A407F5F007B27AC /* libmullvad_ios.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libmullvad_ios.a; path = "../target/aarch64-apple-ios/debug/libmullvad_ios.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>"; }; @@ -1525,7 +1525,7 @@ 584D26C5270C8741004EA533 /* SettingsDNSTextCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsDNSTextCell.swift; sourceTree = "<group>"; }; 58561C98239A5D1500BD6B5E /* IPv4Endpoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPv4Endpoint.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>"; }; + 585A02E82A4B283000C6CAFF /* UnsafeListener.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnsafeListener.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>"; }; @@ -4090,7 +4090,7 @@ A948809A2BC9308D0090A44C /* EphemeralPeerExchangeActor.swift */, A9EB4F9C2B7FAB21002A2D7A /* EphemeralPeerNegotiator.swift */, F0DDE40F2B220458006B57A7 /* ShadowSocksProxy.swift */, - 584023212A406BF5007B27AC /* UDPOverTCPObfuscator.swift */, + 584023212A406BF5007B27AC /* TunnelObfuscator.swift */, 014449942CA293B100C0C2F2 /* EncryptedDNSProxy.swift */, ); path = MullvadRustRuntime; @@ -4099,12 +4099,12 @@ A9D9A4C12C36D53C004088DD /* MullvadRustRuntimeTests */ = { isa = PBXGroup; children = ( - A9C308392C19DDA7008715F1 /* MullvadPostQuantum+Stubs.swift */, A98F1B502C19C48D003C869E /* EphemeralPeerExchangeActorTests.swift */, + A9C308392C19DDA7008715F1 /* MullvadPostQuantum+Stubs.swift */, 585A02EC2A4B28F300C6CAFF /* TCPConnection.swift */, - 585A02E82A4B283000C6CAFF /* TCPUnsafeListener.swift */, 58695A9F2A4ADA9200328DB3 /* TunnelObfuscationTests.swift */, 585A02EA2A4B285800C6CAFF /* UDPConnection.swift */, + 585A02E82A4B283000C6CAFF /* UnsafeListener.swift */, ); path = MullvadRustRuntimeTests; sourceTree = "<group>"; @@ -6233,7 +6233,7 @@ A9D9A4B12C36D10E004088DD /* ShadowSocksProxy.swift in Sources */, 014449952CA293B100C0C2F2 /* EncryptedDNSProxy.swift in Sources */, A9D9A4BB2C36D397004088DD /* EphemeralPeerNegotiator.swift in Sources */, - A9D9A4B22C36D12D004088DD /* UDPOverTCPObfuscator.swift in Sources */, + A9D9A4B22C36D12D004088DD /* TunnelObfuscator.swift in Sources */, A9173C322C36CCDD00F6A08C /* PacketTunnelProvider+TCPConnection.swift in Sources */, F05919802C45515200C301F3 /* EphemeralPeerExchangeActor.swift in Sources */, ); @@ -6247,7 +6247,7 @@ F08B6B772C52878400D0A121 /* EphemeralPeerExchangeActorTests.swift in Sources */, A9D9A4CF2C36D54E004088DD /* TCPConnection.swift in Sources */, A9D9A4CE2C36D54E004088DD /* TunnelObfuscationTests.swift in Sources */, - A9D9A4CC2C36D54E004088DD /* TCPUnsafeListener.swift in Sources */, + A9D9A4CC2C36D54E004088DD /* UnsafeListener.swift in Sources */, A9D9A4CD2C36D54E004088DD /* UDPConnection.swift in Sources */, F0A1638A2C47B77300592300 /* ServerRelaysResponse+Stubs.swift in Sources */, ); diff --git a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift index 34530a0b48..84ff437b20 100644 --- a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift +++ b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift @@ -97,7 +97,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider { guard let self = self else { return } tunnelSettingsListener.onNewSettings?(settings.tunnelSettings) }, - protocolObfuscator: ProtocolObfuscator<UDPOverTCPObfuscator>() + protocolObfuscator: ProtocolObfuscator<TunnelObfuscator>() ) let urlRequestProxy = URLRequestProxy(dispatchQueue: internalQueue, transportProvider: transportProvider) diff --git a/ios/PacketTunnelCore/Actor/ProtocolObfuscator.swift b/ios/PacketTunnelCore/Actor/ProtocolObfuscator.swift index e51549d5a9..8a499a3191 100644 --- a/ios/PacketTunnelCore/Actor/ProtocolObfuscator.swift +++ b/ios/PacketTunnelCore/Actor/ProtocolObfuscator.swift @@ -51,9 +51,11 @@ public class ProtocolObfuscator<Obfuscator: TunnelObfuscation>: ProtocolObfuscat return endpoint } + // At this point, the only possible obfuscation methods should be either `.udpOverTcp` or `.shadowsocks` let obfuscator = Obfuscator( remoteAddress: endpoint.ipv4Relay.ip, - tcpPort: remotePort + tcpPort: remotePort, + obfuscationProtocol: obfuscationMethod == .shadowsocks ? .shadowsocks : .udpOverTcp ) obfuscator.start() diff --git a/ios/PacketTunnelCoreTests/Mocks/TunnelObfuscationStub.swift b/ios/PacketTunnelCoreTests/Mocks/TunnelObfuscationStub.swift index eb1c00ff66..85226869a2 100644 --- a/ios/PacketTunnelCoreTests/Mocks/TunnelObfuscationStub.swift +++ b/ios/PacketTunnelCoreTests/Mocks/TunnelObfuscationStub.swift @@ -15,7 +15,7 @@ struct TunnelObfuscationStub: TunnelObfuscation { var transportLayer: TransportLayer { .udp } let remotePort: UInt16 - init(remoteAddress: IPAddress, tcpPort: UInt16) { + init(remoteAddress: IPAddress, tcpPort: UInt16, obfuscationProtocol: TunnelObfuscationProtocol) { remotePort = tcpPort } diff --git a/mullvad-ios/src/lib.rs b/mullvad-ios/src/lib.rs index 4a24745b09..61c47803a4 100644 --- a/mullvad-ios/src/lib.rs +++ b/mullvad-ios/src/lib.rs @@ -2,7 +2,7 @@ mod encrypted_dns_proxy; mod ephemeral_peer_proxy; mod shadowsocks_proxy; -mod tunnel_obfuscator_proxy; +pub mod tunnel_obfuscator_proxy; #[repr(C)] pub struct ProxyHandle { diff --git a/mullvad-ios/src/tunnel_obfuscator_proxy/ffi.rs b/mullvad-ios/src/tunnel_obfuscator_proxy/ffi.rs index 088d4f2232..e9ad7ba794 100644 --- a/mullvad-ios/src/tunnel_obfuscator_proxy/ffi.rs +++ b/mullvad-ios/src/tunnel_obfuscator_proxy/ffi.rs @@ -7,11 +7,19 @@ use std::{ static INIT_LOGGING: Once = Once::new(); +/// SAFETY: `TunnelObfuscatorProtocol` values must either be `0` or `1` +#[repr(u8)] +pub enum TunnelObfuscatorProtocol { + UdpOverTcp = 0, + Shadowsocks, +} + #[no_mangle] pub unsafe extern "C" fn start_tunnel_obfuscator_proxy( peer_address: *const u8, peer_address_len: usize, peer_port: u16, + obfuscation_protocol: TunnelObfuscatorProtocol, proxy_handle: *mut ProxyHandle, ) -> i32 { INIT_LOGGING.call_once(|| { @@ -27,7 +35,7 @@ pub unsafe extern "C" fn start_tunnel_obfuscator_proxy( return -1; }; - let result = TunnelObfuscatorRuntime::new(peer_sock_addr).and_then(|runtime| runtime.run()); + let result = TunnelObfuscatorRuntime::new(peer_sock_addr, obfuscation_protocol).run(); match result { Ok((local_endpoint, obfuscator_handle)) => { diff --git a/mullvad-ios/src/tunnel_obfuscator_proxy/mod.rs b/mullvad-ios/src/tunnel_obfuscator_proxy/mod.rs index 4db2f36812..f33d6fd6ee 100644 --- a/mullvad-ios/src/tunnel_obfuscator_proxy/mod.rs +++ b/mullvad-ios/src/tunnel_obfuscator_proxy/mod.rs @@ -1,6 +1,12 @@ -use std::{io, net::SocketAddr}; +use ffi::TunnelObfuscatorProtocol; +use std::{ + io, + net::{Ipv4Addr, SocketAddr}, +}; use tokio::task::JoinHandle; -use tunnel_obfuscation::{create_obfuscator, udp2tcp::Settings, Settings as ObfuscationSettings}; +use tunnel_obfuscation::{ + create_obfuscator, shadowsocks, udp2tcp, Settings as ObfuscationSettings, +}; mod ffi; @@ -11,10 +17,20 @@ pub struct TunnelObfuscatorRuntime { } impl TunnelObfuscatorRuntime { - pub fn new(peer: SocketAddr) -> io::Result<Self> { - let settings = ObfuscationSettings::Udp2Tcp(Settings { peer }); + pub fn new(peer: SocketAddr, obfuscation_protocol: TunnelObfuscatorProtocol) -> Self { + let settings: ObfuscationSettings = match obfuscation_protocol { + TunnelObfuscatorProtocol::UdpOverTcp => { + ObfuscationSettings::Udp2Tcp(udp2tcp::Settings { peer }) + } + TunnelObfuscatorProtocol::Shadowsocks => { + ObfuscationSettings::Shadowsocks(shadowsocks::Settings { + shadowsocks_endpoint: peer, + wireguard_endpoint: SocketAddr::from((Ipv4Addr::LOCALHOST, 51820)), + }) + } + }; - Ok(Self { settings }) + Self { settings } } pub fn run(self) -> io::Result<(SocketAddr, TunnelObfuscatorHandle)> { |
