1use std::any::Any;
3use std::boxed::Box;
4use std::convert::TryFrom;
5use std::io::{Read, Write};
6use std::net::{SocketAddr, TcpListener, TcpStream};
7use std::ops::{Deref, DerefMut};
8use std::str::FromStr;
9use std::sync::OnceLock;
10
11use domain::base::name::Name;
13use idna::uts46::{Hyphens, Uts46};
14use idna::{domain_to_ascii_cow, AsciiDenyList};
15use regex::Regex;
16
17use crate::tor_crypto::*;
19
20#[derive(thiserror::Error, Debug)]
22pub enum Error {
23 #[error("Failed to parse '{0}' as {1}")]
24 ParseFailure(String, String),
26
27 #[error("{0}")]
28 Generic(String),
30
31 #[error("Function not implemented")]
32 NotImplemented,
34}
35
36#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
44pub struct OnionAddrV3 {
45 pub(crate) service_id: V3OnionServiceId,
46 pub(crate) virt_port: u16,
47}
48
49impl OnionAddrV3 {
50 pub fn new(service_id: V3OnionServiceId, virt_port: u16) -> OnionAddrV3 {
52 OnionAddrV3 {
53 service_id,
54 virt_port,
55 }
56 }
57
58 pub fn service_id(&self) -> &V3OnionServiceId {
60 &self.service_id
61 }
62
63 pub fn virt_port(&self) -> u16 {
65 self.virt_port
66 }
67}
68
69impl std::fmt::Display for OnionAddrV3 {
70 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
71 write!(f, "{}.onion:{}", self.service_id, self.virt_port)
72 }
73}
74
75#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
77pub enum OnionAddr {
78 V3(OnionAddrV3),
79}
80
81impl FromStr for OnionAddr {
82 type Err = Error;
83 fn from_str(s: &str) -> Result<Self, Self::Err> {
84 static ONION_SERVICE_PATTERN: OnceLock<Regex> = OnceLock::new();
85 let onion_service_pattern = ONION_SERVICE_PATTERN.get_or_init(|| {
86 Regex::new(r"(?m)^(?P<service_id>[a-z2-7]{56})\.onion:(?P<port>[1-9][0-9]{0,4})$")
87 .unwrap()
88 });
89
90 if let Some(caps) = onion_service_pattern.captures(s.to_lowercase().as_ref()) {
91 let service_id = caps
92 .name("service_id")
93 .expect("missing service_id group")
94 .as_str()
95 .to_lowercase();
96 let port = caps.name("port").expect("missing port group").as_str();
97 if let (Ok(service_id), Ok(port)) = (
98 V3OnionServiceId::from_string(service_id.as_ref()),
99 u16::from_str(port),
100 ) {
101 return Ok(OnionAddr::V3(OnionAddrV3::new(service_id, port)));
102 }
103 }
104 Err(Self::Err::ParseFailure(
105 s.to_string(),
106 "OnionAddr".to_string(),
107 ))
108 }
109}
110
111impl std::fmt::Display for OnionAddr {
112 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
113 match self {
114 OnionAddr::V3(onion_addr) => onion_addr.fmt(f),
115 }
116 }
117}
118
119#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
127pub struct DomainAddr {
128 domain: String,
129 port: u16,
130}
131
132impl DomainAddr {
134 pub fn domain(&self) -> &str {
136 self.domain.as_ref()
137 }
138
139 pub fn port(&self) -> u16 {
141 self.port
142 }
143}
144
145impl std::fmt::Display for DomainAddr {
146 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
147 let uts46: Uts46 = Default::default();
148 let (ui_str, _err) = uts46.to_user_interface(
149 self.domain.as_bytes(),
150 AsciiDenyList::URL,
151 Hyphens::Allow,
152 |_, _, _| -> bool { false },
153 );
154 write!(f, "{}:{}", ui_str, self.port)
155 }
156}
157
158impl TryFrom<(String, u16)> for DomainAddr {
159 type Error = Error;
160
161 fn try_from(value: (String, u16)) -> Result<Self, Self::Error> {
162 let (domain, port) = (&value.0, value.1);
163 if let Ok(domain) = domain_to_ascii_cow(domain.as_bytes(), AsciiDenyList::URL) {
164 let domain = domain.to_string();
165 if let Ok(domain) = Name::<Vec<u8>>::from_str(domain.as_ref()) {
166 let domain = domain.to_string();
167 if !domain.ends_with(".onion") {
168 return Ok(Self { domain, port });
169 }
170 }
171 }
172 Err(Self::Error::ParseFailure(
173 format!("{}:{}", domain, port),
174 "DomainAddr".to_string(),
175 ))
176 }
177}
178
179impl FromStr for DomainAddr {
180 type Err = Error;
181 fn from_str(s: &str) -> Result<Self, Self::Err> {
182 static DOMAIN_PATTERN: OnceLock<Regex> = OnceLock::new();
183 let domain_pattern = DOMAIN_PATTERN
184 .get_or_init(|| Regex::new(r"(?m)^(?P<domain>.*):(?P<port>[1-9][0-9]{0,4})$").unwrap());
185 if let Some(caps) = domain_pattern.captures(s) {
186 let domain = caps
187 .name("domain")
188 .expect("missing domain group")
189 .as_str()
190 .to_string();
191 let port = caps.name("port").expect("missing port group").as_str();
192 if let Ok(port) = u16::from_str(port) {
193 return Self::try_from((domain, port));
194 }
195 }
196 Err(Self::Err::ParseFailure(
197 s.to_string(),
198 "DomainAddr".to_string(),
199 ))
200 }
201}
202
203#[derive(Clone, Debug, PartialEq, Eq)]
209pub enum TargetAddr {
210 Socket(std::net::SocketAddr),
212 OnionService(OnionAddr),
214 Domain(DomainAddr),
216}
217
218impl TargetAddr {
219 pub fn host(&self) -> String {
220 match self {
221 TargetAddr::Socket(addr) => addr.ip().to_string(),
222 TargetAddr::OnionService(OnionAddr::V3(addr)) => {
223 let service_id = addr.service_id();
224 format!("{service_id}.onion")
225 },
226 TargetAddr::Domain(addr) => addr.domain().to_string(),
227 }
228 }
229
230 pub fn port(&self) -> u16 {
231 match self {
232 TargetAddr::Socket(addr) => addr.port(),
233 TargetAddr::OnionService(OnionAddr::V3(addr)) => addr.virt_port(),
234 TargetAddr::Domain(addr) => addr.port(),
235 }
236 }
237}
238
239impl FromStr for TargetAddr {
240 type Err = Error;
241 fn from_str(s: &str) -> Result<Self, Self::Err> {
242 if let Ok(socket_addr) = SocketAddr::from_str(s) {
243 return Ok(TargetAddr::Socket(socket_addr));
244 } else if let Ok(onion_addr) = OnionAddr::from_str(s) {
245 return Ok(TargetAddr::OnionService(onion_addr));
246 } else if let Ok(domain_addr) = DomainAddr::from_str(s) {
247 return Ok(TargetAddr::Domain(domain_addr));
248 }
249 Err(Self::Err::ParseFailure(
250 s.to_string(),
251 "TargetAddr".to_string(),
252 ))
253 }
254}
255
256impl From<(V3OnionServiceId, u16)> for TargetAddr {
257 fn from(target_tuple: (V3OnionServiceId, u16)) -> Self {
258 TargetAddr::OnionService(OnionAddr::V3(OnionAddrV3::new(
259 target_tuple.0,
260 target_tuple.1,
261 )))
262 }
263}
264
265impl TryFrom<(String, u16)> for TargetAddr {
266 type Error = Error;
267
268 fn try_from(value: (String, u16)) -> Result<Self, Self::Error> {
269 let host = value.0;
270 let port = value.1;
271 let address = format!("{host}:{port}");
272 TargetAddr::from_str(&address)
273 }
274}
275
276impl std::fmt::Display for TargetAddr {
277 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
278 match self {
279 TargetAddr::Socket(socket_addr) => socket_addr.fmt(f),
280 TargetAddr::OnionService(onion_addr) => onion_addr.fmt(f),
281 TargetAddr::Domain(domain_addr) => domain_addr.fmt(f),
282 }
283 }
284}
285
286#[derive(Debug)]
288pub enum TorEvent {
289 BootstrapStatus {
291 progress: u32,
293 tag: String,
295 summary: String,
297 },
298 BootstrapComplete,
300 LogReceived {
302 line: String,
304 },
305 OnionServicePublished {
307 service_id: V3OnionServiceId,
309 },
310 ConnectComplete {
312 handle: ConnectHandle,
313 stream: OnionStream,
314 },
315 ConnectFailed { handle: ConnectHandle, error: Error },
317}
318
319pub type CircuitToken = usize;
321
322pub type ConnectHandle = usize;
324
325#[derive(Debug)]
333pub struct OnionStream {
334 pub(crate) stream: TcpStream,
335 pub(crate) local_addr: Option<OnionAddr>,
336 pub(crate) peer_addr: Option<TargetAddr>,
337}
338
339impl Deref for OnionStream {
340 type Target = TcpStream;
341 fn deref(&self) -> &Self::Target {
342 &self.stream
343 }
344}
345
346impl DerefMut for OnionStream {
347 fn deref_mut(&mut self) -> &mut Self::Target {
348 &mut self.stream
349 }
350}
351
352impl From<OnionStream> for TcpStream {
353 fn from(onion_stream: OnionStream) -> Self {
354 onion_stream.stream
355 }
356}
357
358impl Read for OnionStream {
359 fn read(&mut self, buf: &mut [u8]) -> Result<usize, std::io::Error> {
360 self.stream.read(buf)
361 }
362}
363
364impl Write for OnionStream {
365 fn write(&mut self, buf: &[u8]) -> Result<usize, std::io::Error> {
366 self.stream.write(buf)
367 }
368
369 fn flush(&mut self) -> Result<(), std::io::Error> {
370 self.stream.flush()
371 }
372}
373
374impl OnionStream {
375 pub fn peer_addr(&self) -> Option<TargetAddr> {
377 self.peer_addr.clone()
378 }
379
380 pub fn local_addr(&self) -> Option<OnionAddr> {
382 self.local_addr.clone()
383 }
384
385 pub fn try_clone(&self) -> Result<Self, std::io::Error> {
387 Ok(Self {
388 stream: self.stream.try_clone()?,
389 local_addr: self.local_addr.clone(),
390 peer_addr: self.peer_addr.clone(),
391 })
392 }
393}
394
395pub(crate) type OnionListenerDropFn = Box<dyn FnMut(Box<dyn Any>) + Send>;
400
401pub struct OnionListener {
405 pub(crate) listener: TcpListener,
406 pub(crate) onion_addr: OnionAddr,
407 pub(crate) data: Option<Box<dyn Any + Send>>,
408 pub(crate) drop: Option<OnionListenerDropFn>,
409}
410
411impl OnionListener {
412 pub(crate) fn new<T: 'static + Send>(
414 listener: TcpListener,
415 onion_addr: OnionAddr,
416 data: T,
417 mut drop: impl FnMut(T) + 'static + Send,
418 ) -> Self {
419 let data: Option<Box<dyn Any + Send>> = Some(Box::new(data));
421 let drop: Option<OnionListenerDropFn> =
423 Some(Box::new(move |data: Box<dyn std::any::Any>| {
424 if let Ok(data) = data.downcast::<T>() {
426 drop(*data);
428 }
429 }));
430
431 Self {
432 listener,
433 onion_addr,
434 data,
435 drop,
436 }
437 }
438
439 pub fn set_nonblocking(&self, nonblocking: bool) -> Result<(), std::io::Error> {
441 self.listener.set_nonblocking(nonblocking)
442 }
443
444 pub fn accept(&self) -> Result<Option<OnionStream>, std::io::Error> {
446 match self.listener.accept() {
447 Ok((stream, _socket_addr)) => Ok(Some(OnionStream {
448 stream,
449 local_addr: Some(self.onion_addr.clone()),
450 peer_addr: None,
451 })),
452 Err(err) => {
453 if err.kind() == std::io::ErrorKind::WouldBlock {
454 Ok(None)
455 } else {
456 Err(err)
457 }
458 }
459 }
460 }
461}
462
463impl Drop for OnionListener {
464 fn drop(&mut self) {
465 if let (Some(data), Some(mut drop)) = (self.data.take(), self.drop.take()) {
466 drop(data)
467 }
468 }
469}
470
471pub trait TorProvider: Send {
473 fn update(&mut self) -> Result<Vec<TorEvent>, Error>;
475 fn bootstrap(&mut self) -> Result<(), Error>;
477 fn add_client_auth(
479 &mut self,
480 service_id: &V3OnionServiceId,
481 client_auth: &X25519PrivateKey,
482 ) -> Result<(), Error>;
483 fn remove_client_auth(&mut self, service_id: &V3OnionServiceId) -> Result<(), Error>;
485 fn connect(
493 &mut self,
494 target: TargetAddr,
495 circuit: Option<CircuitToken>,
496 ) -> Result<OnionStream, Error>;
497 fn connect_async(
505 &mut self,
506 _target: TargetAddr,
507 _circuit: Option<CircuitToken>,
508 ) -> Result<ConnectHandle, Error> {
509 Err(Error::NotImplemented)
510 }
511 fn listener(
515 &mut self,
516 private_key: &Ed25519PrivateKey,
517 virt_port: u16,
518 authorised_clients: Option<&[X25519PublicKey]>,
519 ) -> Result<OnionListener, Error>;
520 fn generate_token(&mut self) -> CircuitToken;
522 fn release_token(&mut self, token: CircuitToken);
524}