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
21#[derive(thiserror::Error, Debug)]
23pub enum Error {
24 #[error("Failed to parse '{0}' as {1}")]
25 ParseFailure(String, String),
27
28 #[error("{0}")]
29 Generic(String),
31}
32
33#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
41pub struct OnionAddrV3 {
42 pub(crate) service_id: V3OnionServiceId,
43 pub(crate) virt_port: u16,
44}
45
46impl OnionAddrV3 {
47 pub fn new(service_id: V3OnionServiceId, virt_port: u16) -> OnionAddrV3 {
49 OnionAddrV3 {
50 service_id,
51 virt_port,
52 }
53 }
54
55 pub fn service_id(&self) -> &V3OnionServiceId {
57 &self.service_id
58 }
59
60 pub fn virt_port(&self) -> u16 {
62 self.virt_port
63 }
64}
65
66impl std::fmt::Display for OnionAddrV3 {
67 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68 write!(f, "{}.onion:{}", self.service_id, self.virt_port)
69 }
70}
71
72#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
74pub enum OnionAddr {
75 V3(OnionAddrV3),
76}
77
78impl FromStr for OnionAddr {
79 type Err = Error;
80 fn from_str(s: &str) -> Result<Self, Self::Err> {
81 static ONION_SERVICE_PATTERN: OnceLock<Regex> = OnceLock::new();
82 let onion_service_pattern = ONION_SERVICE_PATTERN.get_or_init(|| {
83 Regex::new(r"(?m)^(?P<service_id>[a-z2-7]{56})\.onion:(?P<port>[1-9][0-9]{0,4})$")
84 .unwrap()
85 });
86
87 if let Some(caps) = onion_service_pattern.captures(s.to_lowercase().as_ref()) {
88 let service_id = caps
89 .name("service_id")
90 .expect("missing service_id group")
91 .as_str()
92 .to_lowercase();
93 let port = caps.name("port").expect("missing port group").as_str();
94 if let (Ok(service_id), Ok(port)) = (
95 V3OnionServiceId::from_string(service_id.as_ref()),
96 u16::from_str(port),
97 ) {
98 return Ok(OnionAddr::V3(OnionAddrV3::new(service_id, port)));
99 }
100 }
101 Err(Self::Err::ParseFailure(s.to_string(), "OnionAddr".to_string()))
102 }
103}
104
105impl std::fmt::Display for OnionAddr {
106 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107 match self {
108 OnionAddr::V3(onion_addr) => onion_addr.fmt(f),
109 }
110 }
111}
112
113#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
121pub struct DomainAddr {
122 domain: String,
123 port: u16,
124}
125
126impl DomainAddr {
128 pub fn domain(&self) -> &str {
130 self.domain.as_ref()
131 }
132
133 pub fn port(&self) -> u16 {
135 self.port
136 }
137}
138
139impl std::fmt::Display for DomainAddr {
140 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
141 let uts46: Uts46 = Default::default();
142 let (ui_str, _err) = uts46.to_user_interface(
143 self.domain.as_str().as_bytes(),
144 AsciiDenyList::URL,
145 Hyphens::Allow,
146 |_, _, _| -> bool { false },
147 );
148 write!(f, "{}:{}", ui_str, self.port)
149 }
150}
151
152impl TryFrom<(String, u16)> for DomainAddr {
153 type Error = Error;
154
155 fn try_from(value: (String, u16)) -> Result<Self, Self::Error> {
156 let (domain, port) = (&value.0, value.1);
157 if let Ok(domain) = domain_to_ascii_cow(domain.as_bytes(), AsciiDenyList::URL) {
158 let domain = domain.to_string();
159 if let Ok(domain) = Name::<Vec<u8>>::from_str(domain.as_ref()) {
160 let domain = domain.to_string();
161 if !domain.ends_with(".onion") {
162 return Ok(Self {
163 domain,
164 port,
165 });
166 }
167 }
168 }
169 Err(Self::Error::ParseFailure(format!(
170 "{}:{}",
171 domain, port
172 ), "DomainAddr".to_string()))
173 }
174}
175
176impl FromStr for DomainAddr {
177 type Err = Error;
178 fn from_str(s: &str) -> Result<Self, Self::Err> {
179 static DOMAIN_PATTERN: OnceLock<Regex> = OnceLock::new();
180 let domain_pattern = DOMAIN_PATTERN
181 .get_or_init(|| Regex::new(r"(?m)^(?P<domain>.*):(?P<port>[1-9][0-9]{0,4})$").unwrap());
182 if let Some(caps) = domain_pattern.captures(s) {
183 let domain = caps
184 .name("domain")
185 .expect("missing domain group")
186 .as_str()
187 .to_string();
188 let port = caps.name("port").expect("missing port group").as_str();
189 if let Ok(port) = u16::from_str(port) {
190 return Self::try_from((domain, port));
191 }
192 }
193 Err(Self::Err::ParseFailure(s.to_string(), "DomainAddr".to_string()))
194 }
195}
196
197#[derive(Clone, Debug)]
203pub enum TargetAddr {
204 Socket(std::net::SocketAddr),
206 OnionService(OnionAddr),
208 Domain(DomainAddr),
210}
211
212impl From<(V3OnionServiceId, u16)> for TargetAddr {
213 fn from(target_tuple: (V3OnionServiceId, u16)) -> Self {
214 TargetAddr::OnionService(OnionAddr::V3(OnionAddrV3::new(
215 target_tuple.0,
216 target_tuple.1,
217 )))
218 }
219}
220
221impl FromStr for TargetAddr {
222 type Err = Error;
223 fn from_str(s: &str) -> Result<Self, Self::Err> {
224 if let Ok(socket_addr) = SocketAddr::from_str(s) {
225 return Ok(TargetAddr::Socket(socket_addr));
226 } else if let Ok(onion_addr) = OnionAddr::from_str(s) {
227 return Ok(TargetAddr::OnionService(onion_addr));
228 } else if let Ok(domain_addr) = DomainAddr::from_str(s) {
229 return Ok(TargetAddr::Domain(domain_addr));
230 }
231 Err(Self::Err::ParseFailure(s.to_string(), "TargetAddr".to_string()))
232 }
233}
234
235impl std::fmt::Display for TargetAddr {
236 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
237 match self {
238 TargetAddr::Socket(socket_addr) => socket_addr.fmt(f),
239 TargetAddr::OnionService(onion_addr) => onion_addr.fmt(f),
240 TargetAddr::Domain(domain_addr) => domain_addr.fmt(f),
241 }
242 }
243}
244
245#[derive(Debug)]
247pub enum TorEvent {
248 BootstrapStatus {
250 progress: u32,
252 tag: String,
254 summary: String,
256 },
257 BootstrapComplete,
259 LogReceived {
261 line: String,
263 },
264 OnionServicePublished {
266 service_id: V3OnionServiceId,
268 },
269}
270
271pub type CircuitToken = usize;
273
274#[derive(Debug)]
282pub struct OnionStream {
283 pub(crate) stream: TcpStream,
284 pub(crate) local_addr: Option<OnionAddr>,
285 pub(crate) peer_addr: Option<TargetAddr>,
286}
287
288impl Deref for OnionStream {
289 type Target = TcpStream;
290 fn deref(&self) -> &Self::Target {
291 &self.stream
292 }
293}
294
295impl DerefMut for OnionStream {
296 fn deref_mut(&mut self) -> &mut Self::Target {
297 &mut self.stream
298 }
299}
300
301impl From<OnionStream> for TcpStream {
302 fn from(onion_stream: OnionStream) -> Self {
303 onion_stream.stream
304 }
305}
306
307impl Read for OnionStream {
308 fn read(&mut self, buf: &mut [u8]) -> Result<usize, std::io::Error> {
309 self.stream.read(buf)
310 }
311}
312
313impl Write for OnionStream {
314 fn write(&mut self, buf: &[u8]) -> Result<usize, std::io::Error> {
315 self.stream.write(buf)
316 }
317
318 fn flush(&mut self) -> Result<(), std::io::Error> {
319 self.stream.flush()
320 }
321}
322
323impl OnionStream {
324 pub fn peer_addr(&self) -> Option<TargetAddr> {
326 self.peer_addr.clone()
327 }
328
329 pub fn local_addr(&self) -> Option<OnionAddr> {
331 self.local_addr.clone()
332 }
333
334 pub fn try_clone(&self) -> Result<Self, std::io::Error> {
336 Ok(Self {
337 stream: self.stream.try_clone()?,
338 local_addr: self.local_addr.clone(),
339 peer_addr: self.peer_addr.clone(),
340 })
341 }
342}
343
344pub struct OnionListener {
352 pub(crate) listener: TcpListener,
353 pub(crate) onion_addr: OnionAddr,
354 pub(crate) data: Option<Box<dyn Any + Send>>,
355 pub(crate) drop: Option<Box<dyn FnMut(Box<dyn Any>) + Send>>,
356}
357
358impl OnionListener {
359 pub(crate) fn new<T: 'static + Send>(
361 listener: TcpListener,
362 onion_addr: OnionAddr,
363 data: T,
364 mut drop: impl FnMut(T) + 'static + Send) -> Self {
365 let data: Option<Box<dyn Any + Send>> = Some(Box::new(data));
367 let drop: Option<Box<dyn FnMut(Box<dyn Any>) + Send>> = Some(Box::new(move |data: Box<dyn std::any::Any>| {
369 if let Ok(data) = data.downcast::<T>() {
371 drop(*data);
373 }
374 }));
375
376 Self{
377 listener,
378 onion_addr,
379 data,
380 drop,
381 }
382 }
383
384 pub fn set_nonblocking(&self, nonblocking: bool) -> Result<(), std::io::Error> {
386 self.listener.set_nonblocking(nonblocking)
387 }
388
389 pub fn accept(&self) -> Result<Option<OnionStream>, std::io::Error> {
391 match self.listener.accept() {
392 Ok((stream, _socket_addr)) => Ok(Some(OnionStream {
393 stream,
394 local_addr: Some(self.onion_addr.clone()),
395 peer_addr: None,
396 })),
397 Err(err) => {
398 if err.kind() == std::io::ErrorKind::WouldBlock {
399 Ok(None)
400 } else {
401 Err(err)
402 }
403 }
404 }
405 }
406}
407
408impl Drop for OnionListener {
409 fn drop(&mut self) {
410 if let (Some(data), Some(mut drop)) = (self.data.take(), self.drop.take()) {
411 drop(data)
412 }
413 }
414}
415
416pub trait TorProvider: Send {
418 fn update(&mut self) -> Result<Vec<TorEvent>, Error>;
420 fn bootstrap(&mut self) -> Result<(), Error>;
422 fn add_client_auth(
424 &mut self,
425 service_id: &V3OnionServiceId,
426 client_auth: &X25519PrivateKey,
427 ) -> Result<(), Error>;
428 fn remove_client_auth(&mut self, service_id: &V3OnionServiceId) -> Result<(), Error>;
430 fn connect(
438 &mut self,
439 target: TargetAddr,
440 circuit: Option<CircuitToken>,
441 ) -> Result<OnionStream, Error>;
442 fn listener(
446 &mut self,
447 private_key: &Ed25519PrivateKey,
448 virt_port: u16,
449 authorised_clients: Option<&[X25519PublicKey]>,
450 ) -> Result<OnionListener, Error>;
451 fn generate_token(&mut self) -> CircuitToken;
453 fn release_token(&mut self, token: CircuitToken);
455}