1#![allow(dead_code)]
16
17use std::{
18 net::{IpAddr, SocketAddr},
19 sync::Arc,
20};
21
22use futures::{AsyncReadExt, AsyncWriteExt};
23use tor_linkspec::PtTargetAddr;
24use tor_rtcompat::NetStreamProvider;
25use tor_socksproto::{
26 Handshake as _, SocksAddr, SocksAuth, SocksClientHandshake, SocksCmd, SocksRequest,
27 SocksStatus, SocksVersion,
28};
29use tracing::trace;
30
31#[cfg(feature = "pt-client")]
32use super::TransportImplHelper;
33#[cfg(feature = "pt-client")]
34use async_trait::async_trait;
35#[cfg(feature = "pt-client")]
36use tor_error::bad_api_usage;
37#[cfg(feature = "pt-client")]
38use tor_linkspec::{ChannelMethod, HasChanMethod, OwnedChanTarget};
39
40#[derive(Clone, Debug, Eq, PartialEq)]
42#[non_exhaustive]
43pub enum Protocol {
44 Socks(SocksVersion, SocksAuth),
46}
47
48const NO_ADDR: IpAddr = IpAddr::V4(std::net::Ipv4Addr::new(0, 0, 0, 1));
50
51pub(crate) async fn connect_via_proxy<R: NetStreamProvider + Send + Sync>(
68 runtime: &R,
69 proxy: &SocketAddr,
70 protocol: &Protocol,
71 target: &PtTargetAddr,
72) -> Result<R::Stream, ProxyError> {
73 trace!(
74 "Launching a proxied connection to {} via proxy at {} using {:?}",
75 target,
76 proxy,
77 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 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,
139 status
140 );
141
142 if status != SocksStatus::SUCCEEDED {
143 return Err(ProxyError::SocksError(status));
144 }
145
146 Ok(stream)
147}
148
149#[derive(Clone, Debug, thiserror::Error)]
151#[non_exhaustive]
152pub enum ProxyError {
153 #[error("Problem while connecting to proxy")]
155 ProxyConnect(#[source] Arc<std::io::Error>),
156
157 #[error("Problem while communicating with proxy")]
159 ProxyIo(#[source] Arc<std::io::Error>),
160
161 #[error("SOCKS proxy does not support target address")]
163 InvalidSocksAddr(#[source] tor_socksproto::Error),
164
165 #[error("Got an address type we don't recognize")]
167 UnrecognizedAddr,
168
169 #[error("Tried to make an invalid SOCKS request")]
171 InvalidSocksRequest(#[source] tor_socksproto::Error),
172
173 #[error("Protocol error while communicating with SOCKS proxy")]
175 SocksProto(#[source] tor_socksproto::Error),
176
177 #[error("Internal error")]
179 Bug(#[from] tor_error::Bug),
180
181 #[error("Received unexpected early data from peer")]
192 UnexpectedData,
193
194 #[error("SOCKS proxy reported an error: {0}")]
196 SocksError(SocksStatus),
197}
198
199impl From<std::io::Error> for ProxyError {
200 fn from(e: std::io::Error) -> Self {
201 ProxyError::ProxyIo(Arc::new(e))
202 }
203}
204
205impl tor_error::HasKind for ProxyError {
206 fn kind(&self) -> tor_error::ErrorKind {
207 use tor_error::ErrorKind as EK;
208 use ProxyError as E;
209 match self {
210 E::ProxyConnect(_) | E::ProxyIo(_) => EK::LocalNetworkError,
211 E::InvalidSocksAddr(_) | E::InvalidSocksRequest(_) => EK::BadApiUsage,
212 E::UnrecognizedAddr => EK::NotImplemented,
213 E::SocksProto(_) => EK::LocalProtocolViolation,
214 E::Bug(e) => e.kind(),
215 E::UnexpectedData => EK::NotImplemented,
216 E::SocksError(_) => EK::LocalProtocolViolation,
217 }
218 }
219}
220
221impl tor_error::HasRetryTime for ProxyError {
222 fn retry_time(&self) -> tor_error::RetryTime {
223 use tor_error::RetryTime as RT;
224 use ProxyError as E;
225 use SocksStatus as S;
226 match self {
227 E::ProxyConnect(_) | E::ProxyIo(_) => RT::AfterWaiting,
228 E::InvalidSocksAddr(_) => RT::Never,
229 E::UnrecognizedAddr => RT::Never,
230 E::InvalidSocksRequest(_) => RT::Never,
231 E::SocksProto(_) => RT::AfterWaiting,
232 E::Bug(_) => RT::Never,
233 E::UnexpectedData => RT::Never,
234 E::SocksError(e) => match *e {
235 S::CONNECTION_REFUSED
236 | S::GENERAL_FAILURE
237 | S::HOST_UNREACHABLE
238 | S::NETWORK_UNREACHABLE
239 | S::TTL_EXPIRED => RT::AfterWaiting,
240 _ => RT::Never,
241 },
242 }
243 }
244}
245
246#[cfg(feature = "pt-client")]
247#[cfg_attr(docsrs, doc(cfg(feature = "pt-client")))]
248#[derive(Clone, Debug)]
251pub struct ExternalProxyPlugin<R> {
252 runtime: R,
254 proxy_addr: SocketAddr,
256 proxy_version: SocksVersion,
258}
259
260#[cfg(feature = "pt-client")]
261#[cfg_attr(docsrs, doc(cfg(feature = "pt-client")))]
262impl<R: NetStreamProvider + Send + Sync> ExternalProxyPlugin<R> {
263 pub fn new(rt: R, proxy_addr: SocketAddr, proxy_version: SocksVersion) -> Self {
265 Self {
266 runtime: rt,
267 proxy_addr,
268 proxy_version,
269 }
270 }
271}
272
273#[cfg(feature = "pt-client")]
274#[async_trait]
275impl<R: NetStreamProvider + Send + Sync> TransportImplHelper for ExternalProxyPlugin<R> {
276 type Stream = R::Stream;
277
278 async fn connect(
279 &self,
280 target: &OwnedChanTarget,
281 ) -> crate::Result<(OwnedChanTarget, 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
300 Ok((
301 target.clone(),
302 connect_via_proxy(&self.runtime, &self.proxy_addr, &protocol, pt_target.addr()).await?,
303 ))
304 }
305}
306
307#[cfg(feature = "pt-client")]
309fn encode_settings<'a, IT>(settings: IT) -> String
310where
311 IT: Iterator<Item = (&'a str, &'a str)>,
312{
313 enum EscChar {
317 Backslash(char),
319 Literal(char),
321 Done,
323 }
324 impl EscChar {
325 fn new(ch: char, in_key: bool) -> Self {
327 match ch {
328 '\\' | ';' => EscChar::Backslash(ch),
329 '=' if in_key => EscChar::Backslash(ch),
330 _ => EscChar::Literal(ch),
331 }
332 }
333 }
334 impl Iterator for EscChar {
335 type Item = char;
336
337 fn next(&mut self) -> Option<Self::Item> {
338 match *self {
339 EscChar::Backslash(ch) => {
340 *self = EscChar::Literal(ch);
341 Some('\\')
342 }
343 EscChar::Literal(ch) => {
344 *self = EscChar::Done;
345 Some(ch)
346 }
347 EscChar::Done => None,
348 }
349 }
350 }
351
352 fn esc(s: &str, in_key: bool) -> impl Iterator<Item = char> + '_ {
354 s.chars().flat_map(move |c| EscChar::new(c, in_key))
355 }
356
357 let mut result = String::new();
358 for (k, v) in settings {
359 result.extend(esc(k, true));
360 result.push('=');
361 result.extend(esc(v, false));
362 result.push(';');
363 }
364 result.pop(); result
367}
368
369#[cfg(feature = "pt-client")]
373pub fn settings_to_protocol(vers: SocksVersion, s: String) -> Result<Protocol, ProxyError> {
374 let mut bytes: Vec<_> = s.into();
375 Ok(if bytes.is_empty() {
376 Protocol::Socks(vers, SocksAuth::NoAuth)
377 } else if vers == SocksVersion::V4 {
378 if bytes.contains(&0) {
379 return Err(ProxyError::InvalidSocksRequest(
380 tor_socksproto::Error::NotImplemented(
381 "SOCKS 4 doesn't support internal NUL bytes (for PT settings list)".into(),
382 ),
383 ));
384 } else {
385 Protocol::Socks(SocksVersion::V4, SocksAuth::Socks4(bytes))
386 }
387 } else if bytes.len() <= 255 {
388 Protocol::Socks(SocksVersion::V5, SocksAuth::Username(bytes, vec![0]))
390 } else if bytes.len() <= (255 * 2) {
391 let password = bytes.split_off(255);
392 Protocol::Socks(SocksVersion::V5, SocksAuth::Username(bytes, password))
393 } else {
394 return Err(ProxyError::InvalidSocksRequest(
395 tor_socksproto::Error::NotImplemented("PT settings list too long for SOCKS 5".into()),
396 ));
397 })
398}
399
400#[cfg(test)]
401mod test {
402 #![allow(clippy::bool_assert_comparison)]
404 #![allow(clippy::clone_on_copy)]
405 #![allow(clippy::dbg_macro)]
406 #![allow(clippy::mixed_attributes_style)]
407 #![allow(clippy::print_stderr)]
408 #![allow(clippy::print_stdout)]
409 #![allow(clippy::single_char_pattern)]
410 #![allow(clippy::unwrap_used)]
411 #![allow(clippy::unchecked_duration_subtraction)]
412 #![allow(clippy::useless_vec)]
413 #![allow(clippy::needless_pass_by_value)]
414 #[allow(unused_imports)]
416 use super::*;
417
418 #[cfg(feature = "pt-client")]
419 #[test]
420 fn setting_encoding() {
421 fn check(settings: Vec<(&str, &str)>, expected: &str) {
422 assert_eq!(encode_settings(settings.into_iter()), expected);
423 }
424
425 check(vec![], "");
427 check(vec![("hello", "world")], "hello=world");
428 check(
429 vec![("hey", "verden"), ("hello", "world")],
430 "hey=verden;hello=world",
431 );
432 check(
433 vec![("hey", "verden"), ("hello", "world"), ("selv", "tak")],
434 "hey=verden;hello=world;selv=tak",
435 );
436
437 check(
438 vec![("semi;colon", "equals=sign")],
439 r"semi\;colon=equals=sign",
440 );
441 check(
442 vec![("equals=sign", "semi;colon")],
443 r"equals\=sign=semi\;colon",
444 );
445 check(
446 vec![("semi;colon", "equals=sign"), ("also", "back\\slash")],
447 r"semi\;colon=equals=sign;also=back\\slash",
448 );
449 }
450
451 #[cfg(feature = "pt-client")]
452 #[test]
453 fn split_settings() {
454 use SocksVersion::*;
455 let long_string = "examplestrg".to_owned().repeat(50);
456 assert_eq!(long_string.len(), 550);
457 let sv = |v, a, b| settings_to_protocol(v, long_string[a..b].to_owned()).unwrap();
458 let s = |a, b| sv(V5, a, b);
459 let v = |a, b| long_string.as_bytes()[a..b].to_vec();
460
461 assert_eq!(s(0, 0), Protocol::Socks(V5, SocksAuth::NoAuth));
462 assert_eq!(
463 s(0, 50),
464 Protocol::Socks(V5, SocksAuth::Username(v(0, 50), vec![0]))
465 );
466 assert_eq!(
467 s(0, 255),
468 Protocol::Socks(V5, SocksAuth::Username(v(0, 255), vec![0]))
469 );
470 assert_eq!(
471 s(0, 256),
472 Protocol::Socks(V5, SocksAuth::Username(v(0, 255), v(255, 256)))
473 );
474 assert_eq!(
475 s(0, 300),
476 Protocol::Socks(V5, SocksAuth::Username(v(0, 255), v(255, 300)))
477 );
478 assert_eq!(
479 s(0, 510),
480 Protocol::Socks(V5, SocksAuth::Username(v(0, 255), v(255, 510)))
481 );
482
483 assert_eq!(
485 sv(V4, 0, 511),
486 Protocol::Socks(V4, SocksAuth::Socks4(v(0, 511)))
487 );
488
489 assert_eq!(
491 settings_to_protocol(V5, "\0".to_owned()).unwrap(),
492 Protocol::Socks(V5, SocksAuth::Username(vec![0], vec![0]))
493 );
494 assert_eq!(
495 settings_to_protocol(V5, "\0".to_owned().repeat(510)).unwrap(),
496 Protocol::Socks(V5, SocksAuth::Username(vec![0; 255], vec![0; 255]))
497 );
498
499 assert!(settings_to_protocol(V5, "\0".to_owned().repeat(511)).is_err());
501
502 assert!(settings_to_protocol(V5, long_string[0..512].to_owned()).is_err());
504
505 assert!(settings_to_protocol(V4, "\0".to_owned()).is_err());
507 }
508}