Skip to content

Commit

Permalink
Add data-driven TunnelSettingsUpdate type and method for atomically a…
Browse files Browse the repository at this point in the history
…pplying them, modify Preferences{Interactor,ViewController} to use these.
  • Loading branch information
acb-mv authored and buggmagnet committed Feb 14, 2024
1 parent 2105a49 commit e329328
Show file tree
Hide file tree
Showing 9 changed files with 155 additions and 57 deletions.
41 changes: 41 additions & 0 deletions ios/MullvadSettings/TunnelSettingsUpdate.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//
// TunnelSettingsUpdate.swift
// MullvadSettings
//
// Created by Andrew Bulhak on 2024-02-13.
// Copyright © 2024 Mullvad VPN AB. All rights reserved.
//

import Foundation
import MullvadTypes

public enum TunnelSettingsUpdate {
case dnsSettings(DNSSettings)
case obfuscation(WireGuardObfuscationSettings)
case relayConstraints(RelayConstraints)
case quantumResistance(TunnelQuantumResistance)
}

extension TunnelSettingsUpdate {
public func apply(to settings: inout LatestTunnelSettings) {
switch self {
case let .dnsSettings(newDNSSettings):
settings.dnsSettings = newDNSSettings
case let .obfuscation(newObfuscationSettings):
settings.wireGuardObfuscation = newObfuscationSettings
case let .relayConstraints(newRelayConstraints):
settings.relayConstraints = newRelayConstraints
case let .quantumResistance(newQuantumResistance):
settings.tunnelQuantumResistance = newQuantumResistance
}
}

public var subjectName: String {
switch self {
case .dnsSettings: "DNS settings"
case .obfuscation: "obfuscation settings"
case .relayConstraints: "relay constraints"
case .quantumResistance: "quantum resistance"
}
}
}
16 changes: 16 additions & 0 deletions ios/MullvadVPN.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
06799AFC28F98EE300ACD94E /* AddressCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06AC114128F8413A0037AF9A /* AddressCache.swift */; };
0697D6E728F01513007A9E99 /* TransportMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0697D6E628F01513007A9E99 /* TransportMonitor.swift */; };
06AC116228F94C450037AF9A /* ApplicationConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58BFA5CB22A7CE1F00A6173D /* ApplicationConfiguration.swift */; };
449872E12B7BBC5400094DDC /* TunnelSettingsUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 449872E02B7BBC5400094DDC /* TunnelSettingsUpdate.swift */; };
449872E42B7CB96300094DDC /* TunnelSettingsUpdateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 449872E32B7CB96300094DDC /* TunnelSettingsUpdateTests.swift */; };
44DD7D242B6CFFD70005F67F /* StartTunnelOperationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44DD7D232B6CFFD70005F67F /* StartTunnelOperationTests.swift */; };
44DD7D272B6D18FB0005F67F /* MockTunnelInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44DD7D262B6D18FB0005F67F /* MockTunnelInteractor.swift */; };
44DD7D292B7113CA0005F67F /* MockTunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44DD7D282B7113CA0005F67F /* MockTunnel.swift */; };
Expand Down Expand Up @@ -1250,6 +1252,8 @@
06FAE67A28F83CA50033DD93 /* RESTDevicesProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RESTDevicesProxy.swift; sourceTree = "<group>"; };
06FAE67B28F83CA50033DD93 /* REST.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = REST.swift; sourceTree = "<group>"; };
06FAE67D28F83CA50033DD93 /* RESTTransport.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RESTTransport.swift; sourceTree = "<group>"; };
449872E02B7BBC5400094DDC /* TunnelSettingsUpdate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelSettingsUpdate.swift; sourceTree = "<group>"; };
449872E32B7CB96300094DDC /* TunnelSettingsUpdateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelSettingsUpdateTests.swift; sourceTree = "<group>"; };
44DD7D232B6CFFD70005F67F /* StartTunnelOperationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartTunnelOperationTests.swift; sourceTree = "<group>"; };
44DD7D262B6D18FB0005F67F /* MockTunnelInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockTunnelInteractor.swift; sourceTree = "<group>"; };
44DD7D282B7113CA0005F67F /* MockTunnel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockTunnel.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2119,6 +2123,14 @@
path = MullvadREST;
sourceTree = "<group>";
};
449872E22B7CB91B00094DDC /* MullvadSettings */ = {
isa = PBXGroup;
children = (
449872E32B7CB96300094DDC /* TunnelSettingsUpdateTests.swift */,
);
path = MullvadSettings;
sourceTree = "<group>";
};
44DD7D252B6D18E90005F67F /* Mocks */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -2774,6 +2786,7 @@
isa = PBXGroup;
children = (
44DD7D252B6D18E90005F67F /* Mocks */,
449872E22B7CB91B00094DDC /* MullvadSettings */,
A900E9BF2ACC661900C95F67 /* AccessTokenManager+Stubs.swift */,
7A6F2FA42AFA3CB2006D0856 /* AccountExpiryTests.swift */,
A900E9B72ACC5C2B00C95F67 /* AccountsProxy+Stubs.swift */,
Expand Down Expand Up @@ -2873,6 +2886,7 @@
580F8B8228197881002E0998 /* TunnelSettingsV2.swift */,
A988DF282ADE880300D807EF /* TunnelSettingsV3.swift */,
A93181A02B727ED700E341D2 /* TunnelSettingsV4.swift */,
449872E02B7BBC5400094DDC /* TunnelSettingsUpdate.swift */,
A988DF252ADE86ED00D807EF /* WireGuardObfuscationSettings.swift */,
);
path = MullvadSettings;
Expand Down Expand Up @@ -4685,6 +4699,7 @@
A9A5FA262ACB05160083449F /* UpdateDeviceDataOperation.swift in Sources */,
A9A5FA272ACB05160083449F /* VPNConnectionProtocol.swift in Sources */,
A9A5FA282ACB05160083449F /* WgKeyRotation.swift in Sources */,
449872E42B7CB96300094DDC /* TunnelSettingsUpdateTests.swift in Sources */,
A9A5FA292ACB05160083449F /* AddressCacheTests.swift in Sources */,
A9B6AC182ADE8F4300F7802A /* MigrationManagerTests.swift in Sources */,
A9A5FA2A2ACB05160083449F /* CoordinatesTests.swift in Sources */,
Expand Down Expand Up @@ -4731,6 +4746,7 @@
58B2FDDE2AA71D5C003EB5C6 /* Migration.swift in Sources */,
F0D7FF8F2B31DF5900E0FDE5 /* AccessMethodRepository.swift in Sources */,
58B2FDE12AA71D5C003EB5C6 /* TunnelSettingsV1.swift in Sources */,
449872E12B7BBC5400094DDC /* TunnelSettingsUpdate.swift in Sources */,
58B2FDE72AA71D5C003EB5C6 /* SettingsStore.swift in Sources */,
44DD7D2D2B74E44A0005F67F /* QuantumResistanceSettings.swift in Sources */,
F08827872B318C840020A383 /* ShadowsocksCipherOptions.swift in Sources */,
Expand Down
2 changes: 1 addition & 1 deletion ios/MullvadVPN/Coordinators/RelayFilterCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class RelayFilterCoordinator: Coordinator, Presentable, RelayCacheTrackerObserve
var relayConstraints = tunnelManager.settings.relayConstraints
relayConstraints.filter = .only(filter)

tunnelManager.setRelayConstraints(relayConstraints)
tunnelManager.updateSettings([.relayConstraints(relayConstraints)])

didFinish?(self, filter)
}
Expand Down
4 changes: 2 additions & 2 deletions ios/MullvadVPN/Coordinators/SelectLocationCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class SelectLocationCoordinator: Coordinator, Presentable, Presenting, RelayCach
var relayConstraints = tunnelManager.settings.relayConstraints
relayConstraints.location = .only(relay)

tunnelManager.setRelayConstraints(relayConstraints) {
tunnelManager.updateSettings([.relayConstraints(relayConstraints)]) {
self.tunnelManager.startTunnel()
}

Expand All @@ -80,7 +80,7 @@ class SelectLocationCoordinator: Coordinator, Presentable, Presenting, RelayCach
var relayConstraints = tunnelManager.settings.relayConstraints
relayConstraints.filter = .only(filter)

tunnelManager.setRelayConstraints(relayConstraints)
tunnelManager.updateSettings([.relayConstraints(relayConstraints)])
}

selectLocationViewController.didFinish = { [weak self] in
Expand Down
42 changes: 6 additions & 36 deletions ios/MullvadVPN/TunnelManager/TunnelManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -503,49 +503,19 @@ final class TunnelManager: StorePaymentObserver {
return operation
}

func setRelayConstraints(
_ newConstraints: RelayConstraints,
completionHandler: (() -> Void)? = nil
) {
scheduleSettingsUpdate(
taskName: "Set relay constraints",
modificationBlock: { settings in
settings.relayConstraints = newConstraints
},
completionHandler: completionHandler
)
}

func setDNSSettings(_ newDNSSettings: DNSSettings, completionHandler: (() -> Void)? = nil) {
func updateSettings(_ updates: [TunnelSettingsUpdate], completionHandler: (() -> Void)? = nil) {
let taskName = "Set " + updates.map(\.subjectName).joined(separator: ", ")
scheduleSettingsUpdate(
taskName: "Set DNS settings",
taskName: taskName,
modificationBlock: { settings in
settings.dnsSettings = newDNSSettings
for update in updates {
update.apply(to: &settings)
}
},
completionHandler: completionHandler
)
}

func setObfuscationSettings(_ newSettings: WireGuardObfuscationSettings) {
scheduleSettingsUpdate(
taskName: "Set obfuscation settings",
modificationBlock: { settings in
settings.wireGuardObfuscation = newSettings
},
completionHandler: nil
)
}

func setQuantumResistance(_ newSetting: TunnelQuantumResistance) {
scheduleSettingsUpdate(
taskName: "Set quantum resistance",
modificationBlock: { settings in
settings.tunnelQuantumResistance = newSetting
},
completionHandler: nil
)
}

func refreshRelayCacheTracker() throws {
try relayCacheTracker.refreshCachedRelays()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ class CustomDNSViewController: UITableViewController, PreferencesDataSourceDeleg
// MARK: - PreferencesDataSourceDelegate

func didChangeViewModel(_ viewModel: PreferencesViewModel) {
interactor.setDNSSettings(viewModel.asDNSSettings())
interactor.updateSettings([.dnsSettings(viewModel.asDNSSettings())])
}

func showInfo(for item: PreferencesInfoButtonItem) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,8 @@ final class PreferencesInteractor {
})
}

func setDNSSettings(_ newDNSSettings: DNSSettings, completion: (() -> Void)? = nil) {
tunnelManager.setDNSSettings(newDNSSettings, completionHandler: completion)
}

func setObfuscationSettings(_ newSettings: WireGuardObfuscationSettings) {
tunnelManager.setObfuscationSettings(newSettings)
func updateSettings(_ changes: [TunnelSettingsUpdate], completion: (() -> Void)? = nil) {
tunnelManager.updateSettings(changes, completionHandler: completion)
}

func setPort(_ port: UInt16?, completion: (() -> Void)? = nil) {
Expand All @@ -53,11 +49,7 @@ final class PreferencesInteractor {
relayConstraints.port = .any
}

tunnelManager.setRelayConstraints(relayConstraints, completionHandler: completion)
}

func setQuantumResistance(_ newSetting: TunnelQuantumResistance) {
tunnelManager.setQuantumResistance(newSetting)
tunnelManager.updateSettings([.relayConstraints(relayConstraints)], completionHandler: completion)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,18 @@ class PreferencesViewController: UITableViewController, PreferencesDataSourceDel
// MARK: - PreferencesDataSourceDelegate

func didChangeViewModel(_ viewModel: PreferencesViewModel) {
interactor.setObfuscationSettings(WireGuardObfuscationSettings(
state: viewModel.obfuscationState,
port: viewModel.obfuscationPort
))
interactor.setQuantumResistance(viewModel.quantumResistance)
interactor.updateSettings(
[
.obfuscation(WireGuardObfuscationSettings(
state: viewModel.obfuscationState,
port: viewModel.obfuscationPort
)),
.quantumResistance(viewModel.quantumResistance),
]
)
}

// swiftlint:disable:next function_body_length
func showInfo(for item: PreferencesInfoButtonItem) {
var message = ""

Expand Down Expand Up @@ -159,7 +164,8 @@ class PreferencesViewController: UITableViewController, PreferencesDataSourceDel
tableName: "QuantumResistance",
value: """
This feature makes the WireGuard tunnel resistant to potential attacks from quantum computers.
It does this by performing an extra key exchange using a quantum safe algorithm and mixing the result into WireGuard’s regular encryption.
It does this by performing an extra key exchange using a quantum safe algorithm and mixing \
the result into WireGuard’s regular encryption.
This extra step uses approximately 500 kiB of traffic every time a new tunnel is established.
""",
comment: ""
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
//
// TunnelSettingsUpdateTests.swift
// MullvadVPNTests
//
// Created by Andrew Bulhak on 2024-02-14.
// Copyright © 2024 Mullvad VPN AB. All rights reserved.
//

@testable import MullvadSettings
import MullvadTypes
import Network
import XCTest

final class TunnelSettingsUpdateTests: XCTestCase {
func testApplyDNSSettings() {
// Given:
var settings = LatestTunnelSettings()

// When:
var dnsSettings = DNSSettings()
dnsSettings.blockingOptions = [.blockAdvertising, .blockTracking]
dnsSettings.enableCustomDNS = true
dnsSettings.customDNSDomains = [.ipv4(IPv4Address("1.2.3.4")!)]
let update = TunnelSettingsUpdate.dnsSettings(dnsSettings)
update.apply(to: &settings)

// Then:
XCTAssertEqual(settings.dnsSettings.blockingOptions, [.blockAdvertising, .blockTracking])
XCTAssertEqual(settings.dnsSettings.enableCustomDNS, true)
XCTAssertEqual(settings.dnsSettings.customDNSDomains, [.ipv4(IPv4Address("1.2.3.4")!)])
}

func testApplyObfuscation() {
// Given:
var settings = LatestTunnelSettings()

// When:
let update = TunnelSettingsUpdate.obfuscation(.init(state: .on, port: .port5001))
update.apply(to: &settings)

// Then:
XCTAssertEqual(settings.wireGuardObfuscation, WireGuardObfuscationSettings(state: .on, port: .port5001))
}

func testApplyRelayConstraints() {
// Given:
var settings = LatestTunnelSettings()

// When:
let relayConstraints = RelayConstraints(
location: .only(.country("zz")),
port: .only(9999),
filter: .only(.init(ownership: .rented, providers: .only(["foo", "bar"])))
)
let update = TunnelSettingsUpdate.relayConstraints(relayConstraints)
update.apply(to: &settings)

// Then:
XCTAssertEqual(settings.relayConstraints, relayConstraints)
}

func testApplyQuantumResistance() {
// Given:
var settings = LatestTunnelSettings()

// When:
let update = TunnelSettingsUpdate.quantumResistance(.on)
update.apply(to: &settings)

// Then:
XCTAssertEqual(settings.tunnelQuantumResistance, .on)
}
}

0 comments on commit e329328

Please sign in to comment.