Skip to content

Commit 8b647d4

Browse files
author
Jon Petersson
committed
Added general support for multiple selected relays
1 parent 1c599a7 commit 8b647d4

31 files changed

+203
-201
lines changed

ios/MullvadMockData/MullvadREST/RelaySelectorStub.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
// Copyright © 2023 Mullvad VPN AB. All rights reserved.
77
//
88

9-
import MullvadTypes
109
import MullvadREST
10+
import MullvadTypes
1111
import WireGuardKitTypes
1212

1313
/// Relay selector stub that accepts a block that can be used to provide custom implementation.

ios/MullvadREST/Relay/RelaySelector+Wireguard.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import MullvadTypes
1010

1111
extension RelaySelector {
1212
public enum WireGuard {
13-
/// Filters relay list using given constraints and selects random relay for exit relay.
13+
/// Filters relay list using given constraints.
1414
public static func findCandidates(
1515
by relayConstraint: RelayConstraint<UserSelectedRelays>,
1616
in relays: REST.ServerRelaysResponse,
@@ -25,7 +25,7 @@ extension RelaySelector {
2525
)
2626
}
2727

28-
// TODO: Add comment.
28+
/// Picks a random relay from a list.
2929
public static func pickCandidate(
3030
from relayWithLocations: [RelayWithLocation<REST.ServerRelay>],
3131
relays: REST.ServerRelaysResponse,

ios/MullvadVPN/SimulatorTunnelProvider/SimulatorTunnelProviderHost.swift

+16-16
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import PacketTunnelCore
1818

1919
final class SimulatorTunnelProviderHost: SimulatorTunnelProviderDelegate {
2020
private var observedState: ObservedState = .disconnected
21-
private var selectedRelay: SelectedRelay?
21+
private var selectedRelays: SelectedRelays?
2222
private let urlRequestProxy: URLRequestProxy
2323
private let relaySelector: RelaySelectorProtocol
2424

@@ -43,12 +43,12 @@ final class SimulatorTunnelProviderHost: SimulatorTunnelProviderDelegate {
4343
return
4444
}
4545

46-
var selectedRelay: SelectedRelay?
46+
var selectedRelays: SelectedRelays?
4747

4848
do {
4949
let tunnelOptions = PacketTunnelOptions(rawOptions: options ?? [:])
5050

51-
selectedRelay = try tunnelOptions.getSelectedRelay()
51+
selectedRelays = try tunnelOptions.getSelectedRelays()
5252
} catch {
5353
providerLogger.error(
5454
error: error,
@@ -60,7 +60,7 @@ final class SimulatorTunnelProviderHost: SimulatorTunnelProviderDelegate {
6060
}
6161

6262
do {
63-
setInternalStateConnected(with: try selectedRelay ?? pickRelay())
63+
setInternalStateConnected(with: try selectedRelays ?? pickRelays())
6464
completionHandler(nil)
6565
} catch {
6666
providerLogger.error(
@@ -74,7 +74,7 @@ final class SimulatorTunnelProviderHost: SimulatorTunnelProviderDelegate {
7474

7575
override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
7676
dispatchQueue.async { [weak self] in
77-
self?.selectedRelay = nil
77+
self?.selectedRelays = nil
7878
self?.observedState = .disconnected
7979

8080
completionHandler()
@@ -117,17 +117,17 @@ final class SimulatorTunnelProviderHost: SimulatorTunnelProviderDelegate {
117117
reasserting = true
118118

119119
switch nextRelay {
120-
case let .preSelected(selectedRelay):
121-
self.selectedRelay = selectedRelay
120+
case let .preSelected(selectedRelays):
121+
self.selectedRelays = selectedRelays
122122
case .random:
123-
if let nextRelay = try? pickRelay() {
124-
self.selectedRelay = nextRelay
123+
if let nextRelays = try? pickRelays() {
124+
self.selectedRelays = nextRelays
125125
}
126126
case .current:
127127
break
128128
}
129129

130-
setInternalStateConnected(with: selectedRelay)
130+
setInternalStateConnected(with: selectedRelays)
131131
reasserting = false
132132

133133
completionHandler?(nil)
@@ -156,28 +156,28 @@ final class SimulatorTunnelProviderHost: SimulatorTunnelProviderDelegate {
156156
}
157157
}
158158

159-
private func pickRelay() throws -> SelectedRelay {
159+
private func pickRelays() throws -> SelectedRelays {
160160
let tunnelSettings = try SettingsManager.readSettings()
161161

162162
return try relaySelector.selectRelays(
163163
with: tunnelSettings.relayConstraints,
164164
connectionAttemptCount: 0
165-
).exit // TODO: Multihop
165+
)
166166
}
167167

168-
private func setInternalStateConnected(with selectedRelay: SelectedRelay?) {
169-
guard let selectedRelay = selectedRelay else { return }
168+
private func setInternalStateConnected(with selectedRelays: SelectedRelays?) {
169+
guard let selectedRelays = selectedRelays else { return }
170170

171171
do {
172172
let settings = try SettingsManager.readSettings()
173173
observedState = .connected(
174174
ObservedConnectionState(
175-
selectedRelay: selectedRelay,
175+
selectedRelays: selectedRelays,
176176
relayConstraints: settings.relayConstraints,
177177
networkReachability: .reachable,
178178
connectionAttemptCount: 0,
179179
transportLayer: .udp,
180-
remotePort: selectedRelay.endpoint.ipv4Relay.port,
180+
remotePort: selectedRelays.exit.endpoint.ipv4Relay.port, // TODO: Multihop
181181
isPostQuantum: settings.tunnelQuantumResistance.isEnabled
182182
)
183183
)

ios/MullvadVPN/TunnelManager/MapConnectionStatusOperation.swift

+4-4
Original file line numberDiff line numberDiff line change
@@ -51,19 +51,19 @@ class MapConnectionStatusOperation: AsyncOperation {
5151
switch observedState {
5252
case let .connected(connectionState):
5353
return connectionState.isNetworkReachable
54-
? .connected(connectionState.selectedRelay, isPostQuantum: connectionState.isPostQuantum)
54+
? .connected(connectionState.selectedRelays, isPostQuantum: connectionState.isPostQuantum)
5555
: .waitingForConnectivity(.noConnection)
5656
case let .connecting(connectionState):
5757
return connectionState.isNetworkReachable
58-
? .connecting(connectionState.selectedRelay, isPostQuantum: connectionState.isPostQuantum)
58+
? .connecting(connectionState.selectedRelays, isPostQuantum: connectionState.isPostQuantum)
5959
: .waitingForConnectivity(.noConnection)
6060
case let .negotiatingPostQuantumKey(connectionState, privateKey):
6161
return connectionState.isNetworkReachable
62-
? .negotiatingPostQuantumKey(connectionState.selectedRelay, privateKey)
62+
? .negotiatingPostQuantumKey(connectionState.selectedRelays, privateKey)
6363
: .waitingForConnectivity(.noConnection)
6464
case let .reconnecting(connectionState):
6565
return connectionState.isNetworkReachable
66-
? .reconnecting(connectionState.selectedRelay, isPostQuantum: connectionState.isPostQuantum)
66+
? .reconnecting(connectionState.selectedRelays, isPostQuantum: connectionState.isPostQuantum)
6767
: .waitingForConnectivity(.noConnection)
6868
case let .error(blockedState):
6969
return .error(blockedState.reason)

ios/MullvadVPN/TunnelManager/StartTunnelOperation.swift

+4-4
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,12 @@ class StartTunnelOperation: ResultOperation<Void> {
7272
}
7373

7474
private func startTunnel(tunnel: any TunnelProtocol) throws {
75-
let selectedRelay = try? interactor.selectRelay()
75+
let selectedRelays = try? interactor.selectRelays()
7676
var tunnelOptions = PacketTunnelOptions()
7777

7878
do {
79-
if let selectedRelay {
80-
try tunnelOptions.setSelectedRelay(selectedRelay)
79+
if let selectedRelays {
80+
try tunnelOptions.setSelectedRelays(selectedRelays)
8181
}
8282
} catch {
8383
logger.error(
@@ -91,7 +91,7 @@ class StartTunnelOperation: ResultOperation<Void> {
9191
interactor.updateTunnelStatus { tunnelStatus in
9292
tunnelStatus = TunnelStatus()
9393
tunnelStatus.state = .connecting(
94-
selectedRelay,
94+
selectedRelays,
9595
isPostQuantum: interactor.settings.tunnelQuantumResistance.isEnabled
9696
)
9797
}

ios/MullvadVPN/TunnelManager/Tunnel+Messaging.swift

+3-3
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,16 @@ private let dispatchQueue = DispatchQueue(label: "Tunnel.dispatchQueue")
2222
private let proxyRequestTimeout = REST.defaultAPINetworkTimeout + 2
2323

2424
extension TunnelProtocol {
25-
/// Request packet tunnel process to reconnect the tunnel with the given relay.
25+
/// Request packet tunnel process to reconnect the tunnel with the given relays.
2626
func reconnectTunnel(
27-
to nextRelay: NextRelay,
27+
to nextRelays: NextRelays,
2828
completionHandler: @escaping (Result<Void, Error>) -> Void
2929
) -> Cancellable {
3030
let operation = SendTunnelProviderMessageOperation(
3131
dispatchQueue: dispatchQueue,
3232
application: .shared,
3333
tunnel: self,
34-
message: .reconnectTunnel(nextRelay),
34+
message: .reconnectTunnel(nextRelays),
3535
completionHandler: completionHandler
3636
)
3737

ios/MullvadVPN/TunnelManager/TunnelInteractor.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,5 +39,5 @@ protocol TunnelInteractor {
3939

4040
func startTunnel()
4141
func prepareForVPNConfigurationDeletion()
42-
func selectRelay() throws -> SelectedRelay
42+
func selectRelays() throws -> SelectedRelays
4343
}

ios/MullvadVPN/TunnelManager/TunnelManager.swift

+4-4
Original file line numberDiff line numberDiff line change
@@ -782,13 +782,13 @@ final class TunnelManager: StorePaymentObserver {
782782
updateTunnelStatus(tunnel?.status ?? .disconnected)
783783
}
784784

785-
fileprivate func selectRelay() throws -> SelectedRelay {
785+
fileprivate func selectRelays() throws -> SelectedRelays {
786786
let retryAttempts = tunnelStatus.observedState.connectionState?.connectionAttemptCount ?? 0
787787

788788
return try relaySelector.selectRelays(
789789
with: settings.relayConstraints,
790790
connectionAttemptCount: retryAttempts
791-
).exit // TODO: Multihop
791+
)
792792
}
793793

794794
fileprivate func prepareForVPNConfigurationDeletion() {
@@ -1260,8 +1260,8 @@ private struct TunnelInteractorProxy: TunnelInteractor {
12601260
tunnelManager.prepareForVPNConfigurationDeletion()
12611261
}
12621262

1263-
func selectRelay() throws -> SelectedRelay {
1264-
try tunnelManager.selectRelay()
1263+
func selectRelays() throws -> SelectedRelays {
1264+
try tunnelManager.selectRelays()
12651265
}
12661266

12671267
func handleRestError(_ error: Error) {

ios/MullvadVPN/TunnelManager/TunnelState+UI.swift

+6-6
Original file line numberDiff line numberDiff line change
@@ -187,8 +187,8 @@ extension TunnelState {
187187
value: "Quantum secure connection. Connected to %@, %@",
188188
comment: ""
189189
),
190-
tunnelInfo.location.city,
191-
tunnelInfo.location.country
190+
tunnelInfo.exit.location.city, // TODO: Multihop
191+
tunnelInfo.exit.location.country // TODO: Multihop
192192
)
193193
} else {
194194
String(
@@ -198,8 +198,8 @@ extension TunnelState {
198198
value: "Secure connection. Connected to %@, %@",
199199
comment: ""
200200
),
201-
tunnelInfo.location.city,
202-
tunnelInfo.location.country
201+
tunnelInfo.exit.location.city, // TODO: Multihop
202+
tunnelInfo.exit.location.country // TODO: Multihop
203203
)
204204
}
205205

@@ -219,8 +219,8 @@ extension TunnelState {
219219
value: "Reconnecting to %@, %@",
220220
comment: ""
221221
),
222-
tunnelInfo.location.city,
223-
tunnelInfo.location.country
222+
tunnelInfo.exit.location.city, // TODO: Multihop
223+
tunnelInfo.exit.location.country // TODO: Multihop
224224
)
225225

226226
case .waitingForConnectivity(.noConnection), .error:

ios/MullvadVPN/TunnelManager/TunnelState.swift

+20-20
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,13 @@ enum TunnelState: Equatable, CustomStringConvertible {
5050
case pendingReconnect
5151

5252
/// Connecting the tunnel.
53-
case connecting(SelectedRelay?, isPostQuantum: Bool)
53+
case connecting(SelectedRelays?, isPostQuantum: Bool)
5454

5555
/// Negotiating a key for post-quantum resistance
56-
case negotiatingPostQuantumKey(SelectedRelay, PrivateKey)
56+
case negotiatingPostQuantumKey(SelectedRelays, PrivateKey)
5757

5858
/// Connected the tunnel
59-
case connected(SelectedRelay, isPostQuantum: Bool)
59+
case connected(SelectedRelays, isPostQuantum: Bool)
6060

6161
/// Disconnecting the tunnel
6262
case disconnecting(ActionAfterDisconnect)
@@ -66,10 +66,10 @@ enum TunnelState: Equatable, CustomStringConvertible {
6666

6767
/// Reconnecting the tunnel.
6868
/// Transition to this state happens when:
69-
/// 1. Asking the running tunnel to reconnect to new relay via IPC.
70-
/// 2. Tunnel attempts to reconnect to new relay as the current relay appears to be
69+
/// 1. Asking the running tunnel to reconnect to new relays via IPC.
70+
/// 2. Tunnel attempts to reconnect to new relays as the current relays appear to be
7171
/// dysfunctional.
72-
case reconnecting(SelectedRelay, isPostQuantum: Bool)
72+
case reconnecting(SelectedRelays, isPostQuantum: Bool)
7373

7474
/// Waiting for connectivity to come back up.
7575
case waitingForConnectivity(WaitingForConnectionReason)
@@ -81,26 +81,26 @@ enum TunnelState: Equatable, CustomStringConvertible {
8181
switch self {
8282
case .pendingReconnect:
8383
"pending reconnect after disconnect"
84-
case let .connecting(tunnelRelay, isPostQuantum):
85-
if let tunnelRelay {
86-
"connecting \(isPostQuantum ? "(PQ) " : "")to \(tunnelRelay.hostname)"
84+
case let .connecting(tunnelRelays, isPostQuantum):
85+
if let tunnelRelays {
86+
"connecting \(isPostQuantum ? "(PQ) " : "")to \(tunnelRelays.exit.hostname)" // TODO: Multihop
8787
} else {
8888
"connecting\(isPostQuantum ? " (PQ)" : ""), fetching relay"
8989
}
90-
case let .connected(tunnelRelay, isPostQuantum):
91-
"connected \(isPostQuantum ? "(PQ) " : "")to \(tunnelRelay.hostname)"
90+
case let .connected(tunnelRelays, isPostQuantum):
91+
"connected \(isPostQuantum ? "(PQ) " : "")to \(tunnelRelays.exit.hostname)" // TODO: Multihop
9292
case let .disconnecting(actionAfterDisconnect):
9393
"disconnecting and then \(actionAfterDisconnect)"
9494
case .disconnected:
9595
"disconnected"
96-
case let .reconnecting(tunnelRelay, isPostQuantum):
97-
"reconnecting \(isPostQuantum ? "(PQ) " : "")to \(tunnelRelay.hostname)"
96+
case let .reconnecting(tunnelRelays, isPostQuantum):
97+
"reconnecting \(isPostQuantum ? "(PQ) " : "")to \(tunnelRelays.exit.hostname)" // TODO: Multihop
9898
case .waitingForConnectivity:
9999
"waiting for connectivity"
100100
case let .error(blockedStateReason):
101101
"error state: \(blockedStateReason)"
102-
case let .negotiatingPostQuantumKey(tunnelRelay, _):
103-
"negotiating key with \(tunnelRelay.hostname)"
102+
case let .negotiatingPostQuantumKey(tunnelRelays, _):
103+
"negotiating key with \(tunnelRelays.exit.hostname)" // TODO: Multihop
104104
}
105105
}
106106

@@ -114,12 +114,12 @@ enum TunnelState: Equatable, CustomStringConvertible {
114114
}
115115
}
116116

117-
var relay: SelectedRelay? {
117+
var relays: SelectedRelays? {
118118
switch self {
119-
case let .connected(relay, _), let .reconnecting(relay, _), let .negotiatingPostQuantumKey(relay, _):
120-
relay
121-
case let .connecting(relay, _):
122-
relay
119+
case let .connected(relays, _), let .reconnecting(relays, _), let .negotiatingPostQuantumKey(relays, _):
120+
relays
121+
case let .connecting(relays, _):
122+
relays
123123
case .disconnecting, .disconnected, .waitingForConnectivity, .pendingReconnect, .error:
124124
nil
125125
}

ios/MullvadVPN/View controllers/Tunnel/TunnelControlView.swift

+7-7
Original file line numberDiff line numberDiff line change
@@ -145,9 +145,9 @@ final class TunnelControlView: UIView {
145145
updateSecureLabel(tunnelState: tunnelState)
146146
updateActionButtons(tunnelState: tunnelState)
147147
if tunnelState.isSecured {
148-
updateTunnelRelay(tunnelRelay: tunnelState.relay)
148+
updateTunnelRelays(tunnelRelays: tunnelState.relays)
149149
} else {
150-
updateTunnelRelay(tunnelRelay: nil)
150+
updateTunnelRelays(tunnelRelays: nil)
151151
}
152152
}
153153

@@ -224,17 +224,17 @@ final class TunnelControlView: UIView {
224224
connectButtonBlurView.isEnabled = shouldEnableButtons
225225
}
226226

227-
private func updateTunnelRelay(tunnelRelay: SelectedRelay?) {
228-
if let tunnelRelay {
227+
private func updateTunnelRelays(tunnelRelays: SelectedRelays?) {
228+
if let tunnelRelays {
229229
cityLabel.attributedText = attributedStringForLocation(
230-
string: tunnelRelay.location.city
230+
string: tunnelRelays.exit.location.city // TODO: Multihop
231231
)
232232
countryLabel.attributedText = attributedStringForLocation(
233-
string: tunnelRelay.location.country
233+
string: tunnelRelays.exit.location.country // TODO: Multihop
234234
)
235235

236236
connectionPanel.isHidden = false
237-
connectionPanel.connectedRelayName = tunnelRelay.hostname
237+
connectionPanel.connectedRelayName = tunnelRelays.exit.hostname // TODO: Multihop
238238
} else {
239239
countryLabel.attributedText = attributedStringForLocation(string: " ")
240240
cityLabel.attributedText = attributedStringForLocation(string: " ")

0 commit comments

Comments
 (0)