Skip to content

Commit f5fa089

Browse files
committed
Adds one-shot mDNS resolution
RFC 6762 Section 5.1 specifies a one-shot multicast DNS query. This query has minimal differences from a standard DNS query, mostly just using a multicast address and a different port (5353 vs 53). A fully standards compliant mDNS implementation would use UDP source port 5353 as well to issue queries, however we MUST NOT use that port and continue using an ephemeral port until features such as service discovery are implemented. This change also allows specifying what kind of DNS query we wish to perform. https://www.rfc-editor.org/rfc/rfc6762#section-5.1
1 parent 02535a7 commit f5fa089

File tree

2 files changed

+44
-8
lines changed

2 files changed

+44
-8
lines changed

src/socket/dns.rs

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,20 @@ pub const MAX_ADDRESS_COUNT: usize = 4;
1616
pub const MAX_SERVER_COUNT: usize = 4;
1717

1818
const DNS_PORT: u16 = 53;
19+
const MDNS_DNS_PORT: u16 = 5353;
1920
const MAX_NAME_LEN: usize = 255;
2021
const RETRANSMIT_DELAY: Duration = Duration::from_millis(1_000);
2122
const MAX_RETRANSMIT_DELAY: Duration = Duration::from_millis(10_000);
2223
const RETRANSMIT_TIMEOUT: Duration = Duration::from_millis(10_000); // Should generally be 2-10 secs
2324

25+
#[cfg(feature = "proto-ipv6")]
26+
const MDNS_IPV6_ADDR: IpAddress = IpAddress::Ipv6(crate::wire::Ipv6Address([
27+
0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb,
28+
]));
29+
30+
#[cfg(feature = "proto-ipv4")]
31+
const MDNS_IPV4_ADDR: IpAddress = IpAddress::Ipv4(crate::wire::Ipv4Address([224, 0, 0, 251]));
32+
2433
/// Error returned by [`Socket::start_query`]
2534
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
2635
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -81,6 +90,7 @@ struct PendingQuery {
8190
delay: Duration,
8291

8392
server_idx: usize,
93+
mdns: bool,
8494
}
8595

8696
#[derive(Debug)]
@@ -185,6 +195,7 @@ impl<'a> Socket<'a> {
185195
&mut self,
186196
cx: &mut Context,
187197
name: &str,
198+
query_type: Type,
188199
) -> Result<QueryHandle, StartQueryError> {
189200
let mut name = name.as_bytes();
190201

@@ -200,6 +211,12 @@ impl<'a> Socket<'a> {
200211

201212
let mut raw_name: Vec<u8, MAX_NAME_LEN> = Vec::new();
202213

214+
let mut mdns = false;
215+
if name.split(|&c| c == b'.').last().unwrap() == b"local" {
216+
net_trace!("Starting a mDNS query");
217+
mdns = true;
218+
}
219+
203220
for s in name.split(|&c| c == b'.') {
204221
if s.len() > 63 {
205222
net_trace!("invalid name: too long label");
@@ -224,7 +241,7 @@ impl<'a> Socket<'a> {
224241
.push(0x00)
225242
.map_err(|_| StartQueryError::NameTooLong)?;
226243

227-
self.start_query_raw(cx, &raw_name)
244+
self.start_query_raw(cx, &raw_name, query_type, mdns)
228245
}
229246

230247
/// Start a query with a raw (wire-format) DNS name.
@@ -235,19 +252,33 @@ impl<'a> Socket<'a> {
235252
&mut self,
236253
cx: &mut Context,
237254
raw_name: &[u8],
255+
query_type: Type,
256+
mdns: bool,
238257
) -> Result<QueryHandle, StartQueryError> {
239258
let handle = self.find_free_query().ok_or(StartQueryError::NoFreeSlot)?;
240259

260+
if mdns {
261+
// as per RFC 6762 any DNS query ending in .local. MUST be sent as mdns
262+
// so we internally overwrite the servers for any of those queries
263+
self.update_servers(&[
264+
#[cfg(feature = "proto-ipv6")]
265+
MDNS_IPV6_ADDR,
266+
#[cfg(feature = "proto-ipv4")]
267+
MDNS_IPV4_ADDR,
268+
]);
269+
}
270+
241271
self.queries[handle.0] = Some(DnsQuery {
242272
state: State::Pending(PendingQuery {
243273
name: Vec::from_slice(raw_name).map_err(|_| StartQueryError::NameTooLong)?,
244-
type_: Type::A,
274+
type_: query_type,
245275
txid: cx.rand().rand_u16(),
246276
port: cx.rand().rand_source_port(),
247277
delay: RETRANSMIT_DELAY,
248278
timeout_at: None,
249279
retransmit_at: Instant::ZERO,
250280
server_idx: 0,
281+
mdns,
251282
}),
252283
#[cfg(feature = "async")]
253284
waker: WakerRegistration::new(),
@@ -313,11 +344,12 @@ impl<'a> Socket<'a> {
313344
}
314345

315346
pub(crate) fn accepts(&self, ip_repr: &IpRepr, udp_repr: &UdpRepr) -> bool {
316-
udp_repr.src_port == DNS_PORT
347+
(udp_repr.src_port == DNS_PORT
317348
&& self
318349
.servers
319350
.iter()
320-
.any(|server| *server == ip_repr.src_addr())
351+
.any(|server| *server == ip_repr.src_addr()))
352+
|| (udp_repr.src_port == MDNS_DNS_PORT)
321353
}
322354

323355
pub(crate) fn process(
@@ -500,7 +532,6 @@ impl<'a> Socket<'a> {
500532
// Try next server. We check below whether we've tried all servers.
501533
pq.server_idx += 1;
502534
}
503-
504535
// Check if we've run out of servers to try.
505536
if pq.server_idx >= self.servers.len() {
506537
net_trace!("already tried all servers.");
@@ -526,17 +557,19 @@ impl<'a> Socket<'a> {
526557
opcode: Opcode::Query,
527558
question: Question {
528559
name: &pq.name,
529-
type_: Type::A,
560+
type_: pq.type_,
530561
},
531562
};
532563

533564
let mut payload = [0u8; 512];
534565
let payload = &mut payload[..repr.buffer_len()];
535566
repr.emit(&mut Packet::new_unchecked(payload));
536567

568+
let dst_port = if pq.mdns { MDNS_DNS_PORT } else { DNS_PORT };
569+
537570
let udp_repr = UdpRepr {
538571
src_port: pq.port,
539-
dst_port: 53,
572+
dst_port,
540573
};
541574

542575
let dst_addr = self.servers[pq.server_idx];
@@ -550,7 +583,7 @@ impl<'a> Socket<'a> {
550583
);
551584

552585
net_trace!(
553-
"sending {} octets to {:?}:{}",
586+
"sending {} octets to {} from port {}",
554587
payload.len(),
555588
ip_repr.dst_addr(),
556589
udp_repr.src_port

src/wire/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,9 @@ pub use self::dhcpv4::{
248248
MAX_DNS_SERVER_COUNT as DHCP_MAX_DNS_SERVER_COUNT, SERVER_PORT as DHCP_SERVER_PORT,
249249
};
250250

251+
#[cfg(feature = "proto-dns")]
252+
pub use self::dns::{Packet as DnsPacket, Repr as DnsRepr, Type as DnsQueryType};
253+
251254
/// Parsing a packet failed.
252255
///
253256
/// Either it is malformed, or it is not supported by smoltcp.

0 commit comments

Comments
 (0)