summaryrefslogtreecommitdiffhomepage
path: root/ios/MullvadRustRuntime
diff options
context:
space:
mode:
authorJon Petersson <jon.petersson@mullvad.net>2025-01-16 15:52:16 +0100
committerJon Petersson <jon.petersson@mullvad.net>2025-02-21 15:08:01 +0100
commit041bf3f20f3cb0ed6703065eee4d09b5f938f730 (patch)
tree13ed3e3895269a8bb6980b5d6eab3238f1e09252 /ios/MullvadRustRuntime
parent9a8535ef787d784d2d75dbd1474e1a1846413e83 (diff)
downloadmullvadvpn-041bf3f20f3cb0ed6703065eee4d09b5f938f730.tar.xz
mullvadvpn-041bf3f20f3cb0ed6703065eee4d09b5f938f730.zip
Implement an FFI to fetch API IP addresses using mullvad-api
Diffstat (limited to 'ios/MullvadRustRuntime')
-rw-r--r--ios/MullvadRustRuntime/MullvadApiCancellable.swift23
-rw-r--r--ios/MullvadRustRuntime/MullvadApiCompletion.swift28
-rw-r--r--ios/MullvadRustRuntime/MullvadApiContext.swift27
-rw-r--r--ios/MullvadRustRuntime/MullvadApiResponse.swift55
-rw-r--r--ios/MullvadRustRuntime/include/mullvad_rust_runtime.h107
5 files changed, 240 insertions, 0 deletions
diff --git a/ios/MullvadRustRuntime/MullvadApiCancellable.swift b/ios/MullvadRustRuntime/MullvadApiCancellable.swift
new file mode 100644
index 0000000000..0f0e0fe6e4
--- /dev/null
+++ b/ios/MullvadRustRuntime/MullvadApiCancellable.swift
@@ -0,0 +1,23 @@
+//
+// MullvadApiCancellable.swift
+// MullvadVPN
+//
+// Created by Jon Petersson on 2025-02-07.
+// Copyright © 2025 Mullvad VPN AB. All rights reserved.
+//
+
+public class MullvadApiCancellable {
+ private let handle: SwiftCancelHandle
+
+ public init(handle: consuming SwiftCancelHandle) {
+ self.handle = handle
+ }
+
+ deinit {
+ mullvad_api_cancel_task_drop(handle)
+ }
+
+ public func cancel() {
+ mullvad_api_cancel_task(handle)
+ }
+}
diff --git a/ios/MullvadRustRuntime/MullvadApiCompletion.swift b/ios/MullvadRustRuntime/MullvadApiCompletion.swift
new file mode 100644
index 0000000000..ca61c6791f
--- /dev/null
+++ b/ios/MullvadRustRuntime/MullvadApiCompletion.swift
@@ -0,0 +1,28 @@
+//
+// MullvadApiCompletion.swift
+// MullvadVPN
+//
+// Created by Jon Petersson on 2025-01-16.
+// Copyright © 2025 Mullvad VPN AB. All rights reserved.
+//
+
+@_silgen_name("mullvad_api_completion_finish")
+func mullvadApiCompletionFinish(
+ response: SwiftMullvadApiResponse,
+ completionCookie: UnsafeMutableRawPointer
+) {
+ let completionBridge = Unmanaged<MullvadApiCompletion>
+ .fromOpaque(completionCookie)
+ .takeRetainedValue()
+ let apiResponse = MullvadApiResponse(response: response)
+
+ completionBridge.completion(apiResponse)
+}
+
+public class MullvadApiCompletion {
+ public var completion: (MullvadApiResponse) -> Void
+
+ public init(completion: @escaping ((MullvadApiResponse) -> Void)) {
+ self.completion = completion
+ }
+}
diff --git a/ios/MullvadRustRuntime/MullvadApiContext.swift b/ios/MullvadRustRuntime/MullvadApiContext.swift
new file mode 100644
index 0000000000..f637590612
--- /dev/null
+++ b/ios/MullvadRustRuntime/MullvadApiContext.swift
@@ -0,0 +1,27 @@
+//
+// MullvadApiContext.swift
+// MullvadVPN
+//
+// Created by Jon Petersson on 2025-01-24.
+// Copyright © 2025 Mullvad VPN AB. All rights reserved.
+//
+
+import MullvadTypes
+
+public struct MullvadApiContext: Sendable {
+ enum MullvadApiContextError: Error {
+ case failedToConstructApiClient
+ }
+
+ public let context: SwiftApiContext
+
+ public init(host: String, address: AnyIPEndpoint) throws {
+ context = mullvad_api_init_new(host, address.description)
+
+ if context._0 == nil {
+ throw MullvadApiContextError.failedToConstructApiClient
+ }
+ }
+}
+
+extension SwiftApiContext: @unchecked @retroactive Sendable {}
diff --git a/ios/MullvadRustRuntime/MullvadApiResponse.swift b/ios/MullvadRustRuntime/MullvadApiResponse.swift
new file mode 100644
index 0000000000..a709342b4e
--- /dev/null
+++ b/ios/MullvadRustRuntime/MullvadApiResponse.swift
@@ -0,0 +1,55 @@
+//
+// MullvadApiResponse.swift
+// MullvadVPN
+//
+// Created by Jon Petersson on 2025-01-24.
+// Copyright © 2025 Mullvad VPN AB. All rights reserved.
+//
+
+public class MullvadApiResponse {
+ private let response: SwiftMullvadApiResponse
+
+ public init(response: consuming SwiftMullvadApiResponse) {
+ self.response = response
+ }
+
+ deinit {
+ mullvad_response_drop(response)
+ }
+
+ public var body: Data? {
+ guard let body = response.body else {
+ return nil
+ }
+
+ return Data(UnsafeBufferPointer(start: body, count: Int(response.body_size)))
+ }
+
+ public var errorDescription: String? {
+ return if response.error_description == nil {
+ nil
+ } else {
+ String(cString: response.error_description)
+ }
+ }
+
+ public var statusCode: UInt16 {
+ response.status_code
+ }
+
+ public var serverResponseCode: String? {
+ return if response.server_response_code == nil {
+ nil
+ } else {
+ String(cString: response.server_response_code)
+ }
+ }
+
+ public var shouldRetry: Bool {
+ response.should_retry
+ }
+
+ public var success: Bool {
+ response.success
+ }
+}
diff --git a/ios/MullvadRustRuntime/include/mullvad_rust_runtime.h b/ios/MullvadRustRuntime/include/mullvad_rust_runtime.h
index b10f4f81f2..faae315d6d 100644
--- a/ios/MullvadRustRuntime/include/mullvad_rust_runtime.h
+++ b/ios/MullvadRustRuntime/include/mullvad_rust_runtime.h
@@ -14,6 +14,8 @@ enum TunnelObfuscatorProtocol {
};
typedef uint8_t TunnelObfuscatorProtocol;
+typedef struct ApiContext ApiContext;
+
/**
* A thin wrapper around [`mullvad_encrypted_dns_proxy::state::EncryptedDnsProxyState`] that
* can start a local forwarder (see [`Self::start`]).
@@ -22,6 +24,31 @@ typedef struct EncryptedDnsProxyState EncryptedDnsProxyState;
typedef struct ExchangeCancelToken ExchangeCancelToken;
+typedef struct RequestCancelHandle RequestCancelHandle;
+
+typedef struct SwiftApiContext {
+ const struct ApiContext *_0;
+} SwiftApiContext;
+
+typedef struct SwiftCancelHandle {
+ struct RequestCancelHandle *ptr;
+} SwiftCancelHandle;
+
+typedef struct SwiftMullvadApiResponse {
+ uint8_t *body;
+ uintptr_t body_size;
+ uint16_t status_code;
+ uint8_t *error_description;
+ uint8_t *server_response_code;
+ bool success;
+ bool should_retry;
+ uint64_t retry_after;
+} SwiftMullvadApiResponse;
+
+typedef struct CompletionCookie {
+ void *_0;
+} CompletionCookie;
+
typedef struct ProxyHandle {
void *context;
uint16_t port;
@@ -50,6 +77,86 @@ typedef struct EphemeralPeerParameters {
extern const uint16_t CONFIG_SERVICE_PORT;
/**
+ * # Safety
+ *
+ * `host` must be a pointer to a null terminated string representing a hostname for Mullvad API host.
+ * This hostname will be used for TLS validation but not used for domain name resolution.
+ *
+ * `address` must be a pointer to a null terminated string representing a socket address through which
+ * the Mullvad API can be reached directly.
+ *
+ * If a context cannot be constructed this function will panic since the call site would not be able
+ * to proceed in a meaningful way anyway.
+ *
+ * This function is safe.
+ */
+struct SwiftApiContext mullvad_api_init_new(const uint8_t *host,
+ const uint8_t *address);
+
+/**
+ * # Safety
+ *
+ * `api_context` must be pointing to a valid instance of `SwiftApiContext`. A `SwiftApiContext` is created
+ * by calling `mullvad_api_init_new`.
+ *
+ * `completion_cookie` must be pointing to a valid instance of `CompletionCookie`. `CompletionCookie` is
+ * safe because the pointer in `MullvadApiCompletion` is valid for the lifetime of the process where this
+ * type is intended to be used.
+ *
+ * This function is not safe to call multiple times with the same `CompletionCookie`.
+ */
+struct SwiftCancelHandle mullvad_api_get_addresses(struct SwiftApiContext api_context,
+ void *completion_cookie);
+
+/**
+ * Called by the Swift side to signal that a Mullvad API call should be cancelled.
+ * After this call, the cancel token is no longer valid.
+ *
+ * # Safety
+ *
+ * `handle_ptr` must be pointing to a valid instance of `SwiftCancelHandle`. This function
+ * is not safe to call multiple times with the same `SwiftCancelHandle`.
+ */
+void mullvad_api_cancel_task(struct SwiftCancelHandle handle_ptr);
+
+/**
+ * Called by the Swift side to signal that the Rust `SwiftCancelHandle` can be safely
+ * dropped from memory.
+ *
+ * # Safety
+ *
+ * `handle_ptr` must be pointing to a valid instance of `SwiftCancelHandle`. This function
+ * is not safe to call multiple times with the same `SwiftCancelHandle`.
+ */
+void mullvad_api_cancel_task_drop(struct SwiftCancelHandle handle_ptr);
+
+/**
+ * Maps to `mullvadApiCompletionFinish` on Swift side to facilitate callback based completion flow when doing
+ * network calls through Mullvad API on Rust side.
+ *
+ * # Safety
+ *
+ * `response` must be pointing to a valid instance of `SwiftMullvadApiResponse`.
+ *
+ * `completion_cookie` must be pointing to a valid instance of `CompletionCookie`. `CompletionCookie` is safe
+ * because the pointer in `MullvadApiCompletion` is valid for the lifetime of the process where this type is
+ * intended to be used.
+ */
+extern void mullvad_api_completion_finish(struct SwiftMullvadApiResponse response,
+ struct CompletionCookie completion_cookie);
+
+/**
+ * Called by the Swift side to signal that the Rust `SwiftMullvadApiResponse` can be safely
+ * dropped from memory.
+ *
+ * # Safety
+ *
+ * `response` must be pointing to a valid instance of `SwiftMullvadApiResponse`. This function
+ * is not safe to call multiple times with the same `SwiftMullvadApiResponse`.
+ */
+void mullvad_response_drop(struct SwiftMullvadApiResponse response);
+
+/**
* Initializes a valid pointer to an instance of `EncryptedDnsProxyState`.
*
* # Safety