Skip to content

Support /dns protocol in multiaddr #1575

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
May 18, 2020
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@
- `libp2p-core`: Make the number of events buffered to/from tasks configurable.
[PR 1574](https://github.com/libp2p/rust-libp2p/pull/1574)

- `libp2p-dns`, `parity-multiaddr`: Added support for the `/dns` multiaddr
protocol. Additionally, the `multiaddr::from_url` function will now use
`/dns` instead of `/dns4`.
[PR 1575](https://github.com/libp2p/rust-libp2p/pull/1575)

- `libp2p-noise`: Added the `X25519Spec` protocol suite which uses
libp2p-noise-spec compliant signatures on static keys as well as the
`/noise` protocol upgrade, hence providing a libp2p-noise-spec compliant
Expand Down
7 changes: 6 additions & 1 deletion core/src/translation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,14 @@ use multiaddr::{Multiaddr, Protocol};
/// 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(_) | Protocol::Dns4(_) | Protocol::Dns6(_) => match observed.iter().next() {
Protocol::Ip4(_)
| Protocol::Ip6(_)
| Protocol::Dns(_)
| Protocol::Dns4(_)
| Protocol::Dns6(_) => match observed.iter().next() {
x @ Some(Protocol::Ip4(_)) => x,
x @ Some(Protocol::Ip6(_)) => x,
x @ Some(Protocol::Dns(_)) => x,
x @ Some(Protocol::Dns4(_)) => x,
x @ Some(Protocol::Dns6(_)) => x,
_ => None,
Expand Down
14 changes: 7 additions & 7 deletions misc/multiaddr/src/from_url.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ fn from_url_inner_http_ws(url: url::Url, lossy: bool) -> std::result::Result<Mul
if let Ok(ip) = hostname.parse::<IpAddr>() {
Protocol::from(ip)
} else {
Protocol::Dns4(hostname.into())
Protocol::Dns(hostname.into())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, that's actually a great change 🎉

}
} else {
return Err(FromUrlErr::BadUrl);
Expand Down Expand Up @@ -185,31 +185,31 @@ mod tests {
#[test]
fn dns_addr_ws() {
let addr = from_url("ws://example.com").unwrap();
assert_eq!(addr, "/dns4/example.com/tcp/80/ws".parse().unwrap());
assert_eq!(addr, "/dns/example.com/tcp/80/ws".parse().unwrap());
}

#[test]
fn dns_addr_http() {
let addr = from_url("http://example.com").unwrap();
assert_eq!(addr, "/dns4/example.com/tcp/80/http".parse().unwrap());
assert_eq!(addr, "/dns/example.com/tcp/80/http".parse().unwrap());
}

#[test]
fn dns_addr_wss() {
let addr = from_url("wss://example.com").unwrap();
assert_eq!(addr, "/dns4/example.com/tcp/443/wss".parse().unwrap());
assert_eq!(addr, "/dns/example.com/tcp/443/wss".parse().unwrap());
}

#[test]
fn dns_addr_https() {
let addr = from_url("https://example.com").unwrap();
assert_eq!(addr, "/dns4/example.com/tcp/443/https".parse().unwrap());
assert_eq!(addr, "/dns/example.com/tcp/443/https".parse().unwrap());
}

#[test]
fn bad_hostname() {
let addr = from_url("wss://127.0.0.1x").unwrap();
assert_eq!(addr, "/dns4/127.0.0.1x/tcp/443/wss".parse().unwrap());
assert_eq!(addr, "/dns/127.0.0.1x/tcp/443/wss".parse().unwrap());
}

#[test]
Expand All @@ -223,7 +223,7 @@ mod tests {
#[test]
fn dns_and_port() {
let addr = from_url("http://example.com:1000").unwrap();
assert_eq!(addr, "/dns4/example.com/tcp/1000/http".parse().unwrap());
assert_eq!(addr, "/dns/example.com/tcp/1000/http".parse().unwrap());
}

#[test]
Expand Down
19 changes: 19 additions & 0 deletions misc/multiaddr/src/protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use unsigned_varint::{encode, decode};
use crate::onion_addr::Onion3Addr;

const DCCP: u32 = 33;
const DNS: u32 = 53;
const DNS4: u32 = 54;
const DNS6: u32 = 55;
const DNSADDR: u32 = 56;
Expand Down Expand Up @@ -66,6 +67,7 @@ const PATH_SEGMENT_ENCODE_SET: &percent_encoding::AsciiSet = &percent_encoding::
#[derive(PartialEq, Eq, Clone, Debug)]
pub enum Protocol<'a> {
Dccp(u16),
Dns(Cow<'a, str>),
Dns4(Cow<'a, str>),
Dns6(Cow<'a, str>),
Dnsaddr(Cow<'a, str>),
Expand Down Expand Up @@ -125,6 +127,10 @@ impl<'a> Protocol<'a> {
let s = iter.next().ok_or(Error::InvalidProtocolString)?;
Ok(Protocol::Ip6(Ipv6Addr::from_str(s)?))
}
"dns" => {
let s = iter.next().ok_or(Error::InvalidProtocolString)?;
Ok(Protocol::Dns(Cow::Borrowed(s)))
}
"dns4" => {
let s = iter.next().ok_or(Error::InvalidProtocolString)?;
Ok(Protocol::Dns4(Cow::Borrowed(s)))
Expand Down Expand Up @@ -206,6 +212,11 @@ impl<'a> Protocol<'a> {
let num = rdr.read_u16::<BigEndian>()?;
Ok((Protocol::Dccp(num), rest))
}
DNS => {
let (n, input) = decode::usize(input)?;
let (data, rest) = split_at(n, input)?;
Ok((Protocol::Dns(Cow::Borrowed(str::from_utf8(data)?)), rest))
}
DNS4 => {
let (n, input) = decode::usize(input)?;
let (data, rest) = split_at(n, input)?;
Expand Down Expand Up @@ -345,6 +356,12 @@ impl<'a> Protocol<'a> {
w.write_all(encode::u32(SCTP, &mut buf))?;
w.write_u16::<BigEndian>(*port)?
}
Protocol::Dns(s) => {
w.write_all(encode::u32(DNS, &mut buf))?;
let bytes = s.as_bytes();
w.write_all(encode::usize(bytes.len(), &mut encode::usize_buffer()))?;
w.write_all(&bytes)?
}
Protocol::Dns4(s) => {
w.write_all(encode::u32(DNS4, &mut buf))?;
let bytes = s.as_bytes();
Expand Down Expand Up @@ -421,6 +438,7 @@ impl<'a> Protocol<'a> {
use self::Protocol::*;
match self {
Dccp(a) => Dccp(a),
Dns(cow) => Dns(Cow::Owned(cow.into_owned())),
Dns4(cow) => Dns4(Cow::Owned(cow.into_owned())),
Dns6(cow) => Dns6(Cow::Owned(cow.into_owned())),
Dnsaddr(cow) => Dnsaddr(Cow::Owned(cow.into_owned())),
Expand Down Expand Up @@ -454,6 +472,7 @@ impl<'a> fmt::Display for Protocol<'a> {
use self::Protocol::*;
match self {
Dccp(port) => write!(f, "/dccp/{}", port),
Dns(s) => write!(f, "/dns/{}", s),
Dns4(s) => write!(f, "/dns4/{}", s),
Dns6(s) => write!(f, "/dns6/{}", s),
Dnsaddr(s) => write!(f, "/dnsaddr/{}", s),
Expand Down
49 changes: 25 additions & 24 deletions misc/multiaddr/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,36 +76,37 @@ struct Proto(Protocol<'static>);
impl Arbitrary for Proto {
fn arbitrary<G: Gen>(g: &mut G) -> Self {
use Protocol::*;
match g.gen_range(0, 24) { // TODO: Add Protocol::Quic
match g.gen_range(0, 25) { // TODO: Add Protocol::Quic
0 => Proto(Dccp(g.gen())),
1 => Proto(Dns4(Cow::Owned(SubString::arbitrary(g).0))),
2 => Proto(Dns6(Cow::Owned(SubString::arbitrary(g).0))),
3 => Proto(Http),
4 => Proto(Https),
5 => Proto(Ip4(Ipv4Addr::arbitrary(g))),
6 => Proto(Ip6(Ipv6Addr::arbitrary(g))),
7 => Proto(P2pWebRtcDirect),
8 => Proto(P2pWebRtcStar),
9 => Proto(P2pWebSocketStar),
10 => Proto(Memory(g.gen())),
1 => Proto(Dns(Cow::Owned(SubString::arbitrary(g).0))),
2 => Proto(Dns4(Cow::Owned(SubString::arbitrary(g).0))),
3 => Proto(Dns6(Cow::Owned(SubString::arbitrary(g).0))),
4 => Proto(Http),
5 => Proto(Https),
6 => Proto(Ip4(Ipv4Addr::arbitrary(g))),
7 => Proto(Ip6(Ipv6Addr::arbitrary(g))),
8 => Proto(P2pWebRtcDirect),
9 => Proto(P2pWebRtcStar),
10 => Proto(P2pWebSocketStar),
11 => Proto(Memory(g.gen())),
// TODO: impl Arbitrary for Multihash:
11 => Proto(P2p(multihash("QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC"))),
12 => Proto(P2pCircuit),
13 => Proto(Quic),
14 => Proto(Sctp(g.gen())),
15 => Proto(Tcp(g.gen())),
16 => Proto(Udp(g.gen())),
17 => Proto(Udt),
18 => Proto(Unix(Cow::Owned(SubString::arbitrary(g).0))),
19 => Proto(Utp),
20 => Proto(Ws("/".into())),
21 => Proto(Wss("/".into())),
22 => {
12 => Proto(P2p(multihash("QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC"))),
13 => Proto(P2pCircuit),
14 => Proto(Quic),
15 => Proto(Sctp(g.gen())),
16 => Proto(Tcp(g.gen())),
17 => Proto(Udp(g.gen())),
18 => Proto(Udt),
19 => Proto(Unix(Cow::Owned(SubString::arbitrary(g).0))),
20 => Proto(Utp),
21 => Proto(Ws("/".into())),
22 => Proto(Wss("/".into())),
23 => {
let mut a = [0; 10];
g.fill(&mut a);
Proto(Onion(Cow::Owned(a), g.gen_range(1, std::u16::MAX)))
},
23 => {
24 => {
let mut a = [0; 35];
g.fill_bytes(&mut a);
Proto(Onion3((a, g.gen_range(1, std::u16::MAX)).into()))
Expand Down
12 changes: 9 additions & 3 deletions transports/dns/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ where
// As an optimization, we immediately pass through if no component of the address contain
// a DNS protocol.
let contains_dns = addr.iter().any(|cmp| match cmp {
Protocol::Dns(_) => true,
Protocol::Dns4(_) => true,
Protocol::Dns6(_) => true,
_ => false,
Expand All @@ -139,7 +140,7 @@ where
trace!("Dialing address with DNS: {}", addr);
let resolve_futs = addr.iter()
.map(|cmp| match cmp {
Protocol::Dns4(ref name) | Protocol::Dns6(ref name) => {
Protocol::Dns(ref name) | Protocol::Dns4(ref name) | Protocol::Dns6(ref name) => {
let name = name.to_string();
let to_resolve = format!("{}:0", name);
let (tx, rx) = oneshot::channel();
Expand All @@ -151,7 +152,12 @@ where
});
});

let is_dns4 = if let Protocol::Dns4(_) = cmp { true } else { false };
let (dns4, dns6) = match cmp {
Protocol::Dns(_) => (true, true),
Protocol::Dns4(_) => (true, false),
Protocol::Dns6(_) => (false, true),
_ => unreachable!(),
};

async move {
let list = rx.await
Expand All @@ -166,7 +172,7 @@ where

list.into_iter()
.filter_map(|addr| {
if (is_dns4 && addr.is_ipv4()) || (!is_dns4 && addr.is_ipv6()) {
if (dns4 && addr.is_ipv4()) || (dns6 && addr.is_ipv6()) {
Some(Protocol::from(addr))
} else {
None
Expand Down
4 changes: 3 additions & 1 deletion transports/websocket/src/framed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,8 @@ fn host_and_dnsname<T>(addr: &Multiaddr) -> Result<(String, Option<webpki::DNSNa
Ok((format!("{}:{}", ip, port), None)),
(Some(Protocol::Ip6(ip)), Some(Protocol::Tcp(port))) =>
Ok((format!("{}:{}", ip, port), None)),
(Some(Protocol::Dns(h)), Some(Protocol::Tcp(port))) =>
Ok((format!("{}:{}", &h, port), Some(tls::dns_name_ref(&h)?.to_owned()))),
(Some(Protocol::Dns4(h)), Some(Protocol::Tcp(port))) =>
Ok((format!("{}:{}", &h, port), Some(tls::dns_name_ref(&h)?.to_owned()))),
(Some(Protocol::Dns6(h)), Some(Protocol::Tcp(port))) =>
Expand All @@ -371,7 +373,7 @@ fn location_to_multiaddr<T>(location: &str) -> Result<Multiaddr, Error<T>> {
let mut a = Multiaddr::empty();
match url.host() {
Some(url::Host::Domain(h)) => {
a.push(Protocol::Dns4(h.into()))
a.push(Protocol::Dns(h.into()))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same

}
Some(url::Host::Ipv4(ip)) => {
a.push(Protocol::Ip4(ip))
Expand Down