summaryrefslogtreecommitdiffhomepage
path: root/mullvad-ios/src/api_client/helpers.rs
blob: e363c5862c2c060e0a1862124bc43c7c1090f43a (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
use std::{
    ffi::{c_char, c_void},
    net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
};

use talpid_types::net::proxy::{Shadowsocks, Socks5Remote, SocksAuth};

use super::get_string;

/// Constructs a new IP address from a pointer containing bytes representing an IP address.
///
/// SAFETY: `addr` pointer must be non-null, aligned, and point to at least addr_len bytes
pub(crate) unsafe fn parse_ip_addr(addr: *const u8, addr_len: usize) -> Option<IpAddr> {
    match addr_len {
        4 => {
            // SAFETY: `addr` pointer must be non-null, aligned, and point to at least addr_len bytes
            let bytes = unsafe { std::slice::from_raw_parts(addr, addr_len) };
            Some(Ipv4Addr::new(bytes[0], bytes[1], bytes[2], bytes[3]).into())
        }
        16 => {
            // SAFETY: `addr` pointer must be non-null, aligned, and point to at least addr_len bytes
            let bytes = unsafe { std::slice::from_raw_parts(addr, addr_len) };
            let mut addr_arr = [0u8; 16];
            addr_arr.as_mut_slice().copy_from_slice(bytes);

            Some(Ipv6Addr::from(addr_arr).into())
        }
        anything_else => {
            log::error!("Bad IP address length {anything_else}");
            None
        }
    }
}

/// Converts parameters into a boxed `Shadowsocks` configuration that is safe
/// to send across the FFI boundary
///
/// # SAFETY
/// `address` must be a pointer to at least `address_len` bytes.
/// `c_password` and `c_cipher` must be pointers to null terminated strings
#[unsafe(no_mangle)]
pub unsafe extern "C" fn new_shadowsocks_access_method_setting(
    address: *const u8,
    address_len: usize,
    port: u16,
    c_password: *const c_char,
    c_cipher: *const c_char,
) -> *const c_void {
    let endpoint: SocketAddr =
        if let Some(ip_address) = unsafe { parse_ip_addr(address, address_len) } {
            SocketAddr::new(ip_address, port)
        } else {
            return std::ptr::null();
        };

    let password = unsafe { get_string(c_password) };
    let cipher = unsafe { get_string(c_cipher) };

    let shadowsocks_configuration = Shadowsocks {
        endpoint,
        password,
        cipher,
    };

    Box::into_raw(Box::new(shadowsocks_configuration)) as *mut c_void
}

/// Converts parameters into a boxed `Socks5Remote` configuration that is safe
///
/// to send across the FFI boundary
///
/// # SAFETY
/// `address` must be a pointer to at least `address_len` bytes.
/// `c_username` and `c_password` must be pointers to null terminated strings, or null
#[unsafe(no_mangle)]
pub unsafe extern "C" fn new_socks5_access_method_setting(
    address: *const u8,
    address_len: usize,
    port: u16,
    c_username: *const c_char,
    c_password: *const c_char,
) -> *const c_void {
    let endpoint: SocketAddr =
        if let Some(ip_address) = unsafe { parse_ip_addr(address, address_len) } {
            SocketAddr::new(ip_address, port)
        } else {
            return std::ptr::null();
        };

    let auth = {
        if c_username.is_null() || c_password.is_null() {
            None
        } else {
            let username = unsafe { get_string(c_username) };
            let password = unsafe { get_string(c_password) };
            SocksAuth::new(username, password).ok()
        }
    };

    let socks5_configuration = Socks5Remote { endpoint, auth };
    Box::into_raw(Box::new(socks5_configuration)) as *mut c_void
}