proxychains_masq/proxy/
socks5.rs1use std::net::IpAddr;
2
3use anyhow::{bail, Context, Result};
4use tokio::io::{AsyncReadExt, AsyncWriteExt};
5
6use super::{BoxStream, Target};
7
8const METHOD_NO_AUTH: u8 = 0x00;
9const METHOD_USER_PASS: u8 = 0x02;
10const METHOD_NO_ACCEPTABLE: u8 = 0xFF;
11
12pub async fn connect(
24 mut stream: BoxStream,
25 target: &Target,
26 username: Option<&str>,
27 password: Option<&str>,
28) -> Result<BoxStream> {
29 let methods: &[u8] = if username.is_some() {
31 &[METHOD_NO_AUTH, METHOD_USER_PASS]
32 } else {
33 &[METHOD_NO_AUTH]
34 };
35
36 let mut greeting = vec![5u8, methods.len() as u8];
37 greeting.extend_from_slice(methods);
38 stream
39 .write_all(&greeting)
40 .await
41 .context("socks5: write greeting")?;
42
43 let mut sel = [0u8; 2];
44 stream
45 .read_exact(&mut sel)
46 .await
47 .context("socks5: read method selection")?;
48 if sel[0] != 5 {
49 bail!("socks5: unexpected version in method selection: {}", sel[0]);
50 }
51 if sel[1] == METHOD_NO_ACCEPTABLE {
52 bail!("socks5: no acceptable authentication method");
53 }
54
55 if sel[1] == METHOD_USER_PASS {
57 let user = username.unwrap_or("");
58 let pass = password.unwrap_or("");
59
60 let mut auth: Vec<u8> = Vec::with_capacity(3 + user.len() + pass.len());
61 auth.push(1); auth.push(user.len() as u8);
63 auth.extend_from_slice(user.as_bytes());
64 auth.push(pass.len() as u8);
65 auth.extend_from_slice(pass.as_bytes());
66 stream
67 .write_all(&auth)
68 .await
69 .context("socks5: write auth")?;
70
71 let mut ar = [0u8; 2];
72 stream
73 .read_exact(&mut ar)
74 .await
75 .context("socks5: read auth response")?;
76 if ar[1] != 0 {
78 bail!("socks5: authentication failed (status {})", ar[1]);
79 }
80 } else if sel[1] != METHOD_NO_AUTH {
81 bail!("socks5: server chose unexpected method: {}", sel[1]);
82 }
83
84 let mut req: Vec<u8> = Vec::with_capacity(32);
86 req.extend_from_slice(&[5, 1, 0]); match target {
89 Target::Ip(IpAddr::V4(v4), _) => {
90 req.push(1); req.extend_from_slice(&v4.octets());
92 }
93 Target::Ip(IpAddr::V6(v6), _) => {
94 req.push(4); req.extend_from_slice(&v6.octets());
96 }
97 Target::Host(hostname, _) => {
98 req.push(3); req.push(hostname.len() as u8);
100 req.extend_from_slice(hostname.as_bytes());
101 }
102 }
103 req.extend_from_slice(&target.port().to_be_bytes());
104
105 stream
106 .write_all(&req)
107 .await
108 .context("socks5: write CONNECT")?;
109
110 let mut rep = [0u8; 4];
112 stream
113 .read_exact(&mut rep)
114 .await
115 .context("socks5: read reply header")?;
116 if rep[0] != 5 {
117 bail!("socks5: unexpected version in reply: {}", rep[0]);
118 }
119 if rep[1] != 0 {
120 bail!("socks5: CONNECT failed (reply code {})", rep[1]);
121 }
122
123 let atype = rep[3];
125 let addr_len: usize = match atype {
126 1 => 4,
127 4 => 16,
128 3 => {
129 let len = stream.read_u8().await.context("socks5: read domain len")? as usize;
130 len
131 }
132 _ => bail!("socks5: unknown address type in reply: {atype}"),
133 };
134 let mut addr_buf = vec![0u8; addr_len + 2]; stream
136 .read_exact(&mut addr_buf)
137 .await
138 .context("socks5: drain bound addr")?;
139
140 Ok(stream)
141}