tor_socksproto/handshake/
client.rs
1use 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_duration_subtraction)]
281 #![allow(clippy::useless_vec)]
282 #![allow(clippy::needless_pass_by_value)]
283 use super::*;
286 use crate::{msg::SocksCmd, Handshake as _};
287 use hex_literal::hex;
288
289 #[test]
290 fn socks4_ok() {
291 let r = SocksRequest::new(
292 SocksVersion::V4,
293 SocksCmd::CONNECT,
294 SocksAddr::Ip("192.0.2.15".parse().unwrap()),
295 443,
296 SocksAuth::NoAuth,
297 )
298 .unwrap();
299 let mut hs = SocksClientHandshake::new(r);
300 let action = hs.handshake_for_tests(&[]).unwrap().unwrap();
301 assert_eq!(action.drain, 0);
302 assert_eq!(action.reply, hex!("04 01 01BB C000020F 00"));
303 assert_eq!(action.finished, false);
304
305 let action = hs
306 .handshake_for_tests(&hex!("00 5A 01BB C000020F"))
307 .unwrap()
308 .unwrap();
309 assert_eq!(action.drain, 8);
310 assert!(action.reply.is_empty());
311 assert_eq!(action.finished, true);
312
313 let reply = hs.into_reply().unwrap();
314 assert_eq!(reply.status(), SocksStatus::SUCCEEDED);
315 assert_eq!(reply.port(), 443);
316 assert_eq!(reply.addr().to_string(), "192.0.2.15");
317 }
318
319 #[test]
320 fn socks4a_ok() {
321 let r = SocksRequest::new(
322 SocksVersion::V4,
323 SocksCmd::CONNECT,
324 SocksAddr::Hostname("www.torproject.org".to_string().try_into().unwrap()),
325 443,
326 SocksAuth::Socks4(b"hello".to_vec()),
327 )
328 .unwrap();
329 let mut hs = SocksClientHandshake::new(r);
330 let action = hs.handshake_for_tests(&[]).unwrap().unwrap();
331 assert_eq!(action.drain, 0);
332 assert_eq!(
333 action.reply,
334 hex!("04 01 01BB 00000001 68656c6c6f00 7777772e746f7270726f6a6563742e6f726700")
335 );
336 assert_eq!(action.finished, false);
337
338 let action = hs
339 .handshake_for_tests(&hex!("00 5A 01BB C0000215"))
340 .unwrap()
341 .unwrap();
342 assert_eq!(action.drain, 8);
343 assert!(action.reply.is_empty());
344 assert_eq!(action.finished, true);
345
346 let reply = hs.into_reply().unwrap();
347 assert_eq!(reply.status(), SocksStatus::SUCCEEDED);
348 assert_eq!(reply.port(), 443);
349 assert_eq!(reply.addr().to_string(), "192.0.2.21");
350 }
351
352 #[test]
353 fn socks5_with_no_auth() {
354 let r = SocksRequest::new(
355 SocksVersion::V5,
356 SocksCmd::CONNECT,
357 SocksAddr::Hostname("www.torproject.org".to_string().try_into().unwrap()),
358 443,
359 SocksAuth::NoAuth,
360 )
361 .unwrap();
362
363 let mut hs = SocksClientHandshake::new(r);
365 let action = hs.handshake_for_tests(&[]).unwrap().unwrap();
366 assert_eq!(action.drain, 0);
367 assert_eq!(action.reply, hex!("05 01 00"));
368 assert_eq!(action.finished, false);
369
370 let action = hs.handshake_for_tests(&hex!("0500")).unwrap().unwrap();
372 assert_eq!(action.drain, 2);
373 assert_eq!(
374 action.reply,
375 hex!("05 01 00 03 12 7777772e746f7270726f6a6563742e6f7267 01BB")
376 );
377 assert_eq!(action.finished, false);
378
379 let action = hs
382 .handshake_for_tests(&hex!("05 00 00 01 C0000215 01BB"))
383 .unwrap()
384 .unwrap();
385 assert_eq!(action.drain, 10);
386 assert!(action.reply.is_empty());
387 assert_eq!(action.finished, true);
388
389 let reply = hs.into_reply().unwrap();
390 assert_eq!(reply.status(), SocksStatus::SUCCEEDED);
391 assert_eq!(reply.port(), 443);
392 assert_eq!(reply.addr().to_string(), "192.0.2.21");
393 }
394
395 #[test]
396 fn socks5_with_auth_ok() {
397 let r = SocksRequest::new(
398 SocksVersion::V5,
399 SocksCmd::CONNECT,
400 SocksAddr::Hostname("www.torproject.org".to_string().try_into().unwrap()),
401 443,
402 SocksAuth::Username(b"hello".to_vec(), b"world".to_vec()),
403 )
404 .unwrap();
405
406 let mut hs = SocksClientHandshake::new(r);
408 let action = hs.handshake_for_tests(&[]).unwrap().unwrap();
409 assert_eq!(action.drain, 0);
410 assert_eq!(action.reply, hex!("05 02 0200"));
411 assert_eq!(action.finished, false);
412
413 let action = hs.handshake_for_tests(&hex!("0502")).unwrap().unwrap();
415 assert_eq!(action.drain, 2);
416 assert_eq!(action.reply, hex!("01 05 68656c6c6f 05 776f726c64"));
417 assert_eq!(action.finished, false);
418
419 let action = hs.handshake_for_tests(&hex!("0100")).unwrap().unwrap();
422 assert_eq!(action.drain, 2);
423 assert_eq!(
424 action.reply,
425 hex!("05 01 00 03 12 7777772e746f7270726f6a6563742e6f7267 01BB")
426 );
427 assert_eq!(action.finished, false);
428
429 let action = hs
432 .handshake_for_tests(&hex!("05 00 00 01 C0000215 01BB"))
433 .unwrap()
434 .unwrap();
435 assert_eq!(action.drain, 10);
436 assert!(action.reply.is_empty());
437 assert_eq!(action.finished, true);
438
439 let reply = hs.into_reply().unwrap();
440 assert_eq!(reply.status(), SocksStatus::SUCCEEDED);
441 assert_eq!(reply.port(), 443);
442 assert_eq!(reply.addr().to_string(), "192.0.2.21");
443 }
444}