Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add UI for encrypted dns proxy #6844

Merged
merged 1 commit into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions ios/MullvadREST/Transport/EncryptedDNS/EncryptedDNSTransport.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//
// EncryptedDNSTransport.swift
// MullvadVPN
//
// Created by Mojgan on 2024-09-19.
// Copyright © 2024 Mullvad VPN AB. All rights reserved.
//
import Foundation
import MullvadRustRuntime
import MullvadTypes

public final class EncryptedDNSTransport: RESTTransport {
public var name: String {
"encrypted-dns-url-session"
}

/// The `URLSession` used to send requests via `encryptedDNSProxy`
public let urlSession: URLSession

public init(
urlSession: URLSession,
addressCache: REST.AddressCache
) {
self.urlSession = urlSession
}

public func sendRequest(
_ request: URLRequest,
completion: @escaping (Data?, URLResponse?, (any Error)?) -> Void
) -> any Cancellable {
// TODO: Start proxy once the backend is integrated into the Swift code.
let dataTask = urlSession.dataTask(with: request, completionHandler: completion)
dataTask.resume()
return dataTask
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ public class ProxyConfigurationTransportProvider {
configuration: shadowsocksConfiguration,
addressCache: addressCache
)
case .encryptedDNS:
return EncryptedDNSTransport(urlSession: urlSession, addressCache: addressCache)
case let .shadowsocks(shadowSocksConfiguration):
return ShadowsocksTransport(
urlSession: urlSession,
Expand Down
5 changes: 5 additions & 0 deletions ios/MullvadREST/Transport/TransportProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ public final class TransportProvider: RESTTransportProvider {
configuration: configuration,
addressCache: addressCache
)
case .encryptedDNS:
currentTransport = EncryptedDNSTransport(
urlSession: urlSessionTransport.urlSession,
addressCache: addressCache
)
case .none:
currentTransport = nil
}
Expand Down
17 changes: 12 additions & 5 deletions ios/MullvadREST/Transport/TransportStrategy.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,24 @@ public struct TransportStrategy: Equatable {
/// Connecting via socks proxy
case socks5(configuration: Socks5Configuration)

/// Failing to retrive transport
/// Connecting via encrypted DNS proxy
case encryptedDNS

/// Failing to retrieve transport
case none

public static func == (lhs: Self, rhs: Self) -> Bool {
switch (lhs, rhs) {
case(.direct, .direct), (.none, .none):
return true
true
case let (.shadowsocks(lhsConfiguration), .shadowsocks(rhsConfiguration)):
return lhsConfiguration == rhsConfiguration
lhsConfiguration == rhsConfiguration
case let (.socks5(lhsConfiguration), .socks5(rhsConfiguration)):
return lhsConfiguration == rhsConfiguration
lhsConfiguration == rhsConfiguration
case (.encryptedDNS, .encryptedDNS):
true
default:
return false
false
}
}
}
Expand Down Expand Up @@ -70,6 +75,8 @@ public struct TransportStrategy: Equatable {
switch configuration.proxyConfiguration {
case .direct:
return .direct
case .encryptedDNS:
return .encryptedDNS
case .bridges:
do {
let configuration = try shadowsocksLoader.load()
Expand Down
20 changes: 18 additions & 2 deletions ios/MullvadRESTTests/TransportStrategyTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import XCTest
class TransportStrategyTests: XCTestCase {
private var directAccess: PersistentAccessMethod!
private var bridgeAccess: PersistentAccessMethod!
private var encryptedDNS: PersistentAccessMethod!

private var shadowsocksLoader: ShadowsocksLoaderStub!

Expand All @@ -41,15 +42,24 @@ class TransportStrategyTests: XCTestCase {
isEnabled: true,
proxyConfiguration: .bridges
)

encryptedDNS = PersistentAccessMethod(
id: UUID(uuidString: "831CB1F8-1829-42DD-B9DC-82902F298EC0")!,
name: "Encrypted DNS proxy",
isEnabled: true,
proxyConfiguration: .encryptedDNS
)
}

func testDefaultStrategyIsDirectWhenAllMethodsAreDisabled() throws {
directAccess.isEnabled = false
bridgeAccess.isEnabled = false
encryptedDNS.isEnabled = false
let transportStrategy = TransportStrategy(
datasource: AccessMethodRepositoryStub(accessMethods: [
directAccess,
bridgeAccess,
encryptedDNS,
]),
shadowsocksLoader: shadowsocksLoader
)
Expand All @@ -61,10 +71,12 @@ class TransportStrategyTests: XCTestCase {

func testReuseSameStrategyWhenEverythingElseIsDisabled() throws {
directAccess.isEnabled = false
encryptedDNS.isEnabled = false
let transportStrategy = TransportStrategy(
datasource: AccessMethodRepositoryStub(accessMethods: [
directAccess,
bridgeAccess,
encryptedDNS,
]),
shadowsocksLoader: shadowsocksLoader
)
Expand All @@ -84,6 +96,7 @@ class TransportStrategyTests: XCTestCase {
datasource: AccessMethodRepositoryStub(accessMethods: [
directAccess,
bridgeAccess,
encryptedDNS,
PersistentAccessMethod(
id: UUID(uuidString: "8586E75A-CA7B-4432-B70D-EE65F3F95090")!,
name: "",
Expand All @@ -98,7 +111,7 @@ class TransportStrategyTests: XCTestCase {
]),
shadowsocksLoader: shadowsocksLoader
)
let accessMethodsCount = 3
let accessMethodsCount = 4
for i in 0 ..< (accessMethodsCount * 2) {
let previousOne = transportStrategy.connectionTransport()
transportStrategy.didFail()
Expand All @@ -113,10 +126,12 @@ class TransportStrategyTests: XCTestCase {

func testUsesNextWhenItIsNotReachable() {
bridgeAccess.isEnabled = false
encryptedDNS.isEnabled = false
let transportStrategy = TransportStrategy(
datasource: AccessMethodRepositoryStub(accessMethods: [
directAccess,
bridgeAccess,
encryptedDNS,
PersistentAccessMethod(
id: UUID(uuidString: "8586E75A-CA7B-4432-B70D-EE65F3F95090")!,
name: "",
Expand Down Expand Up @@ -150,12 +165,13 @@ class TransportStrategyTests: XCTestCase {
datasource: AccessMethodRepositoryStub(accessMethods: [
directAccess,
bridgeAccess,
encryptedDNS,
]),
shadowsocksLoader: shadowsocksLoader
)

transportStrategy.didFail()
XCTAssertEqual(transportStrategy.connectionTransport(), .direct)
XCTAssertEqual(transportStrategy.connectionTransport(), .encryptedDNS)
}

func testNoLoopOnFailureAtLoadingConfigurationWhenBridgeIsOnlyEnabled() {
Expand Down
7 changes: 6 additions & 1 deletion ios/MullvadSettings/AccessMethodKind.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ public enum AccessMethodKind: Equatable, Hashable, CaseIterable {
/// Communication over bridges.
case bridges

/// Communication over proxy address from a DNS.
case encryptedDNS

/// Communication over shadowsocks.
case shadowsocks

Expand All @@ -27,7 +30,7 @@ public extension AccessMethodKind {
/// Returns `true` if the method is permanent and cannot be deleted.
var isPermanent: Bool {
switch self {
case .direct, .bridges:
case .direct, .bridges, .encryptedDNS:
true
case .shadowsocks, .socks5:
false
Expand All @@ -48,6 +51,8 @@ extension PersistentAccessMethod {
.direct
case .bridges:
.bridges
case .encryptedDNS:
.encryptedDNS
case .shadowsocks:
.shadowsocks
case .socks5:
Expand Down
28 changes: 25 additions & 3 deletions ios/MullvadSettings/AccessMethodRepository.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ public class AccessMethodRepository: AccessMethodRepositoryProtocol {
proxyConfiguration: .bridges
)

private let encryptedDNS = PersistentAccessMethod(
id: UUID(uuidString: "831CB1F8-1829-42DD-B9DC-82902F298EC0")!,
name: "Encrypted DNS proxy",
isEnabled: true,
proxyConfiguration: .encryptedDNS
)

private let accessMethodsSubject: CurrentValueSubject<[PersistentAccessMethod], Never>
public var accessMethodsPublisher: AnyPublisher<[PersistentAccessMethod], Never> {
accessMethodsSubject.eraseToAnyPublisher()
Expand All @@ -46,7 +53,7 @@ public class AccessMethodRepository: AccessMethodRepositoryProtocol {
accessMethodsSubject = CurrentValueSubject([])
lastReachableAccessMethodSubject = CurrentValueSubject(direct)

add([direct, bridge])
addDefaultsMethods()

accessMethodsSubject.send(fetchAll())
lastReachableAccessMethodSubject.send(fetchLastReachable())
Expand Down Expand Up @@ -107,15 +114,30 @@ public class AccessMethodRepository: AccessMethodRepositoryProtocol {
}

public func fetchAll() -> [PersistentAccessMethod] {
#if DEBUG
readApiAccessMethodStore().accessMethods
#else
readApiAccessMethodStore().accessMethods.filter { $0.id != encryptedDNS.id }
#endif
}

public func fetchLastReachable() -> PersistentAccessMethod {
readApiAccessMethodStore().lastReachableAccessMethod
}

public func reloadWithDefaultsAfterDataRemoval() {
add([direct, bridge])
public func addDefaultsMethods() {
#if DEBUG
add([
direct,
bridge,
encryptedDNS,
])
#else
add([
direct,
bridge,
])
#endif
}

private func add(_ methods: [PersistentAccessMethod]) {
Expand Down
2 changes: 1 addition & 1 deletion ios/MullvadSettings/AccessMethodRepositoryProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,5 @@ public protocol AccessMethodRepositoryProtocol: AccessMethodRepositoryDataSource
func fetch(by id: UUID) -> PersistentAccessMethod?

/// Refreshes the storage with default values.
func reloadWithDefaultsAfterDataRemoval()
func addDefaultsMethods()
}
3 changes: 3 additions & 0 deletions ios/MullvadSettings/PersistentAccessMethod.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ public enum PersistentProxyConfiguration: Codable {
/// Communication over bridges.
case bridges

/// Communication over proxy address from a DNS.
case encryptedDNS

/// Communication over shadowsocks.
case shadowsocks(ShadowsocksConfiguration)

Expand Down
18 changes: 17 additions & 1 deletion ios/MullvadVPN.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -970,6 +970,7 @@
F0DDE42B2B220A15006B57A7 /* RelaySelector.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0DDE4282B220A15006B57A7 /* RelaySelector.swift */; };
F0DDE42C2B220A15006B57A7 /* Midpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0DDE4292B220A15006B57A7 /* Midpoint.swift */; };
F0E3618B2A4ADD2F00AEEF2B /* WelcomeContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0E3618A2A4ADD2F00AEEF2B /* WelcomeContentView.swift */; };
F0E5B2F82C9C68CF0007F78C /* EncryptedDNSTransport.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0E5B2F72C9C68CD0007F78C /* EncryptedDNSTransport.swift */; };
F0E61CAA2BF2911D000C4A95 /* TunnelSettingsV5.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0E61CA82BF2911D000C4A95 /* TunnelSettingsV5.swift */; };
F0E61CAB2BF2911D000C4A95 /* MultihopSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0E61CA92BF2911D000C4A95 /* MultihopSettings.swift */; };
F0E8CC032A4C753B007ED3B4 /* WelcomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0E8CC022A4C753B007ED3B4 /* WelcomeViewController.swift */; };
Expand Down Expand Up @@ -2155,6 +2156,7 @@
F0DDE4282B220A15006B57A7 /* RelaySelector.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RelaySelector.swift; sourceTree = "<group>"; };
F0DDE4292B220A15006B57A7 /* Midpoint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Midpoint.swift; sourceTree = "<group>"; };
F0E3618A2A4ADD2F00AEEF2B /* WelcomeContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeContentView.swift; sourceTree = "<group>"; };
F0E5B2F72C9C68CD0007F78C /* EncryptedDNSTransport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncryptedDNSTransport.swift; sourceTree = "<group>"; };
F0E61CA82BF2911D000C4A95 /* TunnelSettingsV5.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TunnelSettingsV5.swift; sourceTree = "<group>"; };
F0E61CA92BF2911D000C4A95 /* MultihopSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MultihopSettings.swift; sourceTree = "<group>"; };
F0E8CC022A4C753B007ED3B4 /* WelcomeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeViewController.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -4176,8 +4178,9 @@
isa = PBXGroup;
children = (
F0164ED02B4F2DCB0020268D /* AccessMethodIterator.swift */,
A932D9EE2B5ADD0700999395 /* ProxyConfigurationTransportProvider.swift */,
F0DC77A32B2315800087F09D /* Direct */,
F0E5B2F62C9C689C0007F78C /* EncryptedDNS */,
A932D9EE2B5ADD0700999395 /* ProxyConfigurationTransportProvider.swift */,
06FAE67D28F83CA50033DD93 /* RESTTransport.swift */,
58E7BA182A975DF70068EC3A /* RESTTransportProvider.swift */,
F0DC77A22B2314EF0087F09D /* Shadowsocks */,
Expand Down Expand Up @@ -4228,6 +4231,14 @@
path = Welcome;
sourceTree = "<group>";
};
F0E5B2F62C9C689C0007F78C /* EncryptedDNS */ = {
isa = PBXGroup;
children = (
F0E5B2F72C9C68CD0007F78C /* EncryptedDNSTransport.swift */,
);
path = EncryptedDNS;
sourceTree = "<group>";
};
F0E8CC082A4EE0DC007ED3B4 /* Completed */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -5200,6 +5211,7 @@
06799ADB28F98E4800ACD94E /* RESTProxyFactory.swift in Sources */,
F0DDE4182B220458006B57A7 /* ShadowsocksConfiguration.swift in Sources */,
7AA7046A2C8EFE2B0045699D /* StoredRelays.swift in Sources */,
F0E5B2F82C9C68CF0007F78C /* EncryptedDNSTransport.swift in Sources */,
06799AF228F98E4800ACD94E /* RESTAccessTokenManager.swift in Sources */,
A90763B12B2857D50045ADF0 /* Socks5Endpoint.swift in Sources */,
06799AF328F98E4800ACD94E /* RESTAuthenticationProxy.swift in Sources */,
Expand Down Expand Up @@ -7183,6 +7195,7 @@
DEBUG_INFORMATION_FORMAT = dwarf;
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
OTHER_CFLAGS = "";
OTHER_LDFLAGS = "";
PATH = "${PATH}:/opt/homebrew/opt/go@1.19/bin";
Expand All @@ -7195,6 +7208,7 @@
baseConfigurationReference = 5808273928487E3E006B77A4 /* Base.xcconfig */;
buildSettings = {
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
OTHER_CFLAGS = "";
OTHER_LDFLAGS = "";
PATH = "${PATH}:/opt/homebrew/opt/go@1.19/bin";
Expand Down Expand Up @@ -7551,6 +7565,7 @@
DEBUG_INFORMATION_FORMAT = dwarf;
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
OTHER_CFLAGS = "";
OTHER_LDFLAGS = "";
PATH = "${PATH}:/opt/homebrew/opt/go@1.19/bin";
Expand Down Expand Up @@ -8388,6 +8403,7 @@
baseConfigurationReference = 5808273928487E3E006B77A4 /* Base.xcconfig */;
buildSettings = {
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
OTHER_CFLAGS = "";
OTHER_LDFLAGS = "";
PATH = "${PATH}:/opt/homebrew/opt/go@1.19/bin";
Expand Down
2 changes: 1 addition & 1 deletion ios/MullvadVPN/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -537,7 +537,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
try? SettingsManager.writeSettings(LatestTunnelSettings())

// Default access methods need to be repopulated again after settings wipe.
self.accessMethodRepository.reloadWithDefaultsAfterDataRemoval()
self.accessMethodRepository.addDefaultsMethods()
// At app startup, the relay cache tracker will get populated with a list of overriden IPs.
// The overriden IPs will get wiped, therefore, the cache needs to be pruned as well.
try? self.relayCacheTracker.refreshCachedRelays()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class MethodSettingsDataSourceConfiguration {
}

switch newValue.method {
case .direct, .bridges:
case .direct, .bridges, .encryptedDNS:
break

case .shadowsocks:
Expand Down Expand Up @@ -106,7 +106,7 @@ class MethodSettingsDataSourceConfiguration {
}

let itemsToReload: [MethodSettingsItemIdentifier] = switch viewModel.method {
case .direct, .bridges:
case .direct, .bridges, .encryptedDNS:
[]
case .shadowsocks:
MethodSettingsItemIdentifier.allShadowsocksItems
Expand Down
Loading
Loading