diff --git a/core/src/nodes/network.rs b/core/src/nodes/network.rs index 8552c5fb..9db4b9f4 100644 --- a/core/src/nodes/network.rs +++ b/core/src/nodes/network.rs @@ -714,14 +714,23 @@ where self.incoming_limit } - /// Call this function in order to know which address remotes should dial in order to access - /// your local node. + /// Call this function in order to know which address remotes should dial to + /// access your local node. /// - /// `observed_addr` should be an address a remote observes you as, which can be obtained for + /// When receiving an observed address on a tcp connection that we initiated, the observed + /// address contains our tcp dial port, not our tcp listen port. We know which port we are + /// listening on, thereby we can replace the port within the observed address. + /// + /// When receiving an observed address on a tcp connection that we did **not** initiated, the + /// observed address should contain our listening port. In case it differs from our listening + /// port there might be a proxy along the path. + /// + /// # Arguments + /// + /// * `observed_addr` - should be an address a remote observes you as, which can be obtained for /// example with the identify protocol. /// - /// For each listener, calls `nat_traversal` with the observed address and returns the outcome. - pub fn nat_traversal<'a>(&'a self, observed_addr: &'a Multiaddr) + pub fn address_translation<'a>(&'a self, observed_addr: &'a Multiaddr) -> impl Iterator + 'a where TMuxer: 'a, diff --git a/core/src/translation.rs b/core/src/translation.rs index 7ba8916b..5dab82a0 100644 --- a/core/src/translation.rs +++ b/core/src/translation.rs @@ -22,22 +22,88 @@ use multiaddr::{Multiaddr, Protocol}; /// Perform IP address translation. /// -/// Given an `original` [`Multiaddr`] and some `observed` [`Multiaddr`], return -/// a translated [`Multiaddr`] which has the first IP address translated by the -/// corresponding one from `observed`. +/// Given an `original` [`Multiaddr`] and some `observed` [`Multiaddr`], replace the first protocol +/// of the `original` with the first protocol of the `observed` [`Multiaddr`] and return this +/// translated [`Multiaddr`]. /// -/// This is a mixed-mode translation, i.e. an IPv4 address may be replaced by -/// an IPv6 address and vice versa. +/// This function can for example be useful when handling tcp connections. Tcp does not listen and +/// dial on the same port by default. Thus when receiving an observed address on a connection that +/// we initiated, it will contain our dialing port, not our listening port. We need to take the ip +/// address or dns address from the observed address and the port from the original address. +/// +/// This is a mixed-mode translation, i.e. an IPv4 / DNS4 address may be replaced by an IPv6 / DNS6 +/// address and vice versa. /// /// If the first [`Protocol`]s are not IP addresses, `None` is returned instead. pub fn address_translation(original: &Multiaddr, observed: &Multiaddr) -> Option { original.replace(0, move |proto| match proto { - Protocol::Ip4(_) | Protocol::Ip6(_) => match observed.iter().next() { - x@Some(Protocol::Ip4(_)) => x, - x@Some(Protocol::Ip6(_)) => x, - _ => None - } - _ => None + Protocol::Ip4(_) | Protocol::Ip6(_) | Protocol::Dns4(_) | Protocol::Dns6(_) => match observed.iter().next() { + x @ Some(Protocol::Ip4(_)) => x, + x @ Some(Protocol::Ip6(_)) => x, + x @ Some(Protocol::Dns4(_)) => x, + x @ Some(Protocol::Dns6(_)) => x, + _ => None, + }, + _ => None, }) } -// TODO: add tests + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_address_translation() { + struct Test { + original: Multiaddr, + observed: Multiaddr, + expected: Multiaddr, + } + + let tests = vec![ + // Basic ipv4. + Test { + original: "/ip4/192.0.2.1/tcp/1".parse().unwrap(), + observed: "/ip4/192.0.2.2/tcp/2".parse().unwrap(), + expected: "/ip4/192.0.2.2/tcp/1".parse().unwrap(), + }, + // Basic ipv6. + Test { + original: "/ip6/2001:db8:0:0:0:0:0:0/tcp/1".parse().unwrap(), + observed: "/ip6/2001:db8:0:0:0:0:0:1/tcp/2".parse().unwrap(), + expected: "/ip6/2001:db8:0:0:0:0:0:1/tcp/1".parse().unwrap(), + }, + // Ipv4 ipv6 mix. + Test { + original: "/ip4/192.0.2.1/tcp/1".parse().unwrap(), + observed: "/ip6/2001:db8:0:0:0:0:0:1/tcp/2".parse().unwrap(), + expected: "/ip6/2001:db8:0:0:0:0:0:1/tcp/1".parse().unwrap(), + }, + // Ipv6 ipv4 mix. + Test { + original: "/ip6/2001:db8:0:0:0:0:0:0/tcp/1".parse().unwrap(), + observed: "/ip4/192.0.2.2/tcp/2".parse().unwrap(), + expected: "/ip4/192.0.2.2/tcp/1".parse().unwrap(), + }, + // Dns. + Test { + original: "/dns4/foo/tcp/1".parse().unwrap(), + observed: "/dns4/bar/tcp/2".parse().unwrap(), + expected: "/dns4/bar/tcp/1".parse().unwrap(), + }, + // Ipv4 Dns mix. + Test { + original: "/ip4/192.0.2.1/tcp/1".parse().unwrap(), + observed: "/dns4/bar/tcp/2".parse().unwrap(), + expected: "/dns4/bar/tcp/1".parse().unwrap(), + }, + ]; + + for test in tests.iter() { + assert_eq!( + address_translation(&test.original, &test.observed), + Some(test.expected.clone()) + ); + } + } +} diff --git a/misc/multiaddr/src/lib.rs b/misc/multiaddr/src/lib.rs index 501a9b64..5d3f0ae6 100644 --- a/misc/multiaddr/src/lib.rs +++ b/misc/multiaddr/src/lib.rs @@ -116,7 +116,9 @@ impl Multiaddr { /// Returns the components of this multiaddress. /// - /// ``` + /// # Example + /// + /// ```rust /// use std::net::Ipv4Addr; /// use parity_multiaddr::{Multiaddr, Protocol}; /// @@ -174,7 +176,7 @@ impl fmt::Debug for Multiaddr { impl fmt::Display for Multiaddr { /// Convert a Multiaddr to a string /// - /// # Examples + /// # Example /// /// ``` /// use parity_multiaddr::Multiaddr; diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index 4f0d9884..d4a32f0c 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -443,7 +443,7 @@ where TBehaviour: NetworkBehaviour, } }, Async::Ready(NetworkBehaviourAction::ReportObservedAddr { address }) => { - for addr in self.network.nat_traversal(&address) { + for addr in self.network.address_translation(&address) { if self.external_addrs.iter().all(|a| *a != addr) { self.behaviour.inject_new_external_addr(&addr); }