mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2024-10-01 01:45:40 -04:00
Merge #594
594: Refuse to dial addresses via Tor that are almost certainly not reachable r=thomaseizinger a=thomaseizinger Co-authored-by: Thomas Eizinger <thomas@eizinger.io>
This commit is contained in:
commit
962b648911
@ -6,8 +6,9 @@ use libp2p::core::transport::TransportError;
|
||||
use libp2p::core::Transport;
|
||||
use libp2p::tcp::tokio::{Tcp, TcpStream};
|
||||
use libp2p::tcp::TcpListenStream;
|
||||
use std::io;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::borrow::Cow;
|
||||
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||
use std::{fmt, io};
|
||||
use tokio_socks::tcp::Socks5Stream;
|
||||
|
||||
/// A [`Transport`] that can dial onion addresses through a running Tor daemon.
|
||||
@ -34,13 +35,17 @@ impl Transport for TorDialOnlyTransport {
|
||||
}
|
||||
|
||||
fn dial(self, addr: Multiaddr) -> Result<Self::Dial, TransportError<Self::Error>> {
|
||||
let tor_address_string = fmt_as_address_string(addr.clone())?;
|
||||
let address = TorCompatibleAddress::from_multiaddr(Cow::Borrowed(&addr))?;
|
||||
|
||||
if address.is_certainly_not_reachable_via_tor_daemon() {
|
||||
return Err(TransportError::MultiaddrNotSupported(addr));
|
||||
}
|
||||
|
||||
let dial_future = async move {
|
||||
tracing::trace!("Connecting through Tor proxy to address: {}", addr);
|
||||
|
||||
let stream =
|
||||
Socks5Stream::connect((Ipv4Addr::LOCALHOST, self.socks_port), tor_address_string)
|
||||
Socks5Stream::connect((Ipv4Addr::LOCALHOST, self.socks_port), address.to_string())
|
||||
.await
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::ConnectionRefused, e))?;
|
||||
|
||||
@ -57,36 +62,72 @@ impl Transport for TorDialOnlyTransport {
|
||||
}
|
||||
}
|
||||
|
||||
/// Formats the given [`Multiaddr`] as an "address" string.
|
||||
///
|
||||
/// For our purposes, we define an address as {HOST}(.{TLD}):{PORT}. This format
|
||||
/// is what is compatible with the Tor daemon and allows us to route traffic
|
||||
/// through Tor.
|
||||
fn fmt_as_address_string(multi: Multiaddr) -> Result<String, TransportError<io::Error>> {
|
||||
let mut protocols = multi.iter();
|
||||
/// Represents an address that is _compatible_ with Tor, i.e. can be resolved by
|
||||
/// the Tor daemon.
|
||||
#[derive(Debug)]
|
||||
enum TorCompatibleAddress {
|
||||
Onion3 { host: String, port: u16 },
|
||||
Dns { address: String, port: u16 },
|
||||
Ip4 { address: Ipv4Addr, port: u16 },
|
||||
Ip6 { address: Ipv6Addr, port: u16 },
|
||||
}
|
||||
|
||||
let address_string = match protocols.next() {
|
||||
// if it is an Onion address, we have all we need and can return
|
||||
Some(Protocol::Onion3(addr)) => {
|
||||
return Ok(format!(
|
||||
"{}.onion:{}",
|
||||
BASE32.encode(addr.hash()).to_lowercase(),
|
||||
addr.port()
|
||||
))
|
||||
impl TorCompatibleAddress {
|
||||
/// Constructs a new [`TorCompatibleAddress`] from a [`Multiaddr`].
|
||||
fn from_multiaddr(multi: Cow<'_, Multiaddr>) -> Result<Self, TransportError<io::Error>> {
|
||||
match multi.iter().collect::<Vec<_>>().as_slice() {
|
||||
[Protocol::Onion3(onion), ..] => Ok(TorCompatibleAddress::Onion3 {
|
||||
host: BASE32.encode(onion.hash()).to_lowercase(),
|
||||
port: onion.port(),
|
||||
}),
|
||||
[Protocol::Ip4(address), Protocol::Tcp(port) | Protocol::Udp(port), ..] => {
|
||||
Ok(TorCompatibleAddress::Ip4 {
|
||||
address: *address,
|
||||
port: *port,
|
||||
})
|
||||
}
|
||||
[Protocol::Dns(address) | Protocol::Dns4(address), Protocol::Tcp(port) | Protocol::Udp(port), ..] => {
|
||||
Ok(TorCompatibleAddress::Dns {
|
||||
address: format!("{}", address),
|
||||
port: *port,
|
||||
})
|
||||
}
|
||||
[Protocol::Ip6(address), Protocol::Tcp(port) | Protocol::Udp(port), ..] => {
|
||||
Ok(TorCompatibleAddress::Ip6 {
|
||||
address: *address,
|
||||
port: *port,
|
||||
})
|
||||
}
|
||||
_ => Err(TransportError::MultiaddrNotSupported(multi.into_owned())),
|
||||
}
|
||||
// Deal with non-onion addresses
|
||||
Some(Protocol::Ip4(addr)) => format!("{}", addr),
|
||||
Some(Protocol::Ip6(addr)) => format!("{}", addr),
|
||||
Some(Protocol::Dns(addr) | Protocol::Dns4(addr)) => format!("{}", addr),
|
||||
_ => return Err(TransportError::MultiaddrNotSupported(multi)),
|
||||
};
|
||||
}
|
||||
|
||||
let port = match protocols.next() {
|
||||
Some(Protocol::Tcp(port) | Protocol::Udp(port)) => port,
|
||||
_ => return Err(TransportError::MultiaddrNotSupported(multi)),
|
||||
};
|
||||
/// Checks if the address is reachable via the Tor daemon.
|
||||
///
|
||||
/// The Tor daemon can dial onion addresses, resolve DNS names and dial
|
||||
/// IP4/IP6 addresses reachable via the public Internet.
|
||||
/// We can't guarantee that an address is reachable via the Internet but we
|
||||
/// can say that some addresses are almost certainly not reachable, for
|
||||
/// example, loopback addresses.
|
||||
fn is_certainly_not_reachable_via_tor_daemon(&self) -> bool {
|
||||
match self {
|
||||
TorCompatibleAddress::Onion3 { .. } => false,
|
||||
TorCompatibleAddress::Dns { address, .. } => address == "localhost",
|
||||
TorCompatibleAddress::Ip4 { address, .. } => address.is_loopback(),
|
||||
TorCompatibleAddress::Ip6 { address, .. } => address.is_loopback(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(format!("{}:{}", address_string, port))
|
||||
impl fmt::Display for TorCompatibleAddress {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
TorCompatibleAddress::Onion3 { host, port } => write!(f, "{}.onion:{}", host, port),
|
||||
TorCompatibleAddress::Dns { address, port } => write!(f, "{}:{}", address, port),
|
||||
TorCompatibleAddress::Ip4 { address, port } => write!(f, "{}:{}", address, port),
|
||||
TorCompatibleAddress::Ip6 { address, port } => write!(f, "{}:{}", address, port),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -95,69 +136,76 @@ pub mod test {
|
||||
|
||||
#[test]
|
||||
fn test_tor_address_string() {
|
||||
let address =
|
||||
"/onion3/oarchy4tamydxcitaki6bc2v4leza6v35iezmu2chg2bap63sv6f2did:1024/p2p/12D3KooWPD4uHN74SHotLN7VCH7Fm8zZgaNVymYcpeF1fpD2guc9"
|
||||
;
|
||||
let address_string = fmt_as_address_string(address.parse().unwrap())
|
||||
.expect("To be a multi formatted address.");
|
||||
let address = tor_compatible_address_from_str("/onion3/oarchy4tamydxcitaki6bc2v4leza6v35iezmu2chg2bap63sv6f2did:1024/p2p/12D3KooWPD4uHN74SHotLN7VCH7Fm8zZgaNVymYcpeF1fpD2guc9");
|
||||
|
||||
assert!(!address.is_certainly_not_reachable_via_tor_daemon());
|
||||
assert_eq!(
|
||||
address_string,
|
||||
address.to_string(),
|
||||
"oarchy4tamydxcitaki6bc2v4leza6v35iezmu2chg2bap63sv6f2did.onion:1024"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tcp_to_address_string_should_be_some() {
|
||||
let address = "/ip4/127.0.0.1/tcp/7777";
|
||||
let address_string = fmt_as_address_string(address.parse().unwrap())
|
||||
.expect("To be a formatted multi address. ");
|
||||
assert_eq!(address_string, "127.0.0.1:7777");
|
||||
let address = tor_compatible_address_from_str("/ip4/127.0.0.1/tcp/7777");
|
||||
|
||||
assert!(address.is_certainly_not_reachable_via_tor_daemon());
|
||||
assert_eq!(address.to_string(), "127.0.0.1:7777");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ip6_to_address_string_should_be_some() {
|
||||
let address = "/ip6/2001:db8:85a3:8d3:1319:8a2e:370:7348/tcp/7777";
|
||||
let address_string = fmt_as_address_string(address.parse().unwrap())
|
||||
.expect("To be a formatted multi address. ");
|
||||
assert_eq!(address_string, "2001:db8:85a3:8d3:1319:8a2e:370:7348:7777");
|
||||
let address =
|
||||
tor_compatible_address_from_str("/ip6/2001:db8:85a3:8d3:1319:8a2e:370:7348/tcp/7777");
|
||||
|
||||
assert!(!address.is_certainly_not_reachable_via_tor_daemon());
|
||||
assert_eq!(
|
||||
address.to_string(),
|
||||
"2001:db8:85a3:8d3:1319:8a2e:370:7348:7777"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn udp_to_address_string_should_be_some() {
|
||||
let address = "/ip4/127.0.0.1/udp/7777";
|
||||
let address_string = fmt_as_address_string(address.parse().unwrap())
|
||||
.expect("To be a formatted multi address. ");
|
||||
assert_eq!(address_string, "127.0.0.1:7777");
|
||||
let address = tor_compatible_address_from_str("/ip4/127.0.0.1/udp/7777");
|
||||
|
||||
assert!(address.is_certainly_not_reachable_via_tor_daemon());
|
||||
assert_eq!(address.to_string(), "127.0.0.1:7777");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ws_to_address_string_should_be_some() {
|
||||
let address = "/ip4/127.0.0.1/tcp/7777/ws";
|
||||
let address_string = fmt_as_address_string(address.parse().unwrap())
|
||||
.expect("To be a formatted multi address. ");
|
||||
assert_eq!(address_string, "127.0.0.1:7777");
|
||||
let address = tor_compatible_address_from_str("/ip4/127.0.0.1/tcp/7777/ws");
|
||||
|
||||
assert!(address.is_certainly_not_reachable_via_tor_daemon());
|
||||
assert_eq!(address.to_string(), "127.0.0.1:7777");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dns4_to_address_string_should_be_some() {
|
||||
let address = "/dns4/randomdomain.com/tcp/7777";
|
||||
let address_string = fmt_as_address_string(address.parse().unwrap())
|
||||
.expect("To be a formatted multi address. ");
|
||||
assert_eq!(address_string, "randomdomain.com:7777");
|
||||
let address = tor_compatible_address_from_str("/dns4/randomdomain.com/tcp/7777");
|
||||
|
||||
assert!(!address.is_certainly_not_reachable_via_tor_daemon());
|
||||
assert_eq!(address.to_string(), "randomdomain.com:7777");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dns_to_address_string_should_be_some() {
|
||||
let address = "/dns/randomdomain.com/tcp/7777";
|
||||
let address_string = fmt_as_address_string(address.parse().unwrap())
|
||||
.expect("To be a formatted multi address. ");
|
||||
assert_eq!(address_string, "randomdomain.com:7777");
|
||||
let address = tor_compatible_address_from_str("/dns/randomdomain.com/tcp/7777");
|
||||
|
||||
assert!(!address.is_certainly_not_reachable_via_tor_daemon());
|
||||
assert_eq!(address.to_string(), "randomdomain.com:7777");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dnsaddr_to_address_string_should_be_none() {
|
||||
fn dnsaddr_to_address_string_should_be_error() {
|
||||
let address = "/dnsaddr/randomdomain.com";
|
||||
let address_string = fmt_as_address_string(address.parse().unwrap()).ok();
|
||||
assert_eq!(address_string, None);
|
||||
|
||||
let _ =
|
||||
TorCompatibleAddress::from_multiaddr(Cow::Owned(address.parse().unwrap())).unwrap_err();
|
||||
}
|
||||
|
||||
fn tor_compatible_address_from_str(str: &str) -> TorCompatibleAddress {
|
||||
TorCompatibleAddress::from_multiaddr(Cow::Owned(str.parse().unwrap())).unwrap()
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user