summaryrefslogtreecommitdiffhomepage
path: root/ios/MullvadRustRuntime/ShadowSocksProxy.swift
blob: 19f07f1a427ecb88673450778d02025d1f1de3c7 (plain)
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
//
//  ShadowsocksProxy.swift
//  MullvadREST
//
//  Created by Emils on 19/04/2023.
//  Copyright © 2023 Mullvad VPN AB. All rights reserved.
//

import Foundation
import MullvadRustRuntimeProxy
import Network

/// A Swift wrapper around a Rust implementation of Shadowsocks proxy instance
public class ShadowsocksProxy: @unchecked Sendable {
    private var proxyConfig: ProxyHandle
    private let forwardAddress: IPAddress
    private let forwardPort: UInt16
    private let bridgeAddress: IPAddress
    private let bridgePort: UInt16
    private let password: String
    private let cipher: String
    private var didStart = false
    private let stateLock = NSLock()

    public init(
        forwardAddress: IPAddress,
        forwardPort: UInt16,
        bridgeAddress: IPAddress,
        bridgePort: UInt16,
        password: String,
        cipher: String
    ) {
        proxyConfig = ProxyHandle(context: nil, port: 0)
        self.forwardAddress = forwardAddress
        self.forwardPort = forwardPort
        self.bridgeAddress = bridgeAddress
        self.bridgePort = bridgePort
        self.password = password
        self.cipher = cipher
    }

    /// The local port for the shadow socks proxy
    ///
    /// - Returns: The local port for the shadow socks proxy when it has started, 0 otherwise.
    public func localPort() -> UInt16 {
        stateLock.lock()
        defer { stateLock.unlock() }
        return proxyConfig.port
    }

    deinit {
        stop()
    }

    /// Starts the socks proxy
    public func start() {
        stateLock.lock()
        defer { stateLock.unlock() }
        guard didStart == false else { return }
        didStart = true

        // Get the raw bytes access to `proxyConfig`
        _ = withUnsafeMutablePointer(to: &proxyConfig) { config in
            start_shadowsocks_proxy(
                forwardAddress.rawValue.map { $0 },
                UInt(forwardAddress.rawValue.count),
                forwardPort,
                bridgeAddress.rawValue.map { $0 },
                UInt(bridgeAddress.rawValue.count),
                bridgePort,
                password,
                UInt(password.count),
                cipher,
                UInt(cipher.count),
                config
            )
        }
    }

    /// Stops the socks proxy
    public func stop() {
        stateLock.lock()
        defer { stateLock.unlock() }
        guard didStart == true else { return }
        didStart = false

        _ = withUnsafeMutablePointer(to: &proxyConfig) { config in
            stop_shadowsocks_proxy(config)
        }
    }
}