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