summaryrefslogtreecommitdiffhomepage
path: root/mullvad-ios/src/shadowsocks_proxy/mod.rs
blob: 3e3b7b151f77331b2332b615b5fa691cf1af9510 (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
121
use super::mullvad_ios_runtime;
use shadowsocks_service::{
    config::{
        Config, ConfigType, LocalConfig, LocalInstanceConfig, ProtocolType, ServerInstanceConfig,
    },
    local::Server,
    shadowsocks::{config::ServerConfig, crypto::CipherKind},
};
use std::{
    io,
    net::{IpAddr, Ipv4Addr, SocketAddr, TcpListener},
    str::FromStr,
};
use tokio::task::JoinHandle;
mod ffi;

pub fn run_forwarding_proxy(
    forward_socket_addr: SocketAddr,
    bridge_socket_addr: SocketAddr,
    password: &str,
    cipher: &str,
) -> io::Result<(u16, ShadowsocksHandle)> {
    let runtime =
        ShadowsocksService::new(forward_socket_addr, bridge_socket_addr, password, cipher)?;
    let port = runtime.port();
    let handle = runtime.run()?;

    Ok((port, handle))
}

struct ShadowsocksService {
    config: Config,
    local_port: u16,
}

pub struct ShadowsocksHandle {
    abort_handle: JoinHandle<()>,
}

impl ShadowsocksHandle {
    pub fn stop(self) {
        self.abort_handle.abort();
    }
}

impl ShadowsocksService {
    pub fn new(
        forward_socket_addr: SocketAddr,
        bridge_socket_addr: SocketAddr,
        password: &str,
        cipher: &str,
    ) -> io::Result<Self> {
        let (config, local_port) =
            Self::create_config(forward_socket_addr, bridge_socket_addr, password, cipher)?;
        Ok(Self { config, local_port })
    }

    pub fn port(&self) -> u16 {
        self.local_port
    }

    pub fn run(self) -> io::Result<ShadowsocksHandle> {
        let runtime = mullvad_ios_runtime().map_err(io::Error::other)?;

        let abort_handle = runtime.spawn(async move {
            self.run_service_inner().await;
        });

        Ok(ShadowsocksHandle { abort_handle })
    }

    async fn run_service_inner(self) {
        let Self { config, .. } = self;

        let _ = Server::new(config)
            .await
            .expect("Could not create Shadowsocks server")
            .run()
            .await;
    }

    pub fn create_config(
        forward_socket_addr: SocketAddr,
        bridge_socket_addr: SocketAddr,
        password: &str,
        cipher: &str,
    ) -> io::Result<(Config, u16)> {
        let mut cfg = Config::new(ConfigType::Local);
        let free_port = get_free_port()?;
        let bind_addr = SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), free_port);

        let mut local_config = LocalConfig::new_with_addr(bind_addr.into(), ProtocolType::Tunnel);
        local_config.forward_addr = Some(forward_socket_addr.into());
        cfg.local = vec![LocalInstanceConfig::with_local_config(local_config)];

        let cipher = match CipherKind::from_str(cipher) {
            Ok(cipher) => cipher,
            Err(err) => {
                return Err(io::Error::new(
                    io::ErrorKind::InvalidInput,
                    format!("Invalid cipher specified: {err}"),
                ));
            }
        };
        let server_config = ServerInstanceConfig::with_server_config(ServerConfig::new(
            bridge_socket_addr,
            password,
            cipher,
        ));

        cfg.server = vec![server_config];

        Ok((cfg, free_port))
    }
}

fn get_free_port() -> io::Result<u16> {
    let bind_addr: SocketAddr = SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 0);
    let port = TcpListener::bind(bind_addr)?.local_addr()?.port();
    Ok(port)
}