shadowsocks_service/local/socks/
socks4.rs

1//! Socks4a Protocol Definition
2//!
3//! <http://ftp.icm.edu.pl/packages/socks/socks4/SOCKS4.protocol>
4
5#![allow(dead_code)]
6
7use std::{
8    fmt,
9    io::{self, ErrorKind},
10    net::{Ipv4Addr, SocketAddr, SocketAddrV4},
11};
12
13use byteorder::{BigEndian, ByteOrder};
14use bytes::{BufMut, BytesMut};
15use thiserror::Error;
16use tokio::io::{AsyncBufRead, AsyncBufReadExt, AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
17
18use shadowsocks::relay::socks5;
19
20#[rustfmt::skip]
21mod consts {
22    pub const SOCKS4_VERSION:                                   u8 = 4;
23
24    pub const SOCKS4_COMMAND_CONNECT:                           u8 = 1;
25    pub const SOCKS4_COMMAND_BIND:                              u8 = 2;
26
27    pub const SOCKS4_RESULT_REQUEST_GRANTED:                    u8 = 90;
28    pub const SOCKS4_RESULT_REQUEST_REJECTED_OR_FAILED:         u8 = 91;
29    pub const SOCKS4_RESULT_REQUEST_REJECTED_CANNOT_CONNECT:    u8 = 92;
30    pub const SOCKS4_RESULT_REQUEST_REJECTED_DIFFERENT_USER_ID: u8 = 93;
31}
32
33/// SOCKS4 Command
34#[derive(Clone, Debug, Copy)]
35pub enum Command {
36    /// CONNECT command
37    Connect,
38    /// BIND command
39    Bind,
40}
41
42impl Command {
43    #[inline]
44    fn as_u8(self) -> u8 {
45        match self {
46            Self::Connect => consts::SOCKS4_COMMAND_CONNECT,
47            Self::Bind => consts::SOCKS4_COMMAND_BIND,
48        }
49    }
50
51    #[inline]
52    fn from_u8(code: u8) -> Option<Self> {
53        match code {
54            consts::SOCKS4_COMMAND_CONNECT => Some(Self::Connect),
55            consts::SOCKS4_COMMAND_BIND => Some(Self::Bind),
56            _ => None,
57        }
58    }
59}
60
61/// SOCKS4 Result Code
62#[derive(Clone, Debug, Copy, Eq, PartialEq)]
63pub enum ResultCode {
64    /// 90: request granted
65    RequestGranted,
66    /// 91: request rejected or failed
67    RequestRejectedOrFailed,
68    /// 92: request rejected because SOCKS server cannot connect to identd on the client
69    RequestRejectedCannotConnect,
70    /// 93: request rejected because the client program and identd report different user-ids
71    RequestRejectedDifferentUserId,
72    /// Other replies
73    Other(u8),
74}
75
76impl ResultCode {
77    #[inline]
78    fn as_u8(self) -> u8 {
79        match self {
80            Self::RequestGranted => consts::SOCKS4_RESULT_REQUEST_GRANTED,
81            Self::RequestRejectedOrFailed => consts::SOCKS4_RESULT_REQUEST_REJECTED_OR_FAILED,
82            Self::RequestRejectedCannotConnect => consts::SOCKS4_RESULT_REQUEST_REJECTED_CANNOT_CONNECT,
83            Self::RequestRejectedDifferentUserId => consts::SOCKS4_RESULT_REQUEST_REJECTED_DIFFERENT_USER_ID,
84            Self::Other(c) => c,
85        }
86    }
87
88    #[inline]
89    fn from_u8(code: u8) -> Self {
90        match code {
91            consts::SOCKS4_RESULT_REQUEST_GRANTED => Self::RequestGranted,
92            consts::SOCKS4_RESULT_REQUEST_REJECTED_OR_FAILED => Self::RequestRejectedOrFailed,
93            consts::SOCKS4_RESULT_REQUEST_REJECTED_CANNOT_CONNECT => Self::RequestRejectedCannotConnect,
94            consts::SOCKS4_RESULT_REQUEST_REJECTED_DIFFERENT_USER_ID => Self::RequestRejectedDifferentUserId,
95            code => Self::Other(code),
96        }
97    }
98}
99
100impl fmt::Display for ResultCode {
101    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102        match *self {
103            Self::RequestGranted => f.write_str("request granted"),
104            Self::RequestRejectedOrFailed => f.write_str("request rejected or failed"),
105            Self::RequestRejectedCannotConnect => {
106                f.write_str("request rejected because SOCKS server cannot connect to identd on the client")
107            }
108            Self::RequestRejectedDifferentUserId => {
109                f.write_str("request rejected because the client program and identd report different user-ids")
110            }
111            Self::Other(code) => write!(f, "other result code {code}"),
112        }
113    }
114}
115
116/// SOCKS4 Address type
117#[derive(Clone, PartialEq, Eq, Hash)]
118pub enum Address {
119    /// Socket address (IP Address)
120    SocketAddress(SocketAddrV4),
121    /// Domain name address (SOCKS4a)
122    DomainNameAddress(String, u16),
123}
124
125impl fmt::Debug for Address {
126    #[inline]
127    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
128        match *self {
129            Self::SocketAddress(ref addr) => write!(f, "{addr}"),
130            Self::DomainNameAddress(ref addr, ref port) => write!(f, "{addr}:{port}"),
131        }
132    }
133}
134
135impl fmt::Display for Address {
136    #[inline]
137    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
138        match *self {
139            Self::SocketAddress(ref addr) => write!(f, "{addr}"),
140            Self::DomainNameAddress(ref addr, ref port) => write!(f, "{addr}:{port}"),
141        }
142    }
143}
144
145impl From<SocketAddrV4> for Address {
146    fn from(s: SocketAddrV4) -> Self {
147        Self::SocketAddress(s)
148    }
149}
150
151impl From<(String, u16)> for Address {
152    fn from((dn, port): (String, u16)) -> Self {
153        Self::DomainNameAddress(dn, port)
154    }
155}
156
157impl From<(&str, u16)> for Address {
158    fn from((dn, port): (&str, u16)) -> Self {
159        Self::DomainNameAddress(dn.to_owned(), port)
160    }
161}
162
163impl From<&Self> for Address {
164    fn from(addr: &Self) -> Self {
165        addr.clone()
166    }
167}
168
169impl From<Address> for socks5::Address {
170    fn from(addr: Address) -> Self {
171        match addr {
172            Address::SocketAddress(a) => Self::SocketAddress(SocketAddr::V4(a)),
173            Address::DomainNameAddress(d, p) => Self::DomainNameAddress(d, p),
174        }
175    }
176}
177
178/// Handshake Request
179///
180/// ```plain
181/// The client connects to the SOCKS server and sends a CONNECT/BIND request when
182/// it wants to establish a connection to an application server. The client
183/// includes in the request packet the IP address and the port number of the
184/// destination host, and userid, in the following format.
185///
186///                 +----+----+----+----+----+----+----+----+----+----+....+----+
187///                 | VN | CD | DSTPORT |      DSTIP        | USERID       |NULL|
188///                 +----+----+----+----+----+----+----+----+----+----+....+----+
189///  # of bytes:      1    1      2              4           variable       1
190///
191/// VN is the SOCKS protocol version number and should be 4. CD is the
192/// SOCKS command code and should be 1 for CONNECT request, 2 for BIND request. NULL is a byte
193/// of all zero bits.
194/// ```
195#[derive(Debug, Clone)]
196pub struct HandshakeRequest {
197    pub cd: Command,
198    pub dst: Address,
199    pub user_id: Vec<u8>,
200}
201
202impl HandshakeRequest {
203    /// Read from a reader
204    pub async fn read_from<R>(r: &mut R) -> Result<Self, Error>
205    where
206        R: AsyncBufRead + Unpin,
207    {
208        let mut buf = [0u8; 8];
209        let _ = r.read_exact(&mut buf).await?;
210
211        let vn = buf[0];
212        if vn != consts::SOCKS4_VERSION {
213            return Err(Error::UnsupportedSocksVersion(vn));
214        }
215
216        let cd = buf[1];
217        let command = match Command::from_u8(cd) {
218            Some(c) => c,
219            None => {
220                return Err(Error::UnsupportedSocksVersion(cd));
221            }
222        };
223
224        let port = BigEndian::read_u16(&buf[2..4]);
225
226        let mut user_id = Vec::new();
227        let _ = r.read_until(b'\0', &mut user_id).await?;
228        if user_id.is_empty() || user_id.last() != Some(&b'\0') {
229            return Err(io::Error::from(ErrorKind::UnexpectedEof).into());
230        }
231        user_id.pop(); // Pops the last b'\0'
232
233        let dst = if buf[4] == 0x00 && buf[5] == 0x00 && buf[6] == 0x00 && buf[7] != 0x00 {
234            // SOCKS4a, indicates that it is a HOST address
235            let mut host = Vec::new();
236            let _ = r.read_until(b'\0', &mut host).await?;
237            if host.is_empty() || host.last() != Some(&b'\0') {
238                return Err(io::Error::from(ErrorKind::UnexpectedEof).into());
239            }
240            host.pop(); // Pops the last b'\0'
241
242            match String::from_utf8(host) {
243                Ok(host) => Address::DomainNameAddress(host, port),
244                Err(..) => {
245                    return Err(Error::AddressHostInvalidEncoding);
246                }
247            }
248        } else {
249            let ip = Ipv4Addr::new(buf[4], buf[5], buf[6], buf[7]);
250            Address::SocketAddress(SocketAddrV4::new(ip, port))
251        };
252
253        Ok(Self {
254            cd: command,
255            dst,
256            user_id,
257        })
258    }
259
260    /// Writes to writer
261    pub async fn write_to<W>(&self, w: &mut W) -> io::Result<()>
262    where
263        W: AsyncWrite + Unpin,
264    {
265        let mut buf = BytesMut::with_capacity(self.serialized_len());
266        self.write_to_buf(&mut buf);
267        w.write_all(&buf).await
268    }
269
270    /// Writes to buffer
271    pub fn write_to_buf<B: BufMut>(&self, buf: &mut B) {
272        debug_assert!(
273            !self.user_id.contains(&b'\0'),
274            "USERID shouldn't contain any NULL characters"
275        );
276
277        buf.put_u8(consts::SOCKS4_VERSION);
278        buf.put_u8(self.cd.as_u8());
279        match self.dst {
280            Address::SocketAddress(ref saddr) => {
281                let port = saddr.port();
282                buf.put_u16(port);
283                buf.put_slice(&saddr.ip().octets());
284
285                buf.put_slice(&self.user_id);
286                buf.put_u8(b'\0');
287            }
288            Address::DomainNameAddress(ref dname, port) => {
289                buf.put_u16(port);
290
291                // 0.0.0.x (x != 0)
292                const PLACEHOLDER: [u8; 4] = [0x00, 0x00, 0x00, 0xff];
293                buf.put_slice(&PLACEHOLDER);
294
295                buf.put_slice(&self.user_id);
296                buf.put_u8(b'\0');
297
298                buf.put_slice(dname.as_bytes());
299                buf.put_u8(b'\0');
300            }
301        }
302    }
303
304    /// Length in bytes
305    #[inline]
306    pub fn serialized_len(&self) -> usize {
307        let mut s = 1 + 1 + 2 + 4 + self.user_id.len() + 1; // USERID.LEN + NULL
308        if let Address::DomainNameAddress(ref dname, _) = self.dst {
309            s += dname.len() + 1;
310        }
311        s
312    }
313}
314
315/// Handshake Response
316///
317/// ```plain
318///             +----+----+----+----+----+----+----+----+
319///             | VN | CD | DSTPORT |      DSTIP        |
320///             +----+----+----+----+----+----+----+----+
321/// # of bytes:   1    1      2              4
322/// ```
323#[derive(Debug, Clone)]
324pub struct HandshakeResponse {
325    pub cd: ResultCode,
326}
327
328impl HandshakeResponse {
329    /// Create a response with code
330    pub fn new(code: ResultCode) -> Self {
331        Self { cd: code }
332    }
333
334    /// Read from a reader
335    pub async fn read_from<R>(r: &mut R) -> Result<Self, Error>
336    where
337        R: AsyncRead + Unpin,
338    {
339        let mut buf = [0u8; 8];
340        let _ = r.read_exact(&mut buf).await?;
341
342        let vn = buf[0];
343        if vn != 0 {
344            return Err(Error::UnsupportedSocksVersion(vn));
345        }
346
347        let cd = buf[1];
348        let result_code = ResultCode::from_u8(cd);
349
350        // DSTPORT, DSTIP are ignored
351
352        Ok(Self { cd: result_code })
353    }
354
355    /// Write data into a writer
356    pub async fn write_to<W>(&self, w: &mut W) -> io::Result<()>
357    where
358        W: AsyncWrite + Unpin,
359    {
360        let mut buf = BytesMut::with_capacity(self.serialized_len());
361        self.write_to_buf(&mut buf);
362        w.write_all(&buf).await
363    }
364
365    /// Writes to buffer
366    pub fn write_to_buf<B: BufMut>(&self, buf: &mut B) {
367        let Self { ref cd } = *self;
368
369        buf.put_slice(&[
370            // VN: Result Code's version, must be 0
371            0x00,
372            // CD: Result Code
373            cd.as_u8(),
374            // DSTPORT: Ignored
375            0x00,
376            0x00,
377            // DSTIP: Ignored
378            0x00,
379            0x00,
380            0x00,
381            0x00,
382        ]);
383    }
384
385    /// Length in bytes
386    #[inline]
387    pub fn serialized_len(&self) -> usize {
388        1 + 1 + 2 + 4
389    }
390}
391
392/// SOCKS 4/4a Error
393#[derive(Error, Debug)]
394pub enum Error {
395    // I/O Error
396    #[error("{0}")]
397    IoError(#[from] io::Error),
398    #[error("host must be UTF-8 encoding")]
399    AddressHostInvalidEncoding,
400    #[error("unsupported socks version {0:#x}")]
401    UnsupportedSocksVersion(u8),
402    #[error("unsupported command {0:#x}")]
403    UnsupportedCommand(u8),
404    #[error("{0}")]
405    Result(ResultCode),
406}
407
408impl From<Error> for io::Error {
409    fn from(err: Error) -> Self {
410        match err {
411            Error::IoError(err) => err,
412            e => Self::other(e),
413        }
414    }
415}