1use futures::io::{AsyncRead, AsyncReadExt, AsyncWrite, BufReader};
4use safelog::sensitive;
5use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
6use tracing::{debug, instrument, warn};
7
8#[allow(unused)]
9use arti_client::HasKind;
10use arti_client::{ErrorKind, IntoTorAddr as _, StreamPrefs};
11#[cfg(feature = "rpc")]
12use tor_rpcbase::{self as rpc};
13use tor_rtcompat::Runtime;
14use tor_socksproto::{Handshake as _, SocksAddr, SocksAuth, SocksCmd, SocksRequest};
15
16use anyhow::{Context, Result, anyhow};
17
18use super::{
19 ListenerIsolation, ProvidedIsolation, ProxyContext, StreamIsolationKey, write_all_and_close,
20 write_all_and_flush,
21};
22cfg_if::cfg_if! {
23 if #[cfg(feature="rpc")] {
24 use crate::rpc::conntarget::ConnTarget;
25 } else {
26 use arti_client::TorClient;
27
28 type ConnTarget<R> = TorClient<R>;
34 }
35}
36
37pub(super) const WRONG_PROTOCOL_PAYLOAD: &[u8] = br#"HTTP/1.0 501 Not running as an HTTP Proxy
40Content-Type: text/html; charset=utf-8
41
42<!DOCTYPE html>
43<html>
44<head>
45<title>This is a SOCKS Proxy, Not An HTTP Proxy</title>
46</head>
47<body>
48<h1>This is a SOCKS proxy, not an HTTP proxy.</h1>
49<p>
50It appears you have configured your web browser to use this Tor port as
51an HTTP proxy.
52</p>
53<p>
54This is not correct: This port is configured as a SOCKS proxy, not
55an HTTP proxy. If you need an HTTP proxy tunnel,
56build Arti with the <code>http-connect</code> feature enabled.
57</p>
58<p>
59See <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.
60</p>
61</body>
62</html>"#;
63
64#[cfg_attr(feature = "experimental-api", visibility::make(pub))]
67fn stream_preference(req: &SocksRequest, addr: &str) -> StreamPrefs {
68 let mut prefs = StreamPrefs::new();
69 if addr.parse::<Ipv4Addr>().is_ok() {
70 prefs.ipv4_only();
72 } else if addr.parse::<Ipv6Addr>().is_ok() {
73 prefs.ipv6_only();
75 } else if req.version() == tor_socksproto::SocksVersion::V4 {
76 prefs.ipv4_only();
78 } else {
79 prefs.ipv4_preferred();
81 }
82 prefs
83}
84
85struct AuthInterpretation {
87 #[cfg(feature = "rpc")]
90 rpc_object: Option<rpc::ObjectId>,
91
92 isolation: ProvidedIsolation,
95}
96
97fn interpret_socks_auth(auth: &SocksAuth) -> Result<AuthInterpretation> {
103 enum Uname<'a> {
107 Legacy,
115 Extended(u8, &'a [u8]),
118 }
119 fn interpret_socks5_username(username: &[u8]) -> Result<Uname<'_>> {
133 const SOCKS_EXT_CONST_ANY: &[u8] = b"<torS0X>";
140 let Some(remainder) = username.strip_prefix(SOCKS_EXT_CONST_ANY) else {
141 return Ok(Uname::Legacy);
142 };
143 let (format_code, remainder) = remainder
144 .split_at_checked(1)
145 .ok_or_else(|| anyhow!("Extended SOCKS information without format code."))?;
146 Ok(Uname::Extended(format_code[0], remainder))
147 }
148
149 let isolation = match auth {
150 SocksAuth::Username(user, pass) => match interpret_socks5_username(user)? {
151 Uname::Legacy => ProvidedIsolation::LegacySocks(auth.clone()),
152 Uname::Extended(b'1', b"") => {
153 return Err(anyhow!("Received empty RPC object ID"));
154 }
155 Uname::Extended(format_code @ b'1', remainder) => {
156 #[cfg(not(feature = "rpc"))]
157 return Err(anyhow!(
158 "Received RPC object ID, but not built with support for RPC"
159 ));
160 #[cfg(feature = "rpc")]
161 return Ok(AuthInterpretation {
162 rpc_object: Some(rpc::ObjectId::from(
163 std::str::from_utf8(remainder).context("Rpc object ID was not utf-8")?,
164 )),
165 isolation: ProvidedIsolation::ExtendedSocks {
166 format_code,
167 isolation: pass.clone().into(),
168 },
169 });
170 }
171 Uname::Extended(format_code @ b'0', b"") => ProvidedIsolation::ExtendedSocks {
172 format_code,
173 isolation: pass.clone().into(),
174 },
175 Uname::Extended(b'0', _) => {
176 return Err(anyhow!("Extraneous information in SOCKS username field."));
177 }
178 _ => return Err(anyhow!("Unrecognized SOCKS format code")),
179 },
180 _ => ProvidedIsolation::LegacySocks(auth.clone()),
181 };
182 tracing::debug!("socks auth {:?} -> isolation {:?}", sensitive(&auth), sensitive(&isolation));
183
184 Ok(AuthInterpretation {
185 #[cfg(feature = "rpc")]
186 rpc_object: None,
187 isolation,
188 })
189}
190
191impl<R: Runtime> super::ProxyContext<R> {
192 fn get_prefs_and_session(
197 &self,
198 request: &SocksRequest,
199 target_addr: &str,
200 conn_isolation: ListenerIsolation,
201 ) -> Result<(StreamPrefs, ConnTarget<R>)> {
202 let mut prefs = stream_preference(request, target_addr);
204
205 let interp = interpret_socks_auth(request.auth())?;
207 prefs.set_isolation(StreamIsolationKey(conn_isolation, interp.isolation));
208
209 #[cfg(feature = "rpc")]
210 if let Some(session) = interp.rpc_object {
211 if let Some(mgr) = &self.rpc_mgr {
212 let (context, object) = mgr
213 .lookup_object(&session)
214 .context("no such session found")?;
215 let target = ConnTarget::Rpc { context, object };
216 return Ok((prefs, target));
217 } else {
218 return Err(anyhow!("no rpc manager found!?"));
219 }
220 }
221
222 let client = self.tor_client.clone();
223 #[cfg(feature = "rpc")]
224 let client = ConnTarget::Client(Box::new(client));
225
226 Ok((prefs, client))
227 }
228}
229
230#[instrument(skip_all, level = "trace")]
237pub(super) async fn handle_socks_conn<R, S>(
238 context: ProxyContext<R>,
239 mut socks_stream: BufReader<S>,
240 isolation_info: ListenerIsolation,
241) -> Result<()>
242where
243 R: Runtime,
244 S: AsyncRead + AsyncWrite + Send + Sync + Unpin + 'static,
245{
246 let mut handshake = tor_socksproto::SocksProxyHandshake::new();
254
255 let mut inbuf = tor_socksproto::Buffer::new();
256 let request = loop {
257 use tor_socksproto::NextStep as NS;
258
259 let step = handshake.step(&mut inbuf)?;
263
264 match step {
265 NS::Recv(mut recv) => {
266 let n = socks_stream
267 .read(recv.buf())
268 .await
269 .context("Error while reading SOCKS handshake")?;
270 recv.note_received(n)?;
271 }
272 NS::Send(data) => write_all_and_flush(&mut socks_stream, &data).await?,
273 NS::Finished(fin) => break fin.into_output_forbid_pipelining()?,
274 }
275 };
276
277 if !socks_stream.buffer().is_empty() {
279 let error = tor_socksproto::Error::ForbiddenPipelining;
280 return reply_error(&mut socks_stream, &request, error.kind()).await;
281 }
282
283 let addr = request.addr().to_string();
285 let port = request.port();
286 debug!(
287 "Got a socks request: {} {}:{}",
288 request.command(),
289 sensitive(&addr),
290 port
291 );
292
293 let (prefs, tor_client) = context.get_prefs_and_session(&request, &addr, isolation_info)?;
294
295 match request.command() {
296 SocksCmd::CONNECT => {
297 let tor_addr = (addr.clone(), port).into_tor_addr()?;
300 let tor_stream = tor_client.connect_with_prefs(&tor_addr, &prefs).await;
301 let tor_stream = match tor_stream {
302 Ok(s) => s,
303 Err(e) => return reply_error(&mut socks_stream, &request, e.kind()).await,
304 };
305 debug!("Got a stream for {}:{}", sensitive(&addr), port);
307
308 let reply = request
311 .reply(tor_socksproto::SocksStatus::SUCCEEDED, None)
312 .context("Encoding socks reply")?;
313 write_all_and_flush(&mut socks_stream, &reply[..]).await?;
314
315 let tor_stream = BufReader::with_capacity(super::APP_STREAM_BUF_LEN, tor_stream);
316
317 futures_copy::copy_buf_bidirectional(
320 socks_stream,
321 tor_stream,
322 futures_copy::eof::Close,
323 futures_copy::eof::Close,
324 )
325 .await?;
326 }
327 SocksCmd::RESOLVE => {
328 let addr = if let Ok(addr) = addr.parse() {
332 Ok(addr)
334 } else {
335 tor_client
336 .resolve_with_prefs(&addr, &prefs)
337 .await
338 .map_err(|e| e.kind())
339 .and_then(|addrs| addrs.first().copied().ok_or(ErrorKind::Other))
340 };
341 match addr {
342 Ok(addr) => {
343 let reply = request
344 .reply(
345 tor_socksproto::SocksStatus::SUCCEEDED,
346 Some(&SocksAddr::Ip(addr)),
347 )
348 .context("Encoding socks reply")?;
349 write_all_and_close(&mut socks_stream, &reply[..]).await?;
350 }
351 Err(e) => return reply_error(&mut socks_stream, &request, e).await,
352 }
353 }
354 SocksCmd::RESOLVE_PTR => {
355 let addr: IpAddr = match addr.parse() {
358 Ok(ip) => ip,
359 Err(e) => {
360 let reply = request
361 .reply(tor_socksproto::SocksStatus::ADDRTYPE_NOT_SUPPORTED, None)
362 .context("Encoding socks reply")?;
363 write_all_and_close(&mut socks_stream, &reply[..]).await?;
364 return Err(anyhow!(e));
365 }
366 };
367 let hosts = match tor_client.resolve_ptr_with_prefs(addr, &prefs).await {
368 Ok(hosts) => hosts,
369 Err(e) => return reply_error(&mut socks_stream, &request, e.kind()).await,
370 };
371 if let Some(host) = hosts.into_iter().next() {
372 let hostname = SocksAddr::Hostname(host.try_into()?);
375 let reply = request
376 .reply(tor_socksproto::SocksStatus::SUCCEEDED, Some(&hostname))
377 .context("Encoding socks reply")?;
378 write_all_and_close(&mut socks_stream, &reply[..]).await?;
379 }
380 }
381 _ => {
382 warn!("Dropping request; {:?} is unsupported", request.command());
384 let reply = request
385 .reply(tor_socksproto::SocksStatus::COMMAND_NOT_SUPPORTED, None)
386 .context("Encoding socks reply")?;
387 write_all_and_close(&mut socks_stream, &reply[..]).await?;
388 }
389 };
390
391 Ok(())
395}
396
397async fn reply_error<W>(
400 writer: &mut W,
401 request: &SocksRequest,
402 error: arti_client::ErrorKind,
403) -> Result<()>
404where
405 W: AsyncWrite + Unpin,
406{
407 use {ErrorKind as EK, tor_socksproto::SocksStatus as S};
408
409 let status = match error {
427 EK::RemoteNetworkFailed => S::TTL_EXPIRED,
428
429 #[cfg(feature = "onion-service-client")]
430 EK::OnionServiceNotFound => S::HS_DESC_NOT_FOUND,
431 #[cfg(feature = "onion-service-client")]
432 EK::OnionServiceAddressInvalid => S::HS_BAD_ADDRESS,
433 #[cfg(feature = "onion-service-client")]
434 EK::OnionServiceMissingClientAuth => S::HS_MISSING_CLIENT_AUTH,
435 #[cfg(feature = "onion-service-client")]
436 EK::OnionServiceWrongClientAuth => S::HS_WRONG_CLIENT_AUTH,
437
438 #[cfg(feature = "onion-service-client")]
444 EK::OnionServiceNotRunning
445 | EK::OnionServiceConnectionFailed
446 | EK::OnionServiceProtocolViolation => S::HS_INTRO_FAILED,
447
448 _ => S::GENERAL_FAILURE,
449 };
450 let reply = request
451 .reply(status, None)
452 .context("Encoding socks reply")?;
453 let _ = write_all_and_close(writer, &reply[..]).await;
455
456 Err(anyhow!(error))
457}