diff options
Diffstat (limited to 'ios')
12 files changed, 183 insertions, 26 deletions
diff --git a/ios/MullvadREST/Relay/ObfuscationMethodSelector.swift b/ios/MullvadREST/Relay/ObfuscationMethodSelector.swift index 069365b64e..dcc7cf05da 100644 --- a/ios/MullvadREST/Relay/ObfuscationMethodSelector.swift +++ b/ios/MullvadREST/Relay/ObfuscationMethodSelector.swift @@ -15,6 +15,7 @@ public struct ObfuscationMethodSelector { connectionAttemptCount: UInt, tunnelSettings: LatestTunnelSettings ) -> WireGuardObfuscationState { + // TODO: Revisit this when QUIC obfuscation is added if tunnelSettings.wireGuardObfuscation.state == .automatic { if connectionAttemptCount.isOrdered(nth: 3, forEverySetOf: 4) { .shadowsocks diff --git a/ios/MullvadREST/Relay/ObfuscatorPortSelector.swift b/ios/MullvadREST/Relay/ObfuscatorPortSelector.swift index 988f6d00e3..0b1939927b 100644 --- a/ios/MullvadREST/Relay/ObfuscatorPortSelector.swift +++ b/ios/MullvadREST/Relay/ObfuscatorPortSelector.swift @@ -55,6 +55,10 @@ struct ObfuscatorPortSelector { tunnelSettings: tunnelSettings, shadowsocksPortRanges: relays.wireguard.shadowsocksPortRanges ) + #if DEBUG + case .quic: + port = .only(443) + #endif default: break } diff --git a/ios/MullvadSettings/WireGuardObfuscationSettings.swift b/ios/MullvadSettings/WireGuardObfuscationSettings.swift index 9d7dc1103b..80a4a6d425 100644 --- a/ios/MullvadSettings/WireGuardObfuscationSettings.swift +++ b/ios/MullvadSettings/WireGuardObfuscationSettings.swift @@ -18,6 +18,9 @@ public enum WireGuardObfuscationState: Codable, Sendable { case automatic case udpOverTcp case shadowsocks + #if DEBUG + case quic + #endif case off public init(from decoder: Decoder) throws { @@ -42,14 +45,24 @@ public enum WireGuardObfuscationState: Codable, Sendable { self = .udpOverTcp case .shadowsocks: self = .shadowsocks + #if DEBUG + case .quic: + self = .quic + #endif case .off: self = .off } } + #if DEBUG + public var isEnabled: Bool { + [.udpOverTcp, .shadowsocks, .quic].contains(self) + } + #else public var isEnabled: Bool { [.udpOverTcp, .shadowsocks].contains(self) } + #endif } public enum WireGuardObfuscationUdpOverTcpPort: Codable, Equatable, CustomStringConvertible, Sendable { diff --git a/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift b/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift index 2b82be84cf..aeae92875e 100644 --- a/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift +++ b/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift @@ -194,6 +194,9 @@ public enum AccessibilityIdentifier: Equatable { case wireGuardObfuscationOff case wireGuardObfuscationUdpOverTcp case wireGuardObfuscationShadowsocks + #if DEBUG + case wireGuardObfuscationQuic + #endif case wireGuardObfuscationUdpOverTcpPort case wireGuardObfuscationShadowsocksPort case wireGuardPort(UInt16?) diff --git a/ios/MullvadVPN/View controllers/SelectLocation/LocationViewControllerWrapper.swift b/ios/MullvadVPN/View controllers/SelectLocation/LocationViewControllerWrapper.swift index 974821fe25..fb77076abd 100644 --- a/ios/MullvadVPN/View controllers/SelectLocation/LocationViewControllerWrapper.swift +++ b/ios/MullvadVPN/View controllers/SelectLocation/LocationViewControllerWrapper.swift @@ -324,7 +324,7 @@ private extension WireGuardObfuscationState { switch self { case .shadowsocks: true - case .on, .off, .automatic, .udpOverTcp: + case .on, .off, .automatic, .udpOverTcp, .quic: false } } diff --git a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsCellFactory.swift b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsCellFactory.swift index f86242b756..eca4097f31 100644 --- a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsCellFactory.swift +++ b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsCellFactory.swift @@ -224,6 +224,21 @@ final class VPNSettingsCellFactory: @preconcurrency CellFactoryProtocol { self?.delegate?.showDetails(for: .wireguardOverShadowsocks) } + #if DEBUG + case .wireGuardObfuscationQuic: + guard let cell = cell as? SelectableSettingsCell else { return } + + cell.titleLabel.text = NSLocalizedString( + "WIREGUARD_OBFUSCATION_QUIC_LABEL", + tableName: "VPNSettings", + value: "QUIC", + comment: "" + ) + + cell.setAccessibilityIdentifier(item.accessibilityIdentifier) + cell.detailTitleLabel.setAccessibilityIdentifier(.wireGuardObfuscationQuic) + cell.applySubCellStyling() + #endif case .wireGuardObfuscationOff: guard let cell = cell as? SelectableSettingsCell else { return } diff --git a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSource.swift b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSource.swift index 6126feaa4e..75f5e974a7 100644 --- a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSource.swift +++ b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSource.swift @@ -83,6 +83,9 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource< case wireGuardObfuscationAutomatic case wireGuardObfuscationUdpOverTcp case wireGuardObfuscationShadowsocks + #if DEBUG + case wireGuardObfuscationQuic + #endif case wireGuardObfuscationOff case wireGuardObfuscationPort(_ port: WireGuardObfuscationUdpOverTcpPort) case quantumResistanceAutomatic @@ -96,15 +99,26 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource< return [.wireGuardPort(nil)] + defaultPorts + [.wireGuardCustomPort] } + #if DEBUG static var wireGuardObfuscation: [Item] { [ .wireGuardObfuscationAutomatic, .wireGuardObfuscationShadowsocks, .wireGuardObfuscationUdpOverTcp, + .wireGuardObfuscationQuic, .wireGuardObfuscationOff, ] } - + #else + static var wireGuardObfuscation: [Item] { + [ + .wireGuardObfuscationAutomatic, + .wireGuardObfuscationShadowsocks, + .wireGuardObfuscationUdpOverTcp, + .wireGuardObfuscationOff, + ] + } + #endif static var wireGuardObfuscationPort: [Item] { [ .wireGuardObfuscationPort(.automatic), @@ -120,58 +134,67 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource< var accessibilityIdentifier: AccessibilityIdentifier { switch self { case .includeAllNetworks: - return .includeAllNetworks + .includeAllNetworks case .localNetworkSharing: - return .localNetworkSharing + .localNetworkSharing case .dnsSettings: - return .dnsSettings + .dnsSettings case .ipOverrides: - return .ipOverrides + .ipOverrides case let .wireGuardPort(port): - return .wireGuardPort(port) + .wireGuardPort(port) case .wireGuardCustomPort: - return .wireGuardCustomPort + .wireGuardCustomPort case .wireGuardObfuscationAutomatic: - return .wireGuardObfuscationAutomatic + .wireGuardObfuscationAutomatic case .wireGuardObfuscationUdpOverTcp: - return .wireGuardObfuscationUdpOverTcp + .wireGuardObfuscationUdpOverTcp case .wireGuardObfuscationShadowsocks: - return .wireGuardObfuscationShadowsocks + .wireGuardObfuscationShadowsocks + #if DEBUG + case .wireGuardObfuscationQuic: + .wireGuardObfuscationQuic + #endif case .wireGuardObfuscationOff: - return .wireGuardObfuscationOff + .wireGuardObfuscationOff case .wireGuardObfuscationPort: - return .wireGuardObfuscationPort + .wireGuardObfuscationPort case .quantumResistanceAutomatic: - return .quantumResistanceAutomatic + .quantumResistanceAutomatic case .quantumResistanceOn: - return .quantumResistanceOn + .quantumResistanceOn case .quantumResistanceOff: - return .quantumResistanceOff + .quantumResistanceOff } } var reuseIdentifier: CellReuseIdentifiers { switch self { case .includeAllNetworks: - return .includeAllNetworks + .includeAllNetworks case .localNetworkSharing: - return .localNetworkSharing + .localNetworkSharing case .dnsSettings: - return .dnsSettings + .dnsSettings case .ipOverrides: - return .ipOverrides + .ipOverrides case .wireGuardPort: - return .wireGuardPort + .wireGuardPort case .wireGuardCustomPort: - return .wireGuardCustomPort + .wireGuardCustomPort + #if DEBUG + case .wireGuardObfuscationAutomatic, .wireGuardObfuscationOff, .wireGuardObfuscationQuic: + .wireGuardObfuscation + #else case .wireGuardObfuscationAutomatic, .wireGuardObfuscationOff: - return .wireGuardObfuscation + .wireGuardObfuscation + #endif case .wireGuardObfuscationUdpOverTcp, .wireGuardObfuscationShadowsocks: - return .wireGuardObfuscationOption + .wireGuardObfuscationOption case .wireGuardObfuscationPort: - return .wireGuardObfuscationPort + .wireGuardObfuscationPort case .quantumResistanceAutomatic, .quantumResistanceOn, .quantumResistanceOff: - return .quantumResistance + .quantumResistance } } } @@ -205,6 +228,9 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource< case .off: .wireGuardObfuscationOff case .on, .udpOverTcp: .wireGuardObfuscationUdpOverTcp case .shadowsocks: .wireGuardObfuscationShadowsocks + #if DEBUG + case .quic: .wireGuardObfuscationQuic + #endif } let quantumResistanceItem: Item = switch viewModel.quantumResistance { @@ -340,6 +366,11 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource< case .wireGuardObfuscationShadowsocks: selectObfuscationState(.shadowsocks) delegate?.didUpdateTunnelSettings(TunnelSettingsUpdate.obfuscation(obfuscationSettings)) + #if DEBUG + case .wireGuardObfuscationQuic: + selectObfuscationState(.quic) + delegate?.didUpdateTunnelSettings(TunnelSettingsUpdate.obfuscation(obfuscationSettings)) + #endif case .wireGuardObfuscationOff: selectObfuscationState(.off) delegate?.didUpdateTunnelSettings(TunnelSettingsUpdate.obfuscation(obfuscationSettings)) diff --git a/ios/MullvadVPNTests/MullvadSettings/TunnelSettingsUpdateTests.swift b/ios/MullvadVPNTests/MullvadSettings/TunnelSettingsUpdateTests.swift index 246da1b07b..b6afffd79f 100644 --- a/ios/MullvadVPNTests/MullvadSettings/TunnelSettingsUpdateTests.swift +++ b/ios/MullvadVPNTests/MullvadSettings/TunnelSettingsUpdateTests.swift @@ -48,6 +48,38 @@ final class TunnelSettingsUpdateTests: XCTestCase { )) } + func testApplyShadowsocksObfuscation() { + // Given: + var settings = LatestTunnelSettings() + + // When: + let update = TunnelSettingsUpdate.obfuscation(WireGuardObfuscationSettings( + state: .shadowsocks + )) + update.apply(to: &settings) + + // Then: + XCTAssertEqual(settings.wireGuardObfuscation, WireGuardObfuscationSettings( + state: .shadowsocks + )) + } + + func testApplyQuicObfuscation() { + // Given: + var settings = LatestTunnelSettings() + + // When: + let update = TunnelSettingsUpdate.obfuscation(WireGuardObfuscationSettings( + state: .quic + )) + update.apply(to: &settings) + + // Then: + XCTAssertEqual(settings.wireGuardObfuscation, WireGuardObfuscationSettings( + state: .quic + )) + } + func testApplyRelayConstraints() { // Given: var settings = LatestTunnelSettings() diff --git a/ios/MullvadVPNUITests/Pages/TunnelControlPage.swift b/ios/MullvadVPNUITests/Pages/TunnelControlPage.swift index 85854163b7..9a1039dfcc 100644 --- a/ios/MullvadVPNUITests/Pages/TunnelControlPage.swift +++ b/ios/MullvadVPNUITests/Pages/TunnelControlPage.swift @@ -133,6 +133,7 @@ class TunnelControlPage: Page { @discardableResult func verifyConnectingOverTCPAfterUDPAttempts() -> Self { let connectionAttempts = waitForConnectionAttempts(4, timeout: 30) + // TODO: Revisit this when QUIC obfuscation is added // Should do four connection attempts but due to UI bug sometimes only two are displayed, sometimes all four if connectionAttempts.count == 4 { // Expected retries flow for (attemptIndex, attempt) in connectionAttempts.enumerated() { diff --git a/ios/MullvadVPNUITests/Pages/VPNSettingsPage.swift b/ios/MullvadVPNUITests/Pages/VPNSettingsPage.swift index 696dab259b..6ed78d2ce0 100644 --- a/ios/MullvadVPNUITests/Pages/VPNSettingsPage.swift +++ b/ios/MullvadVPNUITests/Pages/VPNSettingsPage.swift @@ -128,6 +128,11 @@ class VPNSettingsPage: Page { return self } + @discardableResult func tapWireGuardObufscationQuicCell() -> Self { + app.cells[AccessibilityIdentifier.wireGuardObfuscationQuic].tap() + return self + } + @discardableResult func tapWireGuardObfuscationOffCell() -> Self { app.cells[AccessibilityIdentifier.wireGuardObfuscationOff].tap() diff --git a/ios/MullvadVPNUITests/RelayTests.swift b/ios/MullvadVPNUITests/RelayTests.swift index 8c6aedeeed..17c0da2d2e 100644 --- a/ios/MullvadVPNUITests/RelayTests.swift +++ b/ios/MullvadVPNUITests/RelayTests.swift @@ -324,6 +324,47 @@ class RelayTests: LoggedInWithTimeUITestCase { .tapDisconnectButton() } + func testWireGuardOverQuicManually() throws { + addTeardownBlock { + HeaderBar(self.app) + .tapSettingsButton() + + SettingsPage(self.app) + .tapVPNSettingsCell() + + VPNSettingsPage(self.app) + .tapWireGuardObfuscationExpandButton() + .tapWireGuardObfuscationOffCell() + } + + HeaderBar(app) + .tapSettingsButton() + + SettingsPage(app) + .tapVPNSettingsCell() + + VPNSettingsPage(app) + .tapWireGuardObfuscationExpandButton() + .tapWireGuardObufscationQuicCell() + .tapBackButton() + + SettingsPage(app) + .tapDoneButton() + + TunnelControlPage(app) + .tapConnectButton() + + allowAddVPNConfigurationsIfAsked() + + TunnelControlPage(app) + .waitForConnectedLabel() + + try Networking.verifyCanAccessInternet() + + TunnelControlPage(app) + .tapDisconnectButton() + } + /// Test automatic switching to TCP is functioning when UDP traffic to relay is blocked. This test first connects to a realy to get the IP address of it, in order to block UDP traffic to this relay. func testWireGuardOverTCPAutomatically() throws { FirewallClient().removeRules() diff --git a/ios/PacketTunnelCore/Actor/ProtocolObfuscator.swift b/ios/PacketTunnelCore/Actor/ProtocolObfuscator.swift index 2a0ed7bf06..51070accf8 100644 --- a/ios/PacketTunnelCore/Actor/ProtocolObfuscator.swift +++ b/ios/PacketTunnelCore/Actor/ProtocolObfuscator.swift @@ -60,12 +60,23 @@ public class ProtocolObfuscator<Obfuscator: TunnelObfuscation>: ProtocolObfuscat return .init(endpoint: endpoint, method: .off) } + #if DEBUG + // TODO: Revisit this when QUIC obfuscation is available to use, use shadowsocks over 443 for the time being + let obfuscator = Obfuscator( + remoteAddress: endpoint.ipv4Relay.ip, + tcpPort: remotePort, + obfuscationProtocol: (obfuscationMethod == .shadowsocks || obfuscationMethod == .quic) + ? .shadowsocks + : .udpOverTcp + ) + #else // At this point, the only possible obfuscation methods should be either `.udpOverTcp` or `.shadowsocks` let obfuscator = Obfuscator( remoteAddress: endpoint.ipv4Relay.ip, tcpPort: remotePort, obfuscationProtocol: obfuscationMethod == .shadowsocks ? .shadowsocks : .udpOverTcp ) + #endif obfuscator.start() tunnelObfuscator = obfuscator |
