summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorBug Magnet <marco.nikic@mullvad.net>2024-10-30 17:10:43 +0100
committerBug Magnet <marco.nikic@mullvad.net>2024-11-13 15:41:48 +0100
commit26b75ba407b6099ae815775ac921024c6708d055 (patch)
tree3cf4c06914d66baa6acce0f8298fb77fdcd21c85
parente763bf54a1d49f1452d6328acb2b2aaea26beae9 (diff)
downloadmullvadvpn-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.h10
-rw-r--r--ios/MullvadRustRuntimeTests/TCPConnection.swift6
-rw-r--r--ios/MullvadRustRuntimeTests/TunnelObfuscationTests.swift48
-rw-r--r--ios/MullvadRustRuntimeTests/UDPConnection.swift35
-rw-r--r--ios/MullvadRustRuntimeTests/UnsafeListener.swift (renamed from ios/MullvadRustRuntimeTests/TCPUnsafeListener.swift)21
-rw-r--r--ios/MullvadVPN.xcodeproj/project.pbxproj18
-rw-r--r--ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift2
-rw-r--r--ios/PacketTunnelCore/Actor/ProtocolObfuscator.swift4
-rw-r--r--ios/PacketTunnelCoreTests/Mocks/TunnelObfuscationStub.swift2
-rw-r--r--mullvad-ios/src/lib.rs2
-rw-r--r--mullvad-ios/src/tunnel_obfuscator_proxy/ffi.rs10
-rw-r--r--mullvad-ios/src/tunnel_obfuscator_proxy/mod.rs26
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)> {