Skip to content

Commit

Permalink
Fix ip address selection for ShadowSocks
Browse files Browse the repository at this point in the history
  • Loading branch information
rablador committed Nov 28, 2024
1 parent 38ebee0 commit ddfa220
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 18 deletions.
6 changes: 3 additions & 3 deletions ios/MullvadREST/ApiHandlers/ServerRelaysResponse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ extension REST {
public var daita: Bool?

public func override(ipv4AddrIn: IPv4Address?) -> Self {
return BridgeRelay(
BridgeRelay(
hostname: hostname,
active: active,
owned: owned,
Expand Down Expand Up @@ -65,7 +65,7 @@ extension REST {
public let shadowsocksExtraAddrIn: [String]?

public func override(ipv4AddrIn: IPv4Address?, ipv6AddrIn: IPv6Address?) -> Self {
return ServerRelay(
ServerRelay(
hostname: hostname,
active: active,
owned: owned,
Expand All @@ -82,7 +82,7 @@ extension REST {
}

public func override(daita: Bool) -> Self {
return ServerRelay(
ServerRelay(
hostname: hostname,
active: active,
owned: owned,
Expand Down
20 changes: 12 additions & 8 deletions ios/MullvadREST/Relay/MultihopDecisionFlow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,11 @@ struct OneToOne: MultihopDecisionFlow {
throw NoRelaysSatisfyingConstraintsError(.entryEqualsExit)
}

let exitMatch = try relayPicker.findBestMatch(from: exitCandidates, obfuscate: false)
let exitMatch = try relayPicker.findBestMatch(from: exitCandidates, useObfuscatedPortIfAvailable: false)
let entryMatch = try relayPicker.findBestMatch(
from: entryCandidates,
closeTo: daitaAutomaticRouting ? exitMatch.location : nil,
obfuscate: true
useObfuscatedPortIfAvailable: true
)

return SelectedRelays(entry: entryMatch, exit: exitMatch, retryAttempt: relayPicker.connectionAttemptCount)
Expand Down Expand Up @@ -97,8 +97,12 @@ struct OneToMany: MultihopDecisionFlow {
.pick(entryCandidates: entryCandidates, exitCandidates: exitCandidates, daitaAutomaticRouting: true)
}

let entryMatch = try multihopPicker.findBestMatch(from: entryCandidates, obfuscate: true)
let exitMatch = try multihopPicker.exclude(relay: entryMatch, from: exitCandidates, obfuscate: false)
let entryMatch = try multihopPicker.findBestMatch(from: entryCandidates, useObfuscatedPortIfAvailable: true)
let exitMatch = try multihopPicker.exclude(
relay: entryMatch,
from: exitCandidates,
useObfuscatedPortIfAvailable: false
)

return SelectedRelays(entry: entryMatch, exit: exitMatch, retryAttempt: relayPicker.connectionAttemptCount)
}
Expand Down Expand Up @@ -137,12 +141,12 @@ struct ManyToOne: MultihopDecisionFlow {
)
}

let exitMatch = try multihopPicker.findBestMatch(from: exitCandidates, obfuscate: false)
let exitMatch = try multihopPicker.findBestMatch(from: exitCandidates, useObfuscatedPortIfAvailable: false)
let entryMatch = try multihopPicker.exclude(
relay: exitMatch,
from: entryCandidates,
closeTo: daitaAutomaticRouting ? exitMatch.location : nil,
obfuscate: true
useObfuscatedPortIfAvailable: true
)

return SelectedRelays(entry: entryMatch, exit: exitMatch, retryAttempt: relayPicker.connectionAttemptCount)
Expand Down Expand Up @@ -182,12 +186,12 @@ struct ManyToMany: MultihopDecisionFlow {
)
}

let exitMatch = try multihopPicker.findBestMatch(from: exitCandidates, obfuscate: false)
let exitMatch = try multihopPicker.findBestMatch(from: exitCandidates, useObfuscatedPortIfAvailable: false)
let entryMatch = try multihopPicker.exclude(
relay: exitMatch,
from: entryCandidates,
closeTo: daitaAutomaticRouting ? exitMatch.location : nil,
obfuscate: true
useObfuscatedPortIfAvailable: true
)

return SelectedRelays(entry: entryMatch, exit: exitMatch, retryAttempt: relayPicker.connectionAttemptCount)
Expand Down
3 changes: 2 additions & 1 deletion ios/MullvadREST/Relay/ObfuscatorPortSelector.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import MullvadTypes
struct ObfuscatorPortSelection {
let relays: REST.ServerRelaysResponse
let port: RelayConstraint<UInt16>
let method: WireGuardObfuscationState
}

struct ObfuscatorPortSelector {
Expand Down Expand Up @@ -44,7 +45,7 @@ struct ObfuscatorPortSelector {
break
}

return ObfuscatorPortSelection(relays: relays, port: port)
return ObfuscatorPortSelection(relays: relays, port: port, method: obfuscationMethod)
}

private func obfuscateShadowsocksRelays(tunnelSettings: LatestTunnelSettings) -> REST.ServerRelaysResponse {
Expand Down
46 changes: 40 additions & 6 deletions ios/MullvadREST/Relay/RelayPicking.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import MullvadSettings
import MullvadTypes
import Network

protocol RelayPicking {
var obfuscation: ObfuscatorPortSelection { get }
Expand All @@ -22,22 +23,51 @@ extension RelayPicking {
func findBestMatch(
from candidates: [RelayWithLocation<REST.ServerRelay>],
closeTo location: Location? = nil,
obfuscate: Bool
useObfuscatedPortIfAvailable: Bool
) throws -> SelectedRelay {
let match = try RelaySelector.WireGuard.pickCandidate(
var match = try RelaySelector.WireGuard.pickCandidate(
from: candidates,
relays: relays,
portConstraint: obfuscate ? obfuscation.port : constraints.port,
portConstraint: useObfuscatedPortIfAvailable ? obfuscation.port : constraints.port,
numberOfFailedAttempts: connectionAttemptCount,
closeTo: location
)

if useObfuscatedPortIfAvailable && obfuscation.method == .shadowsocks {
match = applyShadowsocksIpAddress(in: match)
}

return SelectedRelay(
endpoint: match.endpoint,
hostname: match.relay.hostname,
location: match.location
)
}

private func applyShadowsocksIpAddress(in match: RelaySelectorMatch) -> RelaySelectorMatch {
let port = match.endpoint.ipv4Relay.port
let portRanges = RelaySelector.parseRawPortRanges(relays.wireguard.shadowsocksPortRanges)
let portIsWithinRange = portRanges.contains(where: { $0.contains(port) })

var endpoint = match.endpoint

// If the currently selected obfuscation port is not within the allowed range (as specified
// in the relay list), we should use one of the extra Shadowsocks IP addresses instead of
// the default one.
if !portIsWithinRange {
var ipv4Address = match.endpoint.ipv4Relay.ip
if let shadowsocksAddress = match.relay.shadowsocksExtraAddrIn?.randomElement() {
ipv4Address = IPv4Address(shadowsocksAddress) ?? ipv4Address
}

endpoint = match.endpoint.override(ipv4Relay: IPv4Endpoint(
ip: ipv4Address,
port: port
))
}

return RelaySelectorMatch(endpoint: endpoint, relay: match.relay, location: match.location)
}
}

struct SinglehopPicker: RelayPicking {
Expand All @@ -59,7 +89,7 @@ struct SinglehopPicker: RelayPicking {
daitaEnabled: daitaSettings.daitaState.isEnabled
)

let match = try findBestMatch(from: exitCandidates, obfuscate: true)
let match = try findBestMatch(from: exitCandidates, useObfuscatedPortIfAvailable: true)
return SelectedRelays(entry: nil, exit: match, retryAttempt: connectionAttemptCount)
} catch let error as NoRelaysSatisfyingConstraintsError where error.reason == .noDaitaRelaysFound {
// If DAITA is on and Direct only is off, and no supported relays are found, we should try to find the nearest
Expand Down Expand Up @@ -140,12 +170,16 @@ struct MultihopPicker: RelayPicking {
relay: SelectedRelay,
from candidates: [RelayWithLocation<REST.ServerRelay>],
closeTo location: Location? = nil,
obfuscate: Bool
useObfuscatedPortIfAvailable: Bool
) throws -> SelectedRelay {
let filteredCandidates = candidates.filter { relayWithLocation in
relayWithLocation.relay.hostname != relay.hostname
}

return try findBestMatch(from: filteredCandidates, closeTo: location, obfuscate: obfuscate)
return try findBestMatch(
from: filteredCandidates,
closeTo: location,
useObfuscatedPortIfAvailable: useObfuscatedPortIfAvailable
)
}
}
10 changes: 10 additions & 0 deletions ios/MullvadTypes/MullvadEndpoint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,14 @@ public struct MullvadEndpoint: Equatable, Codable {
self.ipv6Gateway = ipv6Gateway
self.publicKey = publicKey
}

public func override(ipv4Relay: IPv4Endpoint) -> Self {
MullvadEndpoint(
ipv4Relay: ipv4Relay,
ipv6Relay: ipv6Relay,
ipv4Gateway: ipv4Gateway,
ipv6Gateway: ipv6Gateway,
publicKey: publicKey
)
}
}

0 comments on commit ddfa220

Please sign in to comment.