core/src/translation: Support dns4 and dns6 (#1204)

* core/src/translation: Add unit tests

* core/src/translation: Support dns4 and dns6

Add dns4 and dns6 as valid protocol replacements for the origin address
to construct external addresses of a given node.

* core/nodes/network: %s/nat_translation/address_translation/

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 NAT along the path.

With the above in mind, the function name `nat_translation` is misleading.
This commit is contained in:
Max Inden
2019-07-18 18:41:09 +02:00
committed by Pierre Krieger
parent cde93f5432
commit 5861474d99
4 changed files with 97 additions and 20 deletions

View File

@ -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<Item = Multiaddr> + 'a
where
TMuxer: 'a,

View File

@ -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<Multiaddr> {
original.replace(0, move |proto| match proto {
Protocol::Ip4(_) | Protocol::Ip6(_) => match observed.iter().next() {
Protocol::Ip4(_) | Protocol::Ip6(_) | Protocol::Dns4(_) | Protocol::Dns6(_) => match observed.iter().next() {
x @ Some(Protocol::Ip4(_)) => x,
x @ Some(Protocol::Ip6(_)) => x,
_ => None
}
_ => None
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())
);
}
}
}

View File

@ -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;

View File

@ -443,7 +443,7 @@ where TBehaviour: NetworkBehaviour<ProtocolsHandler = THandler>,
}
},
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);
}