summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJon Petersson <jon.petersson@kvadrat.se>2024-01-15 16:46:09 +0100
committerJon Petersson <jon.petersson@kvadrat.se>2024-02-07 11:08:48 +0100
commit758e217c05d23ccffdaa80db48edfe2b1cc1464d (patch)
treed9f4c4b354a553dcdbd9b8ac6cf281fe9407c987
parent446ef2b026bebce7d877226a7db0551496acbf28 (diff)
downloadmullvadvpn-758e217c05d23ccffdaa80db48edfe2b1cc1464d.tar.xz
mullvadvpn-758e217c05d23ccffdaa80db48edfe2b1cc1464d.zip
Relay selector should use overridden IP addresses for relays
-rw-r--r--ios/MullvadREST/ApiHandlers/ServerRelaysResponse.swift28
-rw-r--r--ios/MullvadREST/Relay/AnyRelay.swift29
-rw-r--r--ios/MullvadREST/Relay/IPOverrideWrapper.swift72
-rw-r--r--ios/MullvadREST/Relay/RelayCache.swift2
-rw-r--r--ios/MullvadREST/Relay/RelaySelector.swift13
-rw-r--r--ios/MullvadSettings/IPOverride.swift10
-rw-r--r--ios/MullvadSettings/IPOverrideRepository.swift23
-rw-r--r--ios/MullvadVPN.xcodeproj/project.pbxproj16
-rw-r--r--ios/MullvadVPN/AppDelegate.swift15
-rw-r--r--ios/MullvadVPN/Coordinators/ApplicationCoordinator.swift9
-rw-r--r--ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideCoordinator.swift11
-rw-r--r--ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideInteractor.swift23
-rw-r--r--ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideTextViewController.swift1
-rw-r--r--ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideViewController.swift4
-rw-r--r--ios/MullvadVPN/Coordinators/Settings/SettingsCoordinator.swift8
-rw-r--r--ios/MullvadVPN/Extensions/URL+Scoping.swift4
-rw-r--r--ios/MullvadVPN/RelayCacheTracker/RelayCacheTracker.swift26
-rw-r--r--ios/MullvadVPN/SceneDelegate.swift3
-rw-r--r--ios/MullvadVPN/TunnelManager/TunnelManager.swift4
-rw-r--r--ios/MullvadVPN/View controllers/Settings/SettingsInteractorFactory.swift3
-rw-r--r--ios/MullvadVPNTests/IPOverrideRepositoryStub.swift33
-rw-r--r--ios/MullvadVPNTests/IPOverrideRepositoryTests.swift9
-rw-r--r--ios/MullvadVPNTests/IPOverrideWrapperTests.swift102
-rw-r--r--ios/MullvadVPNTests/RelayCacheTests.swift17
-rw-r--r--ios/MullvadVPNTests/RelayCacheTracker+Stubs.swift2
-rw-r--r--ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift56
-rw-r--r--ios/PacketTunnel/PacketTunnelProvider/RelaySelectorWrapper.swift2
27 files changed, 429 insertions, 96 deletions
diff --git a/ios/MullvadREST/ApiHandlers/ServerRelaysResponse.swift b/ios/MullvadREST/ApiHandlers/ServerRelaysResponse.swift
index 63f0822e63..a575da03cc 100644
--- a/ios/MullvadREST/ApiHandlers/ServerRelaysResponse.swift
+++ b/ios/MullvadREST/ApiHandlers/ServerRelaysResponse.swift
@@ -34,6 +34,19 @@ extension REST {
public let ipv4AddrIn: IPv4Address
public let weight: UInt64
public let includeInCountry: Bool
+
+ public func override(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 {
@@ -47,6 +60,21 @@ extension REST {
public let ipv6AddrIn: IPv6Address
public let publicKey: Data
public let includeInCountry: Bool
+
+ public func override(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 {
diff --git a/ios/MullvadREST/Relay/AnyRelay.swift b/ios/MullvadREST/Relay/AnyRelay.swift
new file mode 100644
index 0000000000..6c3c49aa55
--- /dev/null
+++ b/ios/MullvadREST/Relay/AnyRelay.swift
@@ -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 override(ipv4AddrIn: IPv4Address?, ipv6AddrIn: IPv6Address?) -> Self
+}
+
+extension REST.ServerRelay: AnyRelay {}
+extension REST.BridgeRelay: AnyRelay {
+ public func override(ipv4AddrIn: IPv4Address?, ipv6AddrIn: IPv6Address?) -> REST.BridgeRelay {
+ override(ipv4AddrIn: ipv4AddrIn)
+ }
+}
diff --git a/ios/MullvadREST/Relay/IPOverrideWrapper.swift b/ios/MullvadREST/Relay/IPOverrideWrapper.swift
new file mode 100644
index 0000000000..531112cf54
--- /dev/null
+++ b/ios/MullvadREST/Relay/IPOverrideWrapper.swift
@@ -0,0 +1,72 @@
+//
+// IPOverrideWrapper.swift
+// MullvadREST
+//
+// Created by Jon Petersson on 2024-02-05.
+// Copyright © 2024 Mullvad VPN AB. All rights reserved.
+//
+
+import MullvadSettings
+import MullvadTypes
+
+public class IPOverrideWrapper: RelayCacheProtocol {
+ private let relayCache: RelayCacheProtocol
+ private let ipOverrideRepository: any IPOverrideRepositoryProtocol
+
+ public init(relayCache: RelayCacheProtocol, ipOverrideRepository: any IPOverrideRepositoryProtocol) {
+ self.relayCache = relayCache
+ self.ipOverrideRepository = ipOverrideRepository
+ }
+
+ public func read() throws -> CachedRelays {
+ let cache = try relayCache.read()
+ let relayResponse = apply(overrides: ipOverrideRepository.fetchAll(), to: cache.relays)
+
+ return CachedRelays(relays: relayResponse, updatedAt: cache.updatedAt)
+ }
+
+ public func write(record: CachedRelays) throws {
+ try relayCache.write(record: record)
+ }
+
+ private func apply(
+ overrides: [IPOverride],
+ to relayResponse: REST.ServerRelaysResponse
+ ) -> REST.ServerRelaysResponse {
+ let wireguard = relayResponse.wireguard
+ let bridge = relayResponse.bridge
+
+ let overridenWireguardRelays = wireguard.relays.map { relay in
+ return apply(overrides: overrides, to: relay)
+ }
+ let overridenBridgeRelays = bridge.relays.map { relay in
+ return apply(overrides: overrides, to: relay)
+ }
+
+ return REST.ServerRelaysResponse(
+ locations: relayResponse.locations,
+ wireguard: REST.ServerWireguardTunnels(
+ ipv4Gateway: wireguard.ipv4Gateway,
+ ipv6Gateway: wireguard.ipv6Gateway,
+ portRanges: wireguard.portRanges,
+ relays: overridenWireguardRelays
+ ),
+ bridge: REST.ServerBridges(
+ shadowsocks: bridge.shadowsocks,
+ relays: overridenBridgeRelays
+ )
+ )
+ }
+
+ private func apply<T: AnyRelay>(overrides: [IPOverride], to relay: T) -> T {
+ return overrides
+ .first { $0.hostname == relay.hostname }
+ .flatMap {
+ relay.override(
+ ipv4AddrIn: $0.ipv4Address,
+ ipv6AddrIn: $0.ipv6Address
+ )
+ }
+ ?? relay
+ }
+}
diff --git a/ios/MullvadREST/Relay/RelayCache.swift b/ios/MullvadREST/Relay/RelayCache.swift
index 6cc9ddc616..47ea44c6f5 100644
--- a/ios/MullvadREST/Relay/RelayCache.swift
+++ b/ios/MullvadREST/Relay/RelayCache.swift
@@ -14,6 +14,8 @@ public protocol RelayCacheProtocol {
func write(record: CachedRelays) throws
}
+/// - Warning: `RelayCache` should not be used directly. It should be used through `IPOverrideWrapper` to have
+/// ip overrides applied.
public final class RelayCache: RelayCacheProtocol {
private let fileCache: any FileCacheProtocol<CachedRelays>
diff --git a/ios/MullvadREST/Relay/RelaySelector.swift b/ios/MullvadREST/Relay/RelaySelector.swift
index bc0378c217..6fc016d2c7 100644
--- a/ios/MullvadREST/Relay/RelaySelector.swift
+++ b/ios/MullvadREST/Relay/RelaySelector.swift
@@ -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
diff --git a/ios/MullvadSettings/IPOverride.swift b/ios/MullvadSettings/IPOverride.swift
index db65a90869..c25fdf4604 100644
--- a/ios/MullvadSettings/IPOverride.swift
+++ b/ios/MullvadSettings/IPOverride.swift
@@ -16,6 +16,10 @@ public struct RelayOverrides: Codable {
}
}
+public struct IPOverrideFormatError: LocalizedError {
+ public let errorDescription: String?
+}
+
public struct IPOverride: Codable, Equatable {
public let hostname: String
public var ipv4Address: IPv4Address?
@@ -27,7 +31,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
@@ -49,7 +53,3 @@ public struct IPOverride: Codable, Equatable {
}
}
}
-
-public struct IPOverrideFormatError: LocalizedError {
- public let errorDescription: String?
-}
diff --git a/ios/MullvadSettings/IPOverrideRepository.swift b/ios/MullvadSettings/IPOverrideRepository.swift
index 75e57f77ca..867a1c077f 100644
--- a/ios/MullvadSettings/IPOverrideRepository.swift
+++ b/ios/MullvadSettings/IPOverrideRepository.swift
@@ -12,13 +12,13 @@ import MullvadLogging
public protocol IPOverrideRepositoryProtocol {
func add(_ overrides: [IPOverride])
func fetchAll() -> [IPOverride]
- func fetchByHostname(_ hostname: String) -> IPOverride?
func deleteAll()
func parse(data: Data) throws -> [IPOverride]
}
public class IPOverrideRepository: IPOverrideRepositoryProtocol {
private let logger = Logger(label: "IPOverrideRepository")
+ private let readWriteLock = NSLock()
public init() {}
@@ -54,13 +54,11 @@ public class IPOverrideRepository: IPOverrideRepositoryProtocol {
return (try? readIpOverrides()) ?? []
}
- public func fetchByHostname(_ hostname: String) -> IPOverride? {
- return fetchAll().first { $0.hostname == hostname }
- }
-
public func deleteAll() {
do {
- try SettingsManager.store.delete(key: .ipOverrides)
+ try readWriteLock.withLock {
+ try SettingsManager.store.delete(key: .ipOverrides)
+ }
} catch {
logger.error("Could not delete all overrides. \nError: \(error)")
}
@@ -74,17 +72,20 @@ public class IPOverrideRepository: IPOverrideRepositoryProtocol {
}
private func readIpOverrides() throws -> [IPOverride] {
- let parser = makeParser()
- let data = try SettingsManager.store.read(key: .ipOverrides)
-
- return try parser.parseUnversionedPayload(as: [IPOverride].self, from: data)
+ try readWriteLock.withLock {
+ let parser = makeParser()
+ let data = try SettingsManager.store.read(key: .ipOverrides)
+ return try parser.parseUnversionedPayload(as: [IPOverride].self, from: data)
+ }
}
private func writeIpOverrides(_ overrides: [IPOverride]) throws {
let parser = makeParser()
let data = try parser.produceUnversionedPayload(overrides)
- try SettingsManager.store.write(data, for: .ipOverrides)
+ try readWriteLock.withLock {
+ try SettingsManager.store.write(data, for: .ipOverrides)
+ }
}
private func makeParser() -> SettingsParser {
diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj
index 5036a52bbd..17c25c9e92 100644
--- a/ios/MullvadVPN.xcodeproj/project.pbxproj
+++ b/ios/MullvadVPN.xcodeproj/project.pbxproj
@@ -486,6 +486,8 @@
7A3FD1B82AD54AE60042BEA6 /* TimeServerProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58BDEB9A2A98F58600F578F2 /* TimeServerProxy.swift */; };
7A42DEC92A05164100B209BE /* SettingsInputCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A42DEC82A05164100B209BE /* SettingsInputCell.swift */; };
7A516C2E2B6D357500BBD33D /* URL+Scoping.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A516C2D2B6D357500BBD33D /* URL+Scoping.swift */; };
+ 7A516C3A2B7111A700BBD33D /* IPOverrideWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A516C392B7111A700BBD33D /* IPOverrideWrapper.swift */; };
+ 7A516C3C2B712F0B00BBD33D /* IPOverrideWrapperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A516C3B2B712F0B00BBD33D /* IPOverrideWrapperTests.swift */; };
7A5869952B32E9C700640D27 /* LinkButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5869942B32E9C700640D27 /* LinkButton.swift */; };
7A5869972B32EA4500640D27 /* AppButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5869962B32EA4500640D27 /* AppButton.swift */; };
7A58699B2B482FE200640D27 /* UITableViewCell+Disable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A58699A2B482FE200640D27 /* UITableViewCell+Disable.swift */; };
@@ -560,6 +562,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 */; };
@@ -1666,6 +1670,8 @@
7A3FD1B42AD4465A0042BEA6 /* AppMessageHandlerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppMessageHandlerTests.swift; sourceTree = "<group>"; };
7A42DEC82A05164100B209BE /* SettingsInputCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsInputCell.swift; sourceTree = "<group>"; };
7A516C2D2B6D357500BBD33D /* URL+Scoping.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+Scoping.swift"; sourceTree = "<group>"; };
+ 7A516C392B7111A700BBD33D /* IPOverrideWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPOverrideWrapper.swift; sourceTree = "<group>"; };
+ 7A516C3B2B712F0B00BBD33D /* IPOverrideWrapperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPOverrideWrapperTests.swift; sourceTree = "<group>"; };
7A5869942B32E9C700640D27 /* LinkButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkButton.swift; sourceTree = "<group>"; };
7A5869962B32EA4500640D27 /* AppButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppButton.swift; sourceTree = "<group>"; };
7A58699A2B482FE200640D27 /* UITableViewCell+Disable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITableViewCell+Disable.swift"; sourceTree = "<group>"; };
@@ -1732,6 +1738,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>"; };
@@ -2745,8 +2753,10 @@
58B0A2A4238EE67E00BC001D /* Info.plist */,
A9B6AC192ADE8FBB00F7802A /* InMemorySettingsStore.swift */,
F07BF2572A26112D00042943 /* InputTextFormatterTests.swift */,
+ 7ADCB2D92B6A730400C88F89 /* IPOverrideRepositoryStub.swift */,
7A5869C22B5820CE00640D27 /* IPOverrideRepositoryTests.swift */,
7AB4CCB82B69097E006037F5 /* IPOverrideTests.swift */,
+ 7A516C3B2B712F0B00BBD33D /* IPOverrideWrapperTests.swift */,
A9B6AC172ADE8F4300F7802A /* MigrationManagerTests.swift */,
58C3FA652A38549D006A450A /* MockFileCache.swift */,
F09D04B42AE93CB6003D4F89 /* OutgoingConnectionProxy+Stub.swift */,
@@ -3461,8 +3471,10 @@
F0DC779F2B2222D20087F09D /* Relay */ = {
isa = PBXGroup;
children = (
+ 7ADCB2D72B6A6EB300C88F89 /* AnyRelay.swift */,
585DA87626B024A600B8C587 /* CachedRelays.swift */,
F0DDE4272B220A15006B57A7 /* Haversine.swift */,
+ 7A516C392B7111A700BBD33D /* IPOverrideWrapper.swift */,
F0DDE4292B220A15006B57A7 /* Midpoint.swift */,
5820675A26E6576800655B05 /* RelayCache.swift */,
F0DDE4282B220A15006B57A7 /* RelaySelector.swift */,
@@ -4438,6 +4450,7 @@
06799AF228F98E4800ACD94E /* RESTAccessTokenManager.swift in Sources */,
A90763B12B2857D50045ADF0 /* Socks5Endpoint.swift in Sources */,
06799AF328F98E4800ACD94E /* RESTAuthenticationProxy.swift in Sources */,
+ 7A516C3A2B7111A700BBD33D /* IPOverrideWrapper.swift in Sources */,
F0DDE4142B220458006B57A7 /* ShadowSocksProxy.swift in Sources */,
A90763B62B2857D50045ADF0 /* Socks5ConnectNegotiation.swift in Sources */,
F06045E62B231EB700B2D37A /* URLSessionTransport.swift in Sources */,
@@ -4459,6 +4472,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 */,
@@ -4574,6 +4588,7 @@
7A6F2FA52AFA3CB2006D0856 /* AccountExpiryTests.swift in Sources */,
A9A5FA082ACB05160083449F /* StorePaymentBlockObserver.swift in Sources */,
A9E0317C2ACBFC7E0095D843 /* TunnelStore+Stubs.swift in Sources */,
+ 7A516C3C2B712F0B00BBD33D /* IPOverrideWrapperTests.swift in Sources */,
A9A5FA092ACB05160083449F /* SendStoreReceiptOperation.swift in Sources */,
A9A5FA0A2ACB05160083449F /* StorePaymentEvent.swift in Sources */,
A9A5FA0B2ACB05160083449F /* StorePaymentManager.swift in Sources */,
@@ -4624,6 +4639,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 */,
diff --git a/ios/MullvadVPN/AppDelegate.swift b/ios/MullvadVPN/AppDelegate.swift
index 7a1ddb1358..eab4b8bf5d 100644
--- a/ios/MullvadVPN/AppDelegate.swift
+++ b/ios/MullvadVPN/AppDelegate.swift
@@ -45,6 +45,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
private(set) var accessMethodRepository = AccessMethodRepository()
private(set) var shadowsocksLoader: ShadowsocksLoaderProtocol!
private(set) var configuredTransportProvider: ProxyConfigurationTransportProvider!
+ private(set) var ipOverrideRepository = IPOverrideRepository()
// MARK: - Application lifecycle
@@ -66,8 +67,16 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
setUpProxies(containerURL: containerURL)
- let relayCache = RelayCache(cacheDirectory: containerURL)
- relayCacheTracker = RelayCacheTracker(relayCache: relayCache, application: application, apiProxy: apiProxy)
+ let ipOverrideWrapper = IPOverrideWrapper(
+ relayCache: RelayCache(cacheDirectory: containerURL),
+ ipOverrideRepository: ipOverrideRepository
+ )
+
+ relayCacheTracker = RelayCacheTracker(
+ relayCache: ipOverrideWrapper,
+ application: application,
+ apiProxy: apiProxy
+ )
addressCacheTracker = AddressCacheTracker(application: application, apiProxy: apiProxy, store: addressCache)
@@ -93,7 +102,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
shadowsocksLoader = ShadowsocksLoader(
shadowsocksCache: shadowsocksCache,
- relayCache: relayCache,
+ relayCache: ipOverrideWrapper,
constraintsUpdater: constraintsUpdater
)
diff --git a/ios/MullvadVPN/Coordinators/ApplicationCoordinator.swift b/ios/MullvadVPN/Coordinators/ApplicationCoordinator.swift
index ed61054537..5db7d9799e 100644
--- a/ios/MullvadVPN/Coordinators/ApplicationCoordinator.swift
+++ b/ios/MullvadVPN/Coordinators/ApplicationCoordinator.swift
@@ -79,6 +79,7 @@ final class ApplicationCoordinator: Coordinator, Presenting, RootContainerViewCo
private var outgoingConnectionService: OutgoingConnectionServiceHandling
private var accessMethodRepository: AccessMethodRepositoryProtocol
private let configuredTransportProvider: ProxyConfigurationTransportProvider
+ private let ipOverrideRepository: IPOverrideRepository
private var outOfTimeTimer: Timer?
@@ -96,7 +97,9 @@ final class ApplicationCoordinator: Coordinator, Presenting, RootContainerViewCo
outgoingConnectionService: OutgoingConnectionServiceHandling,
appPreferences: AppPreferencesDataSource,
accessMethodRepository: AccessMethodRepositoryProtocol,
- transportProvider: ProxyConfigurationTransportProvider
+ transportProvider: ProxyConfigurationTransportProvider,
+ ipOverrideRepository: IPOverrideRepository
+
) {
self.tunnelManager = tunnelManager
self.storePaymentManager = storePaymentManager
@@ -108,6 +111,7 @@ final class ApplicationCoordinator: Coordinator, Presenting, RootContainerViewCo
self.outgoingConnectionService = outgoingConnectionService
self.accessMethodRepository = accessMethodRepository
self.configuredTransportProvider = transportProvider
+ self.ipOverrideRepository = ipOverrideRepository
super.init()
@@ -770,7 +774,8 @@ final class ApplicationCoordinator: Coordinator, Presenting, RootContainerViewCo
navigationController: navigationController,
interactorFactory: interactorFactory,
accessMethodRepository: accessMethodRepository,
- proxyConfigurationTester: configurationTester
+ proxyConfigurationTester: configurationTester,
+ ipOverrideRepository: ipOverrideRepository
)
coordinator.didFinish = { [weak self] _ in
diff --git a/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideCoordinator.swift b/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideCoordinator.swift
index 53ffe91613..7c444e781b 100644
--- a/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideCoordinator.swift
+++ b/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideCoordinator.swift
@@ -14,7 +14,6 @@ import UIKit
class IPOverrideCoordinator: Coordinator, Presenting, SettingsChildCoordinator {
private let navigationController: UINavigationController
private let interactor: IPOverrideInteractor
- private let repository: IPOverrideRepositoryProtocol
private lazy var ipOverrideViewController: IPOverrideViewController = {
let viewController = IPOverrideViewController(
@@ -29,11 +28,13 @@ class IPOverrideCoordinator: Coordinator, Presenting, SettingsChildCoordinator {
navigationController
}
- init(navigationController: UINavigationController, repository: IPOverrideRepositoryProtocol) {
+ init(
+ navigationController: UINavigationController,
+ repository: IPOverrideRepositoryProtocol,
+ tunnelManager: TunnelManager
+ ) {
self.navigationController = navigationController
- self.repository = repository
-
- interactor = IPOverrideInteractor(repository: repository)
+ interactor = IPOverrideInteractor(repository: repository, tunnelManager: tunnelManager)
}
func start(animated: Bool) {
diff --git a/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideInteractor.swift b/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideInteractor.swift
index f400e7849f..75264eaf30 100644
--- a/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideInteractor.swift
+++ b/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideInteractor.swift
@@ -14,6 +14,7 @@ import MullvadTypes
struct IPOverrideInteractor {
private let logger = Logger(label: "IPOverrideInteractor")
private let repository: IPOverrideRepositoryProtocol
+ private let tunnelManager: TunnelManager
private let statusSubject = CurrentValueSubject<IPOverrideStatus, Never>(.noImports)
var statusPublisher: AnyPublisher<IPOverrideStatus, Never> {
@@ -28,8 +29,9 @@ struct IPOverrideInteractor {
}
}
- init(repository: IPOverrideRepositoryProtocol) {
+ init(repository: IPOverrideRepositoryProtocol, tunnelManager: TunnelManager) {
self.repository = repository
+ self.tunnelManager = tunnelManager
resetToDefaultStatus()
}
@@ -46,6 +48,8 @@ struct IPOverrideInteractor {
func deleteAllOverrides() {
repository.deleteAll()
+
+ updateTunnel()
resetToDefaultStatus()
}
@@ -60,11 +64,28 @@ struct IPOverrideInteractor {
logger.error("Error importing ip overrides: \(error)")
}
+ updateTunnel()
+
// After an import - successful or not - the UI should be reset back to default
// state after a certain amount of time.
resetToDefaultStatus(delay: .seconds(10))
}
+ private func updateTunnel() {
+ do {
+ try tunnelManager.refreshRelayCacheTracker()
+ } catch {
+ logger.error(error: error, message: "Could not refresh relay cache tracker.")
+ }
+
+ switch tunnelManager.tunnelStatus.observedState {
+ case .connecting, .connected, .reconnecting:
+ tunnelManager.reconnectTunnel(selectNewRelay: true)
+ default:
+ break
+ }
+ }
+
private func resetToDefaultStatus(delay: Duration = .zero) {
DispatchQueue.main.asyncAfter(deadline: .now() + delay.timeInterval) {
statusSubject.send(defaultStatus)
diff --git a/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideTextViewController.swift b/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideTextViewController.swift
index 1687ff59c5..ed0ae506c0 100644
--- a/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideTextViewController.swift
+++ b/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideTextViewController.swift
@@ -23,7 +23,6 @@ class IPOverrideTextViewController: UIViewController {
primaryAction: UIAction(handler: { [weak self] _ in
self?.interactor.import(text: self?.textView.text ?? "")
self?.dismiss(animated: true)
-
})
)
}()
diff --git a/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideViewController.swift b/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideViewController.swift
index b4ba782c0f..8bce36d450 100644
--- a/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideViewController.swift
+++ b/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideViewController.swift
@@ -241,8 +241,8 @@ class IPOverrideViewController: UIViewController {
extension IPOverrideViewController: UIDocumentPickerDelegate {
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
if let url = urls.first {
- url.securelyScoped { [weak self] url in
- self?.interactor.import(url: url)
+ url.securelyScoped { [weak self] scopedUrl in
+ scopedUrl.flatMap { self?.interactor.import(url: $0) }
}
}
}
diff --git a/ios/MullvadVPN/Coordinators/Settings/SettingsCoordinator.swift b/ios/MullvadVPN/Coordinators/Settings/SettingsCoordinator.swift
index 4645220152..4ace0cd561 100644
--- a/ios/MullvadVPN/Coordinators/Settings/SettingsCoordinator.swift
+++ b/ios/MullvadVPN/Coordinators/Settings/SettingsCoordinator.swift
@@ -43,6 +43,7 @@ final class SettingsCoordinator: Coordinator, Presentable, Presenting, SettingsV
private var modalRoute: SettingsNavigationRoute?
private let accessMethodRepository: AccessMethodRepositoryProtocol
private let proxyConfigurationTester: ProxyConfigurationTesterProtocol
+ private let ipOverrideRepository: IPOverrideRepository
let navigationController: UINavigationController
@@ -68,12 +69,14 @@ final class SettingsCoordinator: Coordinator, Presentable, Presenting, SettingsV
navigationController: UINavigationController,
interactorFactory: SettingsInteractorFactory,
accessMethodRepository: AccessMethodRepositoryProtocol,
- proxyConfigurationTester: ProxyConfigurationTesterProtocol
+ proxyConfigurationTester: ProxyConfigurationTesterProtocol,
+ ipOverrideRepository: IPOverrideRepository
) {
self.navigationController = navigationController
self.interactorFactory = interactorFactory
self.accessMethodRepository = accessMethodRepository
self.proxyConfigurationTester = proxyConfigurationTester
+ self.ipOverrideRepository = ipOverrideRepository
}
/// Start the coordinator fllow.
@@ -265,7 +268,8 @@ final class SettingsCoordinator: Coordinator, Presentable, Presenting, SettingsV
case .ipOverride:
return .childCoordinator(IPOverrideCoordinator(
navigationController: navigationController,
- repository: IPOverrideRepository()
+ repository: ipOverrideRepository,
+ tunnelManager: interactorFactory.tunnelManager
))
case .faq:
diff --git a/ios/MullvadVPN/Extensions/URL+Scoping.swift b/ios/MullvadVPN/Extensions/URL+Scoping.swift
index 5f5b68637c..a957d4291e 100644
--- a/ios/MullvadVPN/Extensions/URL+Scoping.swift
+++ b/ios/MullvadVPN/Extensions/URL+Scoping.swift
@@ -9,10 +9,12 @@
import Foundation
extension URL {
- func securelyScoped(_ completionHandler: (Self) -> Void) {
+ func securelyScoped(_ completionHandler: (Self?) -> Void) {
if startAccessingSecurityScopedResource() {
completionHandler(self)
stopAccessingSecurityScopedResource()
+ } else {
+ completionHandler(nil)
}
}
}
diff --git a/ios/MullvadVPN/RelayCacheTracker/RelayCacheTracker.swift b/ios/MullvadVPN/RelayCacheTracker/RelayCacheTracker.swift
index eb9050318f..67a8145020 100644
--- a/ios/MullvadVPN/RelayCacheTracker/RelayCacheTracker.swift
+++ b/ios/MullvadVPN/RelayCacheTracker/RelayCacheTracker.swift
@@ -21,6 +21,7 @@ protocol RelayCacheTrackerProtocol {
func getNextUpdateDate() -> Date
func addObserver(_ observer: RelayCacheTrackerObserver)
func removeObserver(_ observer: RelayCacheTrackerObserver)
+ func refreshCachedRelays() throws
}
final class RelayCacheTracker: RelayCacheTrackerProtocol {
@@ -144,6 +145,20 @@ final class RelayCacheTracker: RelayCacheTrackerProtocol {
}
}
+ func refreshCachedRelays() throws {
+ let newCachedRelays = try cache.read()
+
+ nslock.lock()
+ cachedRelays = newCachedRelays
+ nslock.unlock()
+
+ DispatchQueue.main.async {
+ self.observerList.forEach { observer in
+ observer.relayCacheTracker(self, didUpdateCachedRelays: newCachedRelays)
+ }
+ }
+ }
+
func getNextUpdateDate() -> Date {
nslock.lock()
defer { nslock.unlock() }
@@ -208,17 +223,8 @@ final class RelayCacheTracker: RelayCacheTrackerProtocol {
updatedAt: Date()
)
- nslock.lock()
- cachedRelays = newCachedRelays
- nslock.unlock()
-
try cache.write(record: newCachedRelays)
-
- DispatchQueue.main.async {
- self.observerList.forEach { observer in
- observer.relayCacheTracker(self, didUpdateCachedRelays: newCachedRelays)
- }
- }
+ try refreshCachedRelays()
}
private func scheduleRepeatingTimer(startTime: DispatchWallTime) {
diff --git a/ios/MullvadVPN/SceneDelegate.swift b/ios/MullvadVPN/SceneDelegate.swift
index c65b098814..6105d06fdf 100644
--- a/ios/MullvadVPN/SceneDelegate.swift
+++ b/ios/MullvadVPN/SceneDelegate.swift
@@ -77,7 +77,8 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate, SettingsMigrationUIHand
),
appPreferences: AppPreferences(),
accessMethodRepository: accessMethodRepository,
- transportProvider: appDelegate.configuredTransportProvider
+ transportProvider: appDelegate.configuredTransportProvider,
+ ipOverrideRepository: appDelegate.ipOverrideRepository
)
appCoordinator?.onShowSettings = { [weak self] in
diff --git a/ios/MullvadVPN/TunnelManager/TunnelManager.swift b/ios/MullvadVPN/TunnelManager/TunnelManager.swift
index b0c55cdfad..225c25d422 100644
--- a/ios/MullvadVPN/TunnelManager/TunnelManager.swift
+++ b/ios/MullvadVPN/TunnelManager/TunnelManager.swift
@@ -536,6 +536,10 @@ final class TunnelManager: StorePaymentObserver {
)
}
+ func refreshRelayCacheTracker() throws {
+ try relayCacheTracker.refreshCachedRelays()
+ }
+
// MARK: - Tunnel observeration
/// Add tunnel observer.
diff --git a/ios/MullvadVPN/View controllers/Settings/SettingsInteractorFactory.swift b/ios/MullvadVPN/View controllers/Settings/SettingsInteractorFactory.swift
index af9faeb52e..31cf1b0944 100644
--- a/ios/MullvadVPN/View controllers/Settings/SettingsInteractorFactory.swift
+++ b/ios/MullvadVPN/View controllers/Settings/SettingsInteractorFactory.swift
@@ -11,10 +11,11 @@ import MullvadREST
final class SettingsInteractorFactory {
private let storePaymentManager: StorePaymentManager
- private let tunnelManager: TunnelManager
private let apiProxy: APIQuerying
private let relayCacheTracker: RelayCacheTracker
+ let tunnelManager: TunnelManager
+
init(
storePaymentManager: StorePaymentManager,
tunnelManager: TunnelManager,
diff --git a/ios/MullvadVPNTests/IPOverrideRepositoryStub.swift b/ios/MullvadVPNTests/IPOverrideRepositoryStub.swift
new file mode 100644
index 0000000000..633bc44bdb
--- /dev/null
+++ b/ios/MullvadVPNTests/IPOverrideRepositoryStub.swift
@@ -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
+ }
+}
diff --git a/ios/MullvadVPNTests/IPOverrideRepositoryTests.swift b/ios/MullvadVPNTests/IPOverrideRepositoryTests.swift
index 0e90a79e21..961d938b54 100644
--- a/ios/MullvadVPNTests/IPOverrideRepositoryTests.swift
+++ b/ios/MullvadVPNTests/IPOverrideRepositoryTests.swift
@@ -67,15 +67,6 @@ final class IPOverrideRepositoryTests: XCTestCase {
XCTAssertTrue(storedOverrides.first?.ipv6Address == .broadcast)
}
- func testFetchOverrideByHostname() throws {
- let hostname = "Host 1"
- let override = try IPOverride(hostname: hostname, ipv4Address: .any, ipv6Address: nil)
- repository.add([override])
-
- let storedOverride = repository.fetchByHostname(hostname)
- XCTAssertTrue(storedOverride?.hostname == hostname)
- }
-
func testDeleteAllOverrides() throws {
let override = try IPOverride(hostname: "Host 1", ipv4Address: .any, ipv6Address: nil)
repository.add([override])
diff --git a/ios/MullvadVPNTests/IPOverrideWrapperTests.swift b/ios/MullvadVPNTests/IPOverrideWrapperTests.swift
new file mode 100644
index 0000000000..bbaba178ab
--- /dev/null
+++ b/ios/MullvadVPNTests/IPOverrideWrapperTests.swift
@@ -0,0 +1,102 @@
+//
+// IPOverrideWrapperTests.swift
+// MullvadVPNTests
+//
+// Created by Jon Petersson on 2024-02-05.
+// Copyright © 2024 Mullvad VPN AB. All rights reserved.
+//
+
+@testable import MullvadREST
+import MullvadSettings
+import Network
+import XCTest
+
+final class IPOverrideWrapperTests: XCTestCase {
+ func testOverrideServerRelayInCache() throws {
+ let relays = [
+ mockServerRelay.override(ipv4AddrIn: .loopback, ipv6AddrIn: .broadcast),
+ mockServerRelay,
+ ]
+
+ let fileCache = MockFileCache(
+ initialState: .exists(CachedRelays(relays: .mock(serverRelays: relays), updatedAt: .distantPast))
+ )
+
+ let override = try IPOverride(hostname: "Host 1", ipv4Address: .loopback, ipv6Address: .broadcast)
+
+ let overrideWrapper = IPOverrideWrapper(
+ relayCache: RelayCache(fileCache: fileCache),
+ ipOverrideRepository: IPOverrideRepositoryStub(overrides: [override])
+ )
+
+ let storedCache = try overrideWrapper.read()
+
+ // Assert that relay was overridden.
+ let host1 = storedCache.relays.wireguard.relays.first
+ XCTAssertEqual(host1?.ipv4AddrIn, .loopback)
+ XCTAssertEqual(host1?.ipv6AddrIn, .broadcast)
+
+ // Assert that relay was NOT overridden.
+ let host2 = storedCache.relays.wireguard.relays.last
+ XCTAssertEqual(host2?.ipv4AddrIn, .any)
+ XCTAssertEqual(host2?.ipv6AddrIn, .any)
+ }
+
+ func testOverrideBridgeRelayInCache() throws {
+ let relays = [
+ mockBridgeRelay.override(ipv4AddrIn: .loopback),
+ mockBridgeRelay,
+ ]
+
+ let fileCache = MockFileCache(
+ initialState: .exists(CachedRelays(relays: .mock(brideRelays: relays), updatedAt: .distantPast))
+ )
+
+ let override = try IPOverride(hostname: "Host 1", ipv4Address: .loopback, ipv6Address: .broadcast)
+
+ let overrideWrapper = IPOverrideWrapper(
+ relayCache: RelayCache(fileCache: fileCache),
+ ipOverrideRepository: IPOverrideRepositoryStub(overrides: [override])
+ )
+
+ let storedCache = try overrideWrapper.read()
+
+ // Assert that relay was overridden.
+ let host1 = storedCache.relays.bridge.relays.first
+ XCTAssertEqual(host1?.ipv4AddrIn, .loopback)
+
+ // Assert that relay was NOT overridden.
+ let host2 = storedCache.relays.bridge.relays.last
+ XCTAssertEqual(host2?.ipv4AddrIn, .any)
+ }
+}
+
+extension IPOverrideWrapperTests {
+ var mockServerRelay: REST.ServerRelay {
+ REST.ServerRelay(
+ hostname: "",
+ active: true,
+ owned: true,
+ location: "",
+ provider: "",
+ weight: 0,
+ ipv4AddrIn: .any,
+ ipv6AddrIn: .any,
+ publicKey: Data(),
+ includeInCountry: true
+ )
+ }
+
+ var mockBridgeRelay: REST.BridgeRelay {
+ REST.BridgeRelay(
+ hostname: "",
+ active: true,
+ owned: true,
+ location: "",
+ provider: "",
+ ipv4AddrIn: .any,
+ weight: 0,
+ includeInCountry: true
+ )
+ }
+}
diff --git a/ios/MullvadVPNTests/RelayCacheTests.swift b/ios/MullvadVPNTests/RelayCacheTests.swift
index 153f4738ee..fffd9e3383 100644
--- a/ios/MullvadVPNTests/RelayCacheTests.swift
+++ b/ios/MullvadVPNTests/RelayCacheTests.swift
@@ -10,7 +10,7 @@
import XCTest
final class RelayCacheTests: XCTestCase {
- func testCanReadCache() throws {
+ func testReadCache() throws {
let fileCache = MockFileCache(
initialState: .exists(CachedRelays(relays: .mock(), updatedAt: .distantPast))
)
@@ -20,7 +20,7 @@ final class RelayCacheTests: XCTestCase {
XCTAssertEqual(fileCache.getState(), .exists(relays))
}
- func testCanWriteCache() throws {
+ func testWriteCache() throws {
let fileCache = MockFileCache(
initialState: .exists(CachedRelays(relays: .mock(), updatedAt: .distantPast))
)
@@ -31,7 +31,7 @@ final class RelayCacheTests: XCTestCase {
XCTAssertEqual(fileCache.getState(), .exists(newCachedRelays))
}
- func testCanReadPrebundledRelaysWhenNoCacheIsStored() throws {
+ func testReadPrebundledRelaysWhenNoCacheIsStored() throws {
let fileCache = MockFileCache<CachedRelays>(initialState: .fileNotFound)
let cache = RelayCache(fileCache: fileCache)
@@ -39,17 +39,20 @@ final class RelayCacheTests: XCTestCase {
}
}
-private extension REST.ServerRelaysResponse {
- static func mock() -> Self {
+extension REST.ServerRelaysResponse {
+ static func mock(
+ serverRelays: [REST.ServerRelay] = [],
+ brideRelays: [REST.BridgeRelay] = []
+ ) -> Self {
REST.ServerRelaysResponse(
locations: [:],
wireguard: REST.ServerWireguardTunnels(
ipv4Gateway: .loopback,
ipv6Gateway: .loopback,
portRanges: [],
- relays: []
+ relays: serverRelays
),
- bridge: REST.ServerBridges(shadowsocks: [], relays: [])
+ bridge: REST.ServerBridges(shadowsocks: [], relays: brideRelays)
)
}
}
diff --git a/ios/MullvadVPNTests/RelayCacheTracker+Stubs.swift b/ios/MullvadVPNTests/RelayCacheTracker+Stubs.swift
index 4adcccecec..b65aa0d4b7 100644
--- a/ios/MullvadVPNTests/RelayCacheTracker+Stubs.swift
+++ b/ios/MullvadVPNTests/RelayCacheTracker+Stubs.swift
@@ -30,4 +30,6 @@ struct RelayCacheTrackerStub: RelayCacheTrackerProtocol {
func addObserver(_ observer: RelayCacheTrackerObserver) {}
func removeObserver(_ observer: RelayCacheTrackerObserver) {}
+
+ func refreshCachedRelays() throws {}
}
diff --git a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift
index 3cbf52c675..d2aba984ca 100644
--- a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift
+++ b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift
@@ -34,30 +34,19 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
let addressCache = REST.AddressCache(canWriteToCache: false, cacheDirectory: containerURL)
addressCache.loadFromFile()
- let relayCache = RelayCache(cacheDirectory: containerURL)
-
- let urlSession = REST.makeURLSession()
- let urlSessionTransport = URLSessionTransport(urlSession: urlSession)
- let shadowsocksCache = ShadowsocksConfigurationCache(cacheDirectory: containerURL)
-
- // This init cannot fail as long as the security group identifier is valid
- let transportStrategy = TransportStrategy(
- datasource: AccessMethodRepository(),
- shadowsocksLoader: ShadowsocksLoader(
- shadowsocksCache: shadowsocksCache,
- relayCache: relayCache,
- constraintsUpdater: constraintsUpdater
- )
- )
-
- let transportProvider = TransportProvider(
- urlSessionTransport: urlSessionTransport,
- addressCache: addressCache,
- transportStrategy: transportStrategy
+ let ipOverrideWrapper = IPOverrideWrapper(
+ relayCache: RelayCache(cacheDirectory: containerURL),
+ ipOverrideRepository: IPOverrideRepository()
)
super.init()
+ let transportProvider = setUpTransportProvider(
+ appContainerURL: containerURL,
+ ipOverrideWrapper: ipOverrideWrapper,
+ addressCache: addressCache
+ )
+
let adapter = WgAdapter(packetTunnelProvider: self)
let tunnelMonitor = TunnelMonitor(
@@ -82,7 +71,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
tunnelMonitor: tunnelMonitor,
defaultPathObserver: PacketTunnelPathObserver(packetTunnelProvider: self, eventQueue: internalQueue),
blockedStateErrorMapper: BlockedStateErrorMapper(),
- relaySelector: RelaySelectorWrapper(relayCache: relayCache),
+ relaySelector: RelaySelectorWrapper(relayCache: ipOverrideWrapper),
settingsReader: SettingsReader(),
protocolObfuscator: ProtocolObfuscator<UDPOverTCPObfuscator>()
)
@@ -141,6 +130,31 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
override func wake() {
actor.onWake()
}
+
+ private func setUpTransportProvider(
+ appContainerURL: URL,
+ ipOverrideWrapper: IPOverrideWrapper,
+ addressCache: REST.AddressCache
+ ) -> TransportProvider {
+ let urlSession = REST.makeURLSession()
+ let urlSessionTransport = URLSessionTransport(urlSession: urlSession)
+ let shadowsocksCache = ShadowsocksConfigurationCache(cacheDirectory: appContainerURL)
+
+ let transportStrategy = TransportStrategy(
+ datasource: AccessMethodRepository(),
+ shadowsocksLoader: ShadowsocksLoader(
+ shadowsocksCache: shadowsocksCache,
+ relayCache: ipOverrideWrapper,
+ constraintsUpdater: constraintsUpdater
+ )
+ )
+
+ return TransportProvider(
+ urlSessionTransport: urlSessionTransport,
+ addressCache: addressCache,
+ transportStrategy: transportStrategy
+ )
+ }
}
extension PacketTunnelProvider {
diff --git a/ios/PacketTunnel/PacketTunnelProvider/RelaySelectorWrapper.swift b/ios/PacketTunnel/PacketTunnelProvider/RelaySelectorWrapper.swift
index 5127e0a55b..73f6fa2674 100644
--- a/ios/PacketTunnel/PacketTunnelProvider/RelaySelectorWrapper.swift
+++ b/ios/PacketTunnel/PacketTunnelProvider/RelaySelectorWrapper.swift
@@ -12,7 +12,7 @@ import MullvadTypes
import PacketTunnelCore
struct RelaySelectorWrapper: RelaySelectorProtocol {
- let relayCache: RelayCache
+ let relayCache: RelayCacheProtocol
func selectRelay(
with constraints: RelayConstraints,