fips_core/transport/tor/
address.rs1use crate::transport::{TransportAddr, TransportError};
2use std::net::SocketAddr;
3
4#[derive(Clone, Debug)]
10pub enum TorAddr {
11 Onion(String, u16),
13 Clearnet(SocketAddr),
15 ClearnetHostname(String, u16),
19}
20
21pub(super) fn parse_tor_addr(addr: &TransportAddr) -> Result<TorAddr, TransportError> {
27 let s = addr.as_str().ok_or_else(|| {
28 TransportError::InvalidAddress("Tor address must be a valid UTF-8 string".into())
29 })?;
30
31 if s.contains(".onion:") {
32 let (host, port_str) = s.rsplit_once(':').ok_or_else(|| {
34 TransportError::InvalidAddress(format!("invalid onion address: {}", s))
35 })?;
36 let port: u16 = port_str.parse().map_err(|_| {
37 TransportError::InvalidAddress(format!("invalid port in onion address: {}", s))
38 })?;
39 Ok(TorAddr::Onion(host.to_string(), port))
40 } else if let Ok(socket_addr) = s.parse::<SocketAddr>() {
41 Ok(TorAddr::Clearnet(socket_addr))
43 } else {
44 let (host, port_str) = s.rsplit_once(':').ok_or_else(|| {
46 TransportError::InvalidAddress(format!("invalid address (expected host:port): {}", s))
47 })?;
48 let port: u16 = port_str
49 .parse()
50 .map_err(|_| TransportError::InvalidAddress(format!("invalid port: {}", s)))?;
51 if !host.contains('.') {
52 return Err(TransportError::InvalidAddress(format!(
53 "hostname must be fully qualified (contain a dot): {}",
54 host
55 )));
56 }
57 Ok(TorAddr::ClearnetHostname(host.to_string(), port))
58 }
59}
60
61pub(super) fn validate_host_port(addr: &str, field_name: &str) -> Result<(), TransportError> {
67 if addr.parse::<SocketAddr>().is_ok() {
68 return Ok(());
69 }
70 let parts: Vec<&str> = addr.rsplitn(2, ':').collect();
72 if parts.len() != 2 || parts[0].parse::<u16>().is_err() || parts[1].is_empty() {
73 return Err(TransportError::StartFailed(format!(
74 "invalid {} '{}': expected host:port or IP:port",
75 field_name, addr
76 )));
77 }
78 Ok(())
79}