Skip to content

Commit

Permalink
Add UI for encrypted dns proxy
Browse files Browse the repository at this point in the history
  • Loading branch information
mojganii committed Sep 25, 2024
1 parent 453410b commit 741a575
Show file tree
Hide file tree
Showing 16 changed files with 142 additions and 32 deletions.
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

0 comments on commit 741a575

Please sign in to comment.