mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2025-07-30 01:58:55 -04:00
Refactor Tor transport to be dial-only
Libp2p's transports are meant to be composed. Hence, any form of fallback should be implemented by emitting `MultiaddrNotSupported` from the `listen` and `dial` functions. This allows us to completely remove the tcp transport from the tor transport.
This commit is contained in:
parent
8bd6c9dcfc
commit
d19231d811
2 changed files with 66 additions and 87 deletions
|
@ -1,33 +1,28 @@
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::Result;
|
||||||
use data_encoding::BASE32;
|
use data_encoding::BASE32;
|
||||||
use futures::future::{BoxFuture, FutureExt, Ready};
|
use futures::future::{BoxFuture, FutureExt, Ready};
|
||||||
use libp2p::core::multiaddr::{Multiaddr, Protocol};
|
use libp2p::core::multiaddr::{Multiaddr, Protocol};
|
||||||
use libp2p::core::transport::TransportError;
|
use libp2p::core::transport::TransportError;
|
||||||
use libp2p::core::Transport;
|
use libp2p::core::Transport;
|
||||||
use libp2p::tcp::tokio::{Tcp, TcpStream};
|
use libp2p::tcp::tokio::{Tcp, TcpStream};
|
||||||
use libp2p::tcp::{GenTcpConfig, TcpListenStream, TokioTcpConfig};
|
use libp2p::tcp::TcpListenStream;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::net::Ipv4Addr;
|
use std::net::Ipv4Addr;
|
||||||
use tokio_socks::tcp::Socks5Stream;
|
use tokio_socks::tcp::Socks5Stream;
|
||||||
|
|
||||||
/// Represents the configuration for a Tor transport for libp2p.
|
/// A [`Transport`] that can dial onion addresses through a running Tor daemon.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct TorTcpConfig {
|
pub struct TorDialOnlyTransport {
|
||||||
inner: GenTcpConfig<Tcp>,
|
|
||||||
/// Tor SOCKS5 proxy port number.
|
|
||||||
socks_port: u16,
|
socks_port: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TorTcpConfig {
|
impl TorDialOnlyTransport {
|
||||||
pub fn new(tcp: TokioTcpConfig, socks_port: u16) -> Self {
|
pub fn new(socks_port: u16) -> Self {
|
||||||
Self {
|
Self { socks_port }
|
||||||
inner: tcp,
|
|
||||||
socks_port,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Transport for TorTcpConfig {
|
impl Transport for TorDialOnlyTransport {
|
||||||
type Output = TcpStream;
|
type Output = TcpStream;
|
||||||
type Error = io::Error;
|
type Error = io::Error;
|
||||||
type Listener = TcpListenStream<Tcp>;
|
type Listener = TcpListenStream<Tcp>;
|
||||||
|
@ -35,48 +30,41 @@ impl Transport for TorTcpConfig {
|
||||||
type Dial = BoxFuture<'static, Result<Self::Output, Self::Error>>;
|
type Dial = BoxFuture<'static, Result<Self::Output, Self::Error>>;
|
||||||
|
|
||||||
fn listen_on(self, addr: Multiaddr) -> Result<Self::Listener, TransportError<Self::Error>> {
|
fn listen_on(self, addr: Multiaddr) -> Result<Self::Listener, TransportError<Self::Error>> {
|
||||||
self.inner.listen_on(addr)
|
Err(TransportError::MultiaddrNotSupported(addr))
|
||||||
}
|
}
|
||||||
|
|
||||||
// dials via Tor's socks5 proxy if configured and if the provided address is an
|
|
||||||
// onion address. or it falls back to Tcp dialling
|
|
||||||
fn dial(self, addr: Multiaddr) -> Result<Self::Dial, TransportError<Self::Error>> {
|
fn dial(self, addr: Multiaddr) -> Result<Self::Dial, TransportError<Self::Error>> {
|
||||||
match to_address_string(addr.clone()) {
|
let tor_address_string = fmt_as_address_string(addr.clone())?;
|
||||||
Ok(tor_address_string) => Ok(async move {
|
|
||||||
|
let dial_future = async move {
|
||||||
tracing::trace!("Connecting through Tor proxy to address: {}", addr);
|
tracing::trace!("Connecting through Tor proxy to address: {}", addr);
|
||||||
|
|
||||||
let stream = Socks5Stream::connect(
|
let stream =
|
||||||
(Ipv4Addr::LOCALHOST, self.socks_port),
|
Socks5Stream::connect((Ipv4Addr::LOCALHOST, self.socks_port), tor_address_string)
|
||||||
tor_address_string,
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
.map_err(|e| io::Error::new(io::ErrorKind::ConnectionRefused, e))?;
|
.map_err(|e| io::Error::new(io::ErrorKind::ConnectionRefused, e))?;
|
||||||
|
|
||||||
tracing::trace!("Connection through Tor established");
|
tracing::trace!("Connection through Tor established");
|
||||||
|
|
||||||
Ok(TcpStream(stream.into_inner()))
|
Ok(TcpStream(stream.into_inner()))
|
||||||
}
|
};
|
||||||
.boxed()),
|
|
||||||
Err(error) => {
|
Ok(dial_future.boxed())
|
||||||
tracing::warn!(
|
|
||||||
address = %addr,
|
|
||||||
"Address could not be formatted. Dialling via clear net. Error {:#}", error,
|
|
||||||
);
|
|
||||||
self.inner.dial(addr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn address_translation(&self, listen: &Multiaddr, observed: &Multiaddr) -> Option<Multiaddr> {
|
fn address_translation(&self, _: &Multiaddr, _: &Multiaddr) -> Option<Multiaddr> {
|
||||||
self.inner.address_translation(listen, observed)
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tor expects an address format of ADDR:PORT.
|
/// Formats the given [`Multiaddr`] as an "address" string.
|
||||||
/// This helper function tries to convert the provided multi-address into this
|
///
|
||||||
/// format. None is returned if an unsupported protocol was provided.
|
/// For our purposes, we define an address as {HOST}(.{TLD}):{PORT}. This format
|
||||||
fn to_address_string(multi: Multiaddr) -> Result<String> {
|
/// 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();
|
let mut protocols = multi.iter();
|
||||||
|
|
||||||
let address_string = match protocols.next() {
|
let address_string = match protocols.next() {
|
||||||
// if it is an Onion address, we have all we need and can return
|
// if it is an Onion address, we have all we need and can return
|
||||||
Some(Protocol::Onion3(addr)) => {
|
Some(Protocol::Onion3(addr)) => {
|
||||||
|
@ -87,43 +75,33 @@ fn to_address_string(multi: Multiaddr) -> Result<String> {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
// Deal with non-onion addresses
|
// Deal with non-onion addresses
|
||||||
Some(Protocol::Ip4(addr)) => Some(format!("{}", addr)),
|
Some(Protocol::Ip4(addr)) => format!("{}", addr),
|
||||||
Some(Protocol::Ip6(addr)) => Some(format!("{}", addr)),
|
Some(Protocol::Ip6(addr)) => format!("{}", addr),
|
||||||
Some(Protocol::Dns(addr)) => Some(format!("{}", addr)),
|
Some(Protocol::Dns(addr)) => format!("{}", addr),
|
||||||
Some(Protocol::Dns4(addr)) => Some(format!("{}", addr)),
|
Some(Protocol::Dns4(addr)) => format!("{}", addr),
|
||||||
_ => None,
|
_ => return Err(TransportError::MultiaddrNotSupported(multi)),
|
||||||
}
|
};
|
||||||
.ok_or_else(|| {
|
|
||||||
anyhow!(
|
let port = match protocols.next() {
|
||||||
"Could not format address {}. Please consider reporting this issue. ",
|
Some(Protocol::Tcp(port)) => port,
|
||||||
multi
|
Some(Protocol::Udp(port)) => port,
|
||||||
)
|
_ => return Err(TransportError::MultiaddrNotSupported(multi)),
|
||||||
})?;
|
|
||||||
|
|
||||||
let port_string = match protocols.next() {
|
|
||||||
Some(Protocol::Tcp(port)) => Some(format!("{}", port)),
|
|
||||||
Some(Protocol::Udp(port)) => Some(format!("{}", port)),
|
|
||||||
_ => None,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(port) = port_string {
|
|
||||||
Ok(format!("{}:{}", address_string, port))
|
Ok(format!("{}:{}", address_string, port))
|
||||||
} else {
|
|
||||||
Ok(address_string)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod test {
|
pub mod test {
|
||||||
use crate::network::tor_transport::to_address_string;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_tor_address_string() {
|
fn test_tor_address_string() {
|
||||||
let address =
|
let address =
|
||||||
"/onion3/oarchy4tamydxcitaki6bc2v4leza6v35iezmu2chg2bap63sv6f2did:1024/p2p/12D3KooWPD4uHN74SHotLN7VCH7Fm8zZgaNVymYcpeF1fpD2guc9"
|
"/onion3/oarchy4tamydxcitaki6bc2v4leza6v35iezmu2chg2bap63sv6f2did:1024/p2p/12D3KooWPD4uHN74SHotLN7VCH7Fm8zZgaNVymYcpeF1fpD2guc9"
|
||||||
;
|
;
|
||||||
let address_string =
|
let address_string = fmt_as_address_string(address.parse().unwrap())
|
||||||
to_address_string(address.parse().unwrap()).expect("To be a multi formatted address.");
|
.expect("To be a multi formatted address.");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
address_string,
|
address_string,
|
||||||
"oarchy4tamydxcitaki6bc2v4leza6v35iezmu2chg2bap63sv6f2did.onion:1024"
|
"oarchy4tamydxcitaki6bc2v4leza6v35iezmu2chg2bap63sv6f2did.onion:1024"
|
||||||
|
@ -133,55 +111,55 @@ pub mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn tcp_to_address_string_should_be_some() {
|
fn tcp_to_address_string_should_be_some() {
|
||||||
let address = "/ip4/127.0.0.1/tcp/7777";
|
let address = "/ip4/127.0.0.1/tcp/7777";
|
||||||
let address_string =
|
let address_string = fmt_as_address_string(address.parse().unwrap())
|
||||||
to_address_string(address.parse().unwrap()).expect("To be a formatted multi address. ");
|
.expect("To be a formatted multi address. ");
|
||||||
assert_eq!(address_string, "127.0.0.1:7777");
|
assert_eq!(address_string, "127.0.0.1:7777");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ip6_to_address_string_should_be_some() {
|
fn ip6_to_address_string_should_be_some() {
|
||||||
let address = "/ip6/2001:db8:85a3:8d3:1319:8a2e:370:7348/tcp/7777";
|
let address = "/ip6/2001:db8:85a3:8d3:1319:8a2e:370:7348/tcp/7777";
|
||||||
let address_string =
|
let address_string = fmt_as_address_string(address.parse().unwrap())
|
||||||
to_address_string(address.parse().unwrap()).expect("To be a formatted multi address. ");
|
.expect("To be a formatted multi address. ");
|
||||||
assert_eq!(address_string, "2001:db8:85a3:8d3:1319:8a2e:370:7348:7777");
|
assert_eq!(address_string, "2001:db8:85a3:8d3:1319:8a2e:370:7348:7777");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn udp_to_address_string_should_be_some() {
|
fn udp_to_address_string_should_be_some() {
|
||||||
let address = "/ip4/127.0.0.1/udp/7777";
|
let address = "/ip4/127.0.0.1/udp/7777";
|
||||||
let address_string =
|
let address_string = fmt_as_address_string(address.parse().unwrap())
|
||||||
to_address_string(address.parse().unwrap()).expect("To be a formatted multi address. ");
|
.expect("To be a formatted multi address. ");
|
||||||
assert_eq!(address_string, "127.0.0.1:7777");
|
assert_eq!(address_string, "127.0.0.1:7777");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ws_to_address_string_should_be_some() {
|
fn ws_to_address_string_should_be_some() {
|
||||||
let address = "/ip4/127.0.0.1/tcp/7777/ws";
|
let address = "/ip4/127.0.0.1/tcp/7777/ws";
|
||||||
let address_string =
|
let address_string = fmt_as_address_string(address.parse().unwrap())
|
||||||
to_address_string(address.parse().unwrap()).expect("To be a formatted multi address. ");
|
.expect("To be a formatted multi address. ");
|
||||||
assert_eq!(address_string, "127.0.0.1:7777");
|
assert_eq!(address_string, "127.0.0.1:7777");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn dns4_to_address_string_should_be_some() {
|
fn dns4_to_address_string_should_be_some() {
|
||||||
let address = "/dns4/randomdomain.com/tcp/7777";
|
let address = "/dns4/randomdomain.com/tcp/7777";
|
||||||
let address_string =
|
let address_string = fmt_as_address_string(address.parse().unwrap())
|
||||||
to_address_string(address.parse().unwrap()).expect("To be a formatted multi address. ");
|
.expect("To be a formatted multi address. ");
|
||||||
assert_eq!(address_string, "randomdomain.com:7777");
|
assert_eq!(address_string, "randomdomain.com:7777");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn dns_to_address_string_should_be_some() {
|
fn dns_to_address_string_should_be_some() {
|
||||||
let address = "/dns/randomdomain.com/tcp/7777";
|
let address = "/dns/randomdomain.com/tcp/7777";
|
||||||
let address_string =
|
let address_string = fmt_as_address_string(address.parse().unwrap())
|
||||||
to_address_string(address.parse().unwrap()).expect("To be a formatted multi address. ");
|
.expect("To be a formatted multi address. ");
|
||||||
assert_eq!(address_string, "randomdomain.com:7777");
|
assert_eq!(address_string, "randomdomain.com:7777");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn dnsaddr_to_address_string_should_be_none() {
|
fn dnsaddr_to_address_string_should_be_none() {
|
||||||
let address = "/dnsaddr/randomdomain.com";
|
let address = "/dnsaddr/randomdomain.com";
|
||||||
let address_string = to_address_string(address.parse().unwrap()).ok();
|
let address_string = fmt_as_address_string(address.parse().unwrap()).ok();
|
||||||
assert_eq!(address_string, None);
|
assert_eq!(address_string, None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::network::tor_transport::TorTcpConfig;
|
use crate::network::tor_transport::TorDialOnlyTransport;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use libp2p::core::muxing::StreamMuxerBox;
|
use libp2p::core::muxing::StreamMuxerBox;
|
||||||
use libp2p::core::transport::Boxed;
|
use libp2p::core::transport::Boxed;
|
||||||
|
@ -51,12 +51,13 @@ pub fn build_tor(id_keys: &identity::Keypair, tor_socks5_port: u16) -> Result<Sw
|
||||||
let noise = NoiseConfig::xx(dh_keys).into_authenticated();
|
let noise = NoiseConfig::xx(dh_keys).into_authenticated();
|
||||||
|
|
||||||
let tcp = TokioTcpConfig::new().nodelay(true);
|
let tcp = TokioTcpConfig::new().nodelay(true);
|
||||||
let tcp = TorTcpConfig::new(tcp, tor_socks5_port);
|
let tcp_with_dns = TokioDnsConfig::system(tcp)?;
|
||||||
let dns = TokioDnsConfig::system(tcp)?;
|
let websocket_with_dns = WsConfig::new(tcp_with_dns.clone());
|
||||||
let websocket = WsConfig::new(dns.clone());
|
let tor_dial_only = TorDialOnlyTransport::new(tor_socks5_port);
|
||||||
|
|
||||||
let transport = websocket
|
let transport = tor_dial_only
|
||||||
.or_transport(dns)
|
.or_transport(tcp_with_dns)
|
||||||
|
.or_transport(websocket_with_dns)
|
||||||
.upgrade(Version::V1)
|
.upgrade(Version::V1)
|
||||||
.authenticate(noise)
|
.authenticate(noise)
|
||||||
.multiplex(SelectUpgrade::new(
|
.multiplex(SelectUpgrade::new(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue