Skip to content

Commit adf5fe4

Browse files
author
Jon Petersson
committed
Allow relay selector to filter DAITA enabled relays
1 parent 229dc5a commit adf5fe4

27 files changed

+447
-94
lines changed

ios/MullvadMockData/MullvadREST/RelaySelectorStub.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ extension RelaySelectorStub {
5858
/// Returns a relay selector that cannot satisfy constraints .
5959
public static func unsatisfied() -> RelaySelectorStub {
6060
return RelaySelectorStub { _ in
61-
throw NoRelaysSatisfyingConstraintsError()
61+
throw NoRelaysSatisfyingConstraintsError(.relayConstraintNotMatching)
6262
}
6363
}
6464
}

ios/MullvadREST/ApiHandlers/ServerRelaysResponse.swift

+4-1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ extension REST {
3434
public let ipv4AddrIn: IPv4Address
3535
public let weight: UInt64
3636
public let includeInCountry: Bool
37+
public var daita: Bool?
3738

3839
public func override(ipv4AddrIn: IPv4Address?) -> Self {
3940
return BridgeRelay(
@@ -60,6 +61,7 @@ extension REST {
6061
public let ipv6AddrIn: IPv6Address
6162
public let publicKey: Data
6263
public let includeInCountry: Bool
64+
public let daita: Bool?
6365

6466
public func override(ipv4AddrIn: IPv4Address?, ipv6AddrIn: IPv6Address?) -> Self {
6567
return ServerRelay(
@@ -72,7 +74,8 @@ extension REST {
7274
ipv4AddrIn: ipv4AddrIn ?? self.ipv4AddrIn,
7375
ipv6AddrIn: ipv6AddrIn ?? self.ipv6AddrIn,
7476
publicKey: publicKey,
75-
includeInCountry: includeInCountry
77+
includeInCountry: includeInCountry,
78+
daita: daita
7679
)
7780
}
7881
}

ios/MullvadREST/Relay/AnyRelay.swift

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ public protocol AnyRelay {
1717
var weight: UInt64 { get }
1818
var active: Bool { get }
1919
var includeInCountry: Bool { get }
20+
var daita: Bool? { get }
2021

2122
func override(ipv4AddrIn: IPv4Address?, ipv6AddrIn: IPv6Address?) -> Self
2223
}

ios/MullvadREST/Relay/MultihopDecisionFlow.swift

+4-4
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@ struct OneToOne: MultihopDecisionFlow {
2626
func pick(entryCandidates: [RelayCandidate], exitCandidates: [RelayCandidate]) throws -> SelectedRelays {
2727
guard canHandle(entryCandidates: entryCandidates, exitCandidates: exitCandidates) else {
2828
guard let next else {
29-
throw NoRelaysSatisfyingConstraintsError()
29+
throw NoRelaysSatisfyingConstraintsError(.multihopOther)
3030
}
3131
return try next.pick(entryCandidates: entryCandidates, exitCandidates: exitCandidates)
3232
}
3333

3434
guard entryCandidates.first != exitCandidates.first else {
35-
throw NoRelaysSatisfyingConstraintsError()
35+
throw NoRelaysSatisfyingConstraintsError(.multihopEntryEqualsExit)
3636
}
3737

3838
let entryMatch = try relayPicker.findBestMatch(from: entryCandidates)
@@ -61,7 +61,7 @@ struct OneToMany: MultihopDecisionFlow {
6161

6262
guard canHandle(entryCandidates: entryCandidates, exitCandidates: exitCandidates) else {
6363
guard let next else {
64-
throw NoRelaysSatisfyingConstraintsError()
64+
throw NoRelaysSatisfyingConstraintsError(.multihopOther)
6565
}
6666
return try next.pick(entryCandidates: entryCandidates, exitCandidates: exitCandidates)
6767
}
@@ -100,7 +100,7 @@ struct ManyToMany: MultihopDecisionFlow {
100100

101101
guard canHandle(entryCandidates: entryCandidates, exitCandidates: exitCandidates) else {
102102
guard let next else {
103-
throw NoRelaysSatisfyingConstraintsError()
103+
throw NoRelaysSatisfyingConstraintsError(.multihopOther)
104104
}
105105
return try next.pick(entryCandidates: entryCandidates, exitCandidates: exitCandidates)
106106
}

ios/MullvadREST/Relay/NoRelaysSatisfyingConstraintsError.swift

+15-1
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,24 @@
88

99
import Foundation
1010

11+
public enum NoRelaysSatisfyingConstraintsReason {
12+
case filterConstraintNotMatching
13+
case invalidPort
14+
case multihopEntryEqualsExit
15+
case multihopOther
16+
case noActiveRelaysFound
17+
case noDaitaRelaysFound
18+
case relayConstraintNotMatching
19+
}
20+
1121
public struct NoRelaysSatisfyingConstraintsError: LocalizedError {
12-
public init() {}
22+
public let reason: NoRelaysSatisfyingConstraintsReason
1323

1424
public var errorDescription: String? {
1525
"No relays satisfying constraints."
1626
}
27+
28+
public init(_ reason: NoRelaysSatisfyingConstraintsReason) {
29+
self.reason = reason
30+
}
1731
}

ios/MullvadREST/Relay/RelayPicking.swift

+30-8
Original file line numberDiff line numberDiff line change
@@ -37,38 +37,60 @@ extension RelayPicking {
3737

3838
struct SinglehopPicker: RelayPicking {
3939
let constraints: RelayConstraints
40+
let daitaSettings: DAITASettings
4041
let relays: REST.ServerRelaysResponse
4142
let connectionAttemptCount: UInt
4243

4344
func pick() throws -> SelectedRelays {
44-
let candidates = try RelaySelector.WireGuard.findCandidates(
45-
by: constraints.exitLocations,
46-
in: relays,
47-
filterConstraint: constraints.filter
48-
)
45+
var exitCandidates = [RelayWithLocation<REST.ServerRelay>]()
4946

50-
let match = try findBestMatch(from: candidates)
47+
do {
48+
exitCandidates = try RelaySelector.WireGuard.findCandidates(
49+
by: constraints.exitLocations,
50+
in: relays,
51+
filterConstraint: constraints.filter,
52+
daitaEnabled: daitaSettings.state.isEnabled
53+
)
54+
} catch let error as NoRelaysSatisfyingConstraintsError where error.reason == .noDaitaRelaysFound {
55+
#if DEBUG
56+
// If DAITA is enabled and no supported relays are found, we should try to find the nearest
57+
// available relay that supports DAITA and use it as entry in a multihop selection.
58+
var constraints = constraints
59+
constraints.entryLocations = .any
60+
61+
return try MultihopPicker(
62+
constraints: constraints,
63+
daitaSettings: daitaSettings,
64+
relays: relays,
65+
connectionAttemptCount: connectionAttemptCount
66+
).pick()
67+
#endif
68+
}
5169

70+
let match = try findBestMatch(from: exitCandidates)
5271
return SelectedRelays(entry: nil, exit: match, retryAttempt: connectionAttemptCount)
5372
}
5473
}
5574

5675
struct MultihopPicker: RelayPicking {
5776
let constraints: RelayConstraints
77+
let daitaSettings: DAITASettings
5878
let relays: REST.ServerRelaysResponse
5979
let connectionAttemptCount: UInt
6080

6181
func pick() throws -> SelectedRelays {
6282
let entryCandidates = try RelaySelector.WireGuard.findCandidates(
6383
by: constraints.entryLocations,
6484
in: relays,
65-
filterConstraint: constraints.filter
85+
filterConstraint: constraints.filter,
86+
daitaEnabled: daitaSettings.state.isEnabled
6687
)
6788

6889
let exitCandidates = try RelaySelector.WireGuard.findCandidates(
6990
by: constraints.exitLocations,
7091
in: relays,
71-
filterConstraint: constraints.filter
92+
filterConstraint: constraints.filter,
93+
daitaEnabled: false
7294
)
7395

7496
/*

ios/MullvadREST/Relay/RelaySelector+Shadowsocks.swift

+3-2
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,12 @@ extension RelaySelector {
4343
in relaysResponse: REST.ServerRelaysResponse
4444
) -> REST.BridgeRelay? {
4545
let mappedBridges = mapRelays(relays: relaysResponse.bridge.relays, locations: relaysResponse.locations)
46-
let filteredRelays = applyConstraints(
46+
let filteredRelays = (try? applyConstraints(
4747
location,
4848
filterConstraint: filter,
49+
daitaEnabled: false,
4950
relays: mappedBridges
50-
)
51+
)) ?? []
5152
guard filteredRelays.isEmpty == false else { return relay(from: relaysResponse) }
5253

5354
// Compute the midpoint location from all the filtered relays

ios/MullvadREST/Relay/RelaySelector+Wireguard.swift

+11-4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
// Copyright © 2024 Mullvad VPN AB. All rights reserved.
77
//
88

9+
import MullvadSettings
910
import MullvadTypes
1011

1112
extension RelaySelector {
@@ -14,13 +15,15 @@ extension RelaySelector {
1415
public static func findCandidates(
1516
by relayConstraint: RelayConstraint<UserSelectedRelays>,
1617
in relays: REST.ServerRelaysResponse,
17-
filterConstraint: RelayConstraint<RelayFilter>
18+
filterConstraint: RelayConstraint<RelayFilter>,
19+
daitaEnabled: Bool
1820
) throws -> [RelayWithLocation<REST.ServerRelay>] {
1921
let mappedRelays = mapRelays(relays: relays.wireguard.relays, locations: relays.locations)
2022

21-
return applyConstraints(
23+
return try applyConstraints(
2224
relayConstraint,
2325
filterConstraint: filterConstraint,
26+
daitaEnabled: daitaEnabled,
2427
relays: mappedRelays
2528
)
2629
}
@@ -38,8 +41,12 @@ extension RelaySelector {
3841
numberOfFailedAttempts: numberOfFailedAttempts
3942
)
4043

41-
guard let port, let relayWithLocation = pickRandomRelayByWeight(relays: relayWithLocations) else {
42-
throw NoRelaysSatisfyingConstraintsError()
44+
guard let port else {
45+
throw NoRelaysSatisfyingConstraintsError(.invalidPort)
46+
}
47+
48+
guard let relayWithLocation = pickRandomRelayByWeight(relays: relayWithLocations) else {
49+
throw NoRelaysSatisfyingConstraintsError(.relayConstraintNotMatching)
4350
}
4451

4552
let endpoint = MullvadEndpoint(

0 commit comments

Comments
 (0)