Skip to main content

tor_socksproto/
handshake.rs

1//! Implement the socks handshakes.
2
3#[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
17/// Constant for Username/Password-style authentication.
18/// (See RFC 1929)
19const USERNAME_PASSWORD: u8 = 0x02;
20/// Constant for "no authentication".
21const NO_AUTHENTICATION: u8 = 0x00;
22
23/// An action to take in response to a SOCKS handshake message.
24#[derive(Clone, Debug)]
25#[non_exhaustive]
26pub struct Action {
27    /// If nonzero, this many bytes should be drained from the
28    /// client's inputs.
29    pub drain: usize,
30    /// If nonempty, this reply should be sent to the other party.
31    pub reply: Vec<u8>,
32    /// If true, then this handshake is over, either successfully or not.
33    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    // @@ begin test lint list
94    #![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_time_subtraction)]
103    #![allow(clippy::useless_vec)]
104    #![allow(clippy::needless_pass_by_value)]
105    #![allow(clippy::string_slice)] // See arti#2571
106    //! <!-- @@ end test lint list
107
108    use crate::*;
109    use std::collections::VecDeque;
110
111    /// Given a socks request, run a complete (successful round) trip, reply with the
112    /// the given status code, and return both sides' results.
113    ///
114    /// Use the (deprecated) `Handshake::handshake` and `Action` API
115    fn run_handshake_old_api(
116        request: SocksRequest,
117        status: SocksStatus,
118    ) -> (SocksRequest, SocksReply) {
119        let mut client_hs = SocksClientHandshake::new(request);
120        let mut proxy_hs = SocksProxyHandshake::new();
121        let mut received_request = None;
122
123        let mut last_proxy_msg = vec![];
124        // Prevent infinite loop in case of bugs.
125        for _ in 0..100 {
126            // Make sure that the client says "truncated" for all prefixes of the proxy's message.
127            for truncate in 0..last_proxy_msg.len() {
128                let r = client_hs.handshake_for_tests(&last_proxy_msg[..truncate]);
129                assert!(r.is_err());
130            }
131            // Get the client's actual message.
132            let client_action = client_hs
133                .handshake_for_tests(&last_proxy_msg)
134                .unwrap()
135                .unwrap();
136            assert_eq!(client_action.drain, last_proxy_msg.len());
137            if client_action.finished {
138                let received_reply = client_hs.into_reply();
139                return (received_request.unwrap(), received_reply.unwrap());
140            }
141            let client_msg = client_action.reply;
142
143            // Make sure that the proxy says "truncated" for all prefixes of the client's message.
144            for truncate in 0..client_msg.len() {
145                let r = proxy_hs.handshake_for_tests(&client_msg[..truncate]);
146                assert!(r.is_err());
147            }
148            // Get the proxy's actual reply (if any).
149            let proxy_action = proxy_hs.handshake_for_tests(&client_msg).unwrap().unwrap();
150            assert_eq!(proxy_action.drain, client_msg.len());
151            last_proxy_msg = if proxy_action.finished {
152                // The proxy is done: have it reply with a status code.
153                received_request = proxy_hs.clone().into_request();
154                received_request
155                    .as_ref()
156                    .unwrap()
157                    .reply(status, None)
158                    .unwrap()
159            } else {
160                proxy_action.reply
161            };
162        }
163        panic!("Handshake ran for too many steps")
164    }
165
166    /// Given a socks request, run a complete (successful round) trip, reply with the
167    /// the given status code, and return both sides' results.
168    ///
169    /// Use the (new) `Handshake::step` API
170    fn run_handshake_new_api<P: ReadPrecision, const MAX_RECV: usize>(
171        request: SocksRequest,
172        status: SocksStatus,
173    ) -> (SocksRequest, SocksReply) {
174        struct State<P: ReadPrecision, H: Handshake> {
175            hs: H,
176            buf: Buffer<P>,
177            fin: Option<H::Output>,
178        }
179
180        struct DidSomething;
181
182        let mut client = State::<P, _>::new(SocksClientHandshake::new(request));
183        let mut server = State::<P, _>::new(SocksProxyHandshake::new());
184
185        let mut c2s = VecDeque::new();
186        let mut s2c = VecDeque::new();
187
188        let mut status = Some(status);
189
190        impl<P: ReadPrecision, H: Handshake> State<P, H> {
191            fn new(hs: H) -> Self {
192                State {
193                    hs,
194                    buf: Default::default(),
195                    fin: None,
196                }
197            }
198
199            fn progress_1(
200                &mut self,
201                max_recv: usize,
202                rx: &mut VecDeque<u8>,
203                tx: &mut VecDeque<u8>,
204            ) -> Option<DidSomething> {
205                use NextStep as NS;
206
207                if self.fin.is_some() {
208                    return None;
209                }
210
211                match self.hs.step(&mut self.buf).unwrap() {
212                    NS::Recv(mut recv) => {
213                        let n = [recv.buf().len(), rx.len(), max_recv]
214                            .into_iter()
215                            .min()
216                            .unwrap();
217                        for p in &mut recv.buf()[0..n] {
218                            *p = rx.pop_front().unwrap();
219                        }
220                        recv.note_received(n).unwrap_or_else(|e| match e {
221                            // This is actually expected; our test case produces 0-byte reads
222                            // sometimes.
223                            Error::UnexpectedEof => {}
224                            other => panic!("{:?}", other),
225                        });
226                        if n != 0 { Some(DidSomething) } else { None }
227                    }
228                    NS::Send(send) => {
229                        for c in send {
230                            tx.push_back(c);
231                        }
232                        Some(DidSomething)
233                    }
234                    NS::Finished(fin) => {
235                        self.fin = Some(fin.into_output_forbid_pipelining().unwrap());
236                        Some(DidSomething)
237                    }
238                }
239            }
240        }
241
242        loop {
243            let ds = [
244                client.progress_1(MAX_RECV, &mut s2c, &mut c2s),
245                server.progress_1(MAX_RECV, &mut c2s, &mut s2c),
246            ]
247            .into_iter()
248            .flatten()
249            .next();
250
251            if let Some(DidSomething) = ds {
252                continue;
253            }
254
255            let Some(status) = status.take() else { break };
256
257            let reply = server.fin.as_ref().unwrap().reply(status, None).unwrap();
258            for c in reply {
259                s2c.push_back(c);
260            }
261        }
262
263        (server.fin.unwrap(), client.fin.unwrap())
264    }
265
266    // Invoke run_handshake and assert that the output matches the input.
267    fn test_handshake(request: &SocksRequest, status: SocksStatus) {
268        for run_handshake in [
269            run_handshake_old_api,
270            run_handshake_new_api::<(), 1>,
271            run_handshake_new_api::<(), 100>,
272            run_handshake_new_api::<PreciseReads, 1>,
273            run_handshake_new_api::<PreciseReads, 100>,
274        ] {
275            let (request_out, status_out) = run_handshake(request.clone(), status);
276            assert_eq!(&request_out, request);
277            assert_eq!(status_out.status(), status);
278        }
279    }
280
281    #[test]
282    fn socks4() {
283        test_handshake(
284            &SocksRequest::new(
285                SocksVersion::V4,
286                SocksCmd::CONNECT,
287                SocksAddr::Hostname("www.torproject.org".to_string().try_into().unwrap()),
288                443,
289                SocksAuth::NoAuth,
290            )
291            .unwrap(),
292            SocksStatus::SUCCEEDED,
293        );
294
295        test_handshake(
296            &SocksRequest::new(
297                SocksVersion::V4,
298                SocksCmd::CONNECT,
299                SocksAddr::Ip("192.0.2.33".parse().unwrap()),
300                22,
301                SocksAuth::Socks4(b"swordfish".to_vec()),
302            )
303            .unwrap(),
304            SocksStatus::GENERAL_FAILURE,
305        );
306    }
307
308    #[test]
309    fn socks5() {
310        test_handshake(
311            &SocksRequest::new(
312                SocksVersion::V5,
313                SocksCmd::CONNECT,
314                SocksAddr::Hostname("www.torproject.org".to_string().try_into().unwrap()),
315                443,
316                SocksAuth::NoAuth,
317            )
318            .unwrap(),
319            SocksStatus::SUCCEEDED,
320        );
321
322        test_handshake(
323            &SocksRequest::new(
324                SocksVersion::V5,
325                SocksCmd::CONNECT,
326                SocksAddr::Ip("2001:db8::32".parse().unwrap()),
327                443,
328                SocksAuth::Username(b"belbo".to_vec(), b"non".to_vec()),
329            )
330            .unwrap(),
331            SocksStatus::GENERAL_FAILURE,
332        );
333    }
334}