1use futures::io::{AsyncRead, AsyncReadExt, AsyncWrite, BufReader};
4use safelog::sensitive;
5use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
6use std::sync::Arc;
7use tracing::{debug, instrument, warn};
8
9#[allow(unused)]
10use arti_client::HasKind;
11use arti_client::{ErrorKind, IntoTorAddr as _, StreamPrefs};
12use tor_basic_utils::onionperf_types::{OnionperfEvent, OnionperfStreamStatus};
13#[cfg(feature = "rpc")]
14use tor_rpcbase::{self as rpc};
15use tor_rtcompat::Runtime;
16use tor_socksproto::{Handshake as _, SocksAddr, SocksAuth, SocksCmd, SocksRequest};
17
18use anyhow::{Context, Result, anyhow};
19
20use super::{
21 ListenerIsolation, ProvidedIsolation, ProxyContext, StreamIsolationKey, write_all_and_close,
22 write_all_and_flush,
23};
24cfg_if::cfg_if! {
25 if #[cfg(feature="rpc")] {
26 use crate::rpc::conntarget::ConnTarget;
27 } else {
28 use arti_client::TorClient;
29
30 type ConnTarget<R> = Arc<TorClient<R>>;
36 }
37}
38
39pub(super) const WRONG_PROTOCOL_PAYLOAD: &[u8] = br#"HTTP/1.0 501 Not running as an HTTP Proxy
42Content-Type: text/html; charset=utf-8
43
44<!DOCTYPE html>
45<html>
46<head>
47<title>This is a SOCKS Proxy, Not An HTTP Proxy</title>
48</head>
49<body>
50<h1>This is a SOCKS proxy, not an HTTP proxy.</h1>
51<p>
52It appears you have configured your web browser to use this Tor port as
53an HTTP proxy.
54</p>
55<p>
56This is not correct: This port is configured as a SOCKS proxy, not
57an HTTP proxy. If you need an HTTP proxy tunnel,
58build Arti with the <code>http-connect</code> feature enabled.
59</p>
60<p>
61See <a href="https://gitlab.torproject.org/tpo/core/arti/#todo-need-to-change-when-arti-get-a-user-documentation">https://gitlab.torproject.org/tpo/core/arti</a> for more information.
62</p>
63</body>
64</html>"#;
65
66#[cfg_attr(feature = "experimental-api", visibility::make(pub))]
69fn stream_preference(req: &SocksRequest, addr: &str) -> StreamPrefs {
70 let mut prefs = StreamPrefs::new();
71 if addr.parse::<Ipv4Addr>().is_ok() {
72 prefs.ipv4_only();
74 } else if addr.parse::<Ipv6Addr>().is_ok() {
75 prefs.ipv6_only();
77 } else if req.version() == tor_socksproto::SocksVersion::V4 {
78 prefs.ipv4_only();
80 } else {
81 prefs.ipv4_preferred();
83 }
84 prefs
85}
86
87struct AuthInterpretation {
89 #[cfg(feature = "rpc")]
92 rpc_object: Option<rpc::ObjectId>,
93
94 isolation: ProvidedIsolation,
97}
98
99fn interpret_socks_auth(auth: &SocksAuth) -> Result<AuthInterpretation> {
105 enum Uname<'a> {
109 Legacy,
117 Extended(u8, &'a [u8]),
120 }
121 fn interpret_socks5_username(username: &[u8]) -> Result<Uname<'_>> {
135 const SOCKS_EXT_CONST_ANY: &[u8] = b"<torS0X>";
142 let Some(remainder) = username.strip_prefix(SOCKS_EXT_CONST_ANY) else {
143 return Ok(Uname::Legacy);
144 };
145 let (format_code, remainder) = remainder
146 .split_at_checked(1)
147 .ok_or_else(|| anyhow!("Extended SOCKS information without format code."))?;
148 Ok(Uname::Extended(format_code[0], remainder))
149 }
150
151 let isolation = match auth {
152 SocksAuth::Username(user, pass) => match interpret_socks5_username(user)? {
153 Uname::Legacy => ProvidedIsolation::LegacySocks(auth.clone()),
154 Uname::Extended(b'1', b"") => {
155 return Err(anyhow!("Received empty RPC object ID"));
156 }
157 Uname::Extended(format_code @ b'1', remainder) => {
158 #[cfg(not(feature = "rpc"))]
159 return Err(anyhow!(
160 "Received RPC object ID, but not built with support for RPC"
161 ));
162 #[cfg(feature = "rpc")]
163 return Ok(AuthInterpretation {
164 rpc_object: Some(rpc::ObjectId::from(
165 std::str::from_utf8(remainder).context("Rpc object ID was not utf-8")?,
166 )),
167 isolation: ProvidedIsolation::ExtendedSocks {
168 format_code,
169 isolation: pass.clone().into(),
170 },
171 });
172 }
173 Uname::Extended(format_code @ b'0', b"") => ProvidedIsolation::ExtendedSocks {
174 format_code,
175 isolation: pass.clone().into(),
176 },
177 Uname::Extended(b'0', _) => {
178 return Err(anyhow!("Extraneous information in SOCKS username field."));
179 }
180 _ => return Err(anyhow!("Unrecognized SOCKS format code")),
181 },
182 _ => ProvidedIsolation::LegacySocks(auth.clone()),
183 };
184 tracing::debug!(
185 "socks auth {:?} -> isolation {:?}",
186 sensitive(&auth),
187 sensitive(&isolation)
188 );
189
190 Ok(AuthInterpretation {
191 #[cfg(feature = "rpc")]
192 rpc_object: None,
193 isolation,
194 })
195}
196
197impl<R: Runtime> super::ProxyContext<R> {
198 fn get_prefs_and_session(
203 &self,
204 request: &SocksRequest,
205 target_addr: &str,
206 conn_isolation: ListenerIsolation,
207 ) -> Result<(StreamPrefs, ConnTarget<R>)> {
208 let mut prefs = stream_preference(request, target_addr);
210
211 let interp = interpret_socks_auth(request.auth())?;
213 prefs.set_isolation(StreamIsolationKey(conn_isolation, interp.isolation));
214
215 #[cfg(feature = "rpc")]
216 if let Some(session) = interp.rpc_object {
217 if let Some(mgr) = &self.rpc_mgr {
218 let (context, object) = mgr
219 .lookup_object(&session)
220 .context("no such session found")?;
221 let target = ConnTarget::Rpc { context, object };
222 return Ok((prefs, target));
223 } else {
224 return Err(anyhow!("no rpc manager found!?"));
225 }
226 }
227
228 let client = self.tor_client.clone();
229 #[cfg(feature = "rpc")]
230 let client = ConnTarget::Client(Arc::clone(&client));
231
232 Ok((prefs, client))
233 }
234}
235
236#[instrument(skip_all, level = "trace")]
243pub(super) async fn handle_socks_conn<R, S>(
244 context: ProxyContext<R>,
245 mut socks_stream: BufReader<S>,
246 isolation_info: ListenerIsolation,
247) -> Result<()>
248where
249 R: Runtime,
250 S: AsyncRead + AsyncWrite + Send + Sync + Unpin + 'static,
251{
252 let mut handshake = tor_socksproto::SocksProxyHandshake::new();
260
261 let mut inbuf = tor_socksproto::Buffer::new();
262 let request = loop {
263 use tor_socksproto::NextStep as NS;
264
265 let step = handshake.step(&mut inbuf)?;
269
270 match step {
271 NS::Recv(mut recv) => {
272 let n = socks_stream
273 .read(recv.buf())
274 .await
275 .context("Error while reading SOCKS handshake")?;
276 recv.note_received(n)?;
277 }
278 NS::Send(data) => write_all_and_flush(&mut socks_stream, &data).await?,
279 NS::Finished(fin) => break fin.into_output_forbid_pipelining()?,
280 }
281 };
282
283 if !socks_stream.buffer().is_empty() {
285 let error = tor_socksproto::Error::ForbiddenPipelining;
286 return reply_error(&mut socks_stream, &request, error.kind()).await;
287 }
288
289 let addr = request.addr().to_string();
291 let port = request.port();
292 debug!(
293 "Got a socks request: {} {}:{}",
294 request.command(),
295 sensitive(&addr),
296 port
297 );
298
299 let (prefs, tor_client) = context.get_prefs_and_session(&request, &addr, isolation_info)?;
300
301 match request.command() {
302 SocksCmd::CONNECT => {
303 let tor_addr = (addr.clone(), port).into_tor_addr()?;
306 let tor_stream = tor_client.connect_with_prefs(&tor_addr, &prefs).await;
307 let tor_stream = match tor_stream {
308 Ok(s) => s,
309 Err(e) => return reply_error(&mut socks_stream, &request, e.kind()).await,
310 };
311 debug!("Got a stream for {}:{}", sensitive(&addr), port);
313
314 let reply = request
317 .reply(tor_socksproto::SocksStatus::SUCCEEDED, None)
318 .context("Encoding socks reply")?;
319 write_all_and_flush(&mut socks_stream, &reply[..]).await?;
320
321 let tor_stream = BufReader::with_capacity(super::APP_STREAM_BUF_LEN, tor_stream);
322
323 futures_copy::copy_buf_bidirectional(
326 socks_stream,
327 tor_stream,
328 futures_copy::eof::Close,
329 futures_copy::eof::Close,
330 )
331 .await?;
332 }
333 SocksCmd::RESOLVE => {
334 let addr = if let Ok(addr) = addr.parse() {
338 Ok(addr)
340 } else {
341 tor_client
342 .resolve_with_prefs(&addr, &prefs)
343 .await
344 .map_err(|e| e.kind())
345 .and_then(|addrs| addrs.first().copied().ok_or(ErrorKind::Other))
346 };
347 match addr {
348 Ok(addr) => {
349 tracing::trace!(
350 onionperf = true,
351 event = ?OnionperfEvent::Stream(OnionperfStreamStatus::New),
352 );
353 let reply = request
354 .reply(
355 tor_socksproto::SocksStatus::SUCCEEDED,
356 Some(&SocksAddr::Ip(addr)),
357 )
358 .context("Encoding socks reply")?;
359 write_all_and_close(&mut socks_stream, &reply[..]).await?;
360 }
361 Err(e) => return reply_error(&mut socks_stream, &request, e).await,
362 }
363 }
364 SocksCmd::RESOLVE_PTR => {
365 let addr: IpAddr = match addr.parse() {
368 Ok(ip) => ip,
369 Err(e) => {
370 let reply = request
371 .reply(tor_socksproto::SocksStatus::ADDRTYPE_NOT_SUPPORTED, None)
372 .context("Encoding socks reply")?;
373 write_all_and_close(&mut socks_stream, &reply[..]).await?;
374 return Err(anyhow!(e));
375 }
376 };
377 let hosts = match tor_client.resolve_ptr_with_prefs(addr, &prefs).await {
378 Ok(hosts) => hosts,
379 Err(e) => return reply_error(&mut socks_stream, &request, e.kind()).await,
380 };
381 if let Some(host) = hosts.into_iter().next() {
382 let hostname = SocksAddr::Hostname(host.try_into()?);
385 let reply = request
386 .reply(tor_socksproto::SocksStatus::SUCCEEDED, Some(&hostname))
387 .context("Encoding socks reply")?;
388 write_all_and_close(&mut socks_stream, &reply[..]).await?;
389 }
390 }
391 _ => {
392 warn!("Dropping request; {:?} is unsupported", request.command());
394 let reply = request
395 .reply(tor_socksproto::SocksStatus::COMMAND_NOT_SUPPORTED, None)
396 .context("Encoding socks reply")?;
397 write_all_and_close(&mut socks_stream, &reply[..]).await?;
398 }
399 };
400
401 Ok(())
405}
406
407async fn reply_error<W>(
410 writer: &mut W,
411 request: &SocksRequest,
412 error: arti_client::ErrorKind,
413) -> Result<()>
414where
415 W: AsyncWrite + Unpin,
416{
417 use {ErrorKind as EK, tor_socksproto::SocksStatus as S};
418
419 let status = match error {
437 EK::RemoteNetworkFailed => S::TTL_EXPIRED,
438
439 #[cfg(feature = "onion-service-client")]
440 EK::OnionServiceNotFound => S::HS_DESC_NOT_FOUND,
441 #[cfg(feature = "onion-service-client")]
442 EK::OnionServiceAddressInvalid => S::HS_BAD_ADDRESS,
443 #[cfg(feature = "onion-service-client")]
444 EK::OnionServiceMissingClientAuth => S::HS_MISSING_CLIENT_AUTH,
445 #[cfg(feature = "onion-service-client")]
446 EK::OnionServiceWrongClientAuth => S::HS_WRONG_CLIENT_AUTH,
447
448 #[cfg(feature = "onion-service-client")]
454 EK::OnionServiceNotRunning
455 | EK::OnionServiceConnectionFailed
456 | EK::OnionServiceProtocolViolation => S::HS_INTRO_FAILED,
457
458 _ => S::GENERAL_FAILURE,
459 };
460 let reply = request
461 .reply(status, None)
462 .context("Encoding socks reply")?;
463 let _ = write_all_and_close(writer, &reply[..]).await;
465
466 Err(anyhow!(error))
467}