Skip to content

Commit

Permalink
Use the encrypted DNS FFI as a transport provider
Browse files Browse the repository at this point in the history
  • Loading branch information
pinkisemils committed Sep 26, 2024
1 parent c61a054 commit f08f1e5
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 14 deletions.
6 changes: 5 additions & 1 deletion ios/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,13 @@ Line wrap the file at 100 chars. Th
* **Fixed**: for any bug fixes.
* **Security**: in case of vulnerabilities.

## Unreleased
### Added
- Add a new access method that uses the encrypted DNS proxy to reach our API.

## [2024.7 - 2024-09-16]
### Added
- Add DAITA (Defence against AI-guided Traffic Analysis) setting
- Add DAITA (Defence against AI-guided Traffic Analysis) setting.

## [2024.6 - 2024.09-02]
### Fixed
Expand Down
65 changes: 57 additions & 8 deletions ios/MullvadREST/Transport/EncryptedDNS/EncryptedDNSTransport.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,70 @@ public final class EncryptedDNSTransport: RESTTransport {

/// The `URLSession` used to send requests via `encryptedDNSProxy`
public let urlSession: URLSession
private let encryptedDnsProxy: EncryptedDNSProxy
private let dispatchQueue = DispatchQueue(label: "net.mullvad.EncryptedDNSTransport")
private var dnsProxyTask: URLSessionTask!

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

public func stop() {
dispatchQueue.async { [weak self] in
self?.encryptedDnsProxy.stop()
self?.dnsProxyTask = nil
}
}

/// Sends a request via the encrypted DNS proxy.
///
/// Starting the proxy can take a very long time due to domain resolution
/// Cancellation will only take place if the data task was already started, at which point,
/// most of the time starting the DNS proxy was already spent.
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
dispatchQueue.async { [weak self] in
guard let self else { return }
do {
try self.encryptedDnsProxy.start()
} catch {
completion(nil, nil, error)
return
}

var urlRequestCopy = request
urlRequestCopy.url = request.url.flatMap { url in
var components = URLComponents(url: url, resolvingAgainstBaseURL: false)
components?.host = "127.0.0.1"
components?.port = Int(self.encryptedDnsProxy.localPort())
return components?.url
}

let wrappedCompletionHandler: (Data?, URLResponse?, (any Error)?)
-> Void = { [weak self] data, response, maybeError in
if maybeError != nil {
self?.encryptedDnsProxy.stop()
}
// Do not call the completion handler if the request was cancelled in flight
if let cancelledError = maybeError as? URLError, cancelledError.code == .cancelled {
return
}

completion(data, response, maybeError)
}

let dataTask = urlSession.dataTask(with: urlRequestCopy, completionHandler: wrappedCompletionHandler)
dataTask.resume()
dnsProxyTask = dataTask
}

return AnyCancellable { [weak self] in
self?.dispatchQueue.async {
self?.dnsProxyTask.cancel()
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public class ProxyConfigurationTransportProvider {
addressCache: addressCache
)
case .encryptedDNS:
return EncryptedDNSTransport(urlSession: urlSession, addressCache: addressCache)
return EncryptedDNSTransport(urlSession: urlSession)
case let .shadowsocks(shadowSocksConfiguration):
return ShadowsocksTransport(
urlSession: urlSession,
Expand Down
10 changes: 6 additions & 4 deletions ios/MullvadREST/Transport/TransportProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public final class TransportProvider: RESTTransportProvider {
private var currentTransport: RESTTransport?
private var currentTransportType: TransportStrategy.Transport
private let parallelRequestsMutex = NSLock()
private let encryptedDNSTransport: EncryptedDNSTransport!

public init(
urlSessionTransport: URLSessionTransport,
Expand All @@ -27,6 +28,7 @@ public final class TransportProvider: RESTTransportProvider {
self.addressCache = addressCache
self.transportStrategy = transportStrategy
self.currentTransportType = transportStrategy.connectionTransport()
self.encryptedDNSTransport = EncryptedDNSTransport(urlSession: urlSessionTransport.urlSession)
}

public func makeTransport() -> RESTTransport? {
Expand All @@ -53,6 +55,9 @@ public final class TransportProvider: RESTTransportProvider {
defer { parallelRequestsMutex.unlock() }

if strategy == transportStrategy {
if strategy.connectionTransport() == .encryptedDNS {
encryptedDNSTransport.stop()
}
transportStrategy.didFail()
currentTransport = nil
}
Expand Down Expand Up @@ -82,10 +87,7 @@ public final class TransportProvider: RESTTransportProvider {
addressCache: addressCache
)
case .encryptedDNS:
currentTransport = EncryptedDNSTransport(
urlSession: urlSessionTransport.urlSession,
addressCache: addressCache
)
currentTransport = encryptedDNSTransport
case .none:
currentTransport = nil
}
Expand Down
61 changes: 61 additions & 0 deletions ios/MullvadRustRuntime/EncryptedDNSProxy.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//
// EncryptedDNSProxy.swift
// MullvadRustRuntime
//
// Created by Emils on 24/09/2024.
// Copyright © 2024 Mullvad VPN AB. All rights reserved.
//

import Foundation
import MullvadRustRuntimeProxy

enum EncryptedDnsProxyError: Error {
case start(err: Int32)
}

public class EncryptedDNSProxy {
private var proxyConfig: ProxyHandle
private var stateLock = NSLock()
private var didStart = false
private let state: OpaquePointer

public init() {
state = encrypted_dns_proxy_init()
proxyConfig = ProxyHandle(context: nil, port: 0)
}

public func localPort() -> UInt16 {
stateLock.lock()
defer { stateLock.unlock() }
return proxyConfig.port
}

public func start() throws {
stateLock.lock()
defer { stateLock.unlock() }
guard didStart == false else { return }

let err = encrypted_dns_proxy_start(state, &proxyConfig)
if err != 0 {
throw EncryptedDnsProxyError.start(err: err)
}
didStart = true
}

public func stop() {
stateLock.lock()
defer { stateLock.unlock() }
guard didStart == true else { return }
didStart = false

encrypted_dns_proxy_stop(&proxyConfig)
}

deinit {
if didStart {
encrypted_dns_proxy_stop(&proxyConfig)
}

encrypted_dns_proxy_free(state)
}
}
12 changes: 12 additions & 0 deletions ios/MullvadVPN.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
objects = {

/* Begin PBXBuildFile section */
014449952CA293B100C0C2F2 /* EncryptedDNSProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 014449942CA293B100C0C2F2 /* EncryptedDNSProxy.swift */; };
01EF6F342B6A590700125696 /* libmullvad_api.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 01EF6F332B6A590700125696 /* libmullvad_api.a */; };
062B45A328FD4CA700746E77 /* le_root_cert.cer in Resources */ = {isa = PBXBuildFile; fileRef = 06799AB428F98CE700ACD94E /* le_root_cert.cer */; };
062B45BC28FD8C3B00746E77 /* RESTDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 062B45BB28FD8C3B00746E77 /* RESTDefaults.swift */; };
Expand Down Expand Up @@ -1329,6 +1330,7 @@
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
014449942CA293B100C0C2F2 /* EncryptedDNSProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncryptedDNSProxy.swift; sourceTree = "<group>"; };
01EF6F2D2B6A51B100125696 /* mullvad-api.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "mullvad-api.h"; path = "../mullvad-api/include/mullvad-api.h"; sourceTree = "<group>"; };
01EF6F2F2B6A588300125696 /* aarch64-apple-ios */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "aarch64-apple-ios"; path = "../target/aarch64-apple-ios"; sourceTree = "<group>"; };
01EF6F312B6A58F000125696 /* debug */ = {isa = PBXFileReference; lastKnownFileType = folder; name = debug; path = "../target/aarch64-apple-ios/debug"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2356,6 +2358,13 @@
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
014449932CA1B55200C0C2F2 /* EncryptedDnsProxy */ = {
isa = PBXGroup;
children = (
);
path = EncryptedDnsProxy;
sourceTree = "<group>";
};
062B45A228FD4C0F00746E77 /* Assets */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -4026,6 +4035,7 @@
A9EB4F9C2B7FAB21002A2D7A /* EphemeralPeerNegotiator.swift */,
F0DDE40F2B220458006B57A7 /* ShadowSocksProxy.swift */,
584023212A406BF5007B27AC /* UDPOverTCPObfuscator.swift */,
014449942CA293B100C0C2F2 /* EncryptedDNSProxy.swift */,
);
path = MullvadRustRuntime;
sourceTree = "<group>";
Expand Down Expand Up @@ -4177,6 +4187,7 @@
F0DC77A02B2223290087F09D /* Transport */ = {
isa = PBXGroup;
children = (
014449932CA1B55200C0C2F2 /* EncryptedDnsProxy */,
F0164ED02B4F2DCB0020268D /* AccessMethodIterator.swift */,
F0DC77A32B2315800087F09D /* Direct */,
F0E5B2F62C9C689C0007F78C /* EncryptedDNS */,
Expand Down Expand Up @@ -6146,6 +6157,7 @@
buildActionMask = 2147483647;
files = (
A9D9A4B12C36D10E004088DD /* ShadowSocksProxy.swift in Sources */,
014449952CA293B100C0C2F2 /* EncryptedDNSProxy.swift in Sources */,
A9D9A4BB2C36D397004088DD /* EphemeralPeerNegotiator.swift in Sources */,
A9D9A4B22C36D12D004088DD /* UDPOverTCPObfuscator.swift in Sources */,
A9173C322C36CCDD00F6A08C /* PacketTunnelProvider+TCPConnection.swift in Sources */,
Expand Down

0 comments on commit f08f1e5

Please sign in to comment.