shadowsocks/net/
option.rs

1//! Options for connecting to remote server
2
3use std::{net::SocketAddr, time::Duration};
4
5/// Options for connecting to TCP remote server
6#[derive(Debug, Clone, Default)]
7pub struct TcpSocketOpts {
8    /// TCP socket's `SO_SNDBUF`
9    pub send_buffer_size: Option<u32>,
10
11    /// TCP socket's `SO_RCVBUF`
12    pub recv_buffer_size: Option<u32>,
13
14    /// `TCP_NODELAY`
15    pub nodelay: bool,
16
17    /// `TCP_FASTOPEN`, enables TFO
18    pub fastopen: bool,
19
20    /// `SO_KEEPALIVE` and sets `TCP_KEEPIDLE`, `TCP_KEEPINTVL` and `TCP_KEEPCNT` respectively,
21    /// enables keep-alive messages on connection-oriented sockets
22    pub keepalive: Option<Duration>,
23
24    /// Enable Multipath-TCP (mptcp)
25    /// https://en.wikipedia.org/wiki/Multipath_TCP
26    ///
27    /// Currently only supported on
28    /// - macOS (iOS, watchOS, ...) with Client Support only.
29    /// - Linux (>5.19)
30    pub mptcp: bool,
31}
32
33/// Options for UDP server
34#[derive(Debug, Clone, Default)]
35pub struct UdpSocketOpts {
36    /// Maximum Transmission Unit (MTU) for UDP socket `recv`
37    ///
38    /// NOTE: MTU includes IP header, UDP header, UDP payload
39    pub mtu: Option<usize>,
40
41    /// Outbound UDP socket allows IP fragmentation
42    pub allow_fragmentation: bool,
43}
44
45/// Options for connecting to remote server
46#[derive(Debug, Clone, Default)]
47pub struct ConnectOpts {
48    /// Linux mark based routing, going to set by `setsockopt` with `SO_MARK` option
49    #[cfg(any(target_os = "linux", target_os = "android"))]
50    pub fwmark: Option<u32>,
51
52    /// FreeBSD SO_USER_COOKIE
53    /// https://www.freebsd.org/cgi/man.cgi?query=setsockopt&sektion=2
54    #[cfg(target_os = "freebsd")]
55    pub user_cookie: Option<u32>,
56
57    /// An IPC unix socket path for sending file descriptors to call `VpnService.protect`
58    ///
59    /// This is an [Android shadowsocks implementation](https://github.com/shadowsocks/shadowsocks-android) specific feature
60    #[cfg(target_os = "android")]
61    pub vpn_protect_path: Option<std::path::PathBuf>,
62    /// A customizable socket protect implementation for Android for calling `VpnService.protect(fd)`
63    ///
64    /// see [`ConnectOpts::set_vpn_socket_protect`]
65    #[cfg(target_os = "android")]
66    pub vpn_socket_protect: Option<std::sync::Arc<Box<dyn android::SocketProtect + Send + Sync>>>,
67
68    /// Outbound socket binds to this IP address, mostly for choosing network interfaces
69    ///
70    /// It only affects sockets that trying to connect to addresses with the same family
71    pub bind_local_addr: Option<SocketAddr>,
72
73    /// Outbound socket binds to interface
74    pub bind_interface: Option<String>,
75
76    /// TCP options
77    pub tcp: TcpSocketOpts,
78
79    /// UDP options
80    pub udp: UdpSocketOpts,
81}
82
83/// Inbound connection options
84#[derive(Clone, Debug, Default)]
85pub struct AcceptOpts {
86    /// TCP options
87    pub tcp: TcpSocketOpts,
88
89    /// UDP options
90    pub udp: UdpSocketOpts,
91
92    /// Enable IPV6_V6ONLY option for socket
93    pub ipv6_only: bool,
94}
95
96#[cfg(target_os = "android")]
97impl ConnectOpts {
98    /// Set `vpn_protect_path` for Android VPNService.protect implementation
99    ///
100    /// Example:
101    ///
102    /// ```rust
103    /// // Sync function for calling `VpnService.protect(fd)`
104    /// opts.set_vpn_socket_protect(|fd| {
105    ///     // Your implementation here
106    ///     // For example, using `jni` to call Android's VpnService.protect(fd)
107    ///     Ok(())
108    /// });
109    /// ```
110    pub fn set_vpn_socket_protect<F>(&mut self, f: F)
111    where
112        F: android::MakeSocketProtect + Send + Sync + 'static,
113        F::SocketProtectType: android::SocketProtect + Send + Sync + 'static,
114    {
115        self.vpn_socket_protect = Some(std::sync::Arc::new(Box::new(f.make_socket_protect())));
116    }
117}
118
119/// Android specific features
120#[cfg(target_os = "android")]
121pub mod android {
122    use sealed::sealed;
123    use std::{fmt, io, os::unix::io::RawFd};
124
125    /// Android VPN socket protect implemetation
126    #[sealed]
127    pub trait SocketProtect {
128        /// Protects the socket file descriptor by calling `VpnService.protect(fd)`
129        fn protect(&self, fd: RawFd) -> io::Result<()>;
130    }
131
132    impl fmt::Debug for dyn SocketProtect + Send + Sync {
133        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134            f.debug_struct("SocketProtect").finish_non_exhaustive()
135        }
136    }
137
138    /// Creating an instance of `SocketProtect`
139    #[sealed]
140    pub trait MakeSocketProtect {
141        type SocketProtectType: SocketProtect;
142
143        /// Creates an instance of `SocketProtect`
144        fn make_socket_protect(self) -> Self::SocketProtectType;
145    }
146
147    /// A function that implements `SocketProtect` trait
148    pub struct SocketProtectFn<F> {
149        f: F,
150    }
151
152    #[sealed]
153    impl<F> SocketProtect for SocketProtectFn<F>
154    where
155        F: Fn(RawFd) -> io::Result<()> + Send + Sync + 'static,
156    {
157        fn protect(&self, fd: RawFd) -> io::Result<()> {
158            (self.f)(fd)
159        }
160    }
161
162    #[sealed]
163    impl<F> MakeSocketProtect for F
164    where
165        F: Fn(RawFd) -> io::Result<()> + Send + Sync + 'static,
166    {
167        type SocketProtectType = SocketProtectFn<F>;
168
169        fn make_socket_protect(self) -> Self::SocketProtectType {
170            SocketProtectFn { f: self }
171        }
172    }
173}