summaryrefslogtreecommitdiffhomepage
path: root/ios/MullvadREST/Transport/Shadowsocks/ShadowsocksTransport.swift
blob: dc2a67767aaafb4aa19a7dc0175aafb547bef27d (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
//
//  ShadowsocksTransport.swift
//  MullvadTransport
//
//  Created by pronebird on 12/06/2023.
//  Copyright © 2025 Mullvad VPN AB. All rights reserved.
//

import Foundation
import MullvadRustRuntime
import MullvadTypes

public final class ShadowsocksTransport: RESTTransport {
    /// The Shadowsocks proxy instance that proxies all the traffic it receives
    private let shadowsocksProxy: ShadowsocksProxy

    /// The IPv4 representation of the loopback address used by `shadowsocksProxy`
    private let localhost = "127.0.0.1"

    /// The `URLSession` used to send requests via `shadowsocksProxy`
    public let urlSession: URLSession

    public var name: String {
        "shadow-socks-url-session"
    }

    public init(
        urlSession: URLSession,
        configuration: ShadowsocksConfiguration,
        addressCache: REST.AddressCache
    ) {
        self.urlSession = urlSession
        let apiAddress = addressCache.getCurrentEndpoint()

        shadowsocksProxy = ShadowsocksProxy(
            forwardAddress: apiAddress.ip,
            forwardPort: apiAddress.port,
            bridgeAddress: configuration.address,
            bridgePort: configuration.port,
            password: configuration.password,
            cipher: configuration.cipher
        )
    }

    public func sendRequest(
        _ request: URLRequest,
        completion: @escaping @Sendable (Data?, URLResponse?, Swift.Error?) -> Void
    ) -> Cancellable {
        // Start the Shadowsocks proxy in order to get a local port
        shadowsocksProxy.start()

        // Copy the URL request and rewrite the host and port to point to the Shadowsocks proxy instance
        var urlRequestCopy = request
        urlRequestCopy.url = request.url.flatMap { url in
            var components = URLComponents(url: url, resolvingAgainstBaseURL: false)
            components?.host = localhost
            components?.port = Int(shadowsocksProxy.localPort())
            return components?.url
        }

        let dataTask = urlSession.dataTask(with: urlRequestCopy, completionHandler: completion)
        dataTask.resume()
        return URLSessionTaskWrapper(task: dataTask)
    }
}