Skip to content

Commit

Permalink
Relay selector should use overridden IP addresses for relays
Browse files Browse the repository at this point in the history
  • Loading branch information
Jon Petersson committed Feb 1, 2024
1 parent 1b17b2e commit aeca222
Show file tree
Hide file tree
Showing 11 changed files with 256 additions and 45 deletions.
28 changes: 28 additions & 0 deletions ios/MullvadREST/ApiHandlers/ServerRelaysResponse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,19 @@ extension REST {
public let ipv4AddrIn: IPv4Address
public let weight: UInt64
public let includeInCountry: Bool

public func copyWith(ipv4AddrIn: IPv4Address?) -> Self {
return BridgeRelay(
hostname: hostname,
active: active,
owned: owned,
location: location,
provider: provider,
ipv4AddrIn: ipv4AddrIn ?? self.ipv4AddrIn,
weight: weight,
includeInCountry: includeInCountry
)
}
}

public struct ServerRelay: Codable, Equatable {
Expand All @@ -47,6 +60,21 @@ extension REST {
public let ipv6AddrIn: IPv6Address
public let publicKey: Data
public let includeInCountry: Bool

public func copyWith(ipv4AddrIn: IPv4Address?, ipv6AddrIn: IPv6Address?) -> Self {
return ServerRelay(
hostname: hostname,
active: active,
owned: owned,
location: location,
provider: provider,
weight: weight,
ipv4AddrIn: ipv4AddrIn ?? self.ipv4AddrIn,
ipv6AddrIn: ipv6AddrIn ?? self.ipv6AddrIn,
publicKey: publicKey,
includeInCountry: includeInCountry
)
}
}

public struct ServerWireguardTunnels: Codable, Equatable {
Expand Down
29 changes: 29 additions & 0 deletions ios/MullvadREST/Relay/AnyRelay.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//
// AnyRelay.swift
// MullvadREST
//
// Created by Jon Petersson on 2024-01-31.
// Copyright © 2024 Mullvad VPN AB. All rights reserved.
//

import MullvadTypes
import Network

public protocol AnyRelay {
var hostname: String { get }
var owned: Bool { get }
var location: String { get }
var provider: String { get }
var weight: UInt64 { get }
var active: Bool { get }
var includeInCountry: Bool { get }

func copyWith(ipv4AddrIn: IPv4Address?, ipv6AddrIn: IPv6Address?) -> Self
}

extension REST.ServerRelay: AnyRelay {}
extension REST.BridgeRelay: AnyRelay {
public func copyWith(ipv4AddrIn: IPv4Address?, ipv6AddrIn: IPv6Address?) -> REST.BridgeRelay {
copyWith(ipv4AddrIn: ipv4AddrIn)
}
}
57 changes: 53 additions & 4 deletions ios/MullvadREST/Relay/RelayCache.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
// Copyright © 2021 Mullvad VPN AB. All rights reserved.
//

import Foundation
import MullvadSettings
import MullvadTypes
import Network

public protocol RelayCacheProtocol {
func read() throws -> CachedRelays
Expand All @@ -16,22 +17,28 @@ public protocol RelayCacheProtocol {

public final class RelayCache: RelayCacheProtocol {
private let fileCache: any FileCacheProtocol<CachedRelays>
private let ipOverrideRepository: any IPOverrideRepositoryProtocol

/// Designated initializer
public init(cacheDirectory: URL) {
public init(cacheDirectory: URL, ipOverrideRepository: IPOverrideRepositoryProtocol) {
fileCache = FileCache(fileURL: cacheDirectory.appendingPathComponent("relays.json", isDirectory: false))
self.ipOverrideRepository = ipOverrideRepository
}

/// Initializer that accepts a custom FileCache implementation. Used in tests.
init(fileCache: some FileCacheProtocol<CachedRelays>) {
init(fileCache: some FileCacheProtocol<CachedRelays>, ipOverrideRepository: some IPOverrideRepositoryProtocol) {
self.fileCache = fileCache
self.ipOverrideRepository = ipOverrideRepository
}

/// Safely read the cache file from disk using file coordinator and fallback to prebundled
/// relays in case if the relay cache file is missing.
public func read() throws -> CachedRelays {
do {
return try fileCache.read()
let cache = try fileCache.read()
let relayResponse = apply(overrides: ipOverrideRepository.fetchAll(), to: cache.relays)

return CachedRelays(relays: relayResponse, updatedAt: cache.updatedAt)
} catch {
if error is DecodingError || (error as? CocoaError)?.code == .fileReadNoSuchFile {
return try readPrebundledRelays()
Expand Down Expand Up @@ -59,4 +66,46 @@ public final class RelayCache: RelayCacheProtocol {
updatedAt: Date(timeIntervalSince1970: 0)
)
}

private func apply(
overrides: [IPOverride],
to relyResponse: REST.ServerRelaysResponse
) -> REST.ServerRelaysResponse {
let wireguard = relyResponse.wireguard
let bridge = relyResponse.bridge

let wireguardRelays = wireguard.relays.map { relay in
return apply(overrides: overrides, to: relay)
}
let bridgeRelays = bridge.relays.map { relay in
return apply(overrides: overrides, to: relay)
}

return REST.ServerRelaysResponse(
locations: relyResponse.locations,
wireguard: REST.ServerWireguardTunnels(
ipv4Gateway: wireguard.ipv4Gateway,
ipv6Gateway: wireguard.ipv6Gateway,
portRanges: wireguard.portRanges,
relays: wireguardRelays
),
bridge: REST.ServerBridges(
shadowsocks: bridge.shadowsocks,
relays: bridgeRelays
)
)
}

private func apply<T: AnyRelay>(overrides: [IPOverride], to relay: T) -> T {
if let override = overrides.first(where: { host in
host.hostname == relay.hostname
}) {
return relay.copyWith(
ipv4AddrIn: override.ipv4Address,
ipv6AddrIn: override.ipv6Address
)
}

return relay
}
}
13 changes: 0 additions & 13 deletions ios/MullvadREST/Relay/RelaySelector.swift
Original file line number Diff line number Diff line change
Expand Up @@ -306,19 +306,6 @@ public struct RelaySelectorResult: Codable, Equatable {
public var location: Location
}

public protocol AnyRelay {
var hostname: String { get }
var owned: Bool { get }
var location: String { get }
var provider: String { get }
var weight: UInt64 { get }
var active: Bool { get }
var includeInCountry: Bool { get }
}

extension REST.ServerRelay: AnyRelay {}
extension REST.BridgeRelay: AnyRelay {}

private struct RelayWithLocation<T: AnyRelay> {
let relay: T
let serverLocation: Location
Expand Down
2 changes: 1 addition & 1 deletion ios/MullvadSettings/IPOverride.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public struct IPOverride: Codable, Equatable {
case ipv6Address = "ipv6_addr_in"
}

init(hostname: String, ipv4Address: IPv4Address?, ipv6Address: IPv6Address?) throws {
public init(hostname: String, ipv4Address: IPv4Address?, ipv6Address: IPv6Address?) throws {
self.hostname = hostname
self.ipv4Address = ipv4Address
self.ipv6Address = ipv6Address
Expand Down
22 changes: 8 additions & 14 deletions ios/MullvadVPN.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -508,13 +508,6 @@
7A6000F92B6273A4001CF0D9 /* AccessMethodViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 586C0D7B2B03BDD100E7CDD7 /* AccessMethodViewModel.swift */; };
7A6000FC2B628DF6001CF0D9 /* ListCellContentConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6000FB2B628DF6001CF0D9 /* ListCellContentConfiguration.swift */; };
7A6000FE2B628E9F001CF0D9 /* ListCellContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6000FD2B628E9F001CF0D9 /* ListCellContentView.swift */; };
7A5869B72B56B41500640D27 /* IPOverrideTextViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5869B62B56B41500640D27 /* IPOverrideTextViewController.swift */; };
7A5869B92B56E7F000640D27 /* IPOverrideViewControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5869B82B56E7F000640D27 /* IPOverrideViewControllerDelegate.swift */; };
7A5869BC2B56EF3400640D27 /* IPOverrideRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5869BA2B56EE9500640D27 /* IPOverrideRepository.swift */; };
7A5869BD2B56EF7300640D27 /* IPOverride.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5869B22B5697AC00640D27 /* IPOverride.swift */; };
7A5869BF2B57D0A100640D27 /* IPOverrideStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5869BE2B57D0A100640D27 /* IPOverrideStatus.swift */; };
7A5869C12B57D21A00640D27 /* IPOverrideStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5869C02B57D21A00640D27 /* IPOverrideStatusView.swift */; };
7A5869C32B5820CE00640D27 /* IPOverrideRepositoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5869C22B5820CE00640D27 /* IPOverrideRepositoryTests.swift */; };
7A6B4F592AB8412E00123853 /* TunnelMonitorTimings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6B4F582AB8412E00123853 /* TunnelMonitorTimings.swift */; };
7A6F2FA52AFA3CB2006D0856 /* AccountExpiryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6F2FA42AFA3CB2006D0856 /* AccountExpiryTests.swift */; };
7A6F2FA72AFBB9AE006D0856 /* AccountExpiry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6F2FA62AFBB9AE006D0856 /* AccountExpiry.swift */; };
Expand Down Expand Up @@ -567,6 +560,8 @@
7AD0AA1D2AD6A86700119E10 /* PacketTunnelActorProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AD0AA192AD69B6E00119E10 /* PacketTunnelActorProtocol.swift */; };
7AD0AA1F2AD6C8B900119E10 /* URLRequestProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AD0AA1E2AD6C8B900119E10 /* URLRequestProxyProtocol.swift */; };
7AD0AA212AD6CB0000119E10 /* URLRequestProxyStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AD0AA202AD6CB0000119E10 /* URLRequestProxyStub.swift */; };
7ADCB2D82B6A6EB300C88F89 /* AnyRelay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADCB2D72B6A6EB300C88F89 /* AnyRelay.swift */; };
7ADCB2DA2B6A730400C88F89 /* IPOverrideRepositoryStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADCB2D92B6A730400C88F89 /* IPOverrideRepositoryStub.swift */; };
7AE044BB2A935726003915D8 /* Routing.h in Headers */ = {isa = PBXBuildFile; fileRef = 7A88DCD02A8FABBE00D2FF0E /* Routing.h */; settings = {ATTRIBUTES = (Public, ); }; };
7AEF7F1A2AD00F52006FE45D /* AppMessageHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AEF7F192AD00F52006FE45D /* AppMessageHandler.swift */; };
7AF10EB22ADE859200C090B9 /* AlertViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF10EB12ADE859200C090B9 /* AlertViewController.swift */; };
Expand Down Expand Up @@ -1692,13 +1687,6 @@
7A6000F52B60092F001CF0D9 /* AccessMethodViewModelEditing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessMethodViewModelEditing.swift; sourceTree = "<group>"; };
7A6000FB2B628DF6001CF0D9 /* ListCellContentConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListCellContentConfiguration.swift; sourceTree = "<group>"; };
7A6000FD2B628E9F001CF0D9 /* ListCellContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListCellContentView.swift; sourceTree = "<group>"; };
7A5869B22B5697AC00640D27 /* IPOverride.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPOverride.swift; sourceTree = "<group>"; };
7A5869B62B56B41500640D27 /* IPOverrideTextViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPOverrideTextViewController.swift; sourceTree = "<group>"; };
7A5869B82B56E7F000640D27 /* IPOverrideViewControllerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPOverrideViewControllerDelegate.swift; sourceTree = "<group>"; };
7A5869BA2B56EE9500640D27 /* IPOverrideRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPOverrideRepository.swift; sourceTree = "<group>"; };
7A5869BE2B57D0A100640D27 /* IPOverrideStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPOverrideStatus.swift; sourceTree = "<group>"; };
7A5869C02B57D21A00640D27 /* IPOverrideStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPOverrideStatusView.swift; sourceTree = "<group>"; };
7A5869C22B5820CE00640D27 /* IPOverrideRepositoryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPOverrideRepositoryTests.swift; sourceTree = "<group>"; };
7A6B4F582AB8412E00123853 /* TunnelMonitorTimings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelMonitorTimings.swift; sourceTree = "<group>"; };
7A6F2FA42AFA3CB2006D0856 /* AccountExpiryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountExpiryTests.swift; sourceTree = "<group>"; };
7A6F2FA62AFBB9AE006D0856 /* AccountExpiry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountExpiry.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1744,6 +1732,8 @@
7AD0AA1B2AD6A63F00119E10 /* PacketTunnelActorStub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PacketTunnelActorStub.swift; sourceTree = "<group>"; };
7AD0AA1E2AD6C8B900119E10 /* URLRequestProxyProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLRequestProxyProtocol.swift; sourceTree = "<group>"; };
7AD0AA202AD6CB0000119E10 /* URLRequestProxyStub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLRequestProxyStub.swift; sourceTree = "<group>"; };
7ADCB2D72B6A6EB300C88F89 /* AnyRelay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyRelay.swift; sourceTree = "<group>"; };
7ADCB2D92B6A730400C88F89 /* IPOverrideRepositoryStub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPOverrideRepositoryStub.swift; sourceTree = "<group>"; };
7AEF7F192AD00F52006FE45D /* AppMessageHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppMessageHandler.swift; sourceTree = "<group>"; };
7AF10EB12ADE859200C090B9 /* AlertViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlertViewController.swift; sourceTree = "<group>"; };
7AF10EB32ADE85BC00C090B9 /* RelayFilterCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RelayFilterCoordinator.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2782,6 +2772,7 @@
F09D04BA2AE95396003D4F89 /* URLSessionStub.swift */,
58165EBD2A262CBB00688EAD /* WgKeyRotationTests.swift */,
F0B0E6962AFE6E7E001DC66B /* XCTest+Async.swift */,
7ADCB2D92B6A730400C88F89 /* IPOverrideRepositoryStub.swift */,
);
path = MullvadVPNTests;
sourceTree = "<group>";
Expand Down Expand Up @@ -3478,6 +3469,7 @@
F0DC779F2B2222D20087F09D /* Relay */ = {
isa = PBXGroup;
children = (
7ADCB2D72B6A6EB300C88F89 /* AnyRelay.swift */,
585DA87626B024A600B8C587 /* CachedRelays.swift */,
F0DDE4272B220A15006B57A7 /* Haversine.swift */,
F0DDE4292B220A15006B57A7 /* Midpoint.swift */,
Expand Down Expand Up @@ -4477,6 +4469,7 @@
A90763B42B2857D50045ADF0 /* NWConnection+Extensions.swift in Sources */,
F06045EA2B23217E00B2D37A /* ShadowsocksTransport.swift in Sources */,
06799AFC28F98EE300ACD94E /* AddressCache.swift in Sources */,
7ADCB2D82B6A6EB300C88F89 /* AnyRelay.swift in Sources */,
06799AF028F98E4800ACD94E /* REST.swift in Sources */,
06799ADF28F98E4800ACD94E /* RESTDevicesProxy.swift in Sources */,
06799ADA28F98E4800ACD94E /* RESTResponseHandler.swift in Sources */,
Expand Down Expand Up @@ -4642,6 +4635,7 @@
A9A5FA2F2ACB05160083449F /* FixedWidthIntegerArithmeticsTests.swift in Sources */,
A9A5FA302ACB05160083449F /* InputTextFormatterTests.swift in Sources */,
F0B0E6972AFE6E7E001DC66B /* XCTest+Async.swift in Sources */,
7ADCB2DA2B6A730400C88F89 /* IPOverrideRepositoryStub.swift in Sources */,
A9A5FA312ACB05160083449F /* MockFileCache.swift in Sources */,
A9A5FA322ACB05160083449F /* RelayCacheTests.swift in Sources */,
A9A5FA332ACB05160083449F /* RelaySelectorTests.swift in Sources */,
Expand Down
2 changes: 1 addition & 1 deletion ios/MullvadVPN/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD

setUpProxies(containerURL: containerURL)

let relayCache = RelayCache(cacheDirectory: containerURL)
let relayCache = RelayCache(cacheDirectory: containerURL, ipOverrideRepository: IPOverrideRepository())
relayCacheTracker = RelayCacheTracker(relayCache: relayCache, application: application, apiProxy: apiProxy)

addressCacheTracker = AddressCacheTracker(application: application, apiProxy: apiProxy, store: addressCache)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ class IPOverrideViewController: UIViewController {

weak var delegate: IPOverrideViewControllerDelegate?

weak var delegate: IPOverrideViewControllerDelegate?

private lazy var containerView: UIStackView = {
let view = UIStackView()
view.axis = .vertical
Expand Down
33 changes: 33 additions & 0 deletions ios/MullvadVPNTests/IPOverrideRepositoryStub.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//
// IPOverrideRepositoryStub.swift
// MullvadVPNTests
//
// Created by Jon Petersson on 2024-01-31.
// Copyright © 2024 Mullvad VPN AB. All rights reserved.
//

import MullvadSettings

struct IPOverrideRepositoryStub: IPOverrideRepositoryProtocol {
let overrides: [IPOverride]

init(overrides: [IPOverride] = []) {
self.overrides = overrides
}

func add(_ overrides: [IPOverride]) {}

func fetchAll() -> [IPOverride] {
overrides
}

func fetchByHostname(_ hostname: String) -> IPOverride? {
nil
}

func deleteAll() {}

func parse(data: Data) throws -> [IPOverride] {
overrides
}
}
Loading

0 comments on commit aeca222

Please sign in to comment.