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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
//
// TransportStrategy.swift
// MullvadREST
//
// Created by Marco Nikic on 2023-04-27.
// Copyright © 2025 Mullvad VPN AB. All rights reserved.
//
import Foundation
import Logging
@preconcurrency import MullvadRustRuntime
import MullvadSettings
import MullvadTypes
public struct TransportStrategy: Equatable, Sendable {
/// The different transports suggested by the strategy
public enum Transport: Equatable {
/// Connecting a direct connection
case direct
/// Connecting via shadowsocks proxy
case shadowsocks(configuration: ShadowsocksConfiguration)
/// Connecting via socks proxy
case socks5(configuration: Socks5Configuration)
/// Connecting via encrypted DNS proxy
case encryptedDNS
/// Failing to retrieve transport
case none
public static func == (lhs: Self, rhs: Self) -> Bool {
switch (lhs, rhs) {
case (.direct, .direct), (.none, .none):
true
case let (.shadowsocks(lhsConfiguration), .shadowsocks(rhsConfiguration)):
lhsConfiguration == rhsConfiguration
case let (.socks5(lhsConfiguration), .socks5(rhsConfiguration)):
lhsConfiguration == rhsConfiguration
case (.encryptedDNS, .encryptedDNS):
true
default:
false
}
}
}
private let shadowsocksLoader: ShadowsocksLoaderProtocol
private let accessMethodIterator: AccessMethodIterator
private let connectionModeProviderProxy: SwiftConnectionModeProviderProxy
public let opaqueAccessMethodSettingsWrapper: SwiftAccessMethodSettingsWrapper
public init(
datasource: AccessMethodRepositoryDataSource,
shadowsocksLoader: ShadowsocksLoaderProtocol
) {
self.shadowsocksLoader = shadowsocksLoader
self.accessMethodIterator = AccessMethodIterator(dataSource: datasource)
self.connectionModeProviderProxy = SwiftConnectionModeProviderProxy(
provider: accessMethodIterator,
domainName: REST.encryptedDNSHostname
)
self
.opaqueAccessMethodSettingsWrapper = initAccessMethodSettingsWrapper(
methods:
connectionModeProviderProxy
.accessMethods()
)
}
/// Rotating between enabled configurations by what order they were added in
public func didFail() {
let configuration = accessMethodIterator.pick()
switch configuration.kind {
case .bridges:
try? shadowsocksLoader.clear()
fallthrough
default:
self.accessMethodIterator.rotate()
}
}
/// The suggested connection transport
public func connectionTransport() -> Transport {
let configuration = accessMethodIterator.pick()
switch configuration.proxyConfiguration {
case .direct:
return .direct
case .encryptedDNS:
return .encryptedDNS
case .bridges:
do {
let configuration = try shadowsocksLoader.load()
return .shadowsocks(configuration: configuration)
} catch {
didFail()
guard accessMethodIterator.pick().kind != .bridges else { return .none }
return connectionTransport()
}
case let .shadowsocks(configuration):
return .shadowsocks(
configuration: ShadowsocksConfiguration(
address: configuration.server,
port: configuration.port,
password: configuration.password,
cipher: configuration.cipher.rawValue.description
))
case let .socks5(configuration):
return .socks5(
configuration: Socks5Configuration(
proxyEndpoint: configuration.toAnyIPEndpoint,
username: configuration.credential?.username,
password: configuration.credential?.password
))
}
}
public static func == (lhs: TransportStrategy, rhs: TransportStrategy) -> Bool {
lhs.accessMethodIterator.pick() == rhs.accessMethodIterator.pick()
}
}
|