diff options
| author | Jon Petersson <jon.petersson@mullvad.net> | 2025-01-16 15:52:16 +0100 |
|---|---|---|
| committer | Jon Petersson <jon.petersson@mullvad.net> | 2025-02-21 15:08:01 +0100 |
| commit | 041bf3f20f3cb0ed6703065eee4d09b5f938f730 (patch) | |
| tree | 13ed3e3895269a8bb6980b5d6eab3238f1e09252 /ios/MullvadRustRuntime | |
| parent | 9a8535ef787d784d2d75dbd1474e1a1846413e83 (diff) | |
| download | mullvadvpn-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.swift | 23 | ||||
| -rw-r--r-- | ios/MullvadRustRuntime/MullvadApiCompletion.swift | 28 | ||||
| -rw-r--r-- | ios/MullvadRustRuntime/MullvadApiContext.swift | 27 | ||||
| -rw-r--r-- | ios/MullvadRustRuntime/MullvadApiResponse.swift | 55 | ||||
| -rw-r--r-- | ios/MullvadRustRuntime/include/mullvad_rust_runtime.h | 107 |
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 |
