tor_socksproto/handshake/
client.rs1use super::framework::{HandshakeImpl, ImplNextStep};
4use super::{NO_AUTHENTICATION, USERNAME_PASSWORD};
5use crate::msg::{SocksAddr, SocksAuth, SocksReply, SocksRequest, SocksStatus, SocksVersion};
6use crate::{Error, Result};
7
8use tor_bytes::{Reader, Writer};
9use tor_error::{internal, into_internal};
10
11use derive_deftly::Deftly;
12
13use std::net::{IpAddr, Ipv4Addr};
14
15#[derive(Clone, Debug, Deftly)]
20#[derive_deftly(Handshake)]
21pub struct SocksClientHandshake {
22 request: SocksRequest,
24 state: State,
26 #[deftly(handshake(output))]
28 reply: Option<SocksReply>,
29}
30
31#[derive(Clone, Debug)]
33enum State {
34 Initial,
36 Socks4Wait,
38 Socks5AuthWait,
41 Socks5UsernameWait,
44 Socks5Wait,
46 Done,
50 Failed,
52}
53
54impl HandshakeImpl for SocksClientHandshake {
55 fn handshake_impl(&mut self, input: &mut Reader<'_>) -> Result<ImplNextStep> {
56 use State::*;
57 match self.state {
58 Initial => match self.request.version() {
59 SocksVersion::V4 => self.send_v4(),
60 SocksVersion::V5 => self.send_v5_initial(),
61 },
62 Socks4Wait => self.handle_v4(input),
63 Socks5AuthWait => self.handle_v5_auth(input),
64 Socks5UsernameWait => self.handle_v5_username_ack(input),
65 Socks5Wait => self.handle_v5_final(input),
66 Done => Err(Error::AlreadyFinished(internal!(
67 "called handshake() after handshaking succeeded"
68 ))),
69 Failed => Err(Error::AlreadyFinished(internal!(
70 "called handshake() after handshaking failed"
71 ))),
72 }
73 }
74}
75
76impl SocksClientHandshake {
77 pub fn new(request: SocksRequest) -> Self {
80 SocksClientHandshake {
81 request,
82 state: State::Initial,
83 reply: None,
84 }
85 }
86
87 pub fn into_reply(self) -> Option<SocksReply> {
90 self.reply
91 }
92
93 fn send_v4(&mut self) -> Result<ImplNextStep> {
95 let mut msg = Vec::new();
96
97 msg.write_u8(4);
98 msg.write_u8(self.request.command().into());
99 msg.write_u16(self.request.port());
100
101 let use_v4a = match self.request.addr() {
102 SocksAddr::Ip(IpAddr::V4(ipv4)) => {
103 msg.write_u32((*ipv4).into());
104 false
105 }
106 _ => {
107 msg.write_u32(1);
108 true
109 }
110 };
111
112 match self.request.auth() {
113 SocksAuth::NoAuth => msg.write_u8(0),
114 SocksAuth::Socks4(s) => {
115 msg.write_all(s);
116 msg.write_u8(0);
117 }
118 SocksAuth::Username(_, _) => {
119 return Err(internal!("tried to send socks5 auth over socks4.").into());
120 }
121 }
122
123 if use_v4a {
124 msg.write_all(self.request.addr().to_string().as_bytes());
126 msg.write_u8(0);
127 }
128
129 self.state = State::Socks4Wait;
130 Ok(ImplNextStep::Reply { reply: msg })
131 }
132
133 fn handle_v4(&mut self, r: &mut Reader<'_>) -> Result<ImplNextStep> {
135 let ver = r.take_u8()?;
136 if ver != 0 {
137 return Err(Error::Syntax);
138 }
139 let status = r.take_u8()?;
140 let port = r.take_u16()?;
141 let ip: Ipv4Addr = r.extract()?;
142
143 self.state = State::Done;
144 self.reply = Some(SocksReply::new(
145 SocksStatus::from_socks4_status(status),
146 SocksAddr::Ip(ip.into()),
147 port,
148 ));
149
150 Ok(ImplNextStep::Finished)
151 }
152
153 fn send_v5_initial(&mut self) -> Result<ImplNextStep> {
155 let mut msg = Vec::new();
156 msg.write_u8(5);
157 match self.request.auth() {
158 SocksAuth::NoAuth => {
159 msg.write_u8(1); msg.write_u8(NO_AUTHENTICATION);
161 }
162 SocksAuth::Socks4(_) => return Err(internal!("Mismatched authentication type").into()),
163 SocksAuth::Username(_, _) => {
164 msg.write_u8(2); msg.write_u8(USERNAME_PASSWORD);
166 msg.write_u8(NO_AUTHENTICATION);
167 }
168 }
169
170 self.state = State::Socks5AuthWait;
171 Ok(ImplNextStep::Reply { reply: msg })
172 }
173
174 fn handle_v5_auth(&mut self, r: &mut Reader<'_>) -> Result<ImplNextStep> {
177 let ver = r.take_u8()?;
178 if ver != 5 {
179 return Err(Error::Syntax);
180 }
181 let auth = r.take_u8()?;
182 let (msg, next_state) = match auth {
183 USERNAME_PASSWORD => (self.generate_v5_username_auth()?, State::Socks5UsernameWait),
184 NO_AUTHENTICATION => (self.generate_v5_command()?, State::Socks5Wait),
185 other => {
186 return Err(Error::NotImplemented(
187 format!("authentication type {}", other).into(),
188 ));
189 }
190 };
191
192 self.state = next_state;
193 Ok(ImplNextStep::Reply { reply: msg })
194 }
195
196 fn generate_v5_username_auth(&self) -> Result<Vec<u8>> {
198 if let SocksAuth::Username(username, pass) = self.request.auth() {
199 let mut msg = Vec::new();
200
201 msg.write_u8(1); let mut n = msg.write_nested_u8len();
203 n.write_all(username);
204 n.finish().map_err(into_internal!("id too long"))?;
205
206 let mut n = msg.write_nested_u8len();
207 n.write_all(pass);
208 n.finish().map_err(into_internal!("password too long"))?;
209
210 Ok(msg)
211 } else {
212 Err(Error::Syntax)
214 }
215 }
216
217 fn handle_v5_username_ack(&mut self, r: &mut Reader<'_>) -> Result<ImplNextStep> {
220 let ver = r.take_u8()?;
221 if ver != 1 {
222 return Err(Error::Syntax);
223 }
224 let result = r.take_u8()?;
225 if result != 0 {
226 return Err(Error::AuthRejected);
227 }
228
229 self.state = State::Socks5Wait;
230 Ok(ImplNextStep::Reply {
231 reply: self.generate_v5_command()?,
232 })
233 }
234
235 fn generate_v5_command(&self) -> Result<Vec<u8>> {
241 let mut msg = Vec::new();
242 msg.write_u8(5); msg.write_u8(self.request.command().into());
244 msg.write_u8(0); msg.write(self.request.addr())
246 .map_err(into_internal!("Can't encode address"))?;
247 msg.write_u16(self.request.port());
248
249 Ok(msg)
250 }
251
252 fn handle_v5_final(&mut self, r: &mut Reader<'_>) -> Result<ImplNextStep> {
254 let ver = r.take_u8()?;
255 if ver != 5 {
256 return Err(Error::Syntax);
257 }
258 let status: SocksStatus = r.take_u8()?.into();
259 let _reserved = r.take_u8()?;
260 let addr: SocksAddr = r.extract()?;
261 let port = r.take_u16()?;
262
263 self.state = State::Done;
264 self.reply = Some(SocksReply::new(status, addr, port));
265 Ok(ImplNextStep::Finished)
266 }
267}
268
269#[cfg(test)]
270mod test {
271 #![allow(clippy::bool_assert_comparison)]
273 #![allow(clippy::clone_on_copy)]
274 #![allow(clippy::dbg_macro)]
275 #![allow(clippy::mixed_attributes_style)]
276 #![allow(clippy::print_stderr)]
277 #![allow(clippy::print_stdout)]
278 #![allow(clippy::single_char_pattern)]
279 #![allow(clippy::unwrap_used)]
280 #![allow(clippy::unchecked_time_subtraction)]
281 #![allow(clippy::useless_vec)]
282 #![allow(clippy::needless_pass_by_value)]
283 #![allow(clippy::string_slice)] use super::*;
287 use crate::{Handshake as _, msg::SocksCmd};
288 use hex_literal::hex;
289
290 #[test]
291 fn socks4_ok() {
292 let r = SocksRequest::new(
293 SocksVersion::V4,
294 SocksCmd::CONNECT,
295 SocksAddr::Ip("192.0.2.15".parse().unwrap()),
296 443,
297 SocksAuth::NoAuth,
298 )
299 .unwrap();
300 let mut hs = SocksClientHandshake::new(r);
301 let action = hs.handshake_for_tests(&[]).unwrap().unwrap();
302 assert_eq!(action.drain, 0);
303 assert_eq!(action.reply, hex!("04 01 01BB C000020F 00"));
304 assert_eq!(action.finished, false);
305
306 let action = hs
307 .handshake_for_tests(&hex!("00 5A 01BB C000020F"))
308 .unwrap()
309 .unwrap();
310 assert_eq!(action.drain, 8);
311 assert!(action.reply.is_empty());
312 assert_eq!(action.finished, true);
313
314 let reply = hs.into_reply().unwrap();
315 assert_eq!(reply.status(), SocksStatus::SUCCEEDED);
316 assert_eq!(reply.port(), 443);
317 assert_eq!(reply.addr().to_string(), "192.0.2.15");
318 }
319
320 #[test]
321 fn socks4a_ok() {
322 let r = SocksRequest::new(
323 SocksVersion::V4,
324 SocksCmd::CONNECT,
325 SocksAddr::Hostname("www.torproject.org".to_string().try_into().unwrap()),
326 443,
327 SocksAuth::Socks4(b"hello".to_vec()),
328 )
329 .unwrap();
330 let mut hs = SocksClientHandshake::new(r);
331 let action = hs.handshake_for_tests(&[]).unwrap().unwrap();
332 assert_eq!(action.drain, 0);
333 assert_eq!(
334 action.reply,
335 hex!("04 01 01BB 00000001 68656c6c6f00 7777772e746f7270726f6a6563742e6f726700")
336 );
337 assert_eq!(action.finished, false);
338
339 let action = hs
340 .handshake_for_tests(&hex!("00 5A 01BB C0000215"))
341 .unwrap()
342 .unwrap();
343 assert_eq!(action.drain, 8);
344 assert!(action.reply.is_empty());
345 assert_eq!(action.finished, true);
346
347 let reply = hs.into_reply().unwrap();
348 assert_eq!(reply.status(), SocksStatus::SUCCEEDED);
349 assert_eq!(reply.port(), 443);
350 assert_eq!(reply.addr().to_string(), "192.0.2.21");
351 }
352
353 #[test]
354 fn socks5_with_no_auth() {
355 let r = SocksRequest::new(
356 SocksVersion::V5,
357 SocksCmd::CONNECT,
358 SocksAddr::Hostname("www.torproject.org".to_string().try_into().unwrap()),
359 443,
360 SocksAuth::NoAuth,
361 )
362 .unwrap();
363
364 let mut hs = SocksClientHandshake::new(r);
366 let action = hs.handshake_for_tests(&[]).unwrap().unwrap();
367 assert_eq!(action.drain, 0);
368 assert_eq!(action.reply, hex!("05 01 00"));
369 assert_eq!(action.finished, false);
370
371 let action = hs.handshake_for_tests(&hex!("0500")).unwrap().unwrap();
373 assert_eq!(action.drain, 2);
374 assert_eq!(
375 action.reply,
376 hex!("05 01 00 03 12 7777772e746f7270726f6a6563742e6f7267 01BB")
377 );
378 assert_eq!(action.finished, false);
379
380 let action = hs
383 .handshake_for_tests(&hex!("05 00 00 01 C0000215 01BB"))
384 .unwrap()
385 .unwrap();
386 assert_eq!(action.drain, 10);
387 assert!(action.reply.is_empty());
388 assert_eq!(action.finished, true);
389
390 let reply = hs.into_reply().unwrap();
391 assert_eq!(reply.status(), SocksStatus::SUCCEEDED);
392 assert_eq!(reply.port(), 443);
393 assert_eq!(reply.addr().to_string(), "192.0.2.21");
394 }
395
396 #[test]
397 fn socks5_with_auth_ok() {
398 let r = SocksRequest::new(
399 SocksVersion::V5,
400 SocksCmd::CONNECT,
401 SocksAddr::Hostname("www.torproject.org".to_string().try_into().unwrap()),
402 443,
403 SocksAuth::Username(b"hello".to_vec(), b"world".to_vec()),
404 )
405 .unwrap();
406
407 let mut hs = SocksClientHandshake::new(r);
409 let action = hs.handshake_for_tests(&[]).unwrap().unwrap();
410 assert_eq!(action.drain, 0);
411 assert_eq!(action.reply, hex!("05 02 0200"));
412 assert_eq!(action.finished, false);
413
414 let action = hs.handshake_for_tests(&hex!("0502")).unwrap().unwrap();
416 assert_eq!(action.drain, 2);
417 assert_eq!(action.reply, hex!("01 05 68656c6c6f 05 776f726c64"));
418 assert_eq!(action.finished, false);
419
420 let action = hs.handshake_for_tests(&hex!("0100")).unwrap().unwrap();
423 assert_eq!(action.drain, 2);
424 assert_eq!(
425 action.reply,
426 hex!("05 01 00 03 12 7777772e746f7270726f6a6563742e6f7267 01BB")
427 );
428 assert_eq!(action.finished, false);
429
430 let action = hs
433 .handshake_for_tests(&hex!("05 00 00 01 C0000215 01BB"))
434 .unwrap()
435 .unwrap();
436 assert_eq!(action.drain, 10);
437 assert!(action.reply.is_empty());
438 assert_eq!(action.finished, true);
439
440 let reply = hs.into_reply().unwrap();
441 assert_eq!(reply.status(), SocksStatus::SUCCEEDED);
442 assert_eq!(reply.port(), 443);
443 assert_eq!(reply.addr().to_string(), "192.0.2.21");
444 }
445}