sproxy/
lib.rs

1//! socks5 proxy client
2
3#![warn(missing_docs)]
4
5use std::{
6    net::{Ipv4Addr, Ipv6Addr},
7    str::FromStr,
8};
9
10use bytes::{Buf, BufMut, BytesMut};
11
12#[cfg(feature = "async")]
13mod async_;
14#[cfg(feature = "async")]
15pub use async_::*;
16
17#[cfg(feature = "sync")]
18mod sync;
19#[cfg(feature = "sync")]
20pub use sync::*;
21
22/// socks5 protocol version
23pub const VERS: u8 = 0x05;
24/// simple [std::io::Result] wrapper
25pub type IOResult<T> = std::io::Result<T>;
26
27/// socks5 auth method
28pub struct AuthMethod;
29
30impl AuthMethod {
31    /// do not require auth
32    pub fn none() -> u8 {
33        0
34    }
35
36    /// check method is none
37    pub fn is_none(m: u8) -> bool {
38        m == 0
39    }
40
41    /// GSSAPI auth
42    pub fn gssapi() -> u8 {
43        1
44    }
45
46    /// check method is gssapi
47    pub fn is_gssapi(m: u8) -> bool {
48        m == 1
49    }
50
51    /// username, password auth
52    pub fn basic() -> u8 {
53        2
54    }
55
56    /// check method is basic auth
57    pub fn is_basic(m: u8) -> bool {
58        m == 2
59    }
60
61    /// check method is IANA reserved
62    pub fn is_iana_reserved(m: u8) -> bool {
63        m >= 0x3 && m <= 0x7f
64    }
65
66    /// check method is self reserved
67    pub fn is_reserved(m: u8) -> bool {
68        m >= 0x8 && m <= 0xfe
69    }
70
71    /// check method is unavailable
72    pub fn is_unavailable(m: u8) -> bool {
73        m == 0xff
74    }
75
76    /// no acceptable auth method
77    pub fn unavailable() -> u8 {
78        0xff
79    }
80}
81
82fn build_init_req(methods: Vec<u8>) -> BytesMut {
83    assert!(methods.len() <= 255);
84    let mut buf = BytesMut::with_capacity(2 + methods.len());
85    buf.put_u8(VERS);
86    buf.put_u8(methods.len() as u8);
87    buf.extend_from_slice(&methods);
88    buf
89}
90
91fn parse_init_resp(data: [u8; 2]) -> u8 {
92    assert_eq!(data[0], VERS);
93    data[1]
94}
95
96fn build_auth_basic_req(username: String, password: String) -> BytesMut {
97    assert!(username.as_bytes().len() <= 255);
98    assert!(password.as_bytes().len() <= 255);
99    let mut data = BytesMut::new();
100    data.put_u8(1);
101    data.put_u8(username.as_bytes().len() as u8);
102    data.extend_from_slice(username.as_bytes());
103    data.put_u8(password.as_bytes().len() as u8);
104    data.extend_from_slice(password.as_bytes());
105    data
106}
107
108fn parse_auth_basic_resp(data: [u8; 2]) -> bool {
109    assert_eq!(data[0], 01);
110    data[1] == 0
111}
112
113/// socks5 dest addr
114pub enum Addr {
115    /// ipv4 addr 4 bytes
116    V4(Ipv4Addr),
117    /// hostname, first bytes is hostname len
118    Host(String),
119    /// ipv6 addr 16 bytes
120    V6(Ipv6Addr),
121}
122
123impl From<&str> for Addr {
124    fn from(addr: &str) -> Self {
125        Ipv4Addr::from_str(addr)
126            .map(Addr::V4)
127            .or_else(|_| Ipv6Addr::from_str(addr).map(Addr::V6))
128            .unwrap_or(Addr::Host(addr.to_string()))
129    }
130}
131
132impl Addr {
133    /// write addr to buf with socks5 protocol
134    pub fn serde(&self, buf: &mut BytesMut) {
135        match self {
136            Addr::V4(addr) => {
137                buf.put_u8(1);
138                buf.extend_from_slice(&addr.octets());
139            }
140            Addr::Host(host) => {
141                buf.put_u8(03);
142                let data = host.as_bytes();
143                assert!(data.len() <= 255);
144                buf.put_u8(data.len() as u8);
145                buf.extend_from_slice(data);
146            }
147            Addr::V6(addr) => {
148                buf.put_u8(04);
149                buf.extend_from_slice(&addr.octets());
150            }
151        }
152    }
153
154    /// read addr from buf
155    pub fn de_serde(buf: &mut BytesMut) -> Result<Self, String> {
156        let ty = buf.get_u8();
157        match ty {
158            1 => {
159                let mut addr = [0u8; 4];
160                for i in 0..4 {
161                    addr[i] = buf.get_u8();
162                }
163                Ok(Self::V4(Ipv4Addr::from(addr)))
164            }
165            3 => {
166                let len = buf.get_u8() as usize;
167                let data = buf[..len].to_vec();
168                String::from_utf8(data)
169                    .map(Self::Host)
170                    .map_err(|_| "invalid utf8 hostname".to_string())
171            }
172            4 => {
173                let mut addr = [0u8; 16];
174                for i in 0..16 {
175                    addr[i] = buf.get_u8();
176                }
177                Ok(Self::V6(Ipv6Addr::from(addr)))
178            }
179            _ => Err(format!("invalid addr type {}", ty)),
180        }
181    }
182}
183
184/// socks command
185#[derive(Debug, Clone)]
186pub enum Command {
187    /// connect
188    Connect,
189    /// bind(wip)
190    Bind,
191    /// UDP(wip)
192    UdpAssociate,
193}
194
195impl Command {
196    /// cast command as u8
197    pub fn as_u8(&self) -> u8 {
198        match self {
199            Command::Connect => 1,
200            Command::Bind => 2,
201            Command::UdpAssociate => 3,
202        }
203    }
204
205    /// perform cast
206    pub fn from_u8(cmd: u8) -> Result<Self, u8> {
207        match cmd {
208            1 => Ok(Self::Connect),
209            2 => Ok(Self::Bind),
210            3 => Ok(Self::UdpAssociate),
211            _ => Err(cmd),
212        }
213    }
214}
215
216fn build_cmd_req(cmd: Command, addr: Addr, port: u16) -> BytesMut {
217    let mut data = BytesMut::new();
218    data.put_u8(VERS);
219    data.put_u8(cmd.as_u8());
220    data.put_u8(0);
221    addr.serde(&mut data);
222    data.put_u16(port);
223    data
224}
225
226/// command request response status
227#[derive(Debug, Clone)]
228pub enum Reply {
229    /// succeeded
230    Succeeded,
231    /// general SOCKS server failure
232    ProxyServerFail,
233    /// connection not allowed by rule set
234    RuleReject,
235    /// Network unreachable
236    NetworkUnreachable,
237    /// Host unreachable
238    HostUnreachable,
239    /// Connection refused
240    ConnectRefuse,
241    /// TTL expired
242    TTLExpired,
243    /// Command not supported
244    CommandNotSupported,
245    /// Address type not supported
246    AddrTypeNotSupported,
247    /// X'09' to X'FF' unassigned
248    Unassigned(u8),
249}
250
251impl Reply {
252    /// cast to u8
253    pub fn as_u8(&self) -> u8 {
254        match self {
255            Reply::Succeeded => 0,
256            Reply::ProxyServerFail => 1,
257            Reply::RuleReject => 2,
258            Reply::NetworkUnreachable => 3,
259            Reply::HostUnreachable => 4,
260            Reply::ConnectRefuse => 5,
261            Reply::TTLExpired => 6,
262            Reply::CommandNotSupported => 7,
263            Reply::AddrTypeNotSupported => 8,
264            Reply::Unassigned(x) => *x,
265        }
266    }
267
268    /// cast from u8
269    pub fn from_u8(x: u8) -> Self {
270        match x {
271            0 => Self::Succeeded,
272            1 => Self::ProxyServerFail,
273            2 => Self::RuleReject,
274            3 => Self::NetworkUnreachable,
275            4 => Self::HostUnreachable,
276            5 => Self::ConnectRefuse,
277            6 => Self::TTLExpired,
278            7 => Self::CommandNotSupported,
279            8 => Self::AddrTypeNotSupported,
280            _ => Self::Unassigned(x),
281        }
282    }
283}
284
285fn parse_cmd_resp(mut data: BytesMut) -> Result<(Reply, Addr, u16), String> {
286    let ver = data.get_u8();
287    assert_eq!(ver, VERS);
288    let reply = Reply::from_u8(data.get_u8());
289    let rsv = data.get_u8();
290    assert_eq!(rsv, 0);
291    let addr = Addr::de_serde(&mut data)?;
292    let port = data.get_u16();
293    Ok((reply, addr, port))
294}
295
296/// proxy connection config
297#[derive(Debug, Clone)]
298#[cfg_attr(feature = "ser", derive(serde::Serialize, serde::Deserialize))]
299pub struct ProxyConfig {
300    /// proxy server host
301    /// ipv6 should be wrapped by `[]`
302    pub host: String,
303    /// proxy server port
304    pub port: u16,
305
306    /// proxy server auth credential
307    pub auth: Option<AuthCredential>,
308}
309
310/// proxy server auth config
311#[derive(Debug, Clone)]
312#[cfg_attr(feature = "ser", derive(serde::Serialize, serde::Deserialize))]
313pub struct AuthCredential {
314    /// username
315    user: String,
316    /// password
317    passwd: String,
318}