1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
|
//
// TunnelObfuscationTests.swift
// MullvadRustRuntimeTests
//
// Created by pronebird on 27/06/2023.
// Copyright © 2025 Mullvad VPN AB. All rights reserved.
//
import MullvadRustRuntime
import Network
import XCTest
final class TunnelObfuscationTests: XCTestCase {
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 UnsafeListener<TCPConnection>()
try await tcpListener.start()
let obfuscator = TunnelObfuscator(
remoteAddress: IPv4Address.loopback,
tcpPort: tcpListener.listenPort,
obfuscationProtocol: .udpOverTcp
)
obfuscator.start()
// Accept incoming connections
let connectionDataTask = Task {
for await newConnection in tcpListener.newConnections {
try await newConnection.start()
return try await newConnection.receiveData(
minimumLength: packetLength,
maximumLength: packetLength
)
}
throw POSIXError(.ECANCELED)
}
// Send marker data over UDP
let connection = UDPConnection(remote: IPv4Address.loopback, port: obfuscator.localUdpPort)
try await connection.start()
try await connection.sendData(markerData)
// Validate the sent data
let receivedData = try await connectionDataTask.value
XCTAssert(receivedData.count == packetLength)
XCTAssertEqual(receivedData[preambleLength...], markerData)
}
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)
}
}
|