Skip to content

Commit

Permalink
Upgrade settings to associate with multi-hop
Browse files Browse the repository at this point in the history
  • Loading branch information
mojganii committed May 17, 2024
1 parent 123d2e5 commit 68703cf
Show file tree
Hide file tree
Showing 27 changed files with 623 additions and 320 deletions.
15 changes: 15 additions & 0 deletions ios/MullvadREST/Relay/NoRelaysSatisfyingConstraintsError.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//
// NoRelaysSatisfyingConstraintsError.swift
// MullvadREST
//
// Created by Mojgan on 2024-04-26.
// Copyright © 2024 Mullvad VPN AB. All rights reserved.
//

import Foundation

public struct NoRelaysSatisfyingConstraintsError: LocalizedError {
public var errorDescription: String? {
"No relays satisfying constraints."
}
}
478 changes: 269 additions & 209 deletions ios/MullvadREST/Relay/RelaySelector.swift

Large diffs are not rendered by default.

18 changes: 18 additions & 0 deletions ios/MullvadREST/Relay/RelaySelectorResult.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// RelaySelectorResult.swift
// MullvadREST
//
// Created by Mojgan on 2024-05-14.
// Copyright © 2024 Mullvad VPN AB. All rights reserved.
//

import Foundation
import MullvadTypes

public typealias RelaySelectorResult = RelaySelectorMatch

public struct RelaySelectorMatch: Codable, Equatable {
public var endpoint: MullvadEndpoint
public var relay: REST.ServerRelay
public var location: Location
}
17 changes: 13 additions & 4 deletions ios/MullvadREST/Transport/Shadowsocks/ShadowsocksLoader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//

import Foundation
import MullvadSettings
import MullvadTypes

public protocol ShadowsocksLoaderProtocol {
Expand All @@ -19,6 +20,11 @@ public class ShadowsocksLoader: ShadowsocksLoaderProtocol {
private let relayCache: RelayCacheProtocol
private var relayConstraints = RelayConstraints()
private let constraintsUpdater: RelayConstraintsUpdater
private let tunnelSettings: LatestTunnelSettings? = try? SettingsManager.readSettings()

private var location: RelayConstraint<UserSelectedRelays> {
tunnelSettings?.tunnelMultihopState == .on ? relayConstraints.entryLocations : relayConstraints.exitLocations
}

public init(
shadowsocksCache: ShadowsocksConfigurationCache,
Expand Down Expand Up @@ -55,12 +61,15 @@ public class ShadowsocksLoader: ShadowsocksLoaderProtocol {
/// Returns a randomly selected shadowsocks configuration.
private func create() throws -> ShadowsocksConfiguration {
let cachedRelays = try relayCache.read()
let bridgeConfiguration = RelaySelector.shadowsocksTCPBridge(from: cachedRelays.relays)
let closestRelay = RelaySelector.closestShadowsocksRelayConstrained(
by: relayConstraints,
let bridgeConfiguration = RelaySelector.Shadowsocks.tcpBridge(from: cachedRelays.relays)

// TODO: pick entry if multi hop is enabled otherwise pick exit entry
let closestRelay = RelaySelector.Shadowsocks.closestRelay(
location: location,
port: relayConstraints.port,
filter: relayConstraints.filter,
in: cachedRelays.relays
)

guard let bridgeAddress = closestRelay?.ipv4AddrIn, let bridgeConfiguration else { throw POSIXError(.ENOENT) }

return ShadowsocksConfiguration(
Expand Down
15 changes: 15 additions & 0 deletions ios/MullvadSettings/MultihopSettings.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//
// MultihopSettings.swift
// MullvadSettings
//
// Created by Mojgan on 2024-04-26.
// Copyright © 2024 Mullvad VPN AB. All rights reserved.
//

import Foundation

/// Whether Multi-hop is enabled
public enum MultihopState: Codable {
case on
case off
}
12 changes: 9 additions & 3 deletions ios/MullvadSettings/TunnelSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import Foundation

/// Alias to the latest version of the `TunnelSettings`.
public typealias LatestTunnelSettings = TunnelSettingsV4
public typealias LatestTunnelSettings = TunnelSettingsV5

/// Protocol all TunnelSettings must adhere to, for upgrade purposes.
public protocol TunnelSettings: Codable {
Expand All @@ -27,14 +27,19 @@ public enum SchemaVersion: Int, Equatable {
/// V2 format with WireGuard obfuscation options, stored as `TunnelSettingsV3`.
case v3 = 3

/// V3 format with post quantum options, stored as `TunnelSettingsV4`.
case v4 = 4

/// V4 format with multi-hop options, stored as `TunnelSettingsV5`.
case v5 = 5

var settingsType: any TunnelSettings.Type {
switch self {
case .v1: return TunnelSettingsV1.self
case .v2: return TunnelSettingsV2.self
case .v3: return TunnelSettingsV3.self
case .v4: return TunnelSettingsV4.self
case .v5: return TunnelSettingsV5.self
}
}

Expand All @@ -43,10 +48,11 @@ public enum SchemaVersion: Int, Equatable {
case .v1: return .v2
case .v2: return .v3
case .v3: return .v4
case .v4: return .v4
case .v4: return .v5
case .v5: return .v5
}
}

/// Current schema version.
public static let current = SchemaVersion.v4
public static let current = SchemaVersion.v5
}
4 changes: 4 additions & 0 deletions ios/MullvadSettings/TunnelSettingsUpdate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public enum TunnelSettingsUpdate {
case obfuscation(WireGuardObfuscationSettings)
case relayConstraints(RelayConstraints)
case quantumResistance(TunnelQuantumResistance)
case multihop(MultihopState)
}

extension TunnelSettingsUpdate {
Expand All @@ -27,6 +28,8 @@ extension TunnelSettingsUpdate {
settings.relayConstraints = newRelayConstraints
case let .quantumResistance(newQuantumResistance):
settings.tunnelQuantumResistance = newQuantumResistance
case let .multihop(newState):
settings.tunnelMultihopState = newState
}
}

Expand All @@ -36,6 +39,7 @@ extension TunnelSettingsUpdate {
case .obfuscation: "obfuscation settings"
case .relayConstraints: "relay constraints"
case .quantumResistance: "quantum resistance"
case .multihop: "Multihop"
}
}
}
8 changes: 7 additions & 1 deletion ios/MullvadSettings/TunnelSettingsV4.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ public struct TunnelSettingsV4: Codable, Equatable, TunnelSettings {
}

public func upgradeToNextVersion() -> any TunnelSettings {
self
TunnelSettingsV5(
relayConstraints: relayConstraints,
dnsSettings: dnsSettings,
wireGuardObfuscation: wireGuardObfuscation,
tunnelQuantumResistance: tunnelQuantumResistance,
tunnelMultihopState: .off
)
}
}
46 changes: 46 additions & 0 deletions ios/MullvadSettings/TunnelSettingsV5.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//
// TunnelSettingsV5.swift
// MullvadSettings
//
// Created by Mojgan on 2024-05-13.
// Copyright © 2024 Mullvad VPN AB. All rights reserved.
//

import Foundation
import MullvadTypes

public struct TunnelSettingsV5: Codable, Equatable, TunnelSettings {
/// Relay constraints.
public var relayConstraints: RelayConstraints

/// DNS settings.
public var dnsSettings: DNSSettings

/// WireGuard obfuscation settings
public var wireGuardObfuscation: WireGuardObfuscationSettings

/// Whether Post Quantum exchanges are enabled.
public var tunnelQuantumResistance: TunnelQuantumResistance

/// Whether Multi-hop is enabled.
public var tunnelMultihopState: MultihopState

public init(
relayConstraints: RelayConstraints = RelayConstraints(),
dnsSettings: DNSSettings = DNSSettings(),
wireGuardObfuscation: WireGuardObfuscationSettings = WireGuardObfuscationSettings(),
tunnelQuantumResistance: TunnelQuantumResistance = .automatic,
tunnelMultihopState: MultihopState = .off

) {
self.relayConstraints = relayConstraints
self.dnsSettings = dnsSettings
self.wireGuardObfuscation = wireGuardObfuscation
self.tunnelQuantumResistance = tunnelQuantumResistance
self.tunnelMultihopState = tunnelMultihopState
}

public func upgradeToNextVersion() -> any TunnelSettings {
self
}
}
41 changes: 27 additions & 14 deletions ios/MullvadTypes/RelayConstraints.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,28 +20,31 @@ public class RelayConstraintsUpdater: ConstraintsPropagation {
}
}

public struct RelayConstraints: Codable, Equatable, CustomDebugStringConvertible {
public struct RelayConstraints: Codable, Equatable {
@available(*, deprecated, renamed: "locations")
private var location: RelayConstraint<RelayLocation> = .only(.country("se"))

// Added in 2023.3
public var port: RelayConstraint<UInt16>
public var filter: RelayConstraint<RelayFilter>

// Added in 2024.1
// Changed from RelayLocations to UserSelectedRelays in 2024.3
public var locations: RelayConstraint<UserSelectedRelays>
@available(*, deprecated, renamed: "exitLocations")
private var locations: RelayConstraint<UserSelectedRelays> = .only(UserSelectedRelays(locations: [.country("se")]))

public var debugDescription: String {
"RelayConstraints { locations: \(locations), port: \(port), filter: \(filter) }"
}
// Added in 2024.5 to support multi-hop
public var entryLocations: RelayConstraint<UserSelectedRelays>
public var exitLocations: RelayConstraint<UserSelectedRelays>

// Added in 2023.3
public var port: RelayConstraint<UInt16>
public var filter: RelayConstraint<RelayFilter>

public init(
locations: RelayConstraint<UserSelectedRelays> = .only(UserSelectedRelays(locations: [.country("se")])),
entryLocations: RelayConstraint<UserSelectedRelays> = .only(UserSelectedRelays(locations: [.country("se")])),
exitLocations: RelayConstraint<UserSelectedRelays> = .only(UserSelectedRelays(locations: [.country("se")])),
port: RelayConstraint<UInt16> = .any,
filter: RelayConstraint<RelayFilter> = .any
) {
self.locations = locations
self.entryLocations = entryLocations
self.exitLocations = exitLocations
self.port = port
self.filter = filter
}
Expand All @@ -53,9 +56,19 @@ public struct RelayConstraints: Codable, Equatable, CustomDebugStringConvertible
port = try container.decodeIfPresent(RelayConstraint<UInt16>.self, forKey: .port) ?? .any
filter = try container.decodeIfPresent(RelayConstraint<RelayFilter>.self, forKey: .filter) ?? .any

// Added in 2024.1
locations = try container.decodeIfPresent(RelayConstraint<UserSelectedRelays>.self, forKey: .locations)
?? Self.migrateRelayLocation(decoder: decoder)
// Added in 2024.5
entryLocations = try container.decodeIfPresent(
RelayConstraint<UserSelectedRelays>.self,
forKey: .entryLocations
) ?? .only(UserSelectedRelays(locations: [.country("se")]))

exitLocations = try container
.decodeIfPresent(RelayConstraint<UserSelectedRelays>.self, forKey: .exitLocations) ??
container.decodeIfPresent(
RelayConstraint<UserSelectedRelays>.self,
forKey: .locations
) ??
Self.migrateRelayLocation(decoder: decoder)
?? .only(UserSelectedRelays(locations: [.country("se")]))
}
}
Expand Down
Loading

0 comments on commit 68703cf

Please sign in to comment.