Skip to main content

reacher_fast_socks5/
lib.rs

1#[forbid(unsafe_code)]
2#[macro_use]
3extern crate log;
4
5pub mod client;
6pub mod server;
7pub mod util;
8
9#[cfg(feature = "socks4")]
10pub mod socks4;
11
12use anyhow::Context;
13use std::fmt;
14use std::io;
15use thiserror::Error;
16use util::target_addr::read_address;
17use util::target_addr::TargetAddr;
18use util::target_addr::ToTargetAddr;
19
20use tokio::io::AsyncReadExt;
21
22#[rustfmt::skip]
23pub mod consts {
24    pub const SOCKS5_VERSION:                          u8 = 0x05;
25
26    pub const SOCKS5_AUTH_METHOD_NONE:                 u8 = 0x00;
27    pub const SOCKS5_AUTH_METHOD_GSSAPI:               u8 = 0x01;
28    pub const SOCKS5_AUTH_METHOD_PASSWORD:             u8 = 0x02;
29    pub const SOCKS5_AUTH_METHOD_NOT_ACCEPTABLE:       u8 = 0xff;
30
31    pub const SOCKS5_CMD_TCP_CONNECT:                  u8 = 0x01;
32    pub const SOCKS5_CMD_TCP_BIND:                     u8 = 0x02;
33    pub const SOCKS5_CMD_UDP_ASSOCIATE:                u8 = 0x03;
34
35    pub const SOCKS5_ADDR_TYPE_IPV4:                   u8 = 0x01;
36    pub const SOCKS5_ADDR_TYPE_DOMAIN_NAME:            u8 = 0x03;
37    pub const SOCKS5_ADDR_TYPE_IPV6:                   u8 = 0x04;
38
39    pub const SOCKS5_REPLY_SUCCEEDED:                  u8 = 0x00;
40    pub const SOCKS5_REPLY_GENERAL_FAILURE:            u8 = 0x01;
41    pub const SOCKS5_REPLY_CONNECTION_NOT_ALLOWED:     u8 = 0x02;
42    pub const SOCKS5_REPLY_NETWORK_UNREACHABLE:        u8 = 0x03;
43    pub const SOCKS5_REPLY_HOST_UNREACHABLE:           u8 = 0x04;
44    pub const SOCKS5_REPLY_CONNECTION_REFUSED:         u8 = 0x05;
45    pub const SOCKS5_REPLY_TTL_EXPIRED:                u8 = 0x06;
46    pub const SOCKS5_REPLY_COMMAND_NOT_SUPPORTED:      u8 = 0x07;
47    pub const SOCKS5_REPLY_ADDRESS_TYPE_NOT_SUPPORTED: u8 = 0x08;
48}
49
50#[derive(Debug, PartialEq)]
51pub enum Socks5Command {
52    TCPConnect,
53    TCPBind,
54    UDPAssociate,
55}
56
57#[allow(dead_code)]
58impl Socks5Command {
59    #[inline]
60    #[rustfmt::skip]
61    fn as_u8(&self) -> u8 {
62        match self {
63            Socks5Command::TCPConnect   => consts::SOCKS5_CMD_TCP_CONNECT,
64            Socks5Command::TCPBind      => consts::SOCKS5_CMD_TCP_BIND,
65            Socks5Command::UDPAssociate => consts::SOCKS5_CMD_UDP_ASSOCIATE,
66        }
67    }
68
69    #[inline]
70    #[rustfmt::skip]
71    fn from_u8(code: u8) -> Option<Socks5Command> {
72        match code {
73            consts::SOCKS5_CMD_TCP_CONNECT      => Some(Socks5Command::TCPConnect),
74            consts::SOCKS5_CMD_TCP_BIND         => Some(Socks5Command::TCPBind),
75            consts::SOCKS5_CMD_UDP_ASSOCIATE    => Some(Socks5Command::UDPAssociate),
76            _ => None,
77        }
78    }
79}
80
81#[derive(Debug, PartialEq)]
82pub enum AuthenticationMethod {
83    None,
84    Password { username: String, password: String },
85}
86
87impl AuthenticationMethod {
88    #[inline]
89    #[rustfmt::skip]
90    fn as_u8(&self) -> u8 {
91        match self {
92            AuthenticationMethod::None => consts::SOCKS5_AUTH_METHOD_NONE,
93            AuthenticationMethod::Password {..} =>
94                consts::SOCKS5_AUTH_METHOD_PASSWORD
95        }
96    }
97
98    #[inline]
99    #[rustfmt::skip]
100    fn from_u8(code: u8) -> Option<AuthenticationMethod> {
101        match code {
102            consts::SOCKS5_AUTH_METHOD_NONE     => Some(AuthenticationMethod::None),
103            consts::SOCKS5_AUTH_METHOD_PASSWORD => Some(AuthenticationMethod::Password { username: "test".to_string(), password: "test".to_string()}),
104            _                                   => None,
105        }
106    }
107}
108
109impl fmt::Display for AuthenticationMethod {
110    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111        match *self {
112            AuthenticationMethod::None => f.write_str("AuthenticationMethod::None"),
113            AuthenticationMethod::Password { .. } => f.write_str("AuthenticationMethod::Password"),
114        }
115    }
116}
117
118//impl Vec<AuthenticationMethod> {
119//    pub fn as_bytes(&self) -> &[u8] {
120//        self.iter().map(|l| l.as_u8()).collect()
121//    }
122//}
123//
124//impl From<&[AuthenticationMethod]> for &[u8] {
125//    fn from(_: Vec<AuthenticationMethod>) -> Self {
126//        &[0x00]
127//    }
128//}
129
130#[derive(Error, Debug)]
131pub enum SocksError {
132    #[error("i/o error: {0}")]
133    Io(#[from] io::Error),
134    #[error("the data for key `{0}` is not available")]
135    Redaction(String),
136    #[error("invalid header (expected {expected:?}, found {found:?})")]
137    InvalidHeader { expected: String, found: String },
138
139    #[error("Auth method unacceptable `{0:?}`.")]
140    AuthMethodUnacceptable(Vec<u8>),
141    #[error("Unsupported SOCKS version `{0}`.")]
142    UnsupportedSocksVersion(u8),
143    #[error("Domain exceeded max sequence length")]
144    ExceededMaxDomainLen(usize),
145    #[error("Authentication failed `{0}`")]
146    AuthenticationFailed(String),
147    #[error("Authentication rejected `{0}`")]
148    AuthenticationRejected(String),
149
150    #[error("Error with reply: {0}.")]
151    ReplyError(#[from] ReplyError),
152
153    #[cfg(feature = "socks4")]
154    #[error("Error with reply: {0}.")]
155    ReplySocks4Error(#[from] socks4::ReplyError),
156
157    #[error("Argument input error: `{0}`.")]
158    ArgumentInputError(&'static str),
159
160    //    #[error("Other: `{0}`.")]
161    #[error(transparent)]
162    Other(#[from] anyhow::Error),
163}
164
165pub type Result<T, E = SocksError> = core::result::Result<T, E>;
166
167/// SOCKS5 reply code
168#[derive(Error, Debug, Copy, Clone)]
169pub enum ReplyError {
170    #[error("Succeeded")]
171    Succeeded,
172    #[error("General failure")]
173    GeneralFailure,
174    #[error("Connection not allowed by ruleset")]
175    ConnectionNotAllowed,
176    #[error("Network unreachable")]
177    NetworkUnreachable,
178    #[error("Host unreachable")]
179    HostUnreachable,
180    #[error("Connection refused")]
181    ConnectionRefused,
182    #[error("TTL expired")]
183    TtlExpired,
184    #[error("Command not supported")]
185    CommandNotSupported,
186    #[error("Address type not supported")]
187    AddressTypeNotSupported,
188    //    OtherReply(u8),
189}
190
191impl ReplyError {
192    #[inline]
193    #[rustfmt::skip]
194    pub fn as_u8(self) -> u8 {
195        match self {
196            ReplyError::Succeeded               => consts::SOCKS5_REPLY_SUCCEEDED,
197            ReplyError::GeneralFailure          => consts::SOCKS5_REPLY_GENERAL_FAILURE,
198            ReplyError::ConnectionNotAllowed    => consts::SOCKS5_REPLY_CONNECTION_NOT_ALLOWED,
199            ReplyError::NetworkUnreachable      => consts::SOCKS5_REPLY_NETWORK_UNREACHABLE,
200            ReplyError::HostUnreachable         => consts::SOCKS5_REPLY_HOST_UNREACHABLE,
201            ReplyError::ConnectionRefused       => consts::SOCKS5_REPLY_CONNECTION_REFUSED,
202            ReplyError::TtlExpired              => consts::SOCKS5_REPLY_TTL_EXPIRED,
203            ReplyError::CommandNotSupported     => consts::SOCKS5_REPLY_COMMAND_NOT_SUPPORTED,
204            ReplyError::AddressTypeNotSupported => consts::SOCKS5_REPLY_ADDRESS_TYPE_NOT_SUPPORTED,
205//            ReplyError::OtherReply(c)           => c,
206        }
207    }
208
209    #[inline]
210    #[rustfmt::skip]
211    pub fn from_u8(code: u8) -> ReplyError {
212        match code {
213            consts::SOCKS5_REPLY_SUCCEEDED                  => ReplyError::Succeeded,
214            consts::SOCKS5_REPLY_GENERAL_FAILURE            => ReplyError::GeneralFailure,
215            consts::SOCKS5_REPLY_CONNECTION_NOT_ALLOWED     => ReplyError::ConnectionNotAllowed,
216            consts::SOCKS5_REPLY_NETWORK_UNREACHABLE        => ReplyError::NetworkUnreachable,
217            consts::SOCKS5_REPLY_HOST_UNREACHABLE           => ReplyError::HostUnreachable,
218            consts::SOCKS5_REPLY_CONNECTION_REFUSED         => ReplyError::ConnectionRefused,
219            consts::SOCKS5_REPLY_TTL_EXPIRED                => ReplyError::TtlExpired,
220            consts::SOCKS5_REPLY_COMMAND_NOT_SUPPORTED      => ReplyError::CommandNotSupported,
221            consts::SOCKS5_REPLY_ADDRESS_TYPE_NOT_SUPPORTED => ReplyError::AddressTypeNotSupported,
222//            _                                               => ReplyError::OtherReply(code),
223            _                                               => unreachable!("ReplyError code unsupported."),
224        }
225    }
226}
227
228/// Generate UDP header
229///
230/// # UDP Request header structure.
231/// ```text
232/// +----+------+------+----------+----------+----------+
233/// |RSV | FRAG | ATYP | DST.ADDR | DST.PORT |   DATA   |
234/// +----+------+------+----------+----------+----------+
235/// | 2  |  1   |  1   | Variable |    2     | Variable |
236/// +----+------+------+----------+----------+----------+
237///
238/// The fields in the UDP request header are:
239///
240///     o  RSV  Reserved X'0000'
241///     o  FRAG    Current fragment number
242///     o  ATYP    address type of following addresses:
243///        o  IP V4 address: X'01'
244///        o  DOMAINNAME: X'03'
245///        o  IP V6 address: X'04'
246///     o  DST.ADDR       desired destination address
247///     o  DST.PORT       desired destination port
248///     o  DATA     user data
249/// ```
250pub fn new_udp_header<T: ToTargetAddr>(target_addr: T) -> Result<Vec<u8>> {
251    let mut header = vec![
252        0, 0, // RSV
253        0, // FRAG
254    ];
255    header.append(&mut target_addr.to_target_addr()?.to_be_bytes()?);
256
257    Ok(header)
258}
259
260/// Parse data from UDP client on raw buffer, return (frag, target_addr, payload).
261pub async fn parse_udp_request<'a>(mut req: &'a [u8]) -> Result<(u8, TargetAddr, &'a [u8])> {
262    let rsv = read_exact!(req, [0u8; 2]).context("Malformed request")?;
263
264    if !rsv.eq(&[0u8; 2]) {
265        return Err(ReplyError::GeneralFailure.into());
266    }
267
268    let [frag, atyp] = read_exact!(req, [0u8; 2]).context("Malformed request")?;
269
270    let target_addr = read_address(&mut req, atyp).await.map_err(|e| {
271        // print explicit error
272        error!("{:#}", e);
273        // then convert it to a reply
274        ReplyError::AddressTypeNotSupported
275    })?;
276
277    Ok((frag, target_addr, req))
278}
279
280#[cfg(test)]
281mod test {
282    use anyhow::Result;
283    use tokio::{
284        net::{TcpListener, TcpStream, UdpSocket},
285        sync::oneshot::Sender,
286    };
287
288    use crate::{
289        client,
290        server::{self, SimpleUserPassword},
291    };
292    use std::{
293        net::{SocketAddr, ToSocketAddrs},
294        num::ParseIntError,
295        sync::Arc,
296    };
297    use tokio::io::{AsyncReadExt, AsyncWriteExt};
298    use tokio::sync::oneshot;
299    use tokio_test::block_on;
300
301    fn init() {
302        let _ = env_logger::builder().is_test(true).try_init();
303    }
304
305    async fn setup_socks_server(
306        proxy_addr: &str,
307        auth: Option<SimpleUserPassword>,
308        tx: Sender<SocketAddr>,
309    ) -> Result<()> {
310        let mut config = server::Config::default();
311        config.set_udp_support(true);
312        match auth {
313            None => {}
314            Some(up) => {
315                config.set_authentication(up);
316            }
317        }
318
319        let config = Arc::new(config);
320        let listener = TcpListener::bind(proxy_addr).await?;
321        tx.send(listener.local_addr()?).unwrap();
322        loop {
323            let (stream, _) = listener.accept().await?;
324            let mut socks5_socket = server::Socks5Socket::new(stream, config.clone());
325            socks5_socket.set_reply_ip(proxy_addr.parse::<SocketAddr>().unwrap().ip());
326
327            socks5_socket.upgrade_to_socks5().await?;
328        }
329    }
330
331    async fn google(mut socket: TcpStream) -> Result<()> {
332        socket.write_all(b"GET / HTTP/1.0\r\n\r\n").await?;
333        let mut result = vec![];
334        socket.read_to_end(&mut result).await?;
335
336        println!("{}", String::from_utf8_lossy(&result));
337        assert!(result.starts_with(b"HTTP/1.0"));
338        assert!(result.ends_with(b"</HTML>\r\n") || result.ends_with(b"</html>"));
339
340        Ok(())
341    }
342
343    #[test]
344    fn google_no_auth() {
345        init();
346        block_on(async {
347            let (tx, rx) = oneshot::channel();
348            tokio::spawn(setup_socks_server("[::1]:0", None, tx));
349
350            let socket = client::Socks5Stream::connect(
351                rx.await.unwrap(),
352                "google.com".to_owned(),
353                80,
354                client::Config::default(),
355            )
356            .await
357            .unwrap();
358            google(socket.get_socket()).await.unwrap();
359        });
360    }
361
362    #[test]
363    fn mock_udp_assosiate_no_auth() {
364        init();
365        block_on(async {
366            const MOCK_ADDRESS: &str = "[::1]:40235";
367
368            let (tx, rx) = oneshot::channel();
369            tokio::spawn(setup_socks_server("[::1]:0", None, tx));
370            let backing_socket = TcpStream::connect(rx.await.unwrap()).await.unwrap();
371
372            // Creates a UDP tunnel which can be used to forward UDP packets, "[::]:0" indicates the
373            // binding source address used to communicate with the socks5 server.
374            let tunnel = client::Socks5Datagram::bind(backing_socket, "[::]:0")
375                .await
376                .unwrap();
377            let mock_udp_server = UdpSocket::bind(MOCK_ADDRESS).await.unwrap();
378
379            tunnel
380                .send_to(
381                    b"hello world!",
382                    MOCK_ADDRESS.to_socket_addrs().unwrap().next().unwrap(),
383                )
384                .await
385                .unwrap();
386            println!("Send packet to {}", MOCK_ADDRESS);
387
388            let mut buf = [0; 13];
389            let (len, addr) = mock_udp_server.recv_from(&mut buf).await.unwrap();
390            assert_eq!(len, 12);
391            assert_eq!(&buf[..12], b"hello world!");
392
393            mock_udp_server
394                .send_to(b"hello world!", addr)
395                .await
396                .unwrap();
397
398            println!("Recieve packet from {}", MOCK_ADDRESS);
399            let len = tunnel.recv_from(&mut buf).await.unwrap().0;
400            assert_eq!(len, 12);
401            assert_eq!(&buf[..12], b"hello world!");
402        });
403    }
404
405    #[test]
406    fn dns_udp_assosiate_no_auth() {
407        init();
408        block_on(async {
409            const DNS_SERVER: &str = "1.1.1.1:53";
410
411            let (tx, rx) = oneshot::channel();
412            tokio::spawn(setup_socks_server("[::1]:0", None, tx));
413            let backing_socket = TcpStream::connect(rx.await.unwrap()).await.unwrap();
414
415            // Creates a UDP tunnel which can be used to forward UDP packets, "[::]:0" indicates the
416            // binding source address used to communicate with the socks5 server.
417            let tunnel = client::Socks5Datagram::bind(backing_socket, "[::]:0")
418                .await
419                .unwrap();
420
421            #[rustfmt::skip]
422            tunnel.send_to(
423                &decode_hex(&(
424                    "AAAA".to_owned()   // ID
425                    + "0100"            // Query parameters
426                    + "0001"            // Number of questions
427                    + "0000"            // Number of answers
428                    + "0000"            // Number of authority records
429                    + "0000"            // Number of additional records
430                    + "076578616d706c65"// Length + hex("example")
431                    + "03636f6d00"      // Length + hex("com") + zero byte
432                    + "0001"            // QTYPE
433                    + "0001"            // QCLASS
434                ))
435                .unwrap(),
436                DNS_SERVER.to_socket_addrs().unwrap().next().unwrap(),
437            ).await.unwrap();
438            println!("Send packet to {}", DNS_SERVER);
439
440            let mut buf = [0; 128];
441            println!("Recieve packet from {}", DNS_SERVER);
442            tunnel.recv_from(&mut buf).await.unwrap();
443            println!("dns response {:?}", buf);
444
445            #[rustfmt::skip]
446            assert!(buf.starts_with(&decode_hex(&(
447                "AAAA".to_owned()   // ID
448                + "8180"            // FLAGS: RCODE=0, No errors reported
449                + "0001"            // One question
450            )).unwrap()));
451        });
452    }
453
454    fn decode_hex(s: &str) -> Result<Vec<u8>, ParseIntError> {
455        (0..s.len())
456            .step_by(2)
457            .map(|i| u8::from_str_radix(&s[i..i + 2], 16))
458            .collect()
459    }
460}