summaryrefslogtreecommitdiffhomepage
path: root/ios/MullvadRustRuntimeTests
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 /ios/MullvadRustRuntimeTests
parente763bf54a1d49f1452d6328acb2b2aaea26beae9 (diff)
downloadmullvadvpn-26b75ba407b6099ae815775ac921024c6708d055.tar.xz
mullvadvpn-26b75ba407b6099ae815775ac921024c6708d055.zip
Add shadowsocks obfuscation as an option
Diffstat (limited to 'ios/MullvadRustRuntimeTests')
-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
4 files changed, 90 insertions, 20 deletions
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
}