summaryrefslogtreecommitdiffhomepage
path: root/mullvad-ios/src/shadowsocks_proxy/ffi.rs
blob: bd376e6caf5cee7cff0f41dba1965ed1ccb11ffe (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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
use super::{ShadowsocksHandle, run_forwarding_proxy};
use crate::ProxyHandle;
use crate::api_client::helpers::parse_ip_addr;
use std::net::SocketAddr;
#[cfg(any(target_os = "macos", target_os = "ios"))]
use std::sync::Once;

#[cfg(any(target_os = "macos", target_os = "ios"))]
static INIT_LOGGING: Once = Once::new();

/// # Safety
/// `addr`, `password`, `cipher` must be valid for the lifetime of this function call and they must
/// be backed by the amount of bytes as stored in the respective `*_len` parameters.
///
/// `proxy_config` must be pointing to a valid memory region for the size of a `ProxyHandle`
/// instance.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn start_shadowsocks_proxy(
    forward_address: *const u8,
    forward_address_len: usize,
    forward_port: u16,
    addr: *const u8,
    addr_len: usize,
    port: u16,
    password: *const u8,
    password_len: usize,
    cipher: *const u8,
    cipher_len: usize,
    proxy_config: *mut ProxyHandle,
) -> i32 {
    #[cfg(any(target_os = "macos", target_os = "ios"))]
    INIT_LOGGING.call_once(|| {
        let _ = oslog::OsLogger::new("net.mullvad.MullvadVPN.ShadowSocks")
            .level_filter(log::LevelFilter::Info)
            .init();
    });

    let forward_ip = if let Some(forward_address) =
        { unsafe { parse_ip_addr(forward_address, forward_address_len) } }
    {
        forward_address
    } else {
        return -1;
    };

    let forward_socket_addr = SocketAddr::new(forward_ip, forward_port);

    let bridge_ip = if let Some(addr) = { unsafe { parse_ip_addr(addr, addr_len) } } {
        addr
    } else {
        return -1;
    };

    let bridge_socket_addr = SocketAddr::new(bridge_ip, port);

    // SAFETY: See notes for `parse_str`
    let password = if let Some(password) = unsafe { parse_str(password, password_len) } {
        password
    } else {
        return -1;
    };

    // SAFETY: See notes for `parse_str`
    let cipher = if let Some(cipher) = unsafe { parse_str(cipher, cipher_len) } {
        cipher
    } else {
        return -1;
    };

    let (port, handle) =
        match run_forwarding_proxy(forward_socket_addr, bridge_socket_addr, &password, &cipher) {
            Ok((port, handle)) => (port, handle),
            Err(err) => {
                log::error!("Failed to run HTTP proxy {}", err);
                return err.raw_os_error().unwrap_or(-1);
            }
        };
    let handle = Box::new(handle);

    // SAFETY: `proxy_config` is guaranteed to be writeable for the duration of this call.
    // It does not overlap with `handle`
    unsafe {
        std::ptr::write(
            proxy_config,
            ProxyHandle {
                port,
                context: Box::into_raw(handle) as *mut _,
            },
        )
    }

    0
}
/// # Safety
/// `proxy_config` must be pointing to a valid instance of a `ProxyInstance`, as instantiated by
/// `start_shadowsocks_proxy`.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn stop_shadowsocks_proxy(proxy_config: *mut ProxyHandle) -> i32 {
    // SAFETY: `proxy_config` is guaranteed to be a valid pointer
    let context_ptr = unsafe { (*proxy_config).context };
    if context_ptr.is_null() {
        return -1;
    }

    // SAFETY: `context_ptr` is guaranteed to be a valid, non-null pointer
    let proxy_handle: Box<ShadowsocksHandle> = unsafe { Box::from_raw(context_ptr as *mut _) };
    proxy_handle.stop();
    // SAFETY: `proxy_config` is guaranteed to be a valid pointer
    unsafe { (*proxy_config).context = std::ptr::null_mut() };
    0
}

/// Allocates a new string with the contents of `data` if it contains only valid UTF-8 bytes.
///
/// SAFETY: `data` must be a valid pointer to `len` amount of bytes
unsafe fn parse_str(data: *const u8, len: usize) -> Option<String> {
    // SAFETY: data pointer must be valid for the size of len
    let bytes = unsafe { std::slice::from_raw_parts(data, len) };
    String::from_utf8(bytes.to_vec()).ok()
}