summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorSteffen Ernst <steffen.ernst@mullvad.net>2025-05-09 11:10:20 +0200
committerSteffen Ernst <steffen.ernst@mullvad.net>2025-05-20 15:46:45 +0200
commit7d5a8f331556558727ec725bb35ac735a8d5d3f8 (patch)
tree94296bca710251e24a3114175ae6e3388579b388
parentd2e13c40a83889c470d6fbbcfc5b041696a21605 (diff)
downloadmullvadvpn-7d5a8f331556558727ec725bb35ac735a8d5d3f8.tar.xz
mullvadvpn-7d5a8f331556558727ec725bb35ac735a8d5d3f8.zip
Handle port 443 removal and show blocked state banner
-rw-r--r--ios/MullvadREST/Relay/RelaySelectorWrapper.swift27
-rw-r--r--ios/MullvadVPN/Notifications/Notification Providers/TunnelStatusNotificationProvider.swift2
-rw-r--r--ios/MullvadVPNTests/MullvadREST/Relay/RelaySelectorWrapperTests.swift76
-rw-r--r--ios/PacketTunnel/PacketTunnelProvider/BlockedStateErrorMapper.swift2
-rw-r--r--ios/PacketTunnelCore/Actor/State+Extensions.swift3
-rw-r--r--ios/PacketTunnelCore/Actor/State.swift5
6 files changed, 113 insertions, 2 deletions
diff --git a/ios/MullvadREST/Relay/RelaySelectorWrapper.swift b/ios/MullvadREST/Relay/RelaySelectorWrapper.swift
index c52a0a2faf..feb1d1e890 100644
--- a/ios/MullvadREST/Relay/RelaySelectorWrapper.swift
+++ b/ios/MullvadREST/Relay/RelaySelectorWrapper.swift
@@ -20,6 +20,8 @@ public final class RelaySelectorWrapper: RelaySelectorProtocol, Sendable {
tunnelSettings: LatestTunnelSettings,
connectionAttemptCount: UInt
) throws -> SelectedRelays {
+ try validateWireguardPort(tunnelSettings)
+
let obfuscation = try prepareObfuscation(for: tunnelSettings, connectionAttemptCount: connectionAttemptCount)
return switch tunnelSettings.tunnelMultihopState {
@@ -76,4 +78,29 @@ public final class RelaySelectorWrapper: RelaySelectorProtocol, Sendable {
connectionAttemptCount: connectionAttemptCount
)
}
+
+ private func validateWireguardPort(_ tunnelSettings: LatestTunnelSettings) throws {
+ func isPortWithinValidWireGuardRanges(_ port: UInt16) throws -> Bool {
+ return try relayCache
+ .read().relays.wireguard.portRanges
+ .contains { range in
+ if let minPort = range.first, let maxPort = range.last {
+ return (minPort ... maxPort).contains(port)
+ }
+
+ return false
+ }
+ }
+
+ switch tunnelSettings.wireGuardObfuscation.state {
+ case .automatic, .off:
+ if case let .only(port) = tunnelSettings.relayConstraints.port {
+ guard try isPortWithinValidWireGuardRanges(port) else {
+ throw NoRelaysSatisfyingConstraintsError(.invalidPort)
+ }
+ }
+ case .on, .udpOverTcp, .quic, .shadowsocks:
+ break
+ }
+ }
}
diff --git a/ios/MullvadVPN/Notifications/Notification Providers/TunnelStatusNotificationProvider.swift b/ios/MullvadVPN/Notifications/Notification Providers/TunnelStatusNotificationProvider.swift
index e0135cb44f..9325971fef 100644
--- a/ios/MullvadVPN/Notifications/Notification Providers/TunnelStatusNotificationProvider.swift
+++ b/ios/MullvadVPN/Notifications/Notification Providers/TunnelStatusNotificationProvider.swift
@@ -248,6 +248,8 @@ final class TunnelStatusNotificationProvider: NotificationProvider, InAppNotific
errorString = "No DAITA compatible servers match your location settings. Try changing location."
case .noRelaysSatisfyingConstraints:
errorString = "No servers match your settings, try changing server or other settings."
+ case .noRelaysSatisfyingPortConstraints:
+ errorString = "The selected WireGuard port is not supported, please change it under VPN settings."
case .invalidAccount:
errorString = "You are logged in with an invalid account number. Please log out and try another one."
case .deviceLoggedOut:
diff --git a/ios/MullvadVPNTests/MullvadREST/Relay/RelaySelectorWrapperTests.swift b/ios/MullvadVPNTests/MullvadREST/Relay/RelaySelectorWrapperTests.swift
index b84cb528b8..0302ad5380 100644
--- a/ios/MullvadVPNTests/MullvadREST/Relay/RelaySelectorWrapperTests.swift
+++ b/ios/MullvadVPNTests/MullvadREST/Relay/RelaySelectorWrapperTests.swift
@@ -120,4 +120,80 @@ class RelaySelectorWrapperTests: XCTestCase {
let selectedRelays = try wrapper.selectRelays(tunnelSettings: settings, connectionAttemptCount: 0)
XCTAssertNotNil(selectedRelays.entry)
}
+
+ func testValidWireguardPortDoesNotThrow() throws {
+ let wrapper = RelaySelectorWrapper(relayCache: relayCache)
+
+ let settings = LatestTunnelSettings(
+ relayConstraints: .init(
+ port:
+ .only(
+ ServerRelaysResponseStubs.sampleRelays.wireguard.portRanges.first!.first!
+ )
+ )
+ )
+
+ XCTAssertNoThrow(
+ try wrapper
+ .selectRelays(tunnelSettings: settings, connectionAttemptCount: 0)
+ )
+ }
+
+ func testInvalidWireguardPortThrows() throws {
+ let wrapper = RelaySelectorWrapper(relayCache: relayCache)
+
+ var settings = LatestTunnelSettings(
+ relayConstraints: .init(port: .only(1)),
+ wireGuardObfuscation: .init(state: .automatic)
+ )
+
+ XCTAssertThrowsError(
+ try wrapper
+ .selectRelays(tunnelSettings: settings, connectionAttemptCount: 0)
+ )
+
+ settings = LatestTunnelSettings(
+ relayConstraints: .init(port: .only(1)),
+ wireGuardObfuscation: .init(state: .off)
+ )
+
+ XCTAssertThrowsError(
+ try wrapper
+ .selectRelays(tunnelSettings: settings, connectionAttemptCount: 0)
+ )
+ }
+
+ func testInvalidWireguardPortDoesNotThrowWhenObfuscated() throws {
+ let wrapper = RelaySelectorWrapper(relayCache: relayCache)
+
+ var settings = LatestTunnelSettings(
+ relayConstraints: .init(port: .only(1)),
+ wireGuardObfuscation: .init(state: .quic)
+ )
+
+ XCTAssertNoThrow(
+ try wrapper
+ .selectRelays(tunnelSettings: settings, connectionAttemptCount: 0)
+ )
+
+ settings = LatestTunnelSettings(
+ relayConstraints: .init(port: .only(1)),
+ wireGuardObfuscation: .init(state: .udpOverTcp)
+ )
+
+ XCTAssertNoThrow(
+ try wrapper
+ .selectRelays(tunnelSettings: settings, connectionAttemptCount: 0)
+ )
+
+ settings = LatestTunnelSettings(
+ relayConstraints: .init(port: .only(1)),
+ wireGuardObfuscation: .init(state: .shadowsocks)
+ )
+
+ XCTAssertNoThrow(
+ try wrapper
+ .selectRelays(tunnelSettings: settings, connectionAttemptCount: 0)
+ )
+ }
}
diff --git a/ios/PacketTunnel/PacketTunnelProvider/BlockedStateErrorMapper.swift b/ios/PacketTunnel/PacketTunnelProvider/BlockedStateErrorMapper.swift
index 219e7d518d..2383142ba1 100644
--- a/ios/PacketTunnel/PacketTunnelProvider/BlockedStateErrorMapper.swift
+++ b/ios/PacketTunnel/PacketTunnelProvider/BlockedStateErrorMapper.swift
@@ -58,6 +58,8 @@ public struct BlockedStateErrorMapper: BlockedStateErrorMapperProtocol {
.noRelaysSatisfyingDaitaConstraints
case .noObfuscatedRelaysFound:
.noRelaysSatisfyingObfuscationSettings
+ case .invalidPort:
+ .noRelaysSatisfyingPortConstraints
default:
.noRelaysSatisfyingConstraints
}
diff --git a/ios/PacketTunnelCore/Actor/State+Extensions.swift b/ios/PacketTunnelCore/Actor/State+Extensions.swift
index b42a52458e..2421f498a1 100644
--- a/ios/PacketTunnelCore/Actor/State+Extensions.swift
+++ b/ios/PacketTunnelCore/Actor/State+Extensions.swift
@@ -206,7 +206,8 @@ extension BlockedStateReason {
case .noRelaysSatisfyingConstraints, .noRelaysSatisfyingFilterConstraints,
.multihopEntryEqualsExit, .noRelaysSatisfyingObfuscationSettings,
.noRelaysSatisfyingDaitaConstraints, .readSettings, .invalidAccount, .accountExpired, .deviceRevoked,
- .unknown, .deviceLoggedOut, .outdatedSchema, .invalidRelayPublicKey:
+ .unknown, .deviceLoggedOut, .outdatedSchema, .invalidRelayPublicKey,
+ .noRelaysSatisfyingPortConstraints:
return false
}
}
diff --git a/ios/PacketTunnelCore/Actor/State.swift b/ios/PacketTunnelCore/Actor/State.swift
index 3aac312490..6f837fc734 100644
--- a/ios/PacketTunnelCore/Actor/State.swift
+++ b/ios/PacketTunnelCore/Actor/State.swift
@@ -211,9 +211,12 @@ public enum BlockedStateReason: String, Codable, Equatable, Sendable {
/// No relays satisfying DAITA constraints.
case noRelaysSatisfyingDaitaConstraints
- /// No relays satisfying DAITA constraints.
+ /// No relays satisfying obfuscation settings.
case noRelaysSatisfyingObfuscationSettings
+ /// No relays satisfying port constraints.
+ case noRelaysSatisfyingPortConstraints
+
/// Any other failure when reading settings.
case readSettings