1
//! Connect to relays via a proxy.
2
//!
3
//! This code is here for two reasons:
4
//!   1. To connect via external pluggable transports (for which we use SOCKS to
5
//!      build our connections).
6
//!   2. To support users who are behind a firewall that requires them to use a
7
//!      SOCKS proxy to connect.
8
//!
9
//! Currently only SOCKS proxies are supported.
10
//
11
// TODO: Add support for `HTTP(S) CONNECT` someday?
12
//
13
// TODO: Maybe refactor this so that tor-ptmgr can exist in a more freestanding
14
// way, with fewer arti dependencies.
15
#![allow(dead_code)]
16

            
17
use std::{
18
    net::{IpAddr, SocketAddr},
19
    sync::Arc,
20
};
21

            
22
use futures::{AsyncReadExt, AsyncWriteExt};
23
use tor_linkspec::PtTargetAddr;
24
use tor_rtcompat::NetStreamProvider;
25
use tor_socksproto::{
26
    Handshake as _, SocksAddr, SocksAuth, SocksClientHandshake, SocksCmd, SocksRequest,
27
    SocksStatus, SocksVersion,
28
};
29
use tracing::trace;
30

            
31
#[cfg(feature = "pt-client")]
32
use super::TransportImplHelper;
33
#[cfg(feature = "pt-client")]
34
use async_trait::async_trait;
35
#[cfg(feature = "pt-client")]
36
use tor_error::bad_api_usage;
37
#[cfg(feature = "pt-client")]
38
use tor_linkspec::{ChannelMethod, HasChanMethod, OwnedChanTarget};
39
#[cfg(feature = "pt-client")]
40
use tor_proto::peer::PeerAddr;
41

            
42
/// Information about what proxy protocol to use, and how to use it.
43
#[derive(Clone, Debug, Eq, PartialEq)]
44
#[non_exhaustive]
45
pub enum Protocol {
46
    /// Connect via SOCKS 4, SOCKS 4a, or SOCKS 5.
47
    Socks(SocksVersion, SocksAuth),
48
}
49

            
50
/// An address to use when told to connect to "no address."
51
const NO_ADDR: IpAddr = IpAddr::V4(std::net::Ipv4Addr::new(0, 0, 0, 1));
52

            
53
/// Open a connection to `target` via the proxy at `proxy`, using the protocol
54
/// at `protocol`.
55
///
56
/// # Limitations
57
///
58
/// We will give an error if the proxy sends us any data on the connection along
59
/// with its final handshake: due to our implementation, any such data will be
60
/// discarded, and so we give an error rather than fail silently.
61
///
62
/// This limitation doesn't matter when the underlying protocol is Tor, or
63
/// anything else where the initiator is expected to speak before the responder
64
/// says anything.  To lift it, we would have to make this function's return
65
/// type become something buffered.
66
//
67
// TODO: Perhaps we should refactor this someday so it can be a general-purpose
68
// proxy function, not only for Arti.
69
pub(crate) async fn connect_via_proxy<R: NetStreamProvider + Send + Sync>(
70
    runtime: &R,
71
    proxy: &SocketAddr,
72
    protocol: &Protocol,
73
    target: &PtTargetAddr,
74
) -> Result<R::Stream, ProxyError> {
75
    trace!(
76
        "Launching a proxied connection to {} via proxy at {} using {:?}",
77
        target, proxy, protocol
78
    );
79
    let mut stream = runtime
80
        .connect(proxy)
81
        .await
82
        .map_err(|e| ProxyError::ProxyConnect(Arc::new(e)))?;
83

            
84
    let Protocol::Socks(version, auth) = protocol;
85

            
86
    let (target_addr, target_port): (tor_socksproto::SocksAddr, u16) = match target {
87
        PtTargetAddr::IpPort(a) => (SocksAddr::Ip(a.ip()), a.port()),
88
        #[cfg(feature = "pt-client")]
89
        PtTargetAddr::HostPort(host, port) => (
90
            SocksAddr::Hostname(
91
                host.clone()
92
                    .try_into()
93
                    .map_err(ProxyError::InvalidSocksAddr)?,
94
            ),
95
            *port,
96
        ),
97
        #[cfg(feature = "pt-client")]
98
        PtTargetAddr::None => (SocksAddr::Ip(NO_ADDR), 1),
99
        _ => return Err(ProxyError::UnrecognizedAddr),
100
    };
101

            
102
    let request = SocksRequest::new(
103
        *version,
104
        SocksCmd::CONNECT,
105
        target_addr,
106
        target_port,
107
        auth.clone(),
108
    )
109
    .map_err(ProxyError::InvalidSocksRequest)?;
110
    let mut handshake = SocksClientHandshake::new(request);
111

            
112
    // TODO: This code is largely copied from the socks server wrapper code in
113
    // arti::proxy. Perhaps we should condense them into a single thing, if we
114
    // don't just revise the SOCKS code completely.
115
    let mut buf = tor_socksproto::Buffer::new();
116
    let reply = loop {
117
        use tor_socksproto::NextStep as NS;
118
        match handshake.step(&mut buf).map_err(ProxyError::SocksProto)? {
119
            NS::Send(send) => {
120
                stream.write_all(&send).await?;
121
                stream.flush().await?;
122
            }
123
            NS::Finished(fin) => {
124
                break fin
125
                    .into_output_forbid_pipelining()
126
                    .map_err(ProxyError::SocksProto)?;
127
            }
128
            NS::Recv(mut recv) => {
129
                let n = stream.read(recv.buf()).await?;
130
                recv.note_received(n).map_err(ProxyError::SocksProto)?;
131
            }
132
        }
133
    };
134

            
135
    let status = reply.status();
136
    trace!(
137
        "SOCKS handshake with {} succeeded, with status {:?}",
138
        proxy, status
139
    );
140

            
141
    if status != SocksStatus::SUCCEEDED {
142
        return Err(ProxyError::SocksError(status));
143
    }
144

            
145
    Ok(stream)
146
}
147

            
148
/// An error that occurs while negotiating a connection with a proxy.
149
#[derive(Clone, Debug, thiserror::Error)]
150
#[non_exhaustive]
151
pub enum ProxyError {
152
    /// We had an IO error while trying to open a connection to the proxy.
153
    #[error("Problem while connecting to proxy")]
154
    ProxyConnect(#[source] Arc<std::io::Error>),
155

            
156
    /// We had an IO error while talking to the proxy.
157
    #[error("Problem while communicating with proxy")]
158
    ProxyIo(#[source] Arc<std::io::Error>),
159

            
160
    /// We tried to use an address which socks doesn't support.
161
    #[error("SOCKS proxy does not support target address")]
162
    InvalidSocksAddr(#[source] tor_socksproto::Error),
163

            
164
    /// We tried to use an address type which _we_ don't recognize.
165
    #[error("Got an address type we don't recognize")]
166
    UnrecognizedAddr,
167

            
168
    /// Our SOCKS implementation told us that this request cannot be encoded.
169
    #[error("Tried to make an invalid SOCKS request")]
170
    InvalidSocksRequest(#[source] tor_socksproto::Error),
171

            
172
    /// The peer refused our request, or spoke SOCKS incorrectly.
173
    #[error("Protocol error while communicating with SOCKS proxy")]
174
    SocksProto(#[source] tor_socksproto::Error),
175

            
176
    /// We encountered an internal programming error.
177
    #[error("Internal error")]
178
    Bug(#[from] tor_error::Bug),
179

            
180
    /// We got extra data immediately after our handshake, before we actually
181
    /// sent anything.
182
    ///
183
    /// This is not a bug in the calling code or in the peer protocol: it just
184
    /// means that the remote peer sent us data before we actually sent it any
185
    /// data. Unfortunately, there's a limitation in our code that makes it
186
    /// discard any such data, and therefore we have to give this error to
187
    /// prevent bugs.
188
    ///
189
    /// We could someday remove this limitation.
190
    #[error("Received unexpected early data from peer")]
191
    UnexpectedData,
192

            
193
    /// The proxy told us that our attempt failed.
194
    #[error("SOCKS proxy reported an error: {0}")]
195
    SocksError(SocksStatus),
196
}
197

            
198
impl From<std::io::Error> for ProxyError {
199
10
    fn from(e: std::io::Error) -> Self {
200
10
        ProxyError::ProxyIo(Arc::new(e))
201
10
    }
202
}
203

            
204
impl From<ProxyError> for std::io::Error {
205
2
    fn from(e: ProxyError) -> Self {
206
2
        std::io::Error::other(e)
207
2
    }
208
}
209

            
210
impl tor_error::HasKind for ProxyError {
211
    fn kind(&self) -> tor_error::ErrorKind {
212
        use ProxyError as E;
213
        use tor_error::ErrorKind as EK;
214
        match self {
215
            E::ProxyConnect(_) | E::ProxyIo(_) => EK::LocalNetworkError,
216
            E::InvalidSocksAddr(_) | E::InvalidSocksRequest(_) => EK::BadApiUsage,
217
            E::UnrecognizedAddr => EK::NotImplemented,
218
            E::SocksProto(_) => EK::LocalProtocolViolation,
219
            E::Bug(e) => e.kind(),
220
            E::UnexpectedData => EK::NotImplemented,
221
            E::SocksError(_) => EK::LocalProtocolViolation,
222
        }
223
    }
224
}
225

            
226
impl tor_error::HasRetryTime for ProxyError {
227
    fn retry_time(&self) -> tor_error::RetryTime {
228
        use ProxyError as E;
229
        use SocksStatus as S;
230
        use tor_error::RetryTime as RT;
231
        match self {
232
            E::ProxyConnect(_) | E::ProxyIo(_) => RT::AfterWaiting,
233
            E::InvalidSocksAddr(_) => RT::Never,
234
            E::UnrecognizedAddr => RT::Never,
235
            E::InvalidSocksRequest(_) => RT::Never,
236
            E::SocksProto(_) => RT::AfterWaiting,
237
            E::Bug(_) => RT::Never,
238
            E::UnexpectedData => RT::Never,
239
            E::SocksError(e) => match *e {
240
                S::CONNECTION_REFUSED
241
                | S::GENERAL_FAILURE
242
                | S::HOST_UNREACHABLE
243
                | S::NETWORK_UNREACHABLE
244
                | S::TTL_EXPIRED => RT::AfterWaiting,
245
                _ => RT::Never,
246
            },
247
        }
248
    }
249
}
250

            
251
#[cfg(feature = "pt-client")]
252
/// An object that connects to a Tor bridge via an external pluggable transport
253
/// that provides a proxy.
254
#[derive(Clone, Debug)]
255
pub struct ExternalProxyPlugin<R> {
256
    /// The runtime to use for connections.
257
    runtime: R,
258
    /// The location of the proxy.
259
    proxy_addr: SocketAddr,
260
    /// The SOCKS protocol version to use.
261
    proxy_version: SocksVersion,
262
}
263

            
264
#[cfg(feature = "pt-client")]
265
impl<R: NetStreamProvider + Send + Sync> ExternalProxyPlugin<R> {
266
    /// Make a new `ExternalProxyPlugin`.
267
    pub fn new(rt: R, proxy_addr: SocketAddr, proxy_version: SocksVersion) -> Self {
268
        Self {
269
            runtime: rt,
270
            proxy_addr,
271
            proxy_version,
272
        }
273
    }
274
}
275

            
276
#[cfg(feature = "pt-client")]
277
#[async_trait]
278
impl<R: NetStreamProvider + Send + Sync> TransportImplHelper for ExternalProxyPlugin<R> {
279
    type Stream = R::Stream;
280

            
281
    async fn connect(&self, target: &OwnedChanTarget) -> crate::Result<(PeerAddr, R::Stream)> {
282
        let pt_target = match target.chan_method() {
283
            ChannelMethod::Direct(_) => {
284
                return Err(crate::Error::UnusableTarget(bad_api_usage!(
285
                    "Used pluggable transport for a TCP connection."
286
                )));
287
            }
288
            ChannelMethod::Pluggable(target) => target,
289
            other => {
290
                return Err(crate::Error::UnusableTarget(bad_api_usage!(
291
                    "Used unknown, unsupported, transport {:?} for a TCP connection.",
292
                    other,
293
                )));
294
            }
295
        };
296

            
297
        let protocol =
298
            settings_to_protocol(self.proxy_version, encode_settings(pt_target.settings()))?;
299
        let stream =
300
            connect_via_proxy(&self.runtime, &self.proxy_addr, &protocol, pt_target.addr()).await?;
301

            
302
        Ok((pt_target.into(), stream))
303
    }
304
}
305

            
306
/// Encode the PT settings from `IT` in a format that a pluggable transport can use.
307
#[cfg(feature = "pt-client")]
308
14
fn encode_settings<'a, IT>(settings: IT) -> String
309
14
where
310
14
    IT: Iterator<Item = (&'a str, &'a str)>,
311
{
312
    /// Escape a character in the way expected by pluggable transports.
313
    ///
314
    /// This escape machinery is a mirror of that in the standard library.
315
    enum EscChar {
316
        /// Return a backslash then a character.
317
        Backslash(char),
318
        /// Return a character.
319
        Literal(char),
320
        /// Return nothing.
321
        Done,
322
    }
323
    impl EscChar {
324
        /// Create an iterator to escape one character.
325
264
        fn new(ch: char, in_key: bool) -> Self {
326
6
            match ch {
327
8
                '\\' | ';' => EscChar::Backslash(ch),
328
2
                '=' if in_key => EscChar::Backslash(ch),
329
254
                _ => EscChar::Literal(ch),
330
            }
331
264
        }
332
    }
333
    impl Iterator for EscChar {
334
        type Item = char;
335

            
336
538
        fn next(&mut self) -> Option<Self::Item> {
337
538
            match *self {
338
10
                EscChar::Backslash(ch) => {
339
10
                    *self = EscChar::Literal(ch);
340
10
                    Some('\\')
341
                }
342
264
                EscChar::Literal(ch) => {
343
264
                    *self = EscChar::Done;
344
264
                    Some(ch)
345
                }
346
264
                EscChar::Done => None,
347
            }
348
538
        }
349
    }
350

            
351
    /// escape a key or value string.
352
40
    fn esc(s: &str, in_key: bool) -> impl Iterator<Item = char> + '_ {
353
284
        s.chars().flat_map(move |c| EscChar::new(c, in_key))
354
40
    }
355

            
356
14
    let mut result = String::new();
357
34
    for (k, v) in settings {
358
20
        result.extend(esc(k, true));
359
20
        result.push('=');
360
20
        result.extend(esc(v, false));
361
20
        result.push(';');
362
20
    }
363
14
    result.pop(); // remove the final ';' if any. Yes this is ugly.
364

            
365
14
    result
366
14
}
367

            
368
/// Transform a string into a representation that can be sent as SOCKS
369
/// authentication.
370
// NOTE(eta): I am very unsure of the logic in here.
371
#[cfg(feature = "pt-client")]
372
24
pub fn settings_to_protocol(vers: SocksVersion, s: String) -> Result<Protocol, ProxyError> {
373
24
    let mut bytes: Vec<_> = s.into();
374
24
    Ok(if bytes.is_empty() {
375
2
        Protocol::Socks(vers, SocksAuth::NoAuth)
376
22
    } else if vers == SocksVersion::V4 {
377
4
        if bytes.contains(&0) {
378
2
            return Err(ProxyError::InvalidSocksRequest(
379
2
                tor_socksproto::Error::NotImplemented(
380
2
                    "SOCKS 4 doesn't support internal NUL bytes (for PT settings list)".into(),
381
2
                ),
382
2
            ));
383
        } else {
384
2
            Protocol::Socks(SocksVersion::V4, SocksAuth::Socks4(bytes))
385
        }
386
18
    } else if bytes.len() <= 255 {
387
        // The [0] here is mandatory according to the pt-spec.
388
6
        Protocol::Socks(SocksVersion::V5, SocksAuth::Username(bytes, vec![0]))
389
12
    } else if bytes.len() <= (255 * 2) {
390
8
        let password = bytes.split_off(255);
391
8
        Protocol::Socks(SocksVersion::V5, SocksAuth::Username(bytes, password))
392
    } else {
393
4
        return Err(ProxyError::InvalidSocksRequest(
394
4
            tor_socksproto::Error::NotImplemented("PT settings list too long for SOCKS 5".into()),
395
4
        ));
396
    })
397
24
}
398

            
399
#[cfg(test)]
400
mod test {
401
    // @@ begin test lint list maintained by maint/add_warning @@
402
    #![allow(clippy::bool_assert_comparison)]
403
    #![allow(clippy::clone_on_copy)]
404
    #![allow(clippy::dbg_macro)]
405
    #![allow(clippy::mixed_attributes_style)]
406
    #![allow(clippy::print_stderr)]
407
    #![allow(clippy::print_stdout)]
408
    #![allow(clippy::single_char_pattern)]
409
    #![allow(clippy::unwrap_used)]
410
    #![allow(clippy::unchecked_time_subtraction)]
411
    #![allow(clippy::useless_vec)]
412
    #![allow(clippy::needless_pass_by_value)]
413
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
414
    #[allow(unused_imports)]
415
    use super::*;
416

            
417
    #[cfg(feature = "pt-client")]
418
    #[test]
419
    fn setting_encoding() {
420
        fn check(settings: Vec<(&str, &str)>, expected: &str) {
421
            assert_eq!(encode_settings(settings.into_iter()), expected);
422
        }
423

            
424
        // Easy cases, no escapes.
425
        check(vec![], "");
426
        check(vec![("hello", "world")], "hello=world");
427
        check(
428
            vec![("hey", "verden"), ("hello", "world")],
429
            "hey=verden;hello=world",
430
        );
431
        check(
432
            vec![("hey", "verden"), ("hello", "world"), ("selv", "tak")],
433
            "hey=verden;hello=world;selv=tak",
434
        );
435

            
436
        check(
437
            vec![("semi;colon", "equals=sign")],
438
            r"semi\;colon=equals=sign",
439
        );
440
        check(
441
            vec![("equals=sign", "semi;colon")],
442
            r"equals\=sign=semi\;colon",
443
        );
444
        check(
445
            vec![("semi;colon", "equals=sign"), ("also", "back\\slash")],
446
            r"semi\;colon=equals=sign;also=back\\slash",
447
        );
448
    }
449

            
450
    #[cfg(feature = "pt-client")]
451
    #[test]
452
    fn split_settings() {
453
        use SocksVersion::*;
454
        let long_string = "examplestrg".to_owned().repeat(50);
455
        assert_eq!(long_string.len(), 550);
456
        let sv = |v, a, b| settings_to_protocol(v, long_string[a..b].to_owned()).unwrap();
457
        let s = |a, b| sv(V5, a, b);
458
        let v = |a, b| long_string.as_bytes()[a..b].to_vec();
459

            
460
        assert_eq!(s(0, 0), Protocol::Socks(V5, SocksAuth::NoAuth));
461
        assert_eq!(
462
            s(0, 50),
463
            Protocol::Socks(V5, SocksAuth::Username(v(0, 50), vec![0]))
464
        );
465
        assert_eq!(
466
            s(0, 255),
467
            Protocol::Socks(V5, SocksAuth::Username(v(0, 255), vec![0]))
468
        );
469
        assert_eq!(
470
            s(0, 256),
471
            Protocol::Socks(V5, SocksAuth::Username(v(0, 255), v(255, 256)))
472
        );
473
        assert_eq!(
474
            s(0, 300),
475
            Protocol::Socks(V5, SocksAuth::Username(v(0, 255), v(255, 300)))
476
        );
477
        assert_eq!(
478
            s(0, 510),
479
            Protocol::Socks(V5, SocksAuth::Username(v(0, 255), v(255, 510)))
480
        );
481

            
482
        // This one needs to use socks4, or it won't fit. :P
483
        assert_eq!(
484
            sv(V4, 0, 511),
485
            Protocol::Socks(V4, SocksAuth::Socks4(v(0, 511)))
486
        );
487

            
488
        // Small requests with "0" bytes work fine...
489
        assert_eq!(
490
            settings_to_protocol(V5, "\0".to_owned()).unwrap(),
491
            Protocol::Socks(V5, SocksAuth::Username(vec![0], vec![0]))
492
        );
493
        assert_eq!(
494
            settings_to_protocol(V5, "\0".to_owned().repeat(510)).unwrap(),
495
            Protocol::Socks(V5, SocksAuth::Username(vec![0; 255], vec![0; 255]))
496
        );
497

            
498
        // Huge requests with "0" simply can't be encoded.
499
        assert!(settings_to_protocol(V5, "\0".to_owned().repeat(511)).is_err());
500

            
501
        // Huge requests without "0" can't be encoded as V5
502
        assert!(settings_to_protocol(V5, long_string[0..512].to_owned()).is_err());
503

            
504
        // Requests with "0" can't be encoded as V4.
505
        assert!(settings_to_protocol(V4, "\0".to_owned()).is_err());
506
    }
507
}