Skip to content

Commit 82156de

Browse files
mzabaluevtomaka
andauthored
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 multiformats/multiaddr#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 <[email protected]>
1 parent 3a96ebf commit 82156de

File tree

7 files changed

+74
-36
lines changed

7 files changed

+74
-36
lines changed

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@
1010
- `libp2p-core`: Make the number of events buffered to/from tasks configurable.
1111
[PR 1574](https://github.com/libp2p/rust-libp2p/pull/1574)
1212

13+
- `libp2p-dns`, `parity-multiaddr`: Added support for the `/dns` multiaddr
14+
protocol. Additionally, the `multiaddr::from_url` function will now use
15+
`/dns` instead of `/dns4`.
16+
[PR 1575](https://github.com/libp2p/rust-libp2p/pull/1575)
17+
1318
- `libp2p-noise`: Added the `X25519Spec` protocol suite which uses
1419
libp2p-noise-spec compliant signatures on static keys as well as the
1520
`/noise` protocol upgrade, hence providing a libp2p-noise-spec compliant

core/src/translation.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,14 @@ use multiaddr::{Multiaddr, Protocol};
3737
/// If the first [`Protocol`]s are not IP addresses, `None` is returned instead.
3838
pub fn address_translation(original: &Multiaddr, observed: &Multiaddr) -> Option<Multiaddr> {
3939
original.replace(0, move |proto| match proto {
40-
Protocol::Ip4(_) | Protocol::Ip6(_) | Protocol::Dns4(_) | Protocol::Dns6(_) => match observed.iter().next() {
40+
Protocol::Ip4(_)
41+
| Protocol::Ip6(_)
42+
| Protocol::Dns(_)
43+
| Protocol::Dns4(_)
44+
| Protocol::Dns6(_) => match observed.iter().next() {
4145
x @ Some(Protocol::Ip4(_)) => x,
4246
x @ Some(Protocol::Ip6(_)) => x,
47+
x @ Some(Protocol::Dns(_)) => x,
4348
x @ Some(Protocol::Dns4(_)) => x,
4449
x @ Some(Protocol::Dns6(_)) => x,
4550
_ => None,

misc/multiaddr/src/from_url.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ fn from_url_inner_http_ws(url: url::Url, lossy: bool) -> std::result::Result<Mul
7070
if let Ok(ip) = hostname.parse::<IpAddr>() {
7171
Protocol::from(ip)
7272
} else {
73-
Protocol::Dns4(hostname.into())
73+
Protocol::Dns(hostname.into())
7474
}
7575
} else {
7676
return Err(FromUrlErr::BadUrl);
@@ -185,31 +185,31 @@ mod tests {
185185
#[test]
186186
fn dns_addr_ws() {
187187
let addr = from_url("ws://example.com").unwrap();
188-
assert_eq!(addr, "/dns4/example.com/tcp/80/ws".parse().unwrap());
188+
assert_eq!(addr, "/dns/example.com/tcp/80/ws".parse().unwrap());
189189
}
190190

191191
#[test]
192192
fn dns_addr_http() {
193193
let addr = from_url("http://example.com").unwrap();
194-
assert_eq!(addr, "/dns4/example.com/tcp/80/http".parse().unwrap());
194+
assert_eq!(addr, "/dns/example.com/tcp/80/http".parse().unwrap());
195195
}
196196

197197
#[test]
198198
fn dns_addr_wss() {
199199
let addr = from_url("wss://example.com").unwrap();
200-
assert_eq!(addr, "/dns4/example.com/tcp/443/wss".parse().unwrap());
200+
assert_eq!(addr, "/dns/example.com/tcp/443/wss".parse().unwrap());
201201
}
202202

203203
#[test]
204204
fn dns_addr_https() {
205205
let addr = from_url("https://example.com").unwrap();
206-
assert_eq!(addr, "/dns4/example.com/tcp/443/https".parse().unwrap());
206+
assert_eq!(addr, "/dns/example.com/tcp/443/https".parse().unwrap());
207207
}
208208

209209
#[test]
210210
fn bad_hostname() {
211211
let addr = from_url("wss://127.0.0.1x").unwrap();
212-
assert_eq!(addr, "/dns4/127.0.0.1x/tcp/443/wss".parse().unwrap());
212+
assert_eq!(addr, "/dns/127.0.0.1x/tcp/443/wss".parse().unwrap());
213213
}
214214

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

229229
#[test]

misc/multiaddr/src/protocol.rs

+19
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use unsigned_varint::{encode, decode};
1717
use crate::onion_addr::Onion3Addr;
1818

1919
const DCCP: u32 = 33;
20+
const DNS: u32 = 53;
2021
const DNS4: u32 = 54;
2122
const DNS6: u32 = 55;
2223
const DNSADDR: u32 = 56;
@@ -66,6 +67,7 @@ const PATH_SEGMENT_ENCODE_SET: &percent_encoding::AsciiSet = &percent_encoding::
6667
#[derive(PartialEq, Eq, Clone, Debug)]
6768
pub enum Protocol<'a> {
6869
Dccp(u16),
70+
Dns(Cow<'a, str>),
6971
Dns4(Cow<'a, str>),
7072
Dns6(Cow<'a, str>),
7173
Dnsaddr(Cow<'a, str>),
@@ -125,6 +127,10 @@ impl<'a> Protocol<'a> {
125127
let s = iter.next().ok_or(Error::InvalidProtocolString)?;
126128
Ok(Protocol::Ip6(Ipv6Addr::from_str(s)?))
127129
}
130+
"dns" => {
131+
let s = iter.next().ok_or(Error::InvalidProtocolString)?;
132+
Ok(Protocol::Dns(Cow::Borrowed(s)))
133+
}
128134
"dns4" => {
129135
let s = iter.next().ok_or(Error::InvalidProtocolString)?;
130136
Ok(Protocol::Dns4(Cow::Borrowed(s)))
@@ -206,6 +212,11 @@ impl<'a> Protocol<'a> {
206212
let num = rdr.read_u16::<BigEndian>()?;
207213
Ok((Protocol::Dccp(num), rest))
208214
}
215+
DNS => {
216+
let (n, input) = decode::usize(input)?;
217+
let (data, rest) = split_at(n, input)?;
218+
Ok((Protocol::Dns(Cow::Borrowed(str::from_utf8(data)?)), rest))
219+
}
209220
DNS4 => {
210221
let (n, input) = decode::usize(input)?;
211222
let (data, rest) = split_at(n, input)?;
@@ -345,6 +356,12 @@ impl<'a> Protocol<'a> {
345356
w.write_all(encode::u32(SCTP, &mut buf))?;
346357
w.write_u16::<BigEndian>(*port)?
347358
}
359+
Protocol::Dns(s) => {
360+
w.write_all(encode::u32(DNS, &mut buf))?;
361+
let bytes = s.as_bytes();
362+
w.write_all(encode::usize(bytes.len(), &mut encode::usize_buffer()))?;
363+
w.write_all(&bytes)?
364+
}
348365
Protocol::Dns4(s) => {
349366
w.write_all(encode::u32(DNS4, &mut buf))?;
350367
let bytes = s.as_bytes();
@@ -421,6 +438,7 @@ impl<'a> Protocol<'a> {
421438
use self::Protocol::*;
422439
match self {
423440
Dccp(a) => Dccp(a),
441+
Dns(cow) => Dns(Cow::Owned(cow.into_owned())),
424442
Dns4(cow) => Dns4(Cow::Owned(cow.into_owned())),
425443
Dns6(cow) => Dns6(Cow::Owned(cow.into_owned())),
426444
Dnsaddr(cow) => Dnsaddr(Cow::Owned(cow.into_owned())),
@@ -454,6 +472,7 @@ impl<'a> fmt::Display for Protocol<'a> {
454472
use self::Protocol::*;
455473
match self {
456474
Dccp(port) => write!(f, "/dccp/{}", port),
475+
Dns(s) => write!(f, "/dns/{}", s),
457476
Dns4(s) => write!(f, "/dns4/{}", s),
458477
Dns6(s) => write!(f, "/dns6/{}", s),
459478
Dnsaddr(s) => write!(f, "/dnsaddr/{}", s),

misc/multiaddr/tests/lib.rs

+25-24
Original file line numberDiff line numberDiff line change
@@ -76,36 +76,37 @@ struct Proto(Protocol<'static>);
7676
impl Arbitrary for Proto {
7777
fn arbitrary<G: Gen>(g: &mut G) -> Self {
7878
use Protocol::*;
79-
match g.gen_range(0, 24) { // TODO: Add Protocol::Quic
79+
match g.gen_range(0, 25) { // TODO: Add Protocol::Quic
8080
0 => Proto(Dccp(g.gen())),
81-
1 => Proto(Dns4(Cow::Owned(SubString::arbitrary(g).0))),
82-
2 => Proto(Dns6(Cow::Owned(SubString::arbitrary(g).0))),
83-
3 => Proto(Http),
84-
4 => Proto(Https),
85-
5 => Proto(Ip4(Ipv4Addr::arbitrary(g))),
86-
6 => Proto(Ip6(Ipv6Addr::arbitrary(g))),
87-
7 => Proto(P2pWebRtcDirect),
88-
8 => Proto(P2pWebRtcStar),
89-
9 => Proto(P2pWebSocketStar),
90-
10 => Proto(Memory(g.gen())),
81+
1 => Proto(Dns(Cow::Owned(SubString::arbitrary(g).0))),
82+
2 => Proto(Dns4(Cow::Owned(SubString::arbitrary(g).0))),
83+
3 => Proto(Dns6(Cow::Owned(SubString::arbitrary(g).0))),
84+
4 => Proto(Http),
85+
5 => Proto(Https),
86+
6 => Proto(Ip4(Ipv4Addr::arbitrary(g))),
87+
7 => Proto(Ip6(Ipv6Addr::arbitrary(g))),
88+
8 => Proto(P2pWebRtcDirect),
89+
9 => Proto(P2pWebRtcStar),
90+
10 => Proto(P2pWebSocketStar),
91+
11 => Proto(Memory(g.gen())),
9192
// TODO: impl Arbitrary for Multihash:
92-
11 => Proto(P2p(multihash("QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC"))),
93-
12 => Proto(P2pCircuit),
94-
13 => Proto(Quic),
95-
14 => Proto(Sctp(g.gen())),
96-
15 => Proto(Tcp(g.gen())),
97-
16 => Proto(Udp(g.gen())),
98-
17 => Proto(Udt),
99-
18 => Proto(Unix(Cow::Owned(SubString::arbitrary(g).0))),
100-
19 => Proto(Utp),
101-
20 => Proto(Ws("/".into())),
102-
21 => Proto(Wss("/".into())),
103-
22 => {
93+
12 => Proto(P2p(multihash("QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC"))),
94+
13 => Proto(P2pCircuit),
95+
14 => Proto(Quic),
96+
15 => Proto(Sctp(g.gen())),
97+
16 => Proto(Tcp(g.gen())),
98+
17 => Proto(Udp(g.gen())),
99+
18 => Proto(Udt),
100+
19 => Proto(Unix(Cow::Owned(SubString::arbitrary(g).0))),
101+
20 => Proto(Utp),
102+
21 => Proto(Ws("/".into())),
103+
22 => Proto(Wss("/".into())),
104+
23 => {
104105
let mut a = [0; 10];
105106
g.fill(&mut a);
106107
Proto(Onion(Cow::Owned(a), g.gen_range(1, std::u16::MAX)))
107108
},
108-
23 => {
109+
24 => {
109110
let mut a = [0; 35];
110111
g.fill_bytes(&mut a);
111112
Proto(Onion3((a, g.gen_range(1, std::u16::MAX)).into()))

transports/dns/src/lib.rs

+9-3
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ where
124124
// As an optimization, we immediately pass through if no component of the address contain
125125
// a DNS protocol.
126126
let contains_dns = addr.iter().any(|cmp| match cmp {
127+
Protocol::Dns(_) => true,
127128
Protocol::Dns4(_) => true,
128129
Protocol::Dns6(_) => true,
129130
_ => false,
@@ -139,7 +140,7 @@ where
139140
trace!("Dialing address with DNS: {}", addr);
140141
let resolve_futs = addr.iter()
141142
.map(|cmp| match cmp {
142-
Protocol::Dns4(ref name) | Protocol::Dns6(ref name) => {
143+
Protocol::Dns(ref name) | Protocol::Dns4(ref name) | Protocol::Dns6(ref name) => {
143144
let name = name.to_string();
144145
let to_resolve = format!("{}:0", name);
145146
let (tx, rx) = oneshot::channel();
@@ -151,7 +152,12 @@ where
151152
});
152153
});
153154

154-
let is_dns4 = if let Protocol::Dns4(_) = cmp { true } else { false };
155+
let (dns4, dns6) = match cmp {
156+
Protocol::Dns(_) => (true, true),
157+
Protocol::Dns4(_) => (true, false),
158+
Protocol::Dns6(_) => (false, true),
159+
_ => unreachable!(),
160+
};
155161

156162
async move {
157163
let list = rx.await
@@ -166,7 +172,7 @@ where
166172

167173
list.into_iter()
168174
.filter_map(|addr| {
169-
if (is_dns4 && addr.is_ipv4()) || (!is_dns4 && addr.is_ipv6()) {
175+
if (dns4 && addr.is_ipv4()) || (dns6 && addr.is_ipv6()) {
170176
Some(Protocol::from(addr))
171177
} else {
172178
None

transports/websocket/src/framed.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,8 @@ fn host_and_dnsname<T>(addr: &Multiaddr) -> Result<(String, Option<webpki::DNSNa
353353
Ok((format!("{}:{}", ip, port), None)),
354354
(Some(Protocol::Ip6(ip)), Some(Protocol::Tcp(port))) =>
355355
Ok((format!("{}:{}", ip, port), None)),
356+
(Some(Protocol::Dns(h)), Some(Protocol::Tcp(port))) =>
357+
Ok((format!("{}:{}", &h, port), Some(tls::dns_name_ref(&h)?.to_owned()))),
356358
(Some(Protocol::Dns4(h)), Some(Protocol::Tcp(port))) =>
357359
Ok((format!("{}:{}", &h, port), Some(tls::dns_name_ref(&h)?.to_owned()))),
358360
(Some(Protocol::Dns6(h)), Some(Protocol::Tcp(port))) =>
@@ -371,7 +373,7 @@ fn location_to_multiaddr<T>(location: &str) -> Result<Multiaddr, Error<T>> {
371373
let mut a = Multiaddr::empty();
372374
match url.host() {
373375
Some(url::Host::Domain(h)) => {
374-
a.push(Protocol::Dns4(h.into()))
376+
a.push(Protocol::Dns(h.into()))
375377
}
376378
Some(url::Host::Ipv4(ip)) => {
377379
a.push(Protocol::Ip4(ip))

0 commit comments

Comments
 (0)