1#[cfg(any(feature = "proxy-handshake", feature = "client-handshake"))]
4#[macro_use]
5pub(crate) mod framework;
6
7#[cfg(feature = "client-handshake")]
8pub(crate) mod client;
9#[cfg(feature = "proxy-handshake")]
10pub(crate) mod proxy;
11
12use crate::msg::SocksAddr;
13use std::net::IpAddr;
14use tor_bytes::Result as BytesResult;
15use tor_bytes::{EncodeResult, Error as BytesError, Readable, Reader, Writeable, Writer};
16
17const USERNAME_PASSWORD: u8 = 0x02;
20const NO_AUTHENTICATION: u8 = 0x00;
22
23#[derive(Clone, Debug)]
25#[non_exhaustive]
26pub struct Action {
27 pub drain: usize,
30 pub reply: Vec<u8>,
32 pub finished: bool,
34}
35
36impl Readable for SocksAddr {
37 fn take_from(r: &mut Reader<'_>) -> BytesResult<SocksAddr> {
38 let atype = r.take_u8()?;
39 match atype {
40 1 => {
41 let ip4: std::net::Ipv4Addr = r.extract()?;
42 Ok(SocksAddr::Ip(ip4.into()))
43 }
44 3 => {
45 let hlen = r.take_u8()?;
46 let hostname = r.take(hlen as usize)?;
47 let hostname = std::str::from_utf8(hostname)
48 .map_err(|_| BytesError::InvalidMessage("bad utf8 on hostname".into()))?
49 .to_string();
50 let hostname = hostname
51 .try_into()
52 .map_err(|_| BytesError::InvalidMessage("hostname too long".into()))?;
53 Ok(SocksAddr::Hostname(hostname))
54 }
55 4 => {
56 let ip6: std::net::Ipv6Addr = r.extract()?;
57 Ok(SocksAddr::Ip(ip6.into()))
58 }
59 _ => Err(BytesError::InvalidMessage(
60 "unrecognized address type.".into(),
61 )),
62 }
63 }
64}
65
66impl Writeable for SocksAddr {
67 fn write_onto<W: Writer + ?Sized>(&self, w: &mut W) -> EncodeResult<()> {
68 match self {
69 SocksAddr::Ip(IpAddr::V4(ip)) => {
70 w.write_u8(1);
71 w.write(ip)?;
72 }
73 SocksAddr::Ip(IpAddr::V6(ip)) => {
74 w.write_u8(4);
75 w.write(ip)?;
76 }
77 SocksAddr::Hostname(h) => {
78 let h = h.as_ref();
79 assert!(h.len() < 256);
80 let hlen = h.len() as u8;
81 w.write_u8(3);
82 w.write_u8(hlen);
83 w.write(h.as_bytes())?;
84 }
85 }
86 Ok(())
87 }
88}
89
90#[cfg(all(feature = "client-handshake", feature = "proxy-handshake"))]
91#[cfg(test)]
92mod test_roundtrip {
93 #![allow(clippy::bool_assert_comparison)]
95 #![allow(clippy::clone_on_copy)]
96 #![allow(clippy::dbg_macro)]
97 #![allow(clippy::mixed_attributes_style)]
98 #![allow(clippy::print_stderr)]
99 #![allow(clippy::print_stdout)]
100 #![allow(clippy::single_char_pattern)]
101 #![allow(clippy::unwrap_used)]
102 #![allow(clippy::unchecked_duration_subtraction)]
103 #![allow(clippy::useless_vec)]
104 #![allow(clippy::needless_pass_by_value)]
105 use crate::*;
108 use std::collections::VecDeque;
109
110 fn run_handshake_old_api(
115 request: SocksRequest,
116 status: SocksStatus,
117 ) -> (SocksRequest, SocksReply) {
118 let mut client_hs = SocksClientHandshake::new(request);
119 let mut proxy_hs = SocksProxyHandshake::new();
120 let mut received_request = None;
121
122 let mut last_proxy_msg = vec![];
123 for _ in 0..100 {
125 for truncate in 0..last_proxy_msg.len() {
127 let r = client_hs.handshake_for_tests(&last_proxy_msg[..truncate]);
128 assert!(r.is_err());
129 }
130 let client_action = client_hs
132 .handshake_for_tests(&last_proxy_msg)
133 .unwrap()
134 .unwrap();
135 assert_eq!(client_action.drain, last_proxy_msg.len());
136 if client_action.finished {
137 let received_reply = client_hs.into_reply();
138 return (received_request.unwrap(), received_reply.unwrap());
139 }
140 let client_msg = client_action.reply;
141
142 for truncate in 0..client_msg.len() {
144 let r = proxy_hs.handshake_for_tests(&client_msg[..truncate]);
145 assert!(r.is_err());
146 }
147 let proxy_action = proxy_hs.handshake_for_tests(&client_msg).unwrap().unwrap();
149 assert_eq!(proxy_action.drain, client_msg.len());
150 last_proxy_msg = if proxy_action.finished {
151 received_request = proxy_hs.clone().into_request();
153 received_request
154 .as_ref()
155 .unwrap()
156 .reply(status, None)
157 .unwrap()
158 } else {
159 proxy_action.reply
160 };
161 }
162 panic!("Handshake ran for too many steps")
163 }
164
165 fn run_handshake_new_api<P: ReadPrecision, const MAX_RECV: usize>(
170 request: SocksRequest,
171 status: SocksStatus,
172 ) -> (SocksRequest, SocksReply) {
173 struct State<P: ReadPrecision, H: Handshake> {
174 hs: H,
175 buf: Buffer<P>,
176 fin: Option<H::Output>,
177 }
178
179 struct DidSomething;
180
181 let mut client = State::<P, _>::new(SocksClientHandshake::new(request));
182 let mut server = State::<P, _>::new(SocksProxyHandshake::new());
183
184 let mut c2s = VecDeque::new();
185 let mut s2c = VecDeque::new();
186
187 let mut status = Some(status);
188
189 impl<P: ReadPrecision, H: Handshake> State<P, H> {
190 fn new(hs: H) -> Self {
191 State {
192 hs,
193 buf: Default::default(),
194 fin: None,
195 }
196 }
197
198 fn progress_1(
199 &mut self,
200 max_recv: usize,
201 rx: &mut VecDeque<u8>,
202 tx: &mut VecDeque<u8>,
203 ) -> Option<DidSomething> {
204 use NextStep as NS;
205
206 if self.fin.is_some() {
207 return None;
208 }
209
210 match self.hs.step(&mut self.buf).unwrap() {
211 NS::Recv(mut recv) => {
212 let n = [recv.buf().len(), rx.len(), max_recv]
213 .into_iter()
214 .min()
215 .unwrap();
216 for p in &mut recv.buf()[0..n] {
217 *p = rx.pop_front().unwrap();
218 }
219 recv.note_received(n).unwrap_or_else(|e| match e {
220 Error::UnexpectedEof => {}
223 other => panic!("{:?}", other),
224 });
225 if n != 0 {
226 Some(DidSomething)
227 } else {
228 None
229 }
230 }
231 NS::Send(send) => {
232 for c in send {
233 tx.push_back(c);
234 }
235 Some(DidSomething)
236 }
237 NS::Finished(fin) => {
238 self.fin = Some(fin.into_output_forbid_pipelining().unwrap());
239 Some(DidSomething)
240 }
241 }
242 }
243 }
244
245 loop {
246 let ds = [
247 client.progress_1(MAX_RECV, &mut s2c, &mut c2s),
248 server.progress_1(MAX_RECV, &mut c2s, &mut s2c),
249 ]
250 .into_iter()
251 .flatten()
252 .next();
253
254 if let Some(DidSomething) = ds {
255 continue;
256 }
257
258 let Some(status) = status.take() else { break };
259
260 let reply = server.fin.as_ref().unwrap().reply(status, None).unwrap();
261 for c in reply {
262 s2c.push_back(c);
263 }
264 }
265
266 (server.fin.unwrap(), client.fin.unwrap())
267 }
268
269 fn test_handshake(request: &SocksRequest, status: SocksStatus) {
271 for run_handshake in [
272 run_handshake_old_api,
273 run_handshake_new_api::<(), 1>,
274 run_handshake_new_api::<(), 100>,
275 run_handshake_new_api::<PreciseReads, 1>,
276 run_handshake_new_api::<PreciseReads, 100>,
277 ] {
278 let (request_out, status_out) = run_handshake(request.clone(), status);
279 assert_eq!(&request_out, request);
280 assert_eq!(status_out.status(), status);
281 }
282 }
283
284 #[test]
285 fn socks4() {
286 test_handshake(
287 &SocksRequest::new(
288 SocksVersion::V4,
289 SocksCmd::CONNECT,
290 SocksAddr::Hostname("www.torproject.org".to_string().try_into().unwrap()),
291 443,
292 SocksAuth::NoAuth,
293 )
294 .unwrap(),
295 SocksStatus::SUCCEEDED,
296 );
297
298 test_handshake(
299 &SocksRequest::new(
300 SocksVersion::V4,
301 SocksCmd::CONNECT,
302 SocksAddr::Ip("192.0.2.33".parse().unwrap()),
303 22,
304 SocksAuth::Socks4(b"swordfish".to_vec()),
305 )
306 .unwrap(),
307 SocksStatus::GENERAL_FAILURE,
308 );
309 }
310
311 #[test]
312 fn socks5() {
313 test_handshake(
314 &SocksRequest::new(
315 SocksVersion::V5,
316 SocksCmd::CONNECT,
317 SocksAddr::Hostname("www.torproject.org".to_string().try_into().unwrap()),
318 443,
319 SocksAuth::NoAuth,
320 )
321 .unwrap(),
322 SocksStatus::SUCCEEDED,
323 );
324
325 test_handshake(
326 &SocksRequest::new(
327 SocksVersion::V5,
328 SocksCmd::CONNECT,
329 SocksAddr::Ip("2001:db8::32".parse().unwrap()),
330 443,
331 SocksAuth::Username(b"belbo".to_vec(), b"non".to_vec()),
332 )
333 .unwrap(),
334 SocksStatus::GENERAL_FAILURE,
335 );
336 }
337}