Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow relay selector to filter daita enabled relays #6590

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ios/MullvadMockData/MullvadREST/RelaySelectorStub.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ extension RelaySelectorStub {
/// Returns a relay selector that cannot satisfy constraints .
public static func unsatisfied() -> RelaySelectorStub {
return RelaySelectorStub { _ in
throw NoRelaysSatisfyingConstraintsError()
throw NoRelaysSatisfyingConstraintsError(.relayConstraintNotMatching)
}
}
}
5 changes: 4 additions & 1 deletion ios/MullvadREST/ApiHandlers/ServerRelaysResponse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ extension REST {
public let ipv4AddrIn: IPv4Address
public let weight: UInt64
public let includeInCountry: Bool
public var daita: Bool?

public func override(ipv4AddrIn: IPv4Address?) -> Self {
return BridgeRelay(
Expand All @@ -60,6 +61,7 @@ extension REST {
public let ipv6AddrIn: IPv6Address
public let publicKey: Data
public let includeInCountry: Bool
public let daita: Bool?

public func override(ipv4AddrIn: IPv4Address?, ipv6AddrIn: IPv6Address?) -> Self {
return ServerRelay(
Expand All @@ -72,7 +74,8 @@ extension REST {
ipv4AddrIn: ipv4AddrIn ?? self.ipv4AddrIn,
ipv6AddrIn: ipv6AddrIn ?? self.ipv6AddrIn,
publicKey: publicKey,
includeInCountry: includeInCountry
includeInCountry: includeInCountry,
daita: daita
)
}
}
Expand Down
1 change: 1 addition & 0 deletions ios/MullvadREST/Relay/AnyRelay.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public protocol AnyRelay {
var weight: UInt64 { get }
var active: Bool { get }
var includeInCountry: Bool { get }
var daita: Bool? { get }

func override(ipv4AddrIn: IPv4Address?, ipv6AddrIn: IPv6Address?) -> Self
}
Expand Down
8 changes: 4 additions & 4 deletions ios/MullvadREST/Relay/MultihopDecisionFlow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ struct OneToOne: MultihopDecisionFlow {
func pick(entryCandidates: [RelayCandidate], exitCandidates: [RelayCandidate]) throws -> SelectedRelays {
guard canHandle(entryCandidates: entryCandidates, exitCandidates: exitCandidates) else {
guard let next else {
throw NoRelaysSatisfyingConstraintsError()
throw NoRelaysSatisfyingConstraintsError(.multihopInvalidFlow)
}
return try next.pick(entryCandidates: entryCandidates, exitCandidates: exitCandidates)
}

guard entryCandidates.first != exitCandidates.first else {
throw NoRelaysSatisfyingConstraintsError()
throw NoRelaysSatisfyingConstraintsError(.entryEqualsExit)
}

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

guard canHandle(entryCandidates: entryCandidates, exitCandidates: exitCandidates) else {
guard let next else {
throw NoRelaysSatisfyingConstraintsError()
throw NoRelaysSatisfyingConstraintsError(.multihopInvalidFlow)
}
return try next.pick(entryCandidates: entryCandidates, exitCandidates: exitCandidates)
}
Expand Down Expand Up @@ -100,7 +100,7 @@ struct ManyToMany: MultihopDecisionFlow {

guard canHandle(entryCandidates: entryCandidates, exitCandidates: exitCandidates) else {
guard let next else {
throw NoRelaysSatisfyingConstraintsError()
throw NoRelaysSatisfyingConstraintsError(.multihopInvalidFlow)
}
return try next.pick(entryCandidates: entryCandidates, exitCandidates: exitCandidates)
}
Expand Down
33 changes: 31 additions & 2 deletions ios/MullvadREST/Relay/NoRelaysSatisfyingConstraintsError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,39 @@

import Foundation

public enum NoRelaysSatisfyingConstraintsReason {
case filterConstraintNotMatching
case invalidPort
case entryEqualsExit
case multihopInvalidFlow
case noActiveRelaysFound
case noDaitaRelaysFound
case relayConstraintNotMatching
}

public struct NoRelaysSatisfyingConstraintsError: LocalizedError {
public init() {}
public let reason: NoRelaysSatisfyingConstraintsReason

public var errorDescription: String? {
"No relays satisfying constraints."
switch reason {
case .filterConstraintNotMatching:
"Filter yields no matching relays"
case .invalidPort:
"Invalid port selected by RelaySelector"
case .entryEqualsExit:
"Entry and exit relays are the same"
case .multihopInvalidFlow:
"Invalid multihop decision flow"
case .noActiveRelaysFound:
"No active relays found"
case .noDaitaRelaysFound:
"No DAITA relays found"
case .relayConstraintNotMatching:
"Invalid constraint created to pick a relay"
}
}

public init(_ reason: NoRelaysSatisfyingConstraintsReason) {
self.reason = reason
}
}
38 changes: 30 additions & 8 deletions ios/MullvadREST/Relay/RelayPicking.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,38 +37,60 @@ extension RelayPicking {

struct SinglehopPicker: RelayPicking {
let constraints: RelayConstraints
let daitaSettings: DAITASettings
let relays: REST.ServerRelaysResponse
let connectionAttemptCount: UInt

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

let match = try findBestMatch(from: candidates)
do {
exitCandidates = try RelaySelector.WireGuard.findCandidates(
by: constraints.exitLocations,
in: relays,
filterConstraint: constraints.filter,
daitaEnabled: daitaSettings.state.isEnabled
)
} catch let error as NoRelaysSatisfyingConstraintsError where error.reason == .noDaitaRelaysFound {
#if DEBUG
// If DAITA is enabled 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.
var constraints = constraints
constraints.entryLocations = .any

return try MultihopPicker(
constraints: constraints,
daitaSettings: daitaSettings,
relays: relays,
connectionAttemptCount: connectionAttemptCount
).pick()
#endif
}

let match = try findBestMatch(from: exitCandidates)
return SelectedRelays(entry: nil, exit: match, retryAttempt: connectionAttemptCount)
}
}

struct MultihopPicker: RelayPicking {
let constraints: RelayConstraints
let daitaSettings: DAITASettings
let relays: REST.ServerRelaysResponse
let connectionAttemptCount: UInt

func pick() throws -> SelectedRelays {
let entryCandidates = try RelaySelector.WireGuard.findCandidates(
by: constraints.entryLocations,
in: relays,
filterConstraint: constraints.filter
filterConstraint: constraints.filter,
daitaEnabled: daitaSettings.state.isEnabled
)

let exitCandidates = try RelaySelector.WireGuard.findCandidates(
by: constraints.exitLocations,
in: relays,
filterConstraint: constraints.filter
filterConstraint: constraints.filter,
daitaEnabled: false
)

/*
Expand Down
5 changes: 3 additions & 2 deletions ios/MullvadREST/Relay/RelaySelector+Shadowsocks.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,12 @@ extension RelaySelector {
in relaysResponse: REST.ServerRelaysResponse
) -> REST.BridgeRelay? {
let mappedBridges = mapRelays(relays: relaysResponse.bridge.relays, locations: relaysResponse.locations)
let filteredRelays = applyConstraints(
let filteredRelays = (try? applyConstraints(
location,
filterConstraint: filter,
daitaEnabled: false,
relays: mappedBridges
)
)) ?? []
guard filteredRelays.isEmpty == false else { return relay(from: relaysResponse) }

// Compute the midpoint location from all the filtered relays
Expand Down
15 changes: 11 additions & 4 deletions ios/MullvadREST/Relay/RelaySelector+Wireguard.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
// Copyright © 2024 Mullvad VPN AB. All rights reserved.
//

import MullvadSettings
import MullvadTypes

extension RelaySelector {
Expand All @@ -14,13 +15,15 @@ extension RelaySelector {
public static func findCandidates(
by relayConstraint: RelayConstraint<UserSelectedRelays>,
in relays: REST.ServerRelaysResponse,
filterConstraint: RelayConstraint<RelayFilter>
filterConstraint: RelayConstraint<RelayFilter>,
daitaEnabled: Bool
) throws -> [RelayWithLocation<REST.ServerRelay>] {
let mappedRelays = mapRelays(relays: relays.wireguard.relays, locations: relays.locations)

return applyConstraints(
return try applyConstraints(
relayConstraint,
filterConstraint: filterConstraint,
daitaEnabled: daitaEnabled,
relays: mappedRelays
)
}
Expand All @@ -38,8 +41,12 @@ extension RelaySelector {
numberOfFailedAttempts: numberOfFailedAttempts
)

guard let port, let relayWithLocation = pickRandomRelayByWeight(relays: relayWithLocations) else {
throw NoRelaysSatisfyingConstraintsError()
guard let port else {
throw NoRelaysSatisfyingConstraintsError(.invalidPort)
}

guard let relayWithLocation = pickRandomRelayByWeight(relays: relayWithLocations) else {
throw NoRelaysSatisfyingConstraintsError(.relayConstraintNotMatching)
}

let endpoint = MullvadEndpoint(
Expand Down
Loading
Loading