Skip to content

Commit

Permalink
Merge branch 'add-multihop-toggle-to-settings-view-ios-687'
Browse files Browse the repository at this point in the history
  • Loading branch information
buggmagnet committed Jun 13, 2024
2 parents 889af23 + 1d03677 commit 57d678c
Show file tree
Hide file tree
Showing 24 changed files with 362 additions and 145 deletions.
29 changes: 26 additions & 3 deletions ios/MullvadREST/Transport/Shadowsocks/ShadowsocksLoader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,48 @@ public class ShadowsocksLoader: ShadowsocksLoaderProtocol {
let cache: ShadowsocksConfigurationCacheProtocol
let relaySelector: ShadowsocksRelaySelectorProtocol
let constraintsUpdater: RelayConstraintsUpdater
let multihopUpdater: MultihopUpdater
private var multihopState: MultihopState = .off
private var observer: MultihopObserverBlock!

deinit {
self.multihopUpdater.removeObserver(observer)
}

private var relayConstraints = RelayConstraints()

public init(
cache: ShadowsocksConfigurationCacheProtocol,
relaySelector: ShadowsocksRelaySelectorProtocol,
constraintsUpdater: RelayConstraintsUpdater
constraintsUpdater: RelayConstraintsUpdater,
multihopUpdater: MultihopUpdater,
multihopState: MultihopState = .off
) {
self.cache = cache
self.relaySelector = relaySelector
self.constraintsUpdater = constraintsUpdater
self.multihopUpdater = multihopUpdater
self.multihopState = multihopState
self.addObservers()
}

// The constraints gets updated a lot when observing the tunnel, avoid clearing the cache if the constraints haven't changed.
private func addObservers() {
// The constraints gets updated a lot when observing the tunnel, clear the cache if the constraints have changed.
constraintsUpdater.onNewConstraints = { [weak self] newConstraints in
if self?.relayConstraints != newConstraints {
self?.relayConstraints = newConstraints
try? self?.clear()
}
}

// The multihop state gets updated a lot when observing the tunnel, clear the cache if the multihop settings have changed.
self.observer = MultihopObserverBlock(didUpdateMultihop: { [weak self] _, newMultihopState in
if self?.multihopState != newMultihopState {
self?.multihopState = newMultihopState
try? self?.clear()
}
})
multihopUpdater.addObserver(self.observer)
}

public func clear() throws {
Expand All @@ -60,7 +83,7 @@ public class ShadowsocksLoader: ShadowsocksLoaderProtocol {
/// Returns a randomly selected shadowsocks configuration.
private func create() throws -> ShadowsocksConfiguration {
let bridgeConfiguration = try relaySelector.getBridges()
let closestRelay = try relaySelector.selectRelay(with: relayConstraints)
let closestRelay = try relaySelector.selectRelay(with: relayConstraints, multihopState: multihopState)

guard let bridgeAddress = closestRelay?.ipv4AddrIn,
let bridgeConfiguration else { throw POSIXError(.ENOENT) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,42 +12,25 @@ import MullvadTypes

public protocol ShadowsocksRelaySelectorProtocol {
func selectRelay(
with constraints: RelayConstraints
with constraints: RelayConstraints,
multihopState: MultihopState
) throws -> REST.BridgeRelay?

func getBridges() throws -> REST.ServerShadowsocks?
}

final public class ShadowsocksRelaySelector: ShadowsocksRelaySelectorProtocol {
let relayCache: RelayCacheProtocol
let multihopUpdater: MultihopUpdater
private var multihopState: MultihopState
private var observer: MultihopObserverBlock!

deinit {
self.multihopUpdater.removeObserver(observer)
}

public init(
relayCache: RelayCacheProtocol,
multihopUpdater: MultihopUpdater,
multihopState: MultihopState
relayCache: RelayCacheProtocol
) {
self.relayCache = relayCache
self.multihopUpdater = multihopUpdater
self.multihopState = multihopState
self.addObserver()
}

private func addObserver() {
self.observer = MultihopObserverBlock(didUpdateMultihop: { [weak self] _, multihopState in
self?.multihopState = multihopState
})
multihopUpdater.addObserver(observer)
}

public func selectRelay(
with constraints: RelayConstraints
with constraints: RelayConstraints,
multihopState: MultihopState
) throws -> REST.BridgeRelay? {
let cachedRelays = try relayCache.read().relays

Expand Down
4 changes: 4 additions & 0 deletions ios/MullvadSettings/MultihopSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,8 @@ public class MultihopUpdater {
public enum MultihopState: Codable {
case on
case off

public var isEnabled: Bool {
self == .on
}
}
2 changes: 1 addition & 1 deletion ios/MullvadSettings/TunnelSettingsUpdate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ extension TunnelSettingsUpdate {
case .obfuscation: "obfuscation settings"
case .relayConstraints: "relay constraints"
case .quantumResistance: "quantum resistance"
case .multihop: "Multihop"
case .multihop: "multihop"
}
}
}
34 changes: 23 additions & 11 deletions ios/MullvadVPN.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -875,6 +875,7 @@
F06045E62B231EB700B2D37A /* URLSessionTransport.swift in Sources */ = {isa = PBXBuildFile; fileRef = F06045E52B231EB700B2D37A /* URLSessionTransport.swift */; };
F06045EA2B23217E00B2D37A /* ShadowsocksTransport.swift in Sources */ = {isa = PBXBuildFile; fileRef = F06045E92B23217E00B2D37A /* ShadowsocksTransport.swift */; };
F06045EC2B2322A500B2D37A /* Jittered.swift in Sources */ = {isa = PBXBuildFile; fileRef = F06045EB2B2322A500B2D37A /* Jittered.swift */; };
F062B94D2C16E09700B6D47A /* TunnelSettingsManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F062B94C2C16E09700B6D47A /* TunnelSettingsManagerTests.swift */; };
F072D3CF2C07122400906F64 /* MultihopUpdaterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F072D3CE2C07122400906F64 /* MultihopUpdaterTests.swift */; };
F072D3D22C071AD100906F64 /* ShadowsocksLoaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F072D3D12C071AD100906F64 /* ShadowsocksLoaderTests.swift */; };
F07BF2622A26279100042943 /* RedeemVoucherOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F07BF2612A26279100042943 /* RedeemVoucherOperation.swift */; };
Expand Down Expand Up @@ -928,6 +929,8 @@
F0DA87472A9CB9A2006044F1 /* AccountExpiryRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0DA87462A9CB9A2006044F1 /* AccountExpiryRow.swift */; };
F0DA87492A9CBA9F006044F1 /* AccountDeviceRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0DA87482A9CBA9F006044F1 /* AccountDeviceRow.swift */; };
F0DA874B2A9CBACB006044F1 /* AccountNumberRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0DA874A2A9CBACB006044F1 /* AccountNumberRow.swift */; };
F0DAC8AD2C16EFE400F80144 /* TunnelSettingsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F04DD3D72C130DF600E03E28 /* TunnelSettingsManager.swift */; };
F0DAC8AF2C1712C300F80144 /* MultihopPromptAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0DAC8AE2C1712C300F80144 /* MultihopPromptAlert.swift */; };
F0DDE4142B220458006B57A7 /* ShadowSocksProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0DDE40F2B220458006B57A7 /* ShadowSocksProxy.swift */; };
F0DDE4152B220458006B57A7 /* ShadowsocksConfigurationCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0DDE4102B220458006B57A7 /* ShadowsocksConfigurationCache.swift */; };
F0DDE4162B220458006B57A7 /* TransportProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0DDE4112B220458006B57A7 /* TransportProvider.swift */; };
Expand Down Expand Up @@ -2101,6 +2104,7 @@
F02F419C2B9723AF00625A4F /* AddLocationsCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddLocationsCoordinator.swift; sourceTree = "<group>"; };
F03580242A13842C00E5DAFD /* IncreasedHitButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IncreasedHitButton.swift; sourceTree = "<group>"; };
F04413602BA45CD70018A6EE /* CustomListLocationNodeBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomListLocationNodeBuilder.swift; sourceTree = "<group>"; };
F04DD3D72C130DF600E03E28 /* TunnelSettingsManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TunnelSettingsManager.swift; sourceTree = "<group>"; };
F04F95A02B21D24400431E08 /* shadowsocks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = shadowsocks.h; sourceTree = "<group>"; };
F04FBE602A8379EE009278D7 /* AppPreferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppPreferences.swift; sourceTree = "<group>"; };
F050AE4D2B70D7F8003F4EDB /* LocationCellViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationCellViewModel.swift; sourceTree = "<group>"; };
Expand All @@ -2115,6 +2119,7 @@
F06045E52B231EB700B2D37A /* URLSessionTransport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLSessionTransport.swift; sourceTree = "<group>"; };
F06045E92B23217E00B2D37A /* ShadowsocksTransport.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShadowsocksTransport.swift; sourceTree = "<group>"; };
F06045EB2B2322A500B2D37A /* Jittered.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Jittered.swift; sourceTree = "<group>"; };
F062B94C2C16E09700B6D47A /* TunnelSettingsManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelSettingsManagerTests.swift; sourceTree = "<group>"; };
F072D3CE2C07122400906F64 /* MultihopUpdaterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultihopUpdaterTests.swift; sourceTree = "<group>"; };
F072D3D12C071AD100906F64 /* ShadowsocksLoaderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShadowsocksLoaderTests.swift; sourceTree = "<group>"; };
F07BF2572A26112D00042943 /* InputTextFormatterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InputTextFormatterTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2147,6 +2152,7 @@
F0DA87462A9CB9A2006044F1 /* AccountExpiryRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountExpiryRow.swift; sourceTree = "<group>"; };
F0DA87482A9CBA9F006044F1 /* AccountDeviceRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountDeviceRow.swift; sourceTree = "<group>"; };
F0DA874A2A9CBACB006044F1 /* AccountNumberRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountNumberRow.swift; sourceTree = "<group>"; };
F0DAC8AE2C1712C300F80144 /* MultihopPromptAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultihopPromptAlert.swift; sourceTree = "<group>"; };
F0DDE40F2B220458006B57A7 /* ShadowSocksProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShadowSocksProxy.swift; sourceTree = "<group>"; };
F0DDE4102B220458006B57A7 /* ShadowsocksConfigurationCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShadowsocksConfigurationCache.swift; sourceTree = "<group>"; };
F0DDE4112B220458006B57A7 /* TransportProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransportProvider.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -3105,9 +3111,8 @@
children = (
58BDEBA02A9CA14B00F578F2 /* AnyTask.swift */,
58F3F3652AA086A400D3B0A4 /* AutoCancellingTask.swift */,
583832282AC3DF1300EA2071 /* PacketTunnelActorCommand.swift */,
5838322A2AC3EF9600EA2071 /* EventChannel.swift */,
583E60952A9F6D0800DC61EF /* ConfigurationBuilder.swift */,
5838322A2AC3EF9600EA2071 /* EventChannel.swift */,
580D6B892AB31AB400B2D6E0 /* NetworkPath+NetworkReachability.swift */,
58CF95A12AD6F35800B59F5D /* ObservedState.swift */,
587A5E512ADD7569003A70F1 /* ObservedState+Extensions.swift */,
Expand All @@ -3117,18 +3122,20 @@
58FE25F32AA9D730003D1918 /* PacketTunnelActor+Extensions.swift */,
5838321E2AC3160A00EA2071 /* PacketTunnelActor+KeyPolicy.swift */,
583832202AC3174700EA2071 /* PacketTunnelActor+NetworkReachability.swift */,
44DF8AC32BF20BD200869CA4 /* PacketTunnelActor+PostQuantum.swift */,
586C14592AC4735F00245C01 /* PacketTunnelActor+Public.swift */,
583832262AC3193600EA2071 /* PacketTunnelActor+SleepCycle.swift */,
583832282AC3DF1300EA2071 /* PacketTunnelActorCommand.swift */,
7AD0AA192AD69B6E00119E10 /* PacketTunnelActorProtocol.swift */,
44B3C4392BFE2C800079782C /* PacketTunnelActorReducer.swift */,
A97D25AD2B0BB18100946B2D /* ProtocolObfuscator.swift */,
58E7A0312AA0715100C57861 /* Protocols */,
58ED3A132A7C199C0085CE65 /* StartOptions.swift */,
5824030C2A811B0000163DE8 /* State.swift */,
58342C032AAB61FB003BA12D /* State+Extensions.swift */,
586E8DB72AAF4AC4007BF3DA /* Task+Duration.swift */,
58DDA18E2ABC32380039C360 /* Timings.swift */,
44DF8AC32BF20BD200869CA4 /* PacketTunnelActor+PostQuantum.swift */,
44B3C4392BFE2C800079782C /* PacketTunnelActorReducer.swift */,
F04DD3D72C130DF600E03E28 /* TunnelSettingsManager.swift */,
);
path = Actor;
sourceTree = "<group>";
Expand Down Expand Up @@ -3383,14 +3390,15 @@
58C7A4432A863F490060C66F /* PacketTunnelCoreTests */ = {
isa = PBXGroup;
children = (
58EC067D2A8D2B0700BEB973 /* Mocks */,
7A3FD1B42AD4465A0042BEA6 /* AppMessageHandlerTests.swift */,
586C14572AC463BB00245C01 /* EventChannelTests.swift */,
58EC067D2A8D2B0700BEB973 /* Mocks */,
58FE25D32AA729B5003D1918 /* PacketTunnelActorTests.swift */,
58C7A46F2A8649ED0060C66F /* PingerTests.swift */,
A97D25B12B0CB02D00946B2D /* ProtocolObfuscatorTests.swift */,
5838321C2AC1C54600EA2071 /* TaskSleepTests.swift */,
58092E532A8B832E00C3CC72 /* TunnelMonitorTests.swift */,
A97D25B12B0CB02D00946B2D /* ProtocolObfuscatorTests.swift */,
F062B94C2C16E09700B6D47A /* TunnelSettingsManagerTests.swift */,
);
path = PacketTunnelCoreTests;
sourceTree = "<group>";
Expand Down Expand Up @@ -3731,14 +3739,14 @@
58F3F3682AA08E2200D3B0A4 /* PacketTunnelProvider */ = {
isa = PBXGroup;
children = (
58F3F3692AA08E3C00D3B0A4 /* PacketTunnelProvider.swift */,
580D6B912AB360BE00B2D6E0 /* DeviceCheck+BlockedStateReason.swift */,
5864AF7C2A9F4DC9008BC928 /* SettingsReader.swift */,
580D6B8D2AB33BBF00B2D6E0 /* BlockedStateErrorMapper.swift */,
582403812A827E1500163DE8 /* RelaySelectorWrapper.swift */,
580D6B912AB360BE00B2D6E0 /* DeviceCheck+BlockedStateReason.swift */,
58FF23A22AB09BEE003A2AF2 /* DeviceChecker.swift */,
58225D272A84F23B0083D7F1 /* PacketTunnelPathObserver.swift */,
58906DDF2445C7A5002F0673 /* NEProviderStopReason+Debug.swift */,
58225D272A84F23B0083D7F1 /* PacketTunnelPathObserver.swift */,
58F3F3692AA08E3C00D3B0A4 /* PacketTunnelProvider.swift */,
582403812A827E1500163DE8 /* RelaySelectorWrapper.swift */,
5864AF7C2A9F4DC9008BC928 /* SettingsReader.swift */,
);
path = PacketTunnelProvider;
sourceTree = "<group>";
Expand Down Expand Up @@ -3943,6 +3951,7 @@
85557B1D2B5FB8C700795FE1 /* HeaderBar.swift */,
A998DA802BD147AD001D61A2 /* ListCustomListsPage.swift */,
852969342B4E9270007EAD4C /* LoginPage.swift */,
F0DAC8AE2C1712C300F80144 /* MultihopPromptAlert.swift */,
85139B2C2B84B4A700734217 /* OutOfTimePage.swift */,
852969322B4E9232007EAD4C /* Page.swift */,
855D9F5A2B63E56B00D7C64D /* ProblemReportPage.swift */,
Expand Down Expand Up @@ -5517,6 +5526,7 @@
586E8DB82AAF4AC4007BF3DA /* Task+Duration.swift in Sources */,
5838322B2AC3EF9600EA2071 /* EventChannel.swift in Sources */,
586C145A2AC4735F00245C01 /* PacketTunnelActor+Public.swift in Sources */,
F0DAC8AD2C16EFE400F80144 /* TunnelSettingsManager.swift in Sources */,
58342C042AAB61FB003BA12D /* State+Extensions.swift in Sources */,
A95EEE382B722DFC00A8A39B /* PingStats.swift in Sources */,
583832272AC3193600EA2071 /* PacketTunnelActor+SleepCycle.swift in Sources */,
Expand Down Expand Up @@ -5555,6 +5565,7 @@
7A3FD1B52AD4465A0042BEA6 /* AppMessageHandlerTests.swift in Sources */,
58C7A4702A8649ED0060C66F /* PingerTests.swift in Sources */,
A97D25B22B0CB02D00946B2D /* ProtocolObfuscatorTests.swift in Sources */,
F062B94D2C16E09700B6D47A /* TunnelSettingsManagerTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -6109,6 +6120,7 @@
7A45CFC72C071DD400D80B21 /* SnapshotHelper.swift in Sources */,
856952DC2BD2922A008C1F84 /* PartnerAPIClient.swift in Sources */,
85557B162B5ABBBE00795FE1 /* XCUIElementQuery+Extensions.swift in Sources */,
F0DAC8AF2C1712C300F80144 /* MultihopPromptAlert.swift in Sources */,
855D9F5B2B63E56B00D7C64D /* ProblemReportPage.swift in Sources */,
8529693A2B4F0238007EAD4C /* TermsOfServicePage.swift in Sources */,
85A42B882BB44D31007BABF7 /* DeviceManagementPage.swift in Sources */,
Expand Down
14 changes: 8 additions & 6 deletions ios/MullvadVPN/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
let constraintsUpdater = RelayConstraintsUpdater()
let multihopListener = MultihopStateListener()
let multihopUpdater = MultihopUpdater(listener: multihopListener)
let multihopState = (try? SettingsManager.readSettings().tunnelMultihopState) ?? .off

settingsObserver = TunnelBlockObserver(didUpdateTunnelSettings: { _, settings in
settingsObserver = TunnelBlockObserver(didLoadConfiguration: { tunnelManager in
multihopListener.onNewMultihop?(tunnelManager.settings.tunnelMultihopState)
constraintsUpdater.onNewConstraints?(tunnelManager.settings.relayConstraints)
}, didUpdateTunnelSettings: { _, settings in
multihopListener.onNewMultihop?(settings.tunnelMultihopState)
constraintsUpdater.onNewConstraints?(settings.relayConstraints)
})
Expand All @@ -110,15 +112,15 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
let urlSessionTransport = URLSessionTransport(urlSession: REST.makeURLSession())
let shadowsocksCache = ShadowsocksConfigurationCache(cacheDirectory: containerURL)
let shadowsocksRelaySelector = ShadowsocksRelaySelector(
relayCache: ipOverrideWrapper,
multihopUpdater: multihopUpdater,
multihopState: multihopState
relayCache: ipOverrideWrapper
)

shadowsocksLoader = ShadowsocksLoader(
cache: shadowsocksCache,
relaySelector: shadowsocksRelaySelector,
constraintsUpdater: constraintsUpdater
constraintsUpdater: constraintsUpdater,
multihopUpdater: multihopUpdater,
multihopState: tunnelManager.settings.tunnelMultihopState
)

configuredTransportProvider = ProxyConfigurationTransportProvider(
Expand Down
6 changes: 6 additions & 0 deletions ios/MullvadVPN/Classes/AccessbilityIdentifier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ public enum AccessibilityIdentifier: String {
case cityLocationCell
case relayLocationCell
case customListLocationCell
case multihopConfirmAlertBackButton
case multihopConfirmAlertEnableButton

// Labels
case accountPageDeviceNameLabel
Expand Down Expand Up @@ -193,6 +195,10 @@ public enum AccessibilityIdentifier: String {
case quantumResistanceOff
case quantumResistanceOn

// Multihop
case multihopSwitch
case multihopPromptAlert

// Error
case unknown
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ final class CustomDNSDataSource: UITableViewDiffableDataSource<
private let cellFactory: CustomDNSCellFactory
private weak var tableView: UITableView?

weak var delegate: VPNSettingsDataSourceDelegate?
weak var delegate: DNSSettingsDataSourceDelegate?

init(tableView: UITableView) {
self.tableView = tableView
Expand Down
Loading

0 comments on commit 57d678c

Please sign in to comment.