Skip to content

Commit

Permalink
Add documentation on Rust side
Browse files Browse the repository at this point in the history
  • Loading branch information
rablador committed Feb 11, 2025
1 parent d863148 commit 90acf06
Show file tree
Hide file tree
Showing 8 changed files with 170 additions and 61 deletions.
134 changes: 100 additions & 34 deletions ios/MullvadRustRuntime/include/mullvad_rust_runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,29 @@ 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;
Expand All @@ -51,30 +74,87 @@ typedef struct EphemeralPeerParameters {
struct WgTcpConnectionFunctions funcs;
} EphemeralPeerParameters;

typedef struct SwiftApiContext {
const struct ApiContext *_0;
} SwiftApiContext;
extern const uint16_t CONFIG_SERVICE_PORT;

typedef struct SwiftCancelHandle {
struct RequestCancelHandle *ptr;
} SwiftCancelHandle;
/**
* # 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);

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;
/**
* # 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);

typedef struct CompletionCookie {
void *_0;
} CompletionCookie;
/**
* 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);

extern const uint16_t CONFIG_SERVICE_PORT;
/**
* 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`.
Expand Down Expand Up @@ -200,20 +280,6 @@ int32_t start_shadowsocks_proxy(const uint8_t *forward_address,
*/
int32_t stop_shadowsocks_proxy(struct ProxyHandle *proxy_config);

struct SwiftApiContext mullvad_api_init_new(const uint8_t *host, const uint8_t *address);

struct SwiftCancelHandle mullvad_api_get_addresses(struct SwiftApiContext api_context,
void *completion_cookie);

void mullvad_api_cancel_task(struct SwiftCancelHandle handle_ptr);

void mullvad_api_cancel_task_drop(struct SwiftCancelHandle handle_ptr);

extern void mullvad_api_completion_finish(struct SwiftMullvadApiResponse response,
struct CompletionCookie completion_cookie);

void mullvad_response_drop(struct SwiftMullvadApiResponse response);

int32_t start_tunnel_obfuscator_proxy(const uint8_t *peer_address,
uintptr_t peer_address_len,
uint16_t peer_port,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"originHash" : "c15149b2d59d9e9c72375f65339c04f41a19943e1117e682df27fc9f943fdc56",
"pins" : [
{
"identity" : "swift-log",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-log.git",
"state" : {
"revision" : "173f567a2dfec11d74588eea82cecea555bdc0bc",
"version" : "1.4.0"
}
},
{
"identity" : "wireguard-apple",
"kind" : "remoteSourceControl",
"location" : "https://github.com/mullvad/wireguard-apple.git",
"state" : {
"revision" : "f19338dafd349fd6ddb1c1032b5705d362f56d2b"
}
}
],
"version" : 3
}
8 changes: 6 additions & 2 deletions mullvad-ios/src/api/api.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
use mullvad_api::{rest::{self, MullvadRestHandle}, ApiProxy};
use mullvad_api::{
rest::{self, MullvadRestHandle},
ApiProxy,
};

use super::{
cancellation::{RequestCancelHandle, SwiftCancelHandle},
completion::{CompletionCookie, SwiftCompletionHandler},
response::SwiftMullvadApiResponse, SwiftApiContext,
response::SwiftMullvadApiResponse,
SwiftApiContext,
};

#[no_mangle]
Expand Down
10 changes: 10 additions & 0 deletions mullvad-ios/src/api/cancellation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ impl RequestCancelHandle {
}
}

/// 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`.
#[no_mangle]
extern "C" fn mullvad_api_cancel_task(handle_ptr: SwiftCancelHandle) {
if handle_ptr.ptr.is_null() {
Expand All @@ -64,6 +69,11 @@ extern "C" fn mullvad_api_cancel_task(handle_ptr: SwiftCancelHandle) {
handle.cancel()
}

/// 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`.
#[no_mangle]
extern "C" fn mullvad_api_cancel_task_drop(handle_ptr: SwiftCancelHandle) {
if handle_ptr.ptr.is_null() {
Expand Down
8 changes: 8 additions & 0 deletions mullvad-ios/src/api/completion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@ use std::sync::{Arc, Mutex};
use super::response::SwiftMullvadApiResponse;

extern "C" {
/// 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.
pub fn mullvad_api_completion_finish(
response: SwiftMullvadApiResponse,
completion_cookie: CompletionCookie,
Expand Down
29 changes: 14 additions & 15 deletions mullvad-ios/src/api/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{ffi::CStr, ptr::null_mut, sync::Arc};
use std::{ffi::CStr, sync::Arc};

use mullvad_api::{
proxy::{ApiConnectionMode, StaticConnectionModeProvider}, rest::MullvadRestHandle, ApiEndpoint, Runtime
Expand Down Expand Up @@ -32,31 +32,30 @@ impl ApiContext {
}
}

/// # 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.
#[no_mangle]
pub extern "C" fn mullvad_api_init_new(host: *const u8, address: *const u8) -> SwiftApiContext {
let host = unsafe { CStr::from_ptr(host.cast()) };
let address = unsafe { CStr::from_ptr(address.cast()) };

let Ok(host) = host.to_str() else {
return SwiftApiContext(null_mut());
};

let Ok(address) = address.to_str() else {
return SwiftApiContext(null_mut());
};
let host = host.to_str().unwrap();
let address = address.to_str().unwrap();

let endpoint = ApiEndpoint {
host: Some(String::from(host)),
address: Some(address.parse().unwrap()),
};

let tokio_handle = match crate::mullvad_ios_runtime() {
Ok(tokio_handle) => tokio_handle,
Err(err) => {
log::error!("Failed to obtain a handle to a tokio runtime: {err}");
return SwiftApiContext(null_mut());
}
};
let tokio_handle = crate::mullvad_ios_runtime().unwrap();

let api_context = tokio_handle.clone().block_on(async move {
// It is imperative that the REST runtime is created within an async context, otherwise
Expand Down
15 changes: 8 additions & 7 deletions mullvad-ios/src/api/response.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
use std::{
ffi::CString,
ptr::null_mut,
};
use std::{ffi::CString, ptr::null_mut};

use mullvad_api::rest::{self, Response};


#[repr(C)]
pub struct SwiftMullvadApiResponse {
body: *mut u8,
Expand Down Expand Up @@ -50,7 +46,7 @@ impl SwiftMullvadApiResponse {

let should_retry = err.is_network_error();
let error_description = to_cstr_pointer(err.to_string());
let (status_code, server_response_code):(u16, _) =
let (status_code, server_response_code): (u16, _) =
if let rest::Error::ApiError(status_code, error_code) = err {
(status_code.into(), to_cstr_pointer(error_code))
} else {
Expand Down Expand Up @@ -81,7 +77,7 @@ impl SwiftMullvadApiResponse {
retry_after: 0,
}
}

pub fn no_tokio_runtime() -> Self {
Self {
success: false,
Expand All @@ -96,6 +92,11 @@ impl SwiftMullvadApiResponse {
}
}

/// 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`.
#[no_mangle]
pub unsafe extern "C" fn mullvad_response_drop(response: SwiftMullvadApiResponse) {
if !response.body.is_null() {
Expand Down
4 changes: 1 addition & 3 deletions mullvad-ios/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#![cfg(target_os = "ios")]
mod api;
mod encrypted_dns_proxy;
mod ephemeral_peer_proxy;
mod shadowsocks_proxy;
mod api;
pub mod tunnel_obfuscator_proxy;

#[repr(C)]
Expand Down Expand Up @@ -34,5 +34,3 @@ mod ios {
}

use ios::*;


0 comments on commit 90acf06

Please sign in to comment.