Skip to content

Commit 8cb92f6

Browse files
committed
Update data structure to support new obfuscation selection
1 parent 114d191 commit 8cb92f6

File tree

5 files changed

+172
-53
lines changed

5 files changed

+172
-53
lines changed

ios/MullvadSettings/WireGuardObfuscationSettings.swift

+132-28
Original file line numberDiff line numberDiff line change
@@ -8,54 +8,158 @@
88

99
import Foundation
1010

11-
/// Whether UDP-over-TCP obfuscation is enabled
11+
/// Whether obfuscation is enabled and which method is used
1212
///
1313
/// `.automatic` means an algorithm will decide whether to use it or not.
1414
public enum WireGuardObfuscationState: Codable {
15-
case automatic
15+
@available(*, deprecated, renamed: "udpTcp")
1616
case on
17+
18+
case automatic
19+
case udpOverTcp
20+
case shadowsocks
1721
case off
22+
23+
public init(from decoder: Decoder) throws {
24+
let container = try decoder.container(keyedBy: CodingKeys.self)
25+
26+
var allKeys = ArraySlice(container.allKeys)
27+
guard let key = allKeys.popFirst(), allKeys.isEmpty else {
28+
throw DecodingError.typeMismatch(
29+
WireGuardObfuscationState.self,
30+
DecodingError.Context(
31+
codingPath: container.codingPath,
32+
debugDescription: "Invalid number of keys found, expected one.",
33+
underlyingError: nil
34+
)
35+
)
36+
}
37+
38+
switch key {
39+
case .automatic:
40+
self = .automatic
41+
case .on, .udpOverTcp:
42+
self = .udpOverTcp
43+
case .shadowsocks:
44+
self = .shadowsocks
45+
case .off:
46+
self = .off
47+
}
48+
}
1849
}
1950

20-
/// The port to select when using UDP-over-TCP obfuscation
21-
///
22-
/// `.automatic` means an algorith will decide between using `port80` or `port5001`
23-
public enum WireGuardObfuscationPort: UInt16, Codable, CaseIterable {
24-
case automatic = 0
25-
case port80 = 80
26-
case port5001 = 5001
51+
public enum WireGuardObfuscationUdpOverTcpPort: Codable, Equatable, CustomStringConvertible {
52+
case automatic
53+
case port80
54+
case port5001
2755

28-
/// The `UInt16` representation of the port.
29-
/// - Returns: `0` if `.automatic`, `80` or `5001` otherwise.
30-
public var portValue: UInt16 {
31-
self == .automatic ? 0 : rawValue
56+
public var portValue: UInt16? {
57+
switch self {
58+
case .automatic:
59+
nil
60+
case .port80:
61+
80
62+
case .port5001:
63+
5001
64+
}
3265
}
3366

34-
public init?(rawValue: UInt16) {
35-
switch rawValue {
36-
case 80:
37-
self = .port80
38-
case 5001:
39-
self = .port5001
40-
default: self = .automatic
67+
public var description: String {
68+
switch self {
69+
case .automatic:
70+
NSLocalizedString(
71+
"WIREGUARD_OBFUSCATION_UDP_TCP_PORT_AUTOMATIC",
72+
tableName: "VPNSettings",
73+
value: "Automatic",
74+
comment: ""
75+
)
76+
case .port80:
77+
"80"
78+
case .port5001:
79+
"5001"
4180
}
4281
}
82+
}
4383

44-
public init(from decoder: Decoder) throws {
45-
let container = try decoder.singleValueContainer()
46-
let decodedValue = try? container.decode(UInt16.self)
84+
public enum WireGuardObfuscationShadowsockPort: Codable, Equatable, CustomStringConvertible {
85+
case automatic
86+
case custom(UInt16)
4787

48-
let port = WireGuardObfuscationPort.allCases.first(where: { $0.rawValue == decodedValue })
49-
self = port ?? .automatic
88+
public var portValue: UInt16? {
89+
switch self {
90+
case .automatic:
91+
nil
92+
case let .custom(port):
93+
port
94+
}
95+
}
96+
97+
public var description: String {
98+
switch self {
99+
case .automatic:
100+
NSLocalizedString(
101+
"WIREGUARD_OBFUSCATION_SHADOWSOCKS_PORT_AUTOMATIC",
102+
tableName: "VPNSettings",
103+
value: "Automatic",
104+
comment: ""
105+
)
106+
case let .custom(port):
107+
String(port)
108+
}
50109
}
51110
}
52111

112+
// Can't deprecate the whole type since it'll yield a lint warning when decoding
113+
// port in `WireGuardObfuscationSettings`.
114+
private enum WireGuardObfuscationPort: UInt16, Codable {
115+
@available(*, deprecated, message: "Use `udpOverTcpPort` instead")
116+
case automatic = 0
117+
@available(*, deprecated, message: "Use `udpOverTcpPort` instead")
118+
case port80 = 80
119+
@available(*, deprecated, message: "Use `udpOverTcpPort` instead")
120+
case port5001 = 5001
121+
}
122+
53123
public struct WireGuardObfuscationSettings: Codable, Equatable {
124+
@available(*, deprecated, message: "Use `udpOverTcpPort` instead")
125+
private var port: WireGuardObfuscationPort = .automatic
126+
54127
public let state: WireGuardObfuscationState
55-
public let port: WireGuardObfuscationPort
128+
public let udpOverTcpPort: WireGuardObfuscationUdpOverTcpPort
129+
public let shadowsocksPort: WireGuardObfuscationShadowsockPort
56130

57-
public init(state: WireGuardObfuscationState = .automatic, port: WireGuardObfuscationPort = .automatic) {
131+
public init(
132+
state: WireGuardObfuscationState = .automatic,
133+
udpOverTcpPort: WireGuardObfuscationUdpOverTcpPort = .automatic,
134+
shadowsocksPort: WireGuardObfuscationShadowsockPort = .automatic
135+
) {
58136
self.state = state
59-
self.port = port
137+
self.udpOverTcpPort = udpOverTcpPort
138+
self.shadowsocksPort = shadowsocksPort
139+
}
140+
141+
public init(from decoder: Decoder) throws {
142+
let container = try decoder.container(keyedBy: CodingKeys.self)
143+
144+
state = try container.decode(WireGuardObfuscationState.self, forKey: .state)
145+
shadowsocksPort = try container.decodeIfPresent(
146+
WireGuardObfuscationShadowsockPort.self,
147+
forKey: .shadowsocksPort
148+
) ?? .automatic
149+
150+
if let port = try? container.decodeIfPresent(WireGuardObfuscationUdpOverTcpPort.self, forKey: .udpOverTcpPort) {
151+
udpOverTcpPort = port
152+
} else if let port = try? container.decodeIfPresent(WireGuardObfuscationPort.self, forKey: .port) {
153+
switch port {
154+
case .automatic:
155+
udpOverTcpPort = .automatic
156+
case .port80:
157+
udpOverTcpPort = .port80
158+
case .port5001:
159+
udpOverTcpPort = .port5001
160+
}
161+
} else {
162+
udpOverTcpPort = .automatic
163+
}
60164
}
61165
}

ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsCellFactory.swift

+9-5
Original file line numberDiff line numberDiff line change
@@ -145,14 +145,16 @@ final class VPNSettingsCellFactory: CellFactoryProtocol {
145145
value: "UDP-over-TCP",
146146
comment: ""
147147
)
148+
148149
#if DEBUG
149150
cell.detailTitleLabel.text = String(format: NSLocalizedString(
150151
"WIREGUARD_OBFUSCATION_UDP_TCP_PORT",
151152
tableName: "VPNSettings",
152-
value: "Port: %d",
153+
value: "Port: %@",
153154
comment: ""
154-
), viewModel.obfuscationPort.portValue)
155+
), viewModel.obfuscationUpdOverTcpPort.description)
155156
#endif
157+
156158
cell.accessibilityIdentifier = item.accessibilityIdentifier
157159
cell.applySubCellStyling()
158160

@@ -169,14 +171,16 @@ final class VPNSettingsCellFactory: CellFactoryProtocol {
169171
value: "Shadowsocks",
170172
comment: ""
171173
)
174+
172175
#if DEBUG
173176
cell.detailTitleLabel.text = String(format: NSLocalizedString(
174177
"WIREGUARD_OBFUSCATION_SHADOWSOCKS_PORT",
175178
tableName: "VPNSettings",
176-
value: "Port: %d",
179+
value: "Port: %@",
177180
comment: ""
178-
), viewModel.obfuscationPort.portValue)
181+
), viewModel.obfuscationShadowsocksPort.description)
179182
#endif
183+
180184
cell.accessibilityIdentifier = item.accessibilityIdentifier
181185
cell.applySubCellStyling()
182186

@@ -199,7 +203,7 @@ final class VPNSettingsCellFactory: CellFactoryProtocol {
199203
case let .wireGuardObfuscationPort(port):
200204
guard let cell = cell as? SelectableSettingsCell else { return }
201205

202-
let portString = port == 0 ? "Automatic" : "\(port)"
206+
let portString = port.description
203207
cell.titleLabel.text = NSLocalizedString(
204208
"WIREGUARD_OBFUSCATION_PORT_LABEL",
205209
tableName: "VPNSettings",

ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSource.swift

+17-12
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource<
7979
case wireGuardObfuscationUdpOverTcp
8080
case wireGuardObfuscationShadowsocks
8181
case wireGuardObfuscationOff
82-
case wireGuardObfuscationPort(_ port: UInt16)
82+
case wireGuardObfuscationPort(_ port: WireGuardObfuscationUdpOverTcpPort)
8383
case quantumResistanceAutomatic
8484
case quantumResistanceOn
8585
case quantumResistanceOff
@@ -107,7 +107,11 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource<
107107
}
108108

109109
static var wireGuardObfuscationPort: [Item] {
110-
[.wireGuardObfuscationPort(0), .wireGuardObfuscationPort(80), .wireGuardObfuscationPort(5001)]
110+
[
111+
.wireGuardObfuscationPort(.automatic),
112+
.wireGuardObfuscationPort(.port80),
113+
.wireGuardObfuscationPort(.port5001),
114+
]
111115
}
112116

113117
static var quantumResistance: [Item] {
@@ -178,7 +182,8 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource<
178182
private var obfuscationSettings: WireGuardObfuscationSettings {
179183
WireGuardObfuscationSettings(
180184
state: viewModel.obfuscationState,
181-
port: viewModel.obfuscationPort
185+
udpOverTcpPort: viewModel.obfuscationUpdOverTcpPort,
186+
shadowsocksPort: viewModel.obfuscationShadowsocksPort
182187
)
183188
}
184189

@@ -192,7 +197,8 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource<
192197
let obfuscationStateItem: Item = switch viewModel.obfuscationState {
193198
case .automatic: .wireGuardObfuscationAutomatic
194199
case .off: .wireGuardObfuscationOff
195-
case .on: .wireGuardObfuscationUdpOverTcp
200+
case .on, .udpOverTcp: .wireGuardObfuscationUdpOverTcp
201+
case .shadowsocks: .wireGuardObfuscationShadowsocks
196202
}
197203

198204
let quantumResistanceItem: Item = switch viewModel.quantumResistance {
@@ -201,7 +207,7 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource<
201207
case .on: .quantumResistanceOn
202208
}
203209

204-
let obfuscationPortItem: Item = .wireGuardObfuscationPort(viewModel.obfuscationPort.portValue)
210+
let obfuscationPortItem: Item = .wireGuardObfuscationPort(viewModel.obfuscationUpdOverTcpPort)
205211

206212
return [
207213
wireGuardPortItem,
@@ -308,13 +314,13 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource<
308314
selectObfuscationState(.automatic)
309315
delegate?.didUpdateTunnelSettings(TunnelSettingsUpdate.obfuscation(obfuscationSettings))
310316
case .wireGuardObfuscationUdpOverTcp:
311-
selectObfuscationState(.on)
317+
selectObfuscationState(.udpOverTcp)
312318
delegate?.didUpdateTunnelSettings(TunnelSettingsUpdate.obfuscation(obfuscationSettings))
313-
// TODO: When ready, add implementation for selected obfuscation.
319+
// TODO: When ready, add implementation for selected obfuscation (navigate to new view etc).
314320
case .wireGuardObfuscationShadowsocks:
315-
selectObfuscationState(.on)
321+
selectObfuscationState(.shadowsocks)
316322
delegate?.didUpdateTunnelSettings(TunnelSettingsUpdate.obfuscation(obfuscationSettings))
317-
// TODO: When ready, add implementation for selected obfuscation.
323+
// TODO: When ready, add implementation for selected obfuscation (navigate to new view etc).
318324
case .wireGuardObfuscationOff:
319325
selectObfuscationState(.off)
320326
delegate?.didUpdateTunnelSettings(TunnelSettingsUpdate.obfuscation(obfuscationSettings))
@@ -656,9 +662,8 @@ extension VPNSettingsDataSource: VPNSettingsCellEventHandler {
656662
viewModel.setWireGuardObfuscationState(state)
657663
}
658664

659-
func selectObfuscationPort(_ port: UInt16) {
660-
let selectedPort = WireGuardObfuscationPort(rawValue: port)!
661-
viewModel.setWireGuardObfuscationPort(selectedPort)
665+
func selectObfuscationPort(_ port: WireGuardObfuscationUdpOverTcpPort) {
666+
viewModel.setWireGuardObfuscationUdpOverTcpPort(port)
662667
}
663668

664669
func selectQuantumResistance(_ state: TunnelQuantumResistance) {

ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsViewModel.swift

+10-4
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,8 @@ struct VPNSettingsViewModel: Equatable {
9797
var availableWireGuardPortRanges: [[UInt16]] = []
9898

9999
private(set) var obfuscationState: WireGuardObfuscationState
100-
private(set) var obfuscationPort: WireGuardObfuscationPort
100+
private(set) var obfuscationUpdOverTcpPort: WireGuardObfuscationUdpOverTcpPort
101+
private(set) var obfuscationShadowsocksPort: WireGuardObfuscationShadowsockPort
101102

102103
private(set) var quantumResistance: TunnelQuantumResistance
103104
private(set) var multihopState: MultihopState
@@ -178,8 +179,12 @@ struct VPNSettingsViewModel: Equatable {
178179
obfuscationState = newState
179180
}
180181

181-
mutating func setWireGuardObfuscationPort(_ newPort: WireGuardObfuscationPort) {
182-
obfuscationPort = newPort
182+
mutating func setWireGuardObfuscationShadowsockPort(_ newPort: WireGuardObfuscationShadowsockPort) {
183+
obfuscationShadowsocksPort = newPort
184+
}
185+
186+
mutating func setWireGuardObfuscationUdpOverTcpPort(_ newPort: WireGuardObfuscationUdpOverTcpPort) {
187+
obfuscationUpdOverTcpPort = newPort
183188
}
184189

185190
mutating func setQuantumResistance(_ newState: TunnelQuantumResistance) {
@@ -242,7 +247,8 @@ struct VPNSettingsViewModel: Equatable {
242247
wireGuardPort = tunnelSettings.relayConstraints.port.value
243248

244249
obfuscationState = tunnelSettings.wireGuardObfuscation.state
245-
obfuscationPort = tunnelSettings.wireGuardObfuscation.port
250+
obfuscationUpdOverTcpPort = tunnelSettings.wireGuardObfuscation.udpOverTcpPort
251+
obfuscationShadowsocksPort = tunnelSettings.wireGuardObfuscation.shadowsocksPort
246252

247253
quantumResistance = tunnelSettings.tunnelQuantumResistance
248254
multihopState = tunnelSettings.tunnelMultihopState

ios/PacketTunnelCore/Actor/ProtocolObfuscator.swift

+4-4
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public class ProtocolObfuscator<Obfuscator: TunnelObfuscation>: ProtocolObfuscat
4242
let shouldObfuscate = switch settings.obfuscation.state {
4343
case .automatic:
4444
retryAttempts % 4 == 2 || retryAttempts % 4 == 3
45-
case .on:
45+
case .on, .udpOverTcp, .shadowsocks:
4646
true
4747
case .off:
4848
false
@@ -52,15 +52,15 @@ public class ProtocolObfuscator<Obfuscator: TunnelObfuscation>: ProtocolObfuscat
5252
tunnelObfuscator = nil
5353
return endpoint
5454
}
55-
var tcpPort = settings.obfuscation.port
55+
var tcpPort = settings.obfuscation.udpOverTcpPort
5656
if tcpPort == .automatic {
5757
tcpPort = retryAttempts % 2 == 0 ? .port80 : .port5001
5858
}
5959
let obfuscator = Obfuscator(
6060
remoteAddress: obfuscatedEndpoint.ipv4Relay.ip,
61-
tcpPort: tcpPort.portValue
61+
tcpPort: tcpPort.portValue ?? 0
6262
)
63-
remotePort = tcpPort.portValue
63+
remotePort = tcpPort.portValue ?? 0
6464
obfuscator.start()
6565
tunnelObfuscator = obfuscator
6666

0 commit comments

Comments
 (0)