Переглянути джерело

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
Benjamin Brittain 2 роки тому
батько
коміт
f5fa089079
2 змінених файлів з 44 додано та 8 видалено
  1. 41 8
      src/socket/dns.rs
  2. 3 0
      src/wire/mod.rs

+ 41 - 8
src/socket/dns.rs

@@ -16,11 +16,20 @@ pub const MAX_ADDRESS_COUNT: usize = 4;
 pub const MAX_SERVER_COUNT: usize = 4;
 
 const DNS_PORT: u16 = 53;
+const MDNS_DNS_PORT: u16 = 5353;
 const MAX_NAME_LEN: usize = 255;
 const RETRANSMIT_DELAY: Duration = Duration::from_millis(1_000);
 const MAX_RETRANSMIT_DELAY: Duration = Duration::from_millis(10_000);
 const RETRANSMIT_TIMEOUT: Duration = Duration::from_millis(10_000); // Should generally be 2-10 secs
 
+#[cfg(feature = "proto-ipv6")]
+const MDNS_IPV6_ADDR: IpAddress = IpAddress::Ipv6(crate::wire::Ipv6Address([
+    0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb,
+]));
+
+#[cfg(feature = "proto-ipv4")]
+const MDNS_IPV4_ADDR: IpAddress = IpAddress::Ipv4(crate::wire::Ipv4Address([224, 0, 0, 251]));
+
 /// Error returned by [`Socket::start_query`]
 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -81,6 +90,7 @@ struct PendingQuery {
     delay: Duration,
 
     server_idx: usize,
+    mdns: bool,
 }
 
 #[derive(Debug)]
@@ -185,6 +195,7 @@ impl<'a> Socket<'a> {
         &mut self,
         cx: &mut Context,
         name: &str,
+        query_type: Type,
     ) -> Result<QueryHandle, StartQueryError> {
         let mut name = name.as_bytes();
 
@@ -200,6 +211,12 @@ impl<'a> Socket<'a> {
 
         let mut raw_name: Vec<u8, MAX_NAME_LEN> = Vec::new();
 
+        let mut mdns = false;
+        if name.split(|&c| c == b'.').last().unwrap() == b"local" {
+            net_trace!("Starting a mDNS query");
+            mdns = true;
+        }
+
         for s in name.split(|&c| c == b'.') {
             if s.len() > 63 {
                 net_trace!("invalid name: too long label");
@@ -224,7 +241,7 @@ impl<'a> Socket<'a> {
             .push(0x00)
             .map_err(|_| StartQueryError::NameTooLong)?;
 
-        self.start_query_raw(cx, &raw_name)
+        self.start_query_raw(cx, &raw_name, query_type, mdns)
     }
 
     /// Start a query with a raw (wire-format) DNS name.
@@ -235,19 +252,33 @@ impl<'a> Socket<'a> {
         &mut self,
         cx: &mut Context,
         raw_name: &[u8],
+        query_type: Type,
+        mdns: bool,
     ) -> Result<QueryHandle, StartQueryError> {
         let handle = self.find_free_query().ok_or(StartQueryError::NoFreeSlot)?;
 
+        if mdns {
+            // as per RFC 6762 any DNS query ending in .local. MUST be sent as mdns
+            // so we internally overwrite the servers for any of those queries
+            self.update_servers(&[
+                #[cfg(feature = "proto-ipv6")]
+                MDNS_IPV6_ADDR,
+                #[cfg(feature = "proto-ipv4")]
+                MDNS_IPV4_ADDR,
+            ]);
+        }
+
         self.queries[handle.0] = Some(DnsQuery {
             state: State::Pending(PendingQuery {
                 name: Vec::from_slice(raw_name).map_err(|_| StartQueryError::NameTooLong)?,
-                type_: Type::A,
+                type_: query_type,
                 txid: cx.rand().rand_u16(),
                 port: cx.rand().rand_source_port(),
                 delay: RETRANSMIT_DELAY,
                 timeout_at: None,
                 retransmit_at: Instant::ZERO,
                 server_idx: 0,
+                mdns,
             }),
             #[cfg(feature = "async")]
             waker: WakerRegistration::new(),
@@ -313,11 +344,12 @@ impl<'a> Socket<'a> {
     }
 
     pub(crate) fn accepts(&self, ip_repr: &IpRepr, udp_repr: &UdpRepr) -> bool {
-        udp_repr.src_port == DNS_PORT
+        (udp_repr.src_port == DNS_PORT
             && self
                 .servers
                 .iter()
-                .any(|server| *server == ip_repr.src_addr())
+                .any(|server| *server == ip_repr.src_addr()))
+            || (udp_repr.src_port == MDNS_DNS_PORT)
     }
 
     pub(crate) fn process(
@@ -500,7 +532,6 @@ impl<'a> Socket<'a> {
                     // Try next server. We check below whether we've tried all servers.
                     pq.server_idx += 1;
                 }
-
                 // Check if we've run out of servers to try.
                 if pq.server_idx >= self.servers.len() {
                     net_trace!("already tried all servers.");
@@ -526,7 +557,7 @@ impl<'a> Socket<'a> {
                     opcode: Opcode::Query,
                     question: Question {
                         name: &pq.name,
-                        type_: Type::A,
+                        type_: pq.type_,
                     },
                 };
 
@@ -534,9 +565,11 @@ impl<'a> Socket<'a> {
                 let payload = &mut payload[..repr.buffer_len()];
                 repr.emit(&mut Packet::new_unchecked(payload));
 
+                let dst_port = if pq.mdns { MDNS_DNS_PORT } else { DNS_PORT };
+
                 let udp_repr = UdpRepr {
                     src_port: pq.port,
-                    dst_port: 53,
+                    dst_port,
                 };
 
                 let dst_addr = self.servers[pq.server_idx];
@@ -550,7 +583,7 @@ impl<'a> Socket<'a> {
                 );
 
                 net_trace!(
-                    "sending {} octets to {:?}:{}",
+                    "sending {} octets to {} from port {}",
                     payload.len(),
                     ip_repr.dst_addr(),
                     udp_repr.src_port

+ 3 - 0
src/wire/mod.rs

@@ -248,6 +248,9 @@ pub use self::dhcpv4::{
     MAX_DNS_SERVER_COUNT as DHCP_MAX_DNS_SERVER_COUNT, SERVER_PORT as DHCP_SERVER_PORT,
 };
 
+#[cfg(feature = "proto-dns")]
+pub use self::dns::{Packet as DnsPacket, Repr as DnsRepr, Type as DnsQueryType};
+
 /// Parsing a packet failed.
 ///
 /// Either it is malformed, or it is not supported by smoltcp.