1
//! Implement the socks handshakes.
2

            
3
#[cfg(any(feature = "proxy-handshake", feature = "client-handshake"))]
4
#[macro_use]
5
pub(crate) mod framework;
6

            
7
#[cfg(feature = "client-handshake")]
8
pub(crate) mod client;
9
#[cfg(feature = "proxy-handshake")]
10
pub(crate) mod proxy;
11

            
12
use crate::msg::SocksAddr;
13
use std::net::IpAddr;
14
use tor_bytes::Result as BytesResult;
15
use tor_bytes::{EncodeResult, Error as BytesError, Readable, Reader, Writeable, Writer};
16

            
17
/// Constant for Username/Password-style authentication.
18
/// (See RFC 1929)
19
const USERNAME_PASSWORD: u8 = 0x02;
20
/// Constant for "no authentication".
21
const NO_AUTHENTICATION: u8 = 0x00;
22

            
23
/// An action to take in response to a SOCKS handshake message.
24
#[derive(Clone, Debug)]
25
#[non_exhaustive]
26
pub 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

            
36
impl Readable for SocksAddr {
37
406
    fn take_from(r: &mut Reader<'_>) -> BytesResult<SocksAddr> {
38
406
        let atype = r.take_u8()?;
39
374
        match atype {
40
            1 => {
41
106
                let ip4: std::net::Ipv4Addr = r.extract()?;
42
54
                Ok(SocksAddr::Ip(ip4.into()))
43
            }
44
            3 => {
45
144
                let hlen = r.take_u8()?;
46
136
                let hostname = r.take(hlen as usize)?;
47
26
                let hostname = std::str::from_utf8(hostname)
48
26
                    .map_err(|_| BytesError::InvalidMessage("bad utf8 on hostname".into()))?
49
26
                    .to_string();
50
26
                let hostname = hostname
51
26
                    .try_into()
52
26
                    .map_err(|_| BytesError::InvalidMessage("hostname too long".into()))?;
53
26
                Ok(SocksAddr::Hostname(hostname))
54
            }
55
            4 => {
56
124
                let ip6: std::net::Ipv6Addr = r.extract()?;
57
26
                Ok(SocksAddr::Ip(ip6.into()))
58
            }
59
            _ => Err(BytesError::InvalidMessage(
60
                "unrecognized address type.".into(),
61
            )),
62
        }
63
406
    }
64
}
65

            
66
impl Writeable for SocksAddr {
67
50
    fn write_onto<W: Writer + ?Sized>(&self, w: &mut W) -> EncodeResult<()> {
68
34
        match self {
69
22
            SocksAddr::Ip(IpAddr::V4(ip)) => {
70
22
                w.write_u8(1);
71
22
                w.write(ip)?;
72
            }
73
12
            SocksAddr::Ip(IpAddr::V6(ip)) => {
74
12
                w.write_u8(4);
75
12
                w.write(ip)?;
76
            }
77
16
            SocksAddr::Hostname(h) => {
78
16
                let h = h.as_ref();
79
16
                assert!(h.len() < 256);
80
16
                let hlen = h.len() as u8;
81
16
                w.write_u8(3);
82
16
                w.write_u8(hlen);
83
16
                w.write(h.as_bytes())?;
84
            }
85
        }
86
50
        Ok(())
87
50
    }
88
}
89

            
90
#[cfg(all(feature = "client-handshake", feature = "proxy-handshake"))]
91
#[cfg(test)]
92
mod 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_duration_subtraction)]
103
    #![allow(clippy::useless_vec)]
104
    #![allow(clippy::needless_pass_by_value)]
105
    //! <!-- @@ end test lint list
106

            
107
    use crate::*;
108
    use std::collections::VecDeque;
109

            
110
    /// Given a socks request, run a complete (successful round) trip, reply with the
111
    /// the given status code, and return both sides' results.
112
    ///
113
    /// Use the (deprecated) `Handshake::handshake` and `Action` API
114
    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
        // Prevent infinite loop in case of bugs.
124
        for _ in 0..100 {
125
            // Make sure that the client says "truncated" for all prefixes of the proxy's message.
126
            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
            // Get the client's actual message.
131
            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
            // Make sure that the proxy says "truncated" for all prefixes of the client's message.
143
            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
            // Get the proxy's actual reply (if any).
148
            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
                // The proxy is done: have it reply with a status code.
152
                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
    /// Given a socks request, run a complete (successful round) trip, reply with the
166
    /// the given status code, and return both sides' results.
167
    ///
168
    /// Use the (new) `Handshake::step` API
169
    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
                            // This is actually expected; our test case produces 0-byte reads
221
                            // sometimes.
222
                            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
    // Invoke run_handshake and assert that the output matches the input.
270
    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
}