mirror of
https://github.com/fluencelabs/rust-libp2p
synced 2025-06-29 09:41:34 +00:00
Support /dns protocol in multiaddr (#1575)
* Add /dns protocol support to multiaddr The /dns protocol has been added to the spec and has had a de-facto meaning for years. See https://github.com/multiformats/multiaddr/pull/100 This adds address parsing and encoding support for /dns to the multiaddr format library. * Cover Dns protocol in multiaddr property tests * transports/dns: Support the /dns protocol * Support /dns protocol in address translation * Translate an FQDN URL into a /dns multiaddr * transports/websocket: Support /dns multiaddr * Use the /dns protocol in websocket redirects The whole thing with back-translating from an redirect URL looks a bit baroque, but at least now the transport does not completely ignore IPv6 addresses resolved from a hostname in a redirect URL. * Add CHANGELOG entry Co-authored-by: Pierre Krieger <pierre.krieger1708@gmail.com>
This commit is contained in:
@ -10,6 +10,11 @@
|
|||||||
- `libp2p-core`: Make the number of events buffered to/from tasks configurable.
|
- `libp2p-core`: Make the number of events buffered to/from tasks configurable.
|
||||||
[PR 1574](https://github.com/libp2p/rust-libp2p/pull/1574)
|
[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`: Added the `X25519Spec` protocol suite which uses
|
||||||
libp2p-noise-spec compliant signatures on static keys as well as the
|
libp2p-noise-spec compliant signatures on static keys as well as the
|
||||||
`/noise` protocol upgrade, hence providing a libp2p-noise-spec compliant
|
`/noise` protocol upgrade, hence providing a libp2p-noise-spec compliant
|
||||||
|
@ -37,9 +37,14 @@ use multiaddr::{Multiaddr, Protocol};
|
|||||||
/// If the first [`Protocol`]s are not IP addresses, `None` is returned instead.
|
/// If the first [`Protocol`]s are not IP addresses, `None` is returned instead.
|
||||||
pub fn address_translation(original: &Multiaddr, observed: &Multiaddr) -> Option<Multiaddr> {
|
pub fn address_translation(original: &Multiaddr, observed: &Multiaddr) -> Option<Multiaddr> {
|
||||||
original.replace(0, move |proto| match proto {
|
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::Ip4(_)) => x,
|
||||||
x @ Some(Protocol::Ip6(_)) => x,
|
x @ Some(Protocol::Ip6(_)) => x,
|
||||||
|
x @ Some(Protocol::Dns(_)) => x,
|
||||||
x @ Some(Protocol::Dns4(_)) => x,
|
x @ Some(Protocol::Dns4(_)) => x,
|
||||||
x @ Some(Protocol::Dns6(_)) => x,
|
x @ Some(Protocol::Dns6(_)) => x,
|
||||||
_ => None,
|
_ => None,
|
||||||
|
@ -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>() {
|
if let Ok(ip) = hostname.parse::<IpAddr>() {
|
||||||
Protocol::from(ip)
|
Protocol::from(ip)
|
||||||
} else {
|
} else {
|
||||||
Protocol::Dns4(hostname.into())
|
Protocol::Dns(hostname.into())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return Err(FromUrlErr::BadUrl);
|
return Err(FromUrlErr::BadUrl);
|
||||||
@ -185,31 +185,31 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn dns_addr_ws() {
|
fn dns_addr_ws() {
|
||||||
let addr = from_url("ws://example.com").unwrap();
|
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]
|
#[test]
|
||||||
fn dns_addr_http() {
|
fn dns_addr_http() {
|
||||||
let addr = from_url("http://example.com").unwrap();
|
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]
|
#[test]
|
||||||
fn dns_addr_wss() {
|
fn dns_addr_wss() {
|
||||||
let addr = from_url("wss://example.com").unwrap();
|
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]
|
#[test]
|
||||||
fn dns_addr_https() {
|
fn dns_addr_https() {
|
||||||
let addr = from_url("https://example.com").unwrap();
|
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]
|
#[test]
|
||||||
fn bad_hostname() {
|
fn bad_hostname() {
|
||||||
let addr = from_url("wss://127.0.0.1x").unwrap();
|
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]
|
#[test]
|
||||||
@ -223,7 +223,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn dns_and_port() {
|
fn dns_and_port() {
|
||||||
let addr = from_url("http://example.com:1000").unwrap();
|
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]
|
#[test]
|
||||||
|
@ -17,6 +17,7 @@ use unsigned_varint::{encode, decode};
|
|||||||
use crate::onion_addr::Onion3Addr;
|
use crate::onion_addr::Onion3Addr;
|
||||||
|
|
||||||
const DCCP: u32 = 33;
|
const DCCP: u32 = 33;
|
||||||
|
const DNS: u32 = 53;
|
||||||
const DNS4: u32 = 54;
|
const DNS4: u32 = 54;
|
||||||
const DNS6: u32 = 55;
|
const DNS6: u32 = 55;
|
||||||
const DNSADDR: u32 = 56;
|
const DNSADDR: u32 = 56;
|
||||||
@ -66,6 +67,7 @@ const PATH_SEGMENT_ENCODE_SET: &percent_encoding::AsciiSet = &percent_encoding::
|
|||||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||||
pub enum Protocol<'a> {
|
pub enum Protocol<'a> {
|
||||||
Dccp(u16),
|
Dccp(u16),
|
||||||
|
Dns(Cow<'a, str>),
|
||||||
Dns4(Cow<'a, str>),
|
Dns4(Cow<'a, str>),
|
||||||
Dns6(Cow<'a, str>),
|
Dns6(Cow<'a, str>),
|
||||||
Dnsaddr(Cow<'a, str>),
|
Dnsaddr(Cow<'a, str>),
|
||||||
@ -125,6 +127,10 @@ impl<'a> Protocol<'a> {
|
|||||||
let s = iter.next().ok_or(Error::InvalidProtocolString)?;
|
let s = iter.next().ok_or(Error::InvalidProtocolString)?;
|
||||||
Ok(Protocol::Ip6(Ipv6Addr::from_str(s)?))
|
Ok(Protocol::Ip6(Ipv6Addr::from_str(s)?))
|
||||||
}
|
}
|
||||||
|
"dns" => {
|
||||||
|
let s = iter.next().ok_or(Error::InvalidProtocolString)?;
|
||||||
|
Ok(Protocol::Dns(Cow::Borrowed(s)))
|
||||||
|
}
|
||||||
"dns4" => {
|
"dns4" => {
|
||||||
let s = iter.next().ok_or(Error::InvalidProtocolString)?;
|
let s = iter.next().ok_or(Error::InvalidProtocolString)?;
|
||||||
Ok(Protocol::Dns4(Cow::Borrowed(s)))
|
Ok(Protocol::Dns4(Cow::Borrowed(s)))
|
||||||
@ -206,6 +212,11 @@ impl<'a> Protocol<'a> {
|
|||||||
let num = rdr.read_u16::<BigEndian>()?;
|
let num = rdr.read_u16::<BigEndian>()?;
|
||||||
Ok((Protocol::Dccp(num), rest))
|
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 => {
|
DNS4 => {
|
||||||
let (n, input) = decode::usize(input)?;
|
let (n, input) = decode::usize(input)?;
|
||||||
let (data, rest) = split_at(n, input)?;
|
let (data, rest) = split_at(n, input)?;
|
||||||
@ -345,6 +356,12 @@ impl<'a> Protocol<'a> {
|
|||||||
w.write_all(encode::u32(SCTP, &mut buf))?;
|
w.write_all(encode::u32(SCTP, &mut buf))?;
|
||||||
w.write_u16::<BigEndian>(*port)?
|
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) => {
|
Protocol::Dns4(s) => {
|
||||||
w.write_all(encode::u32(DNS4, &mut buf))?;
|
w.write_all(encode::u32(DNS4, &mut buf))?;
|
||||||
let bytes = s.as_bytes();
|
let bytes = s.as_bytes();
|
||||||
@ -421,6 +438,7 @@ impl<'a> Protocol<'a> {
|
|||||||
use self::Protocol::*;
|
use self::Protocol::*;
|
||||||
match self {
|
match self {
|
||||||
Dccp(a) => Dccp(a),
|
Dccp(a) => Dccp(a),
|
||||||
|
Dns(cow) => Dns(Cow::Owned(cow.into_owned())),
|
||||||
Dns4(cow) => Dns4(Cow::Owned(cow.into_owned())),
|
Dns4(cow) => Dns4(Cow::Owned(cow.into_owned())),
|
||||||
Dns6(cow) => Dns6(Cow::Owned(cow.into_owned())),
|
Dns6(cow) => Dns6(Cow::Owned(cow.into_owned())),
|
||||||
Dnsaddr(cow) => Dnsaddr(Cow::Owned(cow.into_owned())),
|
Dnsaddr(cow) => Dnsaddr(Cow::Owned(cow.into_owned())),
|
||||||
@ -454,6 +472,7 @@ impl<'a> fmt::Display for Protocol<'a> {
|
|||||||
use self::Protocol::*;
|
use self::Protocol::*;
|
||||||
match self {
|
match self {
|
||||||
Dccp(port) => write!(f, "/dccp/{}", port),
|
Dccp(port) => write!(f, "/dccp/{}", port),
|
||||||
|
Dns(s) => write!(f, "/dns/{}", s),
|
||||||
Dns4(s) => write!(f, "/dns4/{}", s),
|
Dns4(s) => write!(f, "/dns4/{}", s),
|
||||||
Dns6(s) => write!(f, "/dns6/{}", s),
|
Dns6(s) => write!(f, "/dns6/{}", s),
|
||||||
Dnsaddr(s) => write!(f, "/dnsaddr/{}", s),
|
Dnsaddr(s) => write!(f, "/dnsaddr/{}", s),
|
||||||
|
@ -76,36 +76,37 @@ struct Proto(Protocol<'static>);
|
|||||||
impl Arbitrary for Proto {
|
impl Arbitrary for Proto {
|
||||||
fn arbitrary<G: Gen>(g: &mut G) -> Self {
|
fn arbitrary<G: Gen>(g: &mut G) -> Self {
|
||||||
use Protocol::*;
|
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())),
|
0 => Proto(Dccp(g.gen())),
|
||||||
1 => Proto(Dns4(Cow::Owned(SubString::arbitrary(g).0))),
|
1 => Proto(Dns(Cow::Owned(SubString::arbitrary(g).0))),
|
||||||
2 => Proto(Dns6(Cow::Owned(SubString::arbitrary(g).0))),
|
2 => Proto(Dns4(Cow::Owned(SubString::arbitrary(g).0))),
|
||||||
3 => Proto(Http),
|
3 => Proto(Dns6(Cow::Owned(SubString::arbitrary(g).0))),
|
||||||
4 => Proto(Https),
|
4 => Proto(Http),
|
||||||
5 => Proto(Ip4(Ipv4Addr::arbitrary(g))),
|
5 => Proto(Https),
|
||||||
6 => Proto(Ip6(Ipv6Addr::arbitrary(g))),
|
6 => Proto(Ip4(Ipv4Addr::arbitrary(g))),
|
||||||
7 => Proto(P2pWebRtcDirect),
|
7 => Proto(Ip6(Ipv6Addr::arbitrary(g))),
|
||||||
8 => Proto(P2pWebRtcStar),
|
8 => Proto(P2pWebRtcDirect),
|
||||||
9 => Proto(P2pWebSocketStar),
|
9 => Proto(P2pWebRtcStar),
|
||||||
10 => Proto(Memory(g.gen())),
|
10 => Proto(P2pWebSocketStar),
|
||||||
|
11 => Proto(Memory(g.gen())),
|
||||||
// TODO: impl Arbitrary for Multihash:
|
// TODO: impl Arbitrary for Multihash:
|
||||||
11 => Proto(P2p(multihash("QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC"))),
|
12 => Proto(P2p(multihash("QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC"))),
|
||||||
12 => Proto(P2pCircuit),
|
13 => Proto(P2pCircuit),
|
||||||
13 => Proto(Quic),
|
14 => Proto(Quic),
|
||||||
14 => Proto(Sctp(g.gen())),
|
15 => Proto(Sctp(g.gen())),
|
||||||
15 => Proto(Tcp(g.gen())),
|
16 => Proto(Tcp(g.gen())),
|
||||||
16 => Proto(Udp(g.gen())),
|
17 => Proto(Udp(g.gen())),
|
||||||
17 => Proto(Udt),
|
18 => Proto(Udt),
|
||||||
18 => Proto(Unix(Cow::Owned(SubString::arbitrary(g).0))),
|
19 => Proto(Unix(Cow::Owned(SubString::arbitrary(g).0))),
|
||||||
19 => Proto(Utp),
|
20 => Proto(Utp),
|
||||||
20 => Proto(Ws("/".into())),
|
21 => Proto(Ws("/".into())),
|
||||||
21 => Proto(Wss("/".into())),
|
22 => Proto(Wss("/".into())),
|
||||||
22 => {
|
23 => {
|
||||||
let mut a = [0; 10];
|
let mut a = [0; 10];
|
||||||
g.fill(&mut a);
|
g.fill(&mut a);
|
||||||
Proto(Onion(Cow::Owned(a), g.gen_range(1, std::u16::MAX)))
|
Proto(Onion(Cow::Owned(a), g.gen_range(1, std::u16::MAX)))
|
||||||
},
|
},
|
||||||
23 => {
|
24 => {
|
||||||
let mut a = [0; 35];
|
let mut a = [0; 35];
|
||||||
g.fill_bytes(&mut a);
|
g.fill_bytes(&mut a);
|
||||||
Proto(Onion3((a, g.gen_range(1, std::u16::MAX)).into()))
|
Proto(Onion3((a, g.gen_range(1, std::u16::MAX)).into()))
|
||||||
|
@ -124,6 +124,7 @@ where
|
|||||||
// As an optimization, we immediately pass through if no component of the address contain
|
// As an optimization, we immediately pass through if no component of the address contain
|
||||||
// a DNS protocol.
|
// a DNS protocol.
|
||||||
let contains_dns = addr.iter().any(|cmp| match cmp {
|
let contains_dns = addr.iter().any(|cmp| match cmp {
|
||||||
|
Protocol::Dns(_) => true,
|
||||||
Protocol::Dns4(_) => true,
|
Protocol::Dns4(_) => true,
|
||||||
Protocol::Dns6(_) => true,
|
Protocol::Dns6(_) => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
@ -139,7 +140,7 @@ where
|
|||||||
trace!("Dialing address with DNS: {}", addr);
|
trace!("Dialing address with DNS: {}", addr);
|
||||||
let resolve_futs = addr.iter()
|
let resolve_futs = addr.iter()
|
||||||
.map(|cmp| match cmp {
|
.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 name = name.to_string();
|
||||||
let to_resolve = format!("{}:0", name);
|
let to_resolve = format!("{}:0", name);
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
@ -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 {
|
async move {
|
||||||
let list = rx.await
|
let list = rx.await
|
||||||
@ -166,7 +172,7 @@ where
|
|||||||
|
|
||||||
list.into_iter()
|
list.into_iter()
|
||||||
.filter_map(|addr| {
|
.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))
|
Some(Protocol::from(addr))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -353,6 +353,8 @@ fn host_and_dnsname<T>(addr: &Multiaddr) -> Result<(String, Option<webpki::DNSNa
|
|||||||
Ok((format!("{}:{}", ip, port), None)),
|
Ok((format!("{}:{}", ip, port), None)),
|
||||||
(Some(Protocol::Ip6(ip)), Some(Protocol::Tcp(port))) =>
|
(Some(Protocol::Ip6(ip)), Some(Protocol::Tcp(port))) =>
|
||||||
Ok((format!("{}:{}", ip, port), None)),
|
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))) =>
|
(Some(Protocol::Dns4(h)), Some(Protocol::Tcp(port))) =>
|
||||||
Ok((format!("{}:{}", &h, port), Some(tls::dns_name_ref(&h)?.to_owned()))),
|
Ok((format!("{}:{}", &h, port), Some(tls::dns_name_ref(&h)?.to_owned()))),
|
||||||
(Some(Protocol::Dns6(h)), Some(Protocol::Tcp(port))) =>
|
(Some(Protocol::Dns6(h)), Some(Protocol::Tcp(port))) =>
|
||||||
@ -371,7 +373,7 @@ fn location_to_multiaddr<T>(location: &str) -> Result<Multiaddr, Error<T>> {
|
|||||||
let mut a = Multiaddr::empty();
|
let mut a = Multiaddr::empty();
|
||||||
match url.host() {
|
match url.host() {
|
||||||
Some(url::Host::Domain(h)) => {
|
Some(url::Host::Domain(h)) => {
|
||||||
a.push(Protocol::Dns4(h.into()))
|
a.push(Protocol::Dns(h.into()))
|
||||||
}
|
}
|
||||||
Some(url::Host::Ipv4(ip)) => {
|
Some(url::Host::Ipv4(ip)) => {
|
||||||
a.push(Protocol::Ip4(ip))
|
a.push(Protocol::Ip4(ip))
|
||||||
|
Reference in New Issue
Block a user