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
|
//
// SinglehopPicker.swift
// MullvadVPN
//
// Created by Jon Petersson on 2024-12-11.
// Copyright © 2025 Mullvad VPN AB. All rights reserved.
//
import MullvadSettings
import MullvadTypes
struct SinglehopPicker: RelayPicking {
let obfuscation: RelayObfuscation
let tunnelSettings: LatestTunnelSettings
let connectionAttemptCount: UInt
func pick() throws -> SelectedRelays {
// Guarantee that the chosen relay supports selected obfuscation
let obfuscationBypass = UnsupportedObfuscationProvider(
relayConstraint: tunnelSettings.relayConstraints.exitLocations,
relays: obfuscation.obfuscatedRelays,
filterConstraint: tunnelSettings.relayConstraints.filter,
daitaEnabled: tunnelSettings.daita.daitaState.isEnabled
)
let supportedObfuscation = RelayObfuscator(
relays: obfuscation.allRelays,
tunnelSettings: tunnelSettings,
connectionAttemptCount: connectionAttemptCount,
obfuscationBypass: obfuscationBypass
).obfuscate()
// Create a new picker so that it can use the new obfuscation object.
let picker = SinglehopPicker(
obfuscation: supportedObfuscation,
tunnelSettings: tunnelSettings,
connectionAttemptCount: connectionAttemptCount
)
do {
return try picker.pickRelays()
} catch let error as NoRelaysSatisfyingConstraintsError where error.reason == .noDaitaRelaysFound {
// If DAITA is on, Direct only is off and obfuscation has been ruled out, and no supported relays are found,
// we should try to find the nearest available relay that supports DAITA and use it as entry in a multihop selection.
if tunnelSettings.daita.isAutomaticRouting {
return try MultihopPicker(
obfuscation: obfuscation,
tunnelSettings: tunnelSettings,
connectionAttemptCount: connectionAttemptCount
).pick()
} else {
throw error
}
}
}
private func pickRelays() throws -> SelectedRelays {
let exitCandidates = try RelaySelector.WireGuard.findCandidates(
by: tunnelSettings.relayConstraints.exitLocations,
in: obfuscation.obfuscatedRelays,
filterConstraint: tunnelSettings.relayConstraints.filter,
daitaEnabled: tunnelSettings.daita.daitaState.isEnabled
)
let match = try findBestMatch(from: exitCandidates, applyObfuscatedIps: true)
return SelectedRelays(
entry: nil,
exit: match,
retryAttempt: connectionAttemptCount,
obfuscation: obfuscation.method
)
}
}
|