summaryrefslogtreecommitdiffhomepage
path: root/mullvad-ios/src/api_client/address_cache_provider.rs
blob: 0458ae2fe3bdddc05b42a2fc790d2ae8f1f3d7c0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
use libc::c_char;
use std::{ffi::c_void, net::SocketAddr};

use super::get_string;

unsafe extern "C" {
    /// Return the latest available endpoint, or a default one if none are cached
    ///
    /// # SAFETY
    /// `rawAddressCacheProvider` **must** be provided by a call to `init_swift_address_cache_wrapper`
    /// It is okay to persist it, and use it accross multiple threads.
    pub fn swift_get_cached_endpoint(
        rawAddressCacheProvider: *const c_void,
    ) -> LateStringDeallocator;
}

#[derive(Debug)]
#[repr(C)]
pub struct SwiftAddressCacheWrapper(SwiftAddressCacheProviderContext);

impl SwiftAddressCacheWrapper {
    pub fn new(context: SwiftAddressCacheProviderContext) -> SwiftAddressCacheWrapper {
        SwiftAddressCacheWrapper(context)
    }

    pub fn get_addrs(&self) -> SocketAddr {
        self.context_ref().get_addrs()
    }

    fn context_ref(&self) -> &SwiftAddressCacheProviderContext {
        &self.0
    }
}

// SAFETY: The `address_cache` inside the `SwiftAddressCacheProviderContext`'s context is guaranteed to be thread safe
unsafe impl Sync for SwiftAddressCacheProviderContext {}
// SAFETY: The `address_cache` inside the `SwiftAddressCacheProviderContext`'s context is guaranteed to be Sendable
unsafe impl Send for SwiftAddressCacheProviderContext {}

#[derive(Debug)]
#[repr(C)]
pub struct SwiftAddressCacheProviderContext {
    address_cache: *const c_void,
}

/// A struct used to deallocate a pointer to a C String later than when the pointer's control is relinquished from Swift.
/// Use the `deallocate_ptr` function on `ptr` to call the custom deallocator provided by Swift.
#[repr(C)]
pub struct LateStringDeallocator {
    ptr: *const c_char,
    deallocate_ptr: unsafe extern "C" fn(*const c_char),
}

impl SwiftAddressCacheProviderContext {
    pub fn get_addrs(&self) -> SocketAddr {
        // SAFETY: See notice for `swift_get_cached_endpoint`
        let deallocator = unsafe { swift_get_cached_endpoint(self.address_cache) };

        // SAFETY: The pointer contained in the late deallocator returned by `swift_get_cached_endpoint`
        // is guaranteed to point to a valid UTF-8 String
        // It is also guaranteed to be a valid representation of either an IPv4 or IPv6 address
        let cached_address = unsafe { get_string(deallocator.ptr) }
            .parse()
            .expect("Invalid socket address in cache");

        // SAFETY: The pointer in `deallocator.ptr` must not be used after `deallocate_ptr` has been called.
        // `deallocate_ptr` must be called only once
        unsafe { (deallocator.deallocate_ptr)(deallocator.ptr) };
        cached_address
    }
}

/// Called by the Swift side in order to provide an object to rust that provides API addresses in a UTF-8 string form
///
/// # SAFETY
/// `address_cache` **must be** pointing to a valid instance of a `DefaultAddressCacheProvider`
/// That instance's lifetime has to be equivalent to a `'static` lifetime in Rust
/// This function does not take ownership of `address_cache`
#[unsafe(no_mangle)]
pub unsafe extern "C" fn init_swift_address_cache_wrapper(
    address_cache: *const c_void,
) -> SwiftAddressCacheWrapper {
    let context = SwiftAddressCacheProviderContext { address_cache };
    SwiftAddressCacheWrapper::new(context)
}