eggress_protocol_socks/socks4/
server.rs1use std::net::{IpAddr, Ipv4Addr, SocketAddr};
2
3use tokio::io::{AsyncReadExt, AsyncWriteExt};
4
5use super::error::Socks4Error;
6
7const MAX_USER_ID_LEN: usize = 255;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12#[repr(u8)]
13pub enum Socks4Status {
14 Granted = 90,
15 Failed = 91,
16 FailedNoIdent = 92,
17 FailedDifferentUser = 93,
18}
19
20impl Socks4Status {
21 pub fn from_u8(val: u8) -> Option<Self> {
23 match val {
24 90 => Some(Self::Granted),
25 91 => Some(Self::Failed),
26 92 => Some(Self::FailedNoIdent),
27 93 => Some(Self::FailedDifferentUser),
28 _ => None,
29 }
30 }
31}
32
33#[derive(Debug, Clone)]
35pub struct Socks4Request {
36 pub command: u8,
37 pub port: u16,
38 pub addr: SocketAddr,
39 pub user_id: String,
40 pub domain: Option<String>,
42}
43
44pub async fn read_socks4_request<S: tokio::io::AsyncRead + Unpin>(
55 stream: &mut S,
56) -> Result<Socks4Request, Socks4Error> {
57 let mut header = [0u8; 8];
58 stream.read_exact(&mut header).await?;
59
60 let version = header[0];
61 if version != 0x04 {
62 return Err(Socks4Error::InvalidVersion(version));
63 }
64
65 let command = header[1];
66 if command != 0x01 {
67 return Err(Socks4Error::UnsupportedCommand(command));
68 }
69
70 let port = u16::from_be_bytes([header[2], header[3]]);
71 let ip = Ipv4Addr::new(header[4], header[5], header[6], header[7]);
72
73 let mut user_id_bytes = Vec::with_capacity(64);
75 let mut buf = [0u8; 1];
76 loop {
77 if user_id_bytes.len() > MAX_USER_ID_LEN {
78 return Err(Socks4Error::UserIdTooLong);
79 }
80 let n = stream.read(&mut buf).await?;
81 if n == 0 {
82 return Err(Socks4Error::MalformedRequest(
83 "unexpected EOF reading user ID".into(),
84 ));
85 }
86 if buf[0] == 0x00 {
87 break;
88 }
89 user_id_bytes.push(buf[0]);
90 }
91
92 let user_id = String::from_utf8(user_id_bytes)
93 .map_err(|_| Socks4Error::MalformedRequest("invalid UTF-8 in user ID".into()))?;
94
95 let domain =
97 if ip.octets()[0] == 0 && ip.octets()[1] == 0 && ip.octets()[2] == 0 && ip.octets()[3] != 0
98 {
99 let mut domain_bytes = Vec::with_capacity(256);
100 loop {
101 if domain_bytes.len() > 255 {
102 return Err(Socks4Error::DomainTooLong);
103 }
104 let n = stream.read(&mut buf).await?;
105 if n == 0 {
106 return Err(Socks4Error::MalformedRequest(
107 "unexpected EOF reading domain".into(),
108 ));
109 }
110 if buf[0] == 0x00 {
111 break;
112 }
113 domain_bytes.push(buf[0]);
114 }
115 let domain = String::from_utf8(domain_bytes)
116 .map_err(|_| Socks4Error::MalformedRequest("invalid UTF-8 in domain".into()))?;
117 if domain.is_empty() {
118 return Err(Socks4Error::MalformedRequest(
119 "empty domain in SOCKS4a request".into(),
120 ));
121 }
122 Some(domain)
123 } else {
124 None
125 };
126
127 let addr = SocketAddr::new(IpAddr::V4(ip), port);
128
129 Ok(Socks4Request {
130 command,
131 port,
132 addr,
133 user_id,
134 domain,
135 })
136}
137
138pub async fn write_socks4_reply<S: tokio::io::AsyncWrite + Unpin>(
146 stream: &mut S,
147 status: Socks4Status,
148 addr: SocketAddr,
149) -> Result<(), Socks4Error> {
150 let ip = match addr.ip() {
151 IpAddr::V4(v4) => v4.octets(),
152 IpAddr::V6(_) => Ipv4Addr::UNSPECIFIED.octets(),
153 };
154 let port = addr.port().to_be_bytes();
155 let reply: [u8; 8] = [
156 0x00,
157 status as u8,
158 port[0],
159 port[1],
160 ip[0],
161 ip[1],
162 ip[2],
163 ip[3],
164 ];
165 stream.write_all(&reply).await?;
166 stream.flush().await?;
167 Ok(())
168}