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, proxy, protocol
76 );
77 let mut stream = runtime
78 .connect(proxy)
79 .await
80 .map_err(|e| ProxyError::ProxyConnect(Arc::new(e)))?;
81
82 let Protocol::Socks(version, auth) = protocol;
83
84 let (target_addr, target_port): (tor_socksproto::SocksAddr, u16) = match target {
85 PtTargetAddr::IpPort(a) => (SocksAddr::Ip(a.ip()), a.port()),
86 #[cfg(feature = "pt-client")]
87 PtTargetAddr::HostPort(host, port) => (
88 SocksAddr::Hostname(
89 host.clone()
90 .try_into()
91 .map_err(ProxyError::InvalidSocksAddr)?,
92 ),
93 *port,
94 ),
95 #[cfg(feature = "pt-client")]
96 PtTargetAddr::None => (SocksAddr::Ip(NO_ADDR), 1),
97 _ => return Err(ProxyError::UnrecognizedAddr),
98 };
99
100 let request = SocksRequest::new(
101 *version,
102 SocksCmd::CONNECT,
103 target_addr,
104 target_port,
105 auth.clone(),
106 )
107 .map_err(ProxyError::InvalidSocksRequest)?;
108 let mut handshake = SocksClientHandshake::new(request);
109
110 let mut buf = tor_socksproto::Buffer::new();
114 let reply = loop {
115 use tor_socksproto::NextStep as NS;
116 match handshake.step(&mut buf).map_err(ProxyError::SocksProto)? {
117 NS::Send(send) => {
118 stream.write_all(&send).await?;
119 stream.flush().await?;
120 }
121 NS::Finished(fin) => {
122 break fin
123 .into_output_forbid_pipelining()
124 .map_err(ProxyError::SocksProto)?;
125 }
126 NS::Recv(mut recv) => {
127 let n = stream.read(recv.buf()).await?;
128 recv.note_received(n).map_err(ProxyError::SocksProto)?;
129 }
130 }
131 };
132
133 let status = reply.status();
134 trace!(
135 "SOCKS handshake with {} succeeded, with status {:?}",
136 proxy, status
137 );
138
139 if status != SocksStatus::SUCCEEDED {
140 return Err(ProxyError::SocksError(status));
141 }
142
143 Ok(stream)
144}
145
146#[derive(Clone, Debug, thiserror::Error)]
148#[non_exhaustive]
149pub enum ProxyError {
150 #[error("Problem while connecting to proxy")]
152 ProxyConnect(#[source] Arc<std::io::Error>),
153
154 #[error("Problem while communicating with proxy")]
156 ProxyIo(#[source] Arc<std::io::Error>),
157
158 #[error("SOCKS proxy does not support target address")]
160 InvalidSocksAddr(#[source] tor_socksproto::Error),
161
162 #[error("Got an address type we don't recognize")]
164 UnrecognizedAddr,
165
166 #[error("Tried to make an invalid SOCKS request")]
168 InvalidSocksRequest(#[source] tor_socksproto::Error),
169
170 #[error("Protocol error while communicating with SOCKS proxy")]
172 SocksProto(#[source] tor_socksproto::Error),
173
174 #[error("Internal error")]
176 Bug(#[from] tor_error::Bug),
177
178 #[error("Received unexpected early data from peer")]
189 UnexpectedData,
190
191 #[error("SOCKS proxy reported an error: {0}")]
193 SocksError(SocksStatus),
194}
195
196impl From<std::io::Error> for ProxyError {
197 fn from(e: std::io::Error) -> Self {
198 ProxyError::ProxyIo(Arc::new(e))
199 }
200}
201
202impl tor_error::HasKind for ProxyError {
203 fn kind(&self) -> tor_error::ErrorKind {
204 use ProxyError as E;
205 use tor_error::ErrorKind as EK;
206 match self {
207 E::ProxyConnect(_) | E::ProxyIo(_) => EK::LocalNetworkError,
208 E::InvalidSocksAddr(_) | E::InvalidSocksRequest(_) => EK::BadApiUsage,
209 E::UnrecognizedAddr => EK::NotImplemented,
210 E::SocksProto(_) => EK::LocalProtocolViolation,
211 E::Bug(e) => e.kind(),
212 E::UnexpectedData => EK::NotImplemented,
213 E::SocksError(_) => EK::LocalProtocolViolation,
214 }
215 }
216}
217
218impl tor_error::HasRetryTime for ProxyError {
219 fn retry_time(&self) -> tor_error::RetryTime {
220 use ProxyError as E;
221 use SocksStatus as S;
222 use tor_error::RetryTime as RT;
223 match self {
224 E::ProxyConnect(_) | E::ProxyIo(_) => RT::AfterWaiting,
225 E::InvalidSocksAddr(_) => RT::Never,
226 E::UnrecognizedAddr => RT::Never,
227 E::InvalidSocksRequest(_) => RT::Never,
228 E::SocksProto(_) => RT::AfterWaiting,
229 E::Bug(_) => RT::Never,
230 E::UnexpectedData => RT::Never,
231 E::SocksError(e) => match *e {
232 S::CONNECTION_REFUSED
233 | S::GENERAL_FAILURE
234 | S::HOST_UNREACHABLE
235 | S::NETWORK_UNREACHABLE
236 | S::TTL_EXPIRED => RT::AfterWaiting,
237 _ => RT::Never,
238 },
239 }
240 }
241}
242
243#[cfg(feature = "pt-client")]
244#[cfg_attr(docsrs, doc(cfg(feature = "pt-client")))]
245#[derive(Clone, Debug)]
248pub struct ExternalProxyPlugin<R> {
249 runtime: R,
251 proxy_addr: SocketAddr,
253 proxy_version: SocksVersion,
255}
256
257#[cfg(feature = "pt-client")]
258#[cfg_attr(docsrs, doc(cfg(feature = "pt-client")))]
259impl<R: NetStreamProvider + Send + Sync> ExternalProxyPlugin<R> {
260 pub fn new(rt: R, proxy_addr: SocketAddr, proxy_version: SocksVersion) -> Self {
262 Self {
263 runtime: rt,
264 proxy_addr,
265 proxy_version,
266 }
267 }
268}
269
270#[cfg(feature = "pt-client")]
271#[async_trait]
272impl<R: NetStreamProvider + Send + Sync> TransportImplHelper for ExternalProxyPlugin<R> {
273 type Stream = R::Stream;
274
275 async fn connect(
276 &self,
277 target: &OwnedChanTarget,
278 ) -> crate::Result<(OwnedChanTarget, R::Stream)> {
279 let pt_target = match target.chan_method() {
280 ChannelMethod::Direct(_) => {
281 return Err(crate::Error::UnusableTarget(bad_api_usage!(
282 "Used pluggable transport for a TCP connection."
283 )));
284 }
285 ChannelMethod::Pluggable(target) => target,
286 other => {
287 return Err(crate::Error::UnusableTarget(bad_api_usage!(
288 "Used unknown, unsupported, transport {:?} for a TCP connection.",
289 other,
290 )));
291 }
292 };
293
294 let protocol =
295 settings_to_protocol(self.proxy_version, encode_settings(pt_target.settings()))?;
296
297 Ok((
298 target.clone(),
299 connect_via_proxy(&self.runtime, &self.proxy_addr, &protocol, pt_target.addr()).await?,
300 ))
301 }
302}
303
304#[cfg(feature = "pt-client")]
306fn encode_settings<'a, IT>(settings: IT) -> String
307where
308 IT: Iterator<Item = (&'a str, &'a str)>,
309{
310 enum EscChar {
314 Backslash(char),
316 Literal(char),
318 Done,
320 }
321 impl EscChar {
322 fn new(ch: char, in_key: bool) -> Self {
324 match ch {
325 '\\' | ';' => EscChar::Backslash(ch),
326 '=' if in_key => EscChar::Backslash(ch),
327 _ => EscChar::Literal(ch),
328 }
329 }
330 }
331 impl Iterator for EscChar {
332 type Item = char;
333
334 fn next(&mut self) -> Option<Self::Item> {
335 match *self {
336 EscChar::Backslash(ch) => {
337 *self = EscChar::Literal(ch);
338 Some('\\')
339 }
340 EscChar::Literal(ch) => {
341 *self = EscChar::Done;
342 Some(ch)
343 }
344 EscChar::Done => None,
345 }
346 }
347 }
348
349 fn esc(s: &str, in_key: bool) -> impl Iterator<Item = char> + '_ {
351 s.chars().flat_map(move |c| EscChar::new(c, in_key))
352 }
353
354 let mut result = String::new();
355 for (k, v) in settings {
356 result.extend(esc(k, true));
357 result.push('=');
358 result.extend(esc(v, false));
359 result.push(';');
360 }
361 result.pop(); result
364}
365
366#[cfg(feature = "pt-client")]
370pub fn settings_to_protocol(vers: SocksVersion, s: String) -> Result<Protocol, ProxyError> {
371 let mut bytes: Vec<_> = s.into();
372 Ok(if bytes.is_empty() {
373 Protocol::Socks(vers, SocksAuth::NoAuth)
374 } else if vers == SocksVersion::V4 {
375 if bytes.contains(&0) {
376 return Err(ProxyError::InvalidSocksRequest(
377 tor_socksproto::Error::NotImplemented(
378 "SOCKS 4 doesn't support internal NUL bytes (for PT settings list)".into(),
379 ),
380 ));
381 } else {
382 Protocol::Socks(SocksVersion::V4, SocksAuth::Socks4(bytes))
383 }
384 } else if bytes.len() <= 255 {
385 Protocol::Socks(SocksVersion::V5, SocksAuth::Username(bytes, vec![0]))
387 } else if bytes.len() <= (255 * 2) {
388 let password = bytes.split_off(255);
389 Protocol::Socks(SocksVersion::V5, SocksAuth::Username(bytes, password))
390 } else {
391 return Err(ProxyError::InvalidSocksRequest(
392 tor_socksproto::Error::NotImplemented("PT settings list too long for SOCKS 5".into()),
393 ));
394 })
395}
396
397#[cfg(test)]
398mod test {
399 #![allow(clippy::bool_assert_comparison)]
401 #![allow(clippy::clone_on_copy)]
402 #![allow(clippy::dbg_macro)]
403 #![allow(clippy::mixed_attributes_style)]
404 #![allow(clippy::print_stderr)]
405 #![allow(clippy::print_stdout)]
406 #![allow(clippy::single_char_pattern)]
407 #![allow(clippy::unwrap_used)]
408 #![allow(clippy::unchecked_duration_subtraction)]
409 #![allow(clippy::useless_vec)]
410 #![allow(clippy::needless_pass_by_value)]
411 #[allow(unused_imports)]
413 use super::*;
414
415 #[cfg(feature = "pt-client")]
416 #[test]
417 fn setting_encoding() {
418 fn check(settings: Vec<(&str, &str)>, expected: &str) {
419 assert_eq!(encode_settings(settings.into_iter()), expected);
420 }
421
422 check(vec![], "");
424 check(vec![("hello", "world")], "hello=world");
425 check(
426 vec![("hey", "verden"), ("hello", "world")],
427 "hey=verden;hello=world",
428 );
429 check(
430 vec![("hey", "verden"), ("hello", "world"), ("selv", "tak")],
431 "hey=verden;hello=world;selv=tak",
432 );
433
434 check(
435 vec![("semi;colon", "equals=sign")],
436 r"semi\;colon=equals=sign",
437 );
438 check(
439 vec![("equals=sign", "semi;colon")],
440 r"equals\=sign=semi\;colon",
441 );
442 check(
443 vec![("semi;colon", "equals=sign"), ("also", "back\\slash")],
444 r"semi\;colon=equals=sign;also=back\\slash",
445 );
446 }
447
448 #[cfg(feature = "pt-client")]
449 #[test]
450 fn split_settings() {
451 use SocksVersion::*;
452 let long_string = "examplestrg".to_owned().repeat(50);
453 assert_eq!(long_string.len(), 550);
454 let sv = |v, a, b| settings_to_protocol(v, long_string[a..b].to_owned()).unwrap();
455 let s = |a, b| sv(V5, a, b);
456 let v = |a, b| long_string.as_bytes()[a..b].to_vec();
457
458 assert_eq!(s(0, 0), Protocol::Socks(V5, SocksAuth::NoAuth));
459 assert_eq!(
460 s(0, 50),
461 Protocol::Socks(V5, SocksAuth::Username(v(0, 50), vec![0]))
462 );
463 assert_eq!(
464 s(0, 255),
465 Protocol::Socks(V5, SocksAuth::Username(v(0, 255), vec![0]))
466 );
467 assert_eq!(
468 s(0, 256),
469 Protocol::Socks(V5, SocksAuth::Username(v(0, 255), v(255, 256)))
470 );
471 assert_eq!(
472 s(0, 300),
473 Protocol::Socks(V5, SocksAuth::Username(v(0, 255), v(255, 300)))
474 );
475 assert_eq!(
476 s(0, 510),
477 Protocol::Socks(V5, SocksAuth::Username(v(0, 255), v(255, 510)))
478 );
479
480 assert_eq!(
482 sv(V4, 0, 511),
483 Protocol::Socks(V4, SocksAuth::Socks4(v(0, 511)))
484 );
485
486 assert_eq!(
488 settings_to_protocol(V5, "\0".to_owned()).unwrap(),
489 Protocol::Socks(V5, SocksAuth::Username(vec![0], vec![0]))
490 );
491 assert_eq!(
492 settings_to_protocol(V5, "\0".to_owned().repeat(510)).unwrap(),
493 Protocol::Socks(V5, SocksAuth::Username(vec![0; 255], vec![0; 255]))
494 );
495
496 assert!(settings_to_protocol(V5, "\0".to_owned().repeat(511)).is_err());
498
499 assert!(settings_to_protocol(V5, long_string[0..512].to_owned()).is_err());
501
502 assert!(settings_to_protocol(V4, "\0".to_owned()).is_err());
504 }
505}