Skip to main content

mdns_sd/
dns_parser.rs

1//! DNS parsing utility.
2//!
3//! [DnsIncoming] is the logic representation of an incoming DNS packet.
4//! [DnsOutgoing] is the logic representation of an outgoing DNS message of one or more packets.
5//! [DnsOutPacket] is the encoded one packet for [DnsOutgoing].
6
7#[cfg(feature = "logging")]
8use crate::log::{debug, trace};
9
10use crate::current_time_millis;
11use crate::error::{e_fmt, Error, Result};
12use crate::service_info::{is_unicast_link_local, DnsRegistry, MyIntf, ServiceInfo};
13
14use if_addrs::Interface;
15
16#[cfg(feature = "serde")]
17use serde::{Deserialize, Serialize};
18
19use std::{
20    any::Any,
21    cmp,
22    collections::HashMap,
23    convert::TryInto,
24    fmt,
25    hash::Hash,
26    net::{IpAddr, Ipv4Addr, Ipv6Addr},
27    str,
28};
29
30/// Represents a network interface identifier defined by the OS.
31#[derive(Clone, Debug, Eq, Hash, PartialEq, Default)]
32#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
33pub struct InterfaceId {
34    /// Interface name, e.g. "en0", "wlan0", etc.
35    pub name: String,
36
37    /// Interface index assigned by the OS, e.g. 1, 2, etc.
38    pub index: u32,
39}
40
41impl InterfaceId {
42    /// Returns all IP addresses associated with this interface by querying the OS.
43    pub fn get_addrs(&self) -> Vec<IpAddr> {
44        if_addrs::get_if_addrs()
45            .unwrap_or_default()
46            .into_iter()
47            .filter(|iface| iface.index == Some(self.index))
48            .map(|iface| iface.ip())
49            .collect()
50    }
51}
52
53impl fmt::Display for InterfaceId {
54    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55        write!(f, "{}('{}')", self.index, self.name)
56    }
57}
58
59impl From<&Interface> for InterfaceId {
60    fn from(interface: &Interface) -> Self {
61        InterfaceId {
62            name: interface.name.clone(),
63            index: interface.index.unwrap_or_default(),
64        }
65    }
66}
67
68/// An IPv4 address with interface identifiers indicating which interfaces discovered it.
69#[derive(Debug, Clone, Eq, PartialEq, Hash)]
70#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
71pub struct ScopedIpV4 {
72    addr: Ipv4Addr,
73    /// The interfaces this address was discovered on.
74    interface_ids: Vec<InterfaceId>,
75}
76
77impl ScopedIpV4 {
78    /// Creates a new `ScopedIpV4` with a single interface identifier.
79    pub fn new(addr: Ipv4Addr, interface_id: InterfaceId) -> Self {
80        Self {
81            addr,
82            interface_ids: vec![interface_id],
83        }
84    }
85
86    /// Returns the IPv4 address.
87    pub const fn addr(&self) -> &Ipv4Addr {
88        &self.addr
89    }
90
91    /// Returns the interfaces this address was discovered on.
92    pub fn interface_ids(&self) -> &[InterfaceId] {
93        &self.interface_ids
94    }
95
96    /// Adds an interface identifier if not already present.
97    pub(crate) fn add_interface_id(&mut self, id: InterfaceId) {
98        if !self.interface_ids.contains(&id) {
99            self.interface_ids.push(id);
100        }
101    }
102}
103
104/// An IPv6 address with scope_id (interface identifier).
105#[derive(Debug, Clone, Eq, PartialEq, Hash)]
106#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
107pub struct ScopedIpV6 {
108    addr: Ipv6Addr,
109    scope_id: InterfaceId,
110}
111
112impl ScopedIpV6 {
113    /// Returns the IPv6 address.
114    pub const fn addr(&self) -> &Ipv6Addr {
115        &self.addr
116    }
117
118    /// Returns the scope_id for this IPv6 address.
119    pub const fn scope_id(&self) -> &InterfaceId {
120        &self.scope_id
121    }
122}
123
124/// An IP address, either IPv4 or IPv6, that supports scope_id for IPv6.
125#[derive(Debug, Clone, Eq, PartialEq, Hash)]
126#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
127#[non_exhaustive]
128pub enum ScopedIp {
129    V4(ScopedIpV4),
130    V6(ScopedIpV6),
131}
132
133impl ScopedIp {
134    pub const fn to_ip_addr(&self) -> IpAddr {
135        match self {
136            ScopedIp::V4(v4) => IpAddr::V4(v4.addr),
137            ScopedIp::V6(v6) => IpAddr::V6(v6.addr),
138        }
139    }
140
141    pub const fn is_ipv4(&self) -> bool {
142        matches!(self, ScopedIp::V4(_))
143    }
144
145    pub const fn is_ipv6(&self) -> bool {
146        matches!(self, ScopedIp::V6(_))
147    }
148
149    pub const fn is_loopback(&self) -> bool {
150        match self {
151            ScopedIp::V4(v4) => v4.addr.is_loopback(),
152            ScopedIp::V6(v6) => v6.addr.is_loopback(),
153        }
154    }
155}
156
157impl From<IpAddr> for ScopedIp {
158    fn from(ip: IpAddr) -> Self {
159        match ip {
160            IpAddr::V4(v4) => ScopedIp::V4(ScopedIpV4 {
161                addr: v4,
162                interface_ids: vec![],
163            }),
164            IpAddr::V6(v6) => ScopedIp::V6(ScopedIpV6 {
165                addr: v6,
166                scope_id: InterfaceId::default(),
167            }),
168        }
169    }
170}
171
172impl From<&Interface> for ScopedIp {
173    fn from(interface: &Interface) -> Self {
174        match interface.ip() {
175            IpAddr::V4(v4) => ScopedIp::V4(ScopedIpV4 {
176                addr: v4,
177                interface_ids: vec![InterfaceId::from(interface)],
178            }),
179            IpAddr::V6(v6) => ScopedIp::V6(ScopedIpV6 {
180                addr: v6,
181                scope_id: InterfaceId::from(interface),
182            }),
183        }
184    }
185}
186
187impl fmt::Display for ScopedIp {
188    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
189        match self {
190            ScopedIp::V4(v4) => write!(f, "{}", v4.addr),
191            ScopedIp::V6(v6) => {
192                if v6.scope_id.index != 0 && is_unicast_link_local(&v6.addr) {
193                    #[cfg(windows)]
194                    {
195                        write!(f, "{}%{}", v6.addr, v6.scope_id.index)
196                    }
197                    #[cfg(not(windows))]
198                    {
199                        write!(f, "{}%{}", v6.addr, v6.scope_id.name)
200                    }
201                } else {
202                    write!(f, "{}", v6.addr)
203                }
204            }
205        }
206    }
207}
208
209/// DNS resource record types, stored as `u16`. Can do `as u16` when needed.
210///
211/// See [RFC 1035 section 3.2.2](https://datatracker.ietf.org/doc/html/rfc1035#section-3.2.2)
212#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
213#[non_exhaustive]
214#[repr(u16)]
215pub enum RRType {
216    /// DNS record type for IPv4 address
217    A = 1,
218
219    /// DNS record type for Canonical Name
220    CNAME = 5,
221
222    /// DNS record type for Pointer
223    PTR = 12,
224
225    /// DNS record type for Host Info
226    HINFO = 13,
227
228    /// DNS record type for Text (properties)
229    TXT = 16,
230
231    /// DNS record type for IPv6 address
232    AAAA = 28,
233
234    /// DNS record type for Service
235    SRV = 33,
236
237    /// DNS record type for Negative Responses
238    NSEC = 47,
239
240    /// DNS record type for any records (wildcard)
241    ANY = 255,
242}
243
244impl RRType {
245    /// Converts `u16` into `RRType` if possible.
246    pub const fn from_u16(value: u16) -> Option<Self> {
247        match value {
248            1 => Some(RRType::A),
249            5 => Some(RRType::CNAME),
250            12 => Some(RRType::PTR),
251            13 => Some(RRType::HINFO),
252            16 => Some(RRType::TXT),
253            28 => Some(RRType::AAAA),
254            33 => Some(RRType::SRV),
255            47 => Some(RRType::NSEC),
256            255 => Some(RRType::ANY),
257            _ => None,
258        }
259    }
260}
261
262impl fmt::Display for RRType {
263    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
264        match self {
265            RRType::A => write!(f, "TYPE_A"),
266            RRType::CNAME => write!(f, "TYPE_CNAME"),
267            RRType::PTR => write!(f, "TYPE_PTR"),
268            RRType::HINFO => write!(f, "TYPE_HINFO"),
269            RRType::TXT => write!(f, "TYPE_TXT"),
270            RRType::AAAA => write!(f, "TYPE_AAAA"),
271            RRType::SRV => write!(f, "TYPE_SRV"),
272            RRType::NSEC => write!(f, "TYPE_NSEC"),
273            RRType::ANY => write!(f, "TYPE_ANY"),
274        }
275    }
276}
277
278/// The class value for the Internet.
279pub const CLASS_IN: u16 = 1;
280pub const CLASS_MASK: u16 = 0x7FFF;
281
282/// Cache-flush bit: the most significant bit of the rrclass field of the resource record.  
283pub const CLASS_CACHE_FLUSH: u16 = 0x8000;
284
285/// Absolute max size of UDP datagram payload for an mDNS packet over IPv4.
286///
287/// RFC 6762 section 17:
288/// "Even when fragmentation is used, a Multicast DNS packet, including IP and UDP
289/// headers, MUST NOT exceed 9000 bytes."
290///
291/// It is calculated as: 9000 bytes - IPv4 header 20 bytes - UDP header 8 bytes.
292pub(crate) const MAX_PKT_ABSOLUTE_IPV4: usize = 8972;
293
294/// Absolute max size of UDP datagram payload for an mDNS packet over IPv6.
295///
296/// Same 9000-byte ceiling as [`MAX_PKT_ABSOLUTE_IPV4`], less the bigger IPv6 header:
297/// 9000 bytes - IPv6 header 40 bytes - UDP header 8 bytes.
298pub(crate) const MAX_PKT_ABSOLUTE_IPV6: usize = 8952;
299
300/// Absolute max size of an mDNS packet for the given IP version.
301pub(crate) const fn max_pkt_absolute(is_ipv4: bool) -> usize {
302    if is_ipv4 {
303        MAX_PKT_ABSOLUTE_IPV4
304    } else {
305        MAX_PKT_ABSOLUTE_IPV6
306    }
307}
308
309/// Default max size of a generated (i.e. outgoing) packet.
310///
311/// Calculated as: 1500 bytes Ethernet MTU - IPv6 header 40 bytes - UDP header 8 bytes.
312/// It is safe on both IPv4 and IPv6, at the cost of 20 unused bytes for IPv4.
313///
314/// The idea is to keep generated packets unfragmented at IP layer. See RFC 6762 section 17.
315pub const MAX_PKT_DEFAULT: usize = 1452;
316
317const MSG_HEADER_LEN: usize = 12;
318
319/// Max size of a single DNS label, in bytes.
320///
321/// Reference: [RFC1035 section 2.3.4](https://datatracker.ietf.org/doc/html/rfc1035#section-2.3.4)
322const MAX_LABEL_BYTES: usize = 63;
323
324/// Why a question or a record could not be written into a packet.
325///
326/// In either case nothing is left behind in the packet: the caller rolls back
327/// whatever was written and skips the item.
328#[derive(Debug, PartialEq, Eq)]
329pub enum WriteError {
330    /// A label in a name is longer than [`MAX_LABEL_BYTES`].
331    NameTooLong,
332
333    /// The packet would exceed its max size with this record.
334    PacketFull,
335}
336
337/// `crate::error::Result` shadows the std alias here, hence the full path.
338type WriteResult = core::result::Result<(), WriteError>;
339
340// Definitions for DNS message header "flags" field
341//
342// The "flags" field is 16-bit long, in this format:
343// (RFC 1035 section 4.1.1)
344//
345//   0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
346// |QR|   Opcode  |AA|TC|RD|RA|   Z    |   RCODE   |
347//
348pub const FLAGS_QR_MASK: u16 = 0x8000; // mask for query/response bit
349
350/// Flag bit to indicate a query
351pub const FLAGS_QR_QUERY: u16 = 0x0000;
352
353/// Flag bit to indicate a response
354pub const FLAGS_QR_RESPONSE: u16 = 0x8000;
355
356/// Flag bit for Authoritative Answer
357pub const FLAGS_AA: u16 = 0x0400;
358
359/// mask for TC(Truncated) bit
360///
361/// 2024-08-10: currently this flag is only supported on the querier side,
362///             not supported on the responder side. I.e. the responder only
363///             handles the first packet and ignore this bit. Since the
364///             additional packets have 0 questions, the processing of them
365///             is no-op.
366///             In practice, this means the responder supports Known-Answer
367///             only with single packet, not multi-packet. The querier supports
368///             both single packet and multi-packet.
369pub const FLAGS_TC: u16 = 0x0200;
370
371/// A convenience type alias for DNS record trait objects.
372pub type DnsRecordBox = Box<dyn DnsRecordExt>;
373
374impl Clone for DnsRecordBox {
375    fn clone(&self) -> Self {
376        self.clone_box()
377    }
378}
379
380const U16_SIZE: usize = 2;
381
382/// Returns `RRType` for a given IP address.
383#[inline]
384pub const fn ip_address_rr_type(address: &IpAddr) -> RRType {
385    match address {
386        IpAddr::V4(_) => RRType::A,
387        IpAddr::V6(_) => RRType::AAAA,
388    }
389}
390
391#[derive(Eq, PartialEq, Debug, Clone)]
392pub struct DnsEntry {
393    pub(crate) name: String, // always lower case.
394    pub(crate) ty: RRType,
395    class: u16,
396    cache_flush: bool,
397}
398
399impl DnsEntry {
400    const fn new(name: String, ty: RRType, class: u16) -> Self {
401        Self {
402            name,
403            ty,
404            class: class & CLASS_MASK,
405            cache_flush: (class & CLASS_CACHE_FLUSH) != 0,
406        }
407    }
408}
409
410/// Common methods for all DNS entries:  questions and resource records.
411pub trait DnsEntryExt: fmt::Debug {
412    fn entry_name(&self) -> &str;
413
414    fn entry_type(&self) -> RRType;
415}
416
417/// A DNS question entry
418#[derive(Debug)]
419pub struct DnsQuestion {
420    pub(crate) entry: DnsEntry,
421}
422
423impl DnsEntryExt for DnsQuestion {
424    fn entry_name(&self) -> &str {
425        &self.entry.name
426    }
427
428    fn entry_type(&self) -> RRType {
429        self.entry.ty
430    }
431}
432
433/// A DNS Resource Record - like a DNS entry, but has a TTL.
434/// RFC: https://www.rfc-editor.org/rfc/rfc1035#section-3.2.1
435///      https://www.rfc-editor.org/rfc/rfc1035#section-4.1.3
436#[derive(Debug, Clone)]
437pub struct DnsRecord {
438    pub(crate) entry: DnsEntry,
439    ttl: u32,     // in seconds, 0 means this record should not be cached
440    created: u64, // UNIX time in millis
441    expires: u64, // expires at this UNIX time in millis
442
443    /// Support re-query an instance before its PTR record expires.
444    /// See https://datatracker.ietf.org/doc/html/rfc6762#section-5.2
445    refresh: u64, // UNIX time in millis
446
447    /// If conflict resolution decides to change the name, this is the new one.
448    new_name: Option<String>,
449}
450
451impl DnsRecord {
452    fn new(name: &str, ty: RRType, class: u16, ttl: u32) -> Self {
453        let created = current_time_millis();
454
455        // From RFC 6762 section 5.2:
456        // "... The querier should plan to issue a query at 80% of the record
457        // lifetime, and then if no answer is received, at 85%, 90%, and 95%."
458        let refresh = get_expiration_time(created, ttl, 80);
459
460        let expires = get_expiration_time(created, ttl, 100);
461
462        Self {
463            entry: DnsEntry::new(name.to_string(), ty, class),
464            ttl,
465            created,
466            expires,
467            refresh,
468            new_name: None,
469        }
470    }
471
472    pub const fn get_ttl(&self) -> u32 {
473        self.ttl
474    }
475
476    pub const fn get_expire_time(&self) -> u64 {
477        self.expires
478    }
479
480    pub const fn get_refresh_time(&self) -> u64 {
481        self.refresh
482    }
483
484    pub const fn is_expired(&self, now: u64) -> bool {
485        now >= self.expires
486    }
487
488    /// Returns whether record expires in 1 second.
489    ///
490    /// This is useful because mDNS sets TTL to 1 (not 0) for expiring records.
491    pub const fn expires_soon(&self, now: u64) -> bool {
492        now + 1000 >= self.expires
493    }
494
495    pub const fn refresh_due(&self, now: u64) -> bool {
496        now >= self.refresh
497    }
498
499    /// Returns whether `now` (in millis) has passed half of TTL.
500    pub fn halflife_passed(&self, now: u64) -> bool {
501        let halflife = get_expiration_time(self.created, self.ttl, 50);
502        now > halflife
503    }
504
505    pub fn is_unique(&self) -> bool {
506        self.entry.cache_flush
507    }
508
509    /// Updates the refresh time to be the same as the expire time so that
510    /// this record will not refresh again and will just expire.
511    pub fn refresh_no_more(&mut self) {
512        self.refresh = get_expiration_time(self.created, self.ttl, 100);
513    }
514
515    /// Returns if this record is due for refresh. If yes, `refresh` time is updated.
516    pub fn refresh_maybe(&mut self, now: u64) -> bool {
517        if self.is_expired(now) || !self.refresh_due(now) {
518            return false;
519        }
520
521        trace!(
522            "{} qtype {} is due to refresh",
523            &self.entry.name,
524            self.entry.ty
525        );
526
527        // From RFC 6762 section 5.2:
528        // "... The querier should plan to issue a query at 80% of the record
529        // lifetime, and then if no answer is received, at 85%, 90%, and 95%."
530        //
531        // If the answer is received in time, 'refresh' will be reset outside
532        // this function, back to 80% of the new TTL.
533        if self.refresh == get_expiration_time(self.created, self.ttl, 80) {
534            self.refresh = get_expiration_time(self.created, self.ttl, 85);
535        } else if self.refresh == get_expiration_time(self.created, self.ttl, 85) {
536            self.refresh = get_expiration_time(self.created, self.ttl, 90);
537        } else if self.refresh == get_expiration_time(self.created, self.ttl, 90) {
538            self.refresh = get_expiration_time(self.created, self.ttl, 95);
539        } else {
540            self.refresh_no_more();
541        }
542
543        true
544    }
545
546    /// Returns the remaining TTL in seconds
547    fn get_remaining_ttl(&self, now: u64) -> u32 {
548        let remaining_millis = get_expiration_time(self.created, self.ttl, 100) - now;
549        cmp::max(0, remaining_millis / 1000) as u32
550    }
551
552    /// Return the absolute time for this record being created
553    pub const fn get_created(&self) -> u64 {
554        self.created
555    }
556
557    /// Set the absolute expiration time in millis
558    fn set_expire(&mut self, expire_at: u64) {
559        self.expires = expire_at;
560    }
561
562    fn reset_ttl(&mut self, other: &Self) {
563        self.ttl = other.ttl;
564        self.created = other.created;
565        self.expires = get_expiration_time(self.created, self.ttl, 100);
566        self.refresh = if self.ttl > 1 {
567            get_expiration_time(self.created, self.ttl, 80)
568        } else {
569            // If TTL is 1, it means this record is expiring,
570            // then we set refresh to the same time as expires.
571            self.expires
572        };
573    }
574
575    /// Modify TTL to reflect the remaining life time from `now`.
576    pub fn update_ttl(&mut self, now: u64) {
577        if now > self.created {
578            let elapsed = now - self.created;
579            self.ttl -= (elapsed / 1000) as u32;
580        }
581    }
582
583    pub fn set_new_name(&mut self, new_name: String) {
584        if new_name == self.entry.name {
585            self.new_name = None;
586        } else {
587            self.new_name = Some(new_name);
588        }
589    }
590
591    pub fn get_new_name(&self) -> Option<&str> {
592        self.new_name.as_deref()
593    }
594
595    /// Return the new name if exists, otherwise the regular name in DnsEntry.
596    pub(crate) fn get_name(&self) -> &str {
597        self.new_name.as_deref().unwrap_or(&self.entry.name)
598    }
599
600    pub fn get_original_name(&self) -> &str {
601        &self.entry.name
602    }
603}
604
605impl PartialEq for DnsRecord {
606    fn eq(&self, other: &Self) -> bool {
607        self.entry == other.entry
608    }
609}
610
611/// Common methods for DNS resource records.
612pub trait DnsRecordExt: fmt::Debug {
613    fn get_record(&self) -> &DnsRecord;
614    fn get_record_mut(&mut self) -> &mut DnsRecord;
615    /// Writes the rdata of this record into `packet`.
616    fn write(&self, packet: &mut DnsOutPacket) -> WriteResult;
617    fn any(&self) -> &dyn Any;
618
619    /// Returns whether `other` record is considered the same except TTL.
620    fn matches(&self, other: &dyn DnsRecordExt) -> bool;
621
622    /// Returns whether `other` record has the same rdata.
623    fn rrdata_match(&self, other: &dyn DnsRecordExt) -> bool;
624
625    /// Returns the result based on a byte-level comparison of `rdata`.
626    /// If `other` is not valid, returns `Greater`.
627    fn compare_rdata(&self, other: &dyn DnsRecordExt) -> cmp::Ordering;
628
629    /// Returns the result based on "lexicographically later" defined below.
630    fn compare(&self, other: &dyn DnsRecordExt) -> cmp::Ordering {
631        /*
632        RFC 6762: https://datatracker.ietf.org/doc/html/rfc6762#section-8.2
633
634        ... The determination of "lexicographically later" is performed by first
635        comparing the record class (excluding the cache-flush bit described
636        in Section 10.2), then the record type, then raw comparison of the
637        binary content of the rdata without regard for meaning or structure.
638        If the record classes differ, then the numerically greater class is
639        considered "lexicographically later".  Otherwise, if the record types
640        differ, then the numerically greater type is considered
641        "lexicographically later".  If the rrtype and rrclass both match,
642        then the rdata is compared. ...
643        */
644        match self.get_class().cmp(&other.get_class()) {
645            cmp::Ordering::Equal => match self.get_type().cmp(&other.get_type()) {
646                cmp::Ordering::Equal => self.compare_rdata(other),
647                not_equal => not_equal,
648            },
649            not_equal => not_equal,
650        }
651    }
652
653    /// Returns a human-readable string of rdata.
654    fn rdata_print(&self) -> String;
655
656    /// Returns the class only, excluding class_flush / unique bit.
657    fn get_class(&self) -> u16 {
658        self.get_record().entry.class
659    }
660
661    fn get_cache_flush(&self) -> bool {
662        self.get_record().entry.cache_flush
663    }
664
665    /// Return the new name if exists, otherwise the regular name in DnsEntry.
666    fn get_name(&self) -> &str {
667        self.get_record().get_name()
668    }
669
670    fn get_type(&self) -> RRType {
671        self.get_record().entry.ty
672    }
673
674    /// Resets TTL using `other` record.
675    /// `self.refresh` and `self.expires` are also reset.
676    fn reset_ttl(&mut self, other: &dyn DnsRecordExt) {
677        self.get_record_mut().reset_ttl(other.get_record());
678    }
679
680    fn get_created(&self) -> u64 {
681        self.get_record().get_created()
682    }
683
684    fn get_expire(&self) -> u64 {
685        self.get_record().get_expire_time()
686    }
687
688    fn set_expire(&mut self, expire_at: u64) {
689        self.get_record_mut().set_expire(expire_at);
690    }
691
692    /// Set expire as `expire_at` if it is sooner than the current `expire`.
693    fn set_expire_sooner(&mut self, expire_at: u64) {
694        if expire_at < self.get_expire() {
695            self.get_record_mut().set_expire(expire_at);
696        }
697    }
698
699    /// Returns true if the record expires in 1 second from `now`.
700    fn expires_soon(&self, now: u64) -> bool {
701        self.get_record().expires_soon(now)
702    }
703
704    /// Given `now`, if the record is due to refresh, this method updates the refresh time
705    /// and returns the new refresh time. Otherwise, returns None.
706    fn updated_refresh_time(&mut self, now: u64) -> Option<u64> {
707        if self.get_record_mut().refresh_maybe(now) {
708            Some(self.get_record().get_refresh_time())
709        } else {
710            None
711        }
712    }
713
714    /// Returns true if another record has matched content,
715    /// and if its TTL is at least half of this record's.
716    fn suppressed_by_answer(&self, other: &dyn DnsRecordExt) -> bool {
717        self.matches(other) && (other.get_record().ttl > self.get_record().ttl / 2)
718    }
719
720    /// Required by RFC 6762 Section 7.1: Known-Answer Suppression.
721    fn suppressed_by(&self, msg: &DnsIncoming) -> bool {
722        for answer in msg.answers.iter() {
723            if self.suppressed_by_answer(answer.as_ref()) {
724                return true;
725            }
726        }
727        false
728    }
729
730    fn clone_box(&self) -> DnsRecordBox;
731
732    fn boxed(self) -> DnsRecordBox;
733}
734
735/// Resource Record for IPv4 address or IPv6 address.
736#[derive(Debug, Clone)]
737pub(crate) struct DnsAddress {
738    pub(crate) record: DnsRecord,
739    address: IpAddr,
740    pub(crate) interface_id: InterfaceId,
741}
742
743impl DnsAddress {
744    pub fn new(
745        name: &str,
746        ty: RRType,
747        class: u16,
748        ttl: u32,
749        address: IpAddr,
750        interface_id: InterfaceId,
751    ) -> Self {
752        let record = DnsRecord::new(name, ty, class, ttl);
753        Self {
754            record,
755            address,
756            interface_id,
757        }
758    }
759
760    pub fn address(&self) -> ScopedIp {
761        match self.address {
762            IpAddr::V4(v4) => ScopedIp::V4(ScopedIpV4 {
763                addr: v4,
764                interface_ids: vec![self.interface_id.clone()],
765            }),
766            IpAddr::V6(v6) => ScopedIp::V6(ScopedIpV6 {
767                addr: v6,
768                scope_id: self.interface_id.clone(),
769            }),
770        }
771    }
772}
773
774impl DnsRecordExt for DnsAddress {
775    fn get_record(&self) -> &DnsRecord {
776        &self.record
777    }
778
779    fn get_record_mut(&mut self) -> &mut DnsRecord {
780        &mut self.record
781    }
782
783    fn write(&self, packet: &mut DnsOutPacket) -> WriteResult {
784        match self.address {
785            IpAddr::V4(addr) => packet.write_bytes(addr.octets().as_ref()),
786            IpAddr::V6(addr) => packet.write_bytes(addr.octets().as_ref()),
787        };
788        Ok(())
789    }
790
791    fn any(&self) -> &dyn Any {
792        self
793    }
794
795    fn matches(&self, other: &dyn DnsRecordExt) -> bool {
796        if let Some(other_a) = other.any().downcast_ref::<Self>() {
797            return self.address == other_a.address
798                && self.record.entry == other_a.record.entry
799                && self.interface_id == other_a.interface_id;
800        }
801        false
802    }
803
804    fn rrdata_match(&self, other: &dyn DnsRecordExt) -> bool {
805        if let Some(other_a) = other.any().downcast_ref::<Self>() {
806            return self.address == other_a.address;
807        }
808        false
809    }
810
811    fn compare_rdata(&self, other: &dyn DnsRecordExt) -> cmp::Ordering {
812        if let Some(other_a) = other.any().downcast_ref::<Self>() {
813            self.address.cmp(&other_a.address)
814        } else {
815            cmp::Ordering::Greater
816        }
817    }
818
819    fn rdata_print(&self) -> String {
820        format!("{}", self.address)
821    }
822
823    fn clone_box(&self) -> DnsRecordBox {
824        Box::new(self.clone())
825    }
826
827    fn boxed(self) -> DnsRecordBox {
828        Box::new(self)
829    }
830}
831
832/// Resource Record for a DNS pointer
833#[derive(Debug, Clone)]
834pub struct DnsPointer {
835    record: DnsRecord,
836    alias: String, // the full name of Service Instance
837}
838
839impl DnsPointer {
840    pub fn new(name: &str, ty: RRType, class: u16, ttl: u32, alias: String) -> Self {
841        let record = DnsRecord::new(name, ty, class, ttl);
842        Self { record, alias }
843    }
844
845    pub fn alias(&self) -> &str {
846        &self.alias
847    }
848}
849
850impl DnsRecordExt for DnsPointer {
851    fn get_record(&self) -> &DnsRecord {
852        &self.record
853    }
854
855    fn get_record_mut(&mut self) -> &mut DnsRecord {
856        &mut self.record
857    }
858
859    fn write(&self, packet: &mut DnsOutPacket) -> WriteResult {
860        packet.write_name(&self.alias)
861    }
862
863    fn any(&self) -> &dyn Any {
864        self
865    }
866
867    fn matches(&self, other: &dyn DnsRecordExt) -> bool {
868        if let Some(other_ptr) = other.any().downcast_ref::<Self>() {
869            return self.alias == other_ptr.alias && self.record.entry == other_ptr.record.entry;
870        }
871        false
872    }
873
874    fn rrdata_match(&self, other: &dyn DnsRecordExt) -> bool {
875        if let Some(other_ptr) = other.any().downcast_ref::<Self>() {
876            return self.alias == other_ptr.alias;
877        }
878        false
879    }
880
881    fn compare_rdata(&self, other: &dyn DnsRecordExt) -> cmp::Ordering {
882        if let Some(other_ptr) = other.any().downcast_ref::<Self>() {
883            self.alias.cmp(&other_ptr.alias)
884        } else {
885            cmp::Ordering::Greater
886        }
887    }
888
889    fn rdata_print(&self) -> String {
890        self.alias.clone()
891    }
892
893    fn clone_box(&self) -> DnsRecordBox {
894        Box::new(self.clone())
895    }
896
897    fn boxed(self) -> DnsRecordBox {
898        Box::new(self)
899    }
900}
901
902/// Resource Record for a DNS service.
903#[derive(Debug, Clone)]
904pub struct DnsSrv {
905    pub(crate) record: DnsRecord,
906    pub(crate) priority: u16, // lower number means higher priority. Should be 0 in common cases.
907    pub(crate) weight: u16,   // Should be 0 in common cases
908    host: String,
909    port: u16,
910}
911
912impl DnsSrv {
913    pub fn new(
914        name: &str,
915        class: u16,
916        ttl: u32,
917        priority: u16,
918        weight: u16,
919        port: u16,
920        host: String,
921    ) -> Self {
922        let record = DnsRecord::new(name, RRType::SRV, class, ttl);
923        Self {
924            record,
925            priority,
926            weight,
927            host,
928            port,
929        }
930    }
931
932    pub fn host(&self) -> &str {
933        &self.host
934    }
935
936    pub fn port(&self) -> u16 {
937        self.port
938    }
939
940    pub fn set_host(&mut self, host: String) {
941        self.host = host;
942    }
943}
944
945impl DnsRecordExt for DnsSrv {
946    fn get_record(&self) -> &DnsRecord {
947        &self.record
948    }
949
950    fn get_record_mut(&mut self) -> &mut DnsRecord {
951        &mut self.record
952    }
953
954    fn write(&self, packet: &mut DnsOutPacket) -> WriteResult {
955        packet.write_short(self.priority);
956        packet.write_short(self.weight);
957        packet.write_short(self.port);
958        packet.write_name(&self.host)
959    }
960
961    fn any(&self) -> &dyn Any {
962        self
963    }
964
965    fn matches(&self, other: &dyn DnsRecordExt) -> bool {
966        if let Some(other_svc) = other.any().downcast_ref::<Self>() {
967            return self.host == other_svc.host
968                && self.port == other_svc.port
969                && self.weight == other_svc.weight
970                && self.priority == other_svc.priority
971                && self.record.entry == other_svc.record.entry;
972        }
973        false
974    }
975
976    fn rrdata_match(&self, other: &dyn DnsRecordExt) -> bool {
977        if let Some(other_srv) = other.any().downcast_ref::<Self>() {
978            return self.host == other_srv.host
979                && self.port == other_srv.port
980                && self.weight == other_srv.weight
981                && self.priority == other_srv.priority;
982        }
983        false
984    }
985
986    fn compare_rdata(&self, other: &dyn DnsRecordExt) -> cmp::Ordering {
987        let Some(other_srv) = other.any().downcast_ref::<Self>() else {
988            return cmp::Ordering::Greater;
989        };
990
991        // 1. compare `priority`
992        match self
993            .priority
994            .to_be_bytes()
995            .cmp(&other_srv.priority.to_be_bytes())
996        {
997            cmp::Ordering::Equal => {
998                // 2. compare `weight`
999                match self
1000                    .weight
1001                    .to_be_bytes()
1002                    .cmp(&other_srv.weight.to_be_bytes())
1003                {
1004                    cmp::Ordering::Equal => {
1005                        // 3. compare `port`.
1006                        match self.port.to_be_bytes().cmp(&other_srv.port.to_be_bytes()) {
1007                            cmp::Ordering::Equal => self.host.cmp(&other_srv.host),
1008                            not_equal => not_equal,
1009                        }
1010                    }
1011                    not_equal => not_equal,
1012                }
1013            }
1014            not_equal => not_equal,
1015        }
1016    }
1017
1018    fn rdata_print(&self) -> String {
1019        format!(
1020            "priority: {}, weight: {}, port: {}, host: {}",
1021            self.priority, self.weight, self.port, self.host
1022        )
1023    }
1024
1025    fn clone_box(&self) -> DnsRecordBox {
1026        Box::new(self.clone())
1027    }
1028
1029    fn boxed(self) -> DnsRecordBox {
1030        Box::new(self)
1031    }
1032}
1033
1034/// Resource Record for a DNS TXT record.
1035///
1036/// From [RFC 6763 section 6]:
1037///
1038/// The format of each constituent string within the DNS TXT record is a
1039/// single length byte, followed by 0-255 bytes of text data.
1040///
1041/// DNS-SD uses DNS TXT records to store arbitrary key/value pairs
1042///    conveying additional information about the named service.  Each
1043///    key/value pair is encoded as its own constituent string within the
1044///    DNS TXT record, in the form "key=value" (without the quotation
1045///    marks).  Everything up to the first '=' character is the key (Section
1046///    6.4).  Everything after the first '=' character to the end of the
1047///    string (including subsequent '=' characters, if any) is the value
1048#[derive(Clone)]
1049pub struct DnsTxt {
1050    pub(crate) record: DnsRecord,
1051    text: Vec<u8>,
1052}
1053
1054impl DnsTxt {
1055    pub fn new(name: &str, class: u16, ttl: u32, text: Vec<u8>) -> Self {
1056        let record = DnsRecord::new(name, RRType::TXT, class, ttl);
1057        Self { record, text }
1058    }
1059
1060    pub fn text(&self) -> &[u8] {
1061        &self.text
1062    }
1063}
1064
1065impl DnsRecordExt for DnsTxt {
1066    fn get_record(&self) -> &DnsRecord {
1067        &self.record
1068    }
1069
1070    fn get_record_mut(&mut self) -> &mut DnsRecord {
1071        &mut self.record
1072    }
1073
1074    fn write(&self, packet: &mut DnsOutPacket) -> WriteResult {
1075        packet.write_bytes(&self.text);
1076        Ok(())
1077    }
1078
1079    fn any(&self) -> &dyn Any {
1080        self
1081    }
1082
1083    fn matches(&self, other: &dyn DnsRecordExt) -> bool {
1084        if let Some(other_txt) = other.any().downcast_ref::<Self>() {
1085            return self.text == other_txt.text && self.record.entry == other_txt.record.entry;
1086        }
1087        false
1088    }
1089
1090    fn rrdata_match(&self, other: &dyn DnsRecordExt) -> bool {
1091        if let Some(other_txt) = other.any().downcast_ref::<Self>() {
1092            return self.text == other_txt.text;
1093        }
1094        false
1095    }
1096
1097    fn compare_rdata(&self, other: &dyn DnsRecordExt) -> cmp::Ordering {
1098        if let Some(other_txt) = other.any().downcast_ref::<Self>() {
1099            self.text.cmp(&other_txt.text)
1100        } else {
1101            cmp::Ordering::Greater
1102        }
1103    }
1104
1105    fn rdata_print(&self) -> String {
1106        format!("{:?}", decode_txt(&self.text))
1107    }
1108
1109    fn clone_box(&self) -> DnsRecordBox {
1110        Box::new(self.clone())
1111    }
1112
1113    fn boxed(self) -> DnsRecordBox {
1114        Box::new(self)
1115    }
1116}
1117
1118impl fmt::Debug for DnsTxt {
1119    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1120        let properties = decode_txt(&self.text);
1121        write!(
1122            f,
1123            "DnsTxt {{ record: {:?}, text: {:?} }}",
1124            self.record, properties
1125        )
1126    }
1127}
1128
1129// Convert from DNS TXT record content to key/value pairs
1130fn decode_txt(txt: &[u8]) -> Vec<TxtProperty> {
1131    let mut properties = Vec::new();
1132    let mut offset = 0;
1133    while offset < txt.len() {
1134        let length = txt[offset] as usize;
1135        if length == 0 {
1136            break; // reached the end
1137        }
1138        offset += 1; // move over the length byte
1139
1140        let offset_end = offset + length;
1141        if offset_end > txt.len() {
1142            trace!("ERROR: DNS TXT: size given for property is out of range. (offset={}, length={}, offset_end={}, record length={})", offset, length, offset_end, txt.len());
1143            break; // Skipping the rest of the record content, as the size for this property would already be out of range.
1144        }
1145        let kv_bytes = &txt[offset..offset_end];
1146
1147        // split key and val using the first `=`
1148        let (k, v) = kv_bytes.iter().position(|&x| x == b'=').map_or_else(
1149            || (kv_bytes.to_vec(), None),
1150            |idx| (kv_bytes[..idx].to_vec(), Some(kv_bytes[idx + 1..].to_vec())),
1151        );
1152
1153        // Make sure the key can be stored in UTF-8.
1154        match String::from_utf8(k) {
1155            Ok(k_string) => {
1156                properties.push(TxtProperty {
1157                    key: k_string,
1158                    val: v,
1159                });
1160            }
1161            Err(e) => trace!("ERROR: convert to String from key: {}", e),
1162        }
1163
1164        offset += length;
1165    }
1166
1167    properties
1168}
1169
1170/// Represents a property in a TXT record.
1171#[derive(Clone, PartialEq, Eq)]
1172pub struct TxtProperty {
1173    /// The name of the property. The original cases are kept.
1174    key: String,
1175
1176    /// RFC 6763 says values are bytes, not necessarily UTF-8.
1177    /// It is also possible that there is no value, in which case
1178    /// the key is a boolean key.
1179    val: Option<Vec<u8>>,
1180}
1181
1182impl TxtProperty {
1183    /// Returns the value of a property as str.
1184    pub fn val_str(&self) -> &str {
1185        self.val
1186            .as_ref()
1187            .map_or("", |v| std::str::from_utf8(&v[..]).unwrap_or_default())
1188    }
1189}
1190
1191/// Supports constructing from a tuple.
1192impl<K, V> From<&(K, V)> for TxtProperty
1193where
1194    K: ToString,
1195    V: ToString,
1196{
1197    fn from(prop: &(K, V)) -> Self {
1198        Self {
1199            key: prop.0.to_string(),
1200            val: Some(prop.1.to_string().into_bytes()),
1201        }
1202    }
1203}
1204
1205impl<K, V> From<(K, V)> for TxtProperty
1206where
1207    K: ToString,
1208    V: AsRef<[u8]>,
1209{
1210    fn from(prop: (K, V)) -> Self {
1211        Self {
1212            key: prop.0.to_string(),
1213            val: Some(prop.1.as_ref().into()),
1214        }
1215    }
1216}
1217
1218/// Support a property that has no value.
1219impl From<&str> for TxtProperty {
1220    fn from(key: &str) -> Self {
1221        Self {
1222            key: key.to_string(),
1223            val: None,
1224        }
1225    }
1226}
1227
1228impl fmt::Display for TxtProperty {
1229    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1230        write!(f, "{}={}", self.key, self.val_str())
1231    }
1232}
1233
1234/// Mimic the default debug output for a struct, with a twist:
1235/// - If self.var is UTF-8, will output it as a string in double quotes.
1236/// - If self.var is not UTF-8, will output its bytes as in hex.
1237impl fmt::Debug for TxtProperty {
1238    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1239        let val_string = self.val.as_ref().map_or_else(
1240            || "None".to_string(),
1241            |v| {
1242                std::str::from_utf8(&v[..]).map_or_else(
1243                    |_| format!("Some({})", u8_slice_to_hex(&v[..])),
1244                    |s| format!("Some(\"{s}\")"),
1245                )
1246            },
1247        );
1248
1249        write!(
1250            f,
1251            "TxtProperty {{key: \"{}\", val: {}}}",
1252            &self.key, &val_string,
1253        )
1254    }
1255}
1256
1257const HEX_TABLE: [char; 16] = [
1258    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
1259];
1260
1261/// Create a hex string from `slice`, with a "0x" prefix.
1262///
1263/// For example, [1u8, 2u8] -> "0x0102"
1264fn u8_slice_to_hex(slice: &[u8]) -> String {
1265    let mut hex = String::with_capacity(slice.len() * 2 + 2);
1266    hex.push_str("0x");
1267    for b in slice {
1268        hex.push(HEX_TABLE[(b >> 4) as usize]);
1269        hex.push(HEX_TABLE[(b & 0x0F) as usize]);
1270    }
1271    hex
1272}
1273
1274/// A DNS host information record
1275#[derive(Debug, Clone)]
1276struct DnsHostInfo {
1277    record: DnsRecord,
1278    cpu: String,
1279    os: String,
1280}
1281
1282impl DnsHostInfo {
1283    fn new(name: &str, ty: RRType, class: u16, ttl: u32, cpu: String, os: String) -> Self {
1284        let record = DnsRecord::new(name, ty, class, ttl);
1285        Self { record, cpu, os }
1286    }
1287}
1288
1289impl DnsRecordExt for DnsHostInfo {
1290    fn get_record(&self) -> &DnsRecord {
1291        &self.record
1292    }
1293
1294    fn get_record_mut(&mut self) -> &mut DnsRecord {
1295        &mut self.record
1296    }
1297
1298    fn write(&self, packet: &mut DnsOutPacket) -> WriteResult {
1299        debug!("Writing HInfo: cpu {} os {}", &self.cpu, &self.os);
1300        packet.write_bytes(self.cpu.as_bytes());
1301        packet.write_bytes(self.os.as_bytes());
1302        Ok(())
1303    }
1304
1305    fn any(&self) -> &dyn Any {
1306        self
1307    }
1308
1309    fn matches(&self, other: &dyn DnsRecordExt) -> bool {
1310        if let Some(other_hinfo) = other.any().downcast_ref::<Self>() {
1311            return self.cpu == other_hinfo.cpu
1312                && self.os == other_hinfo.os
1313                && self.record.entry == other_hinfo.record.entry;
1314        }
1315        false
1316    }
1317
1318    fn rrdata_match(&self, other: &dyn DnsRecordExt) -> bool {
1319        if let Some(other_hinfo) = other.any().downcast_ref::<Self>() {
1320            return self.cpu == other_hinfo.cpu && self.os == other_hinfo.os;
1321        }
1322        false
1323    }
1324
1325    fn compare_rdata(&self, other: &dyn DnsRecordExt) -> cmp::Ordering {
1326        if let Some(other_hinfo) = other.any().downcast_ref::<Self>() {
1327            match self.cpu.cmp(&other_hinfo.cpu) {
1328                cmp::Ordering::Equal => self.os.cmp(&other_hinfo.os),
1329                ordering => ordering,
1330            }
1331        } else {
1332            cmp::Ordering::Greater
1333        }
1334    }
1335
1336    fn rdata_print(&self) -> String {
1337        format!("cpu: {}, os: {}", self.cpu, self.os)
1338    }
1339
1340    fn clone_box(&self) -> DnsRecordBox {
1341        Box::new(self.clone())
1342    }
1343
1344    fn boxed(self) -> DnsRecordBox {
1345        Box::new(self)
1346    }
1347}
1348
1349/// Resource Record for negative responses
1350///
1351/// [RFC4034 section 4.1](https://datatracker.ietf.org/doc/html/rfc4034#section-4.1)
1352/// and
1353/// [RFC6762 section 6.1](https://datatracker.ietf.org/doc/html/rfc6762#section-6.1)
1354#[derive(Debug, Clone)]
1355pub struct DnsNSec {
1356    record: DnsRecord,
1357    next_domain: String,
1358    type_bitmap: Vec<u8>,
1359}
1360
1361impl DnsNSec {
1362    pub fn new(
1363        name: &str,
1364        class: u16,
1365        ttl: u32,
1366        next_domain: String,
1367        type_bitmap: Vec<u8>,
1368    ) -> Self {
1369        let record = DnsRecord::new(name, RRType::NSEC, class, ttl);
1370        Self {
1371            record,
1372            next_domain,
1373            type_bitmap,
1374        }
1375    }
1376
1377    /// Returns the types marked by `type_bitmap`
1378    pub fn _types(&self) -> Vec<u16> {
1379        // From RFC 4034: 4.1.2 The Type Bit Maps Field
1380        // https://datatracker.ietf.org/doc/html/rfc4034#section-4.1.2
1381        //
1382        // Each bitmap encodes the low-order 8 bits of RR types within the
1383        // window block, in network bit order.  The first bit is bit 0.  For
1384        // window block 0, bit 1 corresponds to RR type 1 (A), bit 2 corresponds
1385        // to RR type 2 (NS), and so forth.
1386
1387        let mut bit_num = 0;
1388        let mut results = Vec::new();
1389
1390        for byte in self.type_bitmap.iter() {
1391            let mut bit_mask: u8 = 0x80; // for bit 0 in network bit order
1392
1393            // check every bit in this byte, one by one.
1394            for _ in 0..8 {
1395                if (byte & bit_mask) != 0 {
1396                    results.push(bit_num);
1397                }
1398                bit_num += 1;
1399                bit_mask >>= 1; // mask for the next bit
1400            }
1401        }
1402        results
1403    }
1404}
1405
1406impl DnsRecordExt for DnsNSec {
1407    fn get_record(&self) -> &DnsRecord {
1408        &self.record
1409    }
1410
1411    fn get_record_mut(&mut self) -> &mut DnsRecord {
1412        &mut self.record
1413    }
1414
1415    fn write(&self, packet: &mut DnsOutPacket) -> WriteResult {
1416        packet.write_bytes(self.next_domain.as_bytes());
1417        packet.write_bytes(&self.type_bitmap);
1418        Ok(())
1419    }
1420
1421    fn any(&self) -> &dyn Any {
1422        self
1423    }
1424
1425    fn matches(&self, other: &dyn DnsRecordExt) -> bool {
1426        if let Some(other_record) = other.any().downcast_ref::<Self>() {
1427            return self.next_domain == other_record.next_domain
1428                && self.type_bitmap == other_record.type_bitmap
1429                && self.record.entry == other_record.record.entry;
1430        }
1431        false
1432    }
1433
1434    fn rrdata_match(&self, other: &dyn DnsRecordExt) -> bool {
1435        if let Some(other_record) = other.any().downcast_ref::<Self>() {
1436            return self.next_domain == other_record.next_domain
1437                && self.type_bitmap == other_record.type_bitmap;
1438        }
1439        false
1440    }
1441
1442    fn compare_rdata(&self, other: &dyn DnsRecordExt) -> cmp::Ordering {
1443        if let Some(other_nsec) = other.any().downcast_ref::<Self>() {
1444            match self.next_domain.cmp(&other_nsec.next_domain) {
1445                cmp::Ordering::Equal => self.type_bitmap.cmp(&other_nsec.type_bitmap),
1446                ordering => ordering,
1447            }
1448        } else {
1449            cmp::Ordering::Greater
1450        }
1451    }
1452
1453    fn rdata_print(&self) -> String {
1454        format!(
1455            "next_domain: {}, type_bitmap len: {}",
1456            self.next_domain,
1457            self.type_bitmap.len()
1458        )
1459    }
1460
1461    fn clone_box(&self) -> DnsRecordBox {
1462        Box::new(self.clone())
1463    }
1464
1465    fn boxed(self) -> DnsRecordBox {
1466        Box::new(self)
1467    }
1468}
1469
1470/// Which section of a DNS message an item belongs to.
1471#[derive(Clone, Copy, Debug)]
1472enum Section {
1473    Question,
1474    Answer,
1475    Authority,
1476    Additional,
1477}
1478
1479/// A single packet for outgoing DNS message.
1480pub struct DnsOutPacket {
1481    /// All bytes in `data` is the actual packet on the wire.
1482    data: Vec<u8>,
1483
1484    /// k: name, v: offset
1485    names: HashMap<String, u16>,
1486
1487    /// Max byte size of `data`. i.e. the max packet size.
1488    max_size: usize,
1489
1490    /// How many items `data` holds in each section, i.e. the header counts.
1491    question_count: u16,
1492    answer_count: u16,
1493    auth_count: u16,
1494    addi_count: u16,
1495}
1496
1497impl DnsOutPacket {
1498    fn new(max_size: usize) -> Self {
1499        Self {
1500            data: vec![0; MSG_HEADER_LEN],
1501            names: HashMap::new(),
1502            max_size,
1503            question_count: 0,
1504            answer_count: 0,
1505            auth_count: 0,
1506            addi_count: 0,
1507        }
1508    }
1509
1510    pub fn size(&self) -> usize {
1511        self.data.len()
1512    }
1513
1514    pub fn as_bytes(&self) -> &[u8] {
1515        &self.data
1516    }
1517
1518    /// True if nothing has been written into this packet yet.
1519    fn is_empty(&self) -> bool {
1520        self.question_count == 0
1521            && self.answer_count == 0
1522            && self.auth_count == 0
1523            && self.addi_count == 0
1524    }
1525
1526    /// Counts one more item in `section`.
1527    fn bump(&mut self, section: Section) {
1528        match section {
1529            Section::Question => self.question_count += 1,
1530            Section::Answer => self.answer_count += 1,
1531            Section::Authority => self.auth_count += 1,
1532            Section::Additional => self.addi_count += 1,
1533        }
1534    }
1535
1536    fn write_question(&mut self, question: &DnsQuestion) -> WriteResult {
1537        let start_size = self.size();
1538
1539        self.write_name(&question.entry.name).map_err(|e| {
1540            self.rollback(start_size);
1541            e
1542        })?;
1543        self.write_short(question.entry.ty as u16);
1544        self.write_short(question.entry.class);
1545
1546        if self.size() > self.max_size {
1547            self.rollback(start_size);
1548            return Err(WriteError::PacketFull);
1549        }
1550
1551        Ok(())
1552    }
1553
1554    /// Discards everything written since `start_size`, including the name
1555    /// compression offsets that point into the discarded bytes.
1556    fn rollback(&mut self, start_size: usize) {
1557        self.data.truncate(start_size);
1558        self.names
1559            .retain(|_, offset| (*offset as usize) < start_size);
1560    }
1561
1562    /// Writes a record (answer, authoritative answer, additional).
1563    ///
1564    /// In error cases nothing is written to the packet.
1565    fn write_record(&mut self, record_ext: &dyn DnsRecordExt, now: u64) -> WriteResult {
1566        let start_size = self.size();
1567
1568        let record = record_ext.get_record();
1569        self.write_name(record.get_name())?;
1570        self.write_short(record.entry.ty as u16);
1571        if record.entry.cache_flush {
1572            // check "multicast"
1573            self.write_short(record.entry.class | CLASS_CACHE_FLUSH);
1574        } else {
1575            self.write_short(record.entry.class);
1576        }
1577
1578        if now == 0 {
1579            self.write_u32(record.ttl);
1580        } else {
1581            self.write_u32(record.get_remaining_ttl(now));
1582        }
1583
1584        // Placeholder for record size
1585        self.write_short(0);
1586        let record_offset = self.size();
1587
1588        if let Err(e) = record_ext.write(self) {
1589            self.rollback(start_size);
1590            return Err(e);
1591        }
1592
1593        self.set_short_at(record_offset - 2, (self.size() - record_offset) as u16);
1594
1595        if self.size() > self.max_size {
1596            self.rollback(start_size);
1597            return Err(WriteError::PacketFull);
1598        }
1599
1600        Ok(())
1601    }
1602
1603    fn set_short_at(&mut self, index: usize, value: u16) {
1604        self.data[index..index + 2].copy_from_slice(&value.to_be_bytes());
1605    }
1606
1607    /// Parses a DNS name that may contain escaped characters according to RFC 6763 Section 4.3.
1608    /// Returns a vector of labels where each label is the unescaped content.
1609    ///
1610    /// Escape sequences:
1611    /// - \\. becomes . (literal dot)
1612    /// - \\\\ becomes \\ (literal backslash)
1613    fn parse_escaped_name(name: &str) -> Vec<String> {
1614        let mut labels = Vec::new();
1615        let mut current_label = String::new();
1616        let mut chars = name.chars().peekable();
1617
1618        while let Some(ch) = chars.next() {
1619            match ch {
1620                '\\' => {
1621                    // Backslash escape sequence
1622                    if let Some(&next_ch) = chars.peek() {
1623                        match next_ch {
1624                            '.' | '\\' => {
1625                                // \\. or \\\\ - consume the backslash and add the escaped char
1626                                chars.next();
1627                                current_label.push(next_ch);
1628                            }
1629                            _ => {
1630                                // Not a recognized escape - treat backslash literally
1631                                current_label.push(ch);
1632                            }
1633                        }
1634                    } else {
1635                        // Trailing backslash - add it literally
1636                        current_label.push(ch);
1637                    }
1638                }
1639                '.' => {
1640                    // Unescaped dot - label separator
1641                    if !current_label.is_empty() {
1642                        labels.push(current_label.clone());
1643                        current_label.clear();
1644                    }
1645                }
1646                _ => {
1647                    current_label.push(ch);
1648                }
1649            }
1650        }
1651
1652        // Add the last label if not empty
1653        if !current_label.is_empty() {
1654            labels.push(current_label);
1655        }
1656
1657        labels
1658    }
1659
1660    // Write name to packet
1661    //
1662    // [RFC1035]
1663    // 4.1.4. Message compression
1664    //
1665    // In order to reduce the size of messages, the domain system utilizes a
1666    // compression scheme which eliminates the repetition of domain names in a
1667    // message.  In this scheme, an entire domain name or a list of labels at
1668    // the end of a domain name is replaced with a pointer to a prior occurrence
1669    // of the same name.
1670    // The pointer takes the form of a two octet sequence:
1671    //     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
1672    //     | 1  1|                OFFSET                   |
1673    //     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
1674    // The first two bits are ones.  This allows a pointer to be distinguished
1675    // from a label, since the label must begin with two zero bits because
1676    // labels are restricted to 63 octets or less.  (The 10 and 01 combinations
1677    // are reserved for future use.)  The OFFSET field specifies an offset from
1678    // the start of the message (i.e., the first octet of the ID field in the
1679    // domain header).  A zero offset specifies the first byte of the ID field,
1680    // etc.
1681    //
1682    // This function also handles RFC 6763 Section 4.3 escaping where dots and backslashes
1683    // in instance names are escaped (e.g., "My\\.Service" represents a single label "My.Service").
1684    // The actual name sent over the wire is the unescaped version.
1685    fn write_name(&mut self, name: &str) -> WriteResult {
1686        // Remove trailing dot if present
1687        let name_to_parse = name.strip_suffix('.').unwrap_or(name);
1688
1689        // Parse the name considering escape sequences
1690        let labels = Self::parse_escaped_name(name_to_parse);
1691
1692        if labels.is_empty() {
1693            self.write_byte(0);
1694            return Ok(());
1695        }
1696
1697        // Validate before writing anything.
1698        if labels.iter().any(|label| label.len() > MAX_LABEL_BYTES) {
1699            return Err(WriteError::NameTooLong);
1700        }
1701
1702        // Write each label
1703        for (i, label) in labels.iter().enumerate() {
1704            // Build the remaining name for compression (with dots as separators)
1705            let remaining: String = labels[i..].join(".");
1706
1707            // Check if we can use compression for the remaining part
1708            const POINTER_MASK: u16 = 0xC000;
1709            if let Some(&offset) = self.names.get(&remaining) {
1710                let pointer = offset | POINTER_MASK;
1711                self.write_short(pointer);
1712                return Ok(());
1713            }
1714
1715            // Store this position for potential future compression
1716            self.names.insert(remaining, self.size() as u16);
1717
1718            // Write the label
1719            self.write_utf8(label)?;
1720        }
1721
1722        // Write terminating zero byte
1723        self.write_byte(0);
1724        Ok(())
1725    }
1726
1727    fn write_byte(&mut self, v: u8) {
1728        self.data.push(v);
1729    }
1730
1731    fn write_bytes(&mut self, s: &[u8]) {
1732        self.data.extend(s);
1733    }
1734
1735    /// Writes a single label. Nothing is written if the label is too long to
1736    /// be encoded.
1737    fn write_utf8(&mut self, s: &str) -> WriteResult {
1738        if s.len() > MAX_LABEL_BYTES {
1739            return Err(WriteError::NameTooLong);
1740        }
1741        self.write_byte(s.len() as u8);
1742        self.write_bytes(s.as_bytes());
1743        Ok(())
1744    }
1745
1746    fn write_u32(&mut self, v: u32) {
1747        self.data.extend(&v.to_be_bytes());
1748    }
1749
1750    fn write_short(&mut self, v: u16) {
1751        self.data.extend(&v.to_be_bytes());
1752    }
1753
1754    /// Marks this finished packet as truncated, i.e. the message continues in
1755    /// the next packet.
1756    fn set_truncated(&mut self) {
1757        let flags = u16::from_be_bytes([self.data[2], self.data[3]]);
1758        self.set_short_at(2, flags | FLAGS_TC);
1759    }
1760
1761    /// Writes the header fields and finish the packet.
1762    /// This function should be only called when finishing a packet.
1763    ///
1764    /// The header format is based on RFC 1035 section 4.1.1:
1765    /// https://datatracker.ietf.org/doc/html/rfc1035#section-4.1.1
1766    //
1767    //                                  1  1  1  1  1  1
1768    //    0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
1769    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
1770    //    |                      ID                       |
1771    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
1772    //    |QR|   Opcode  |AA|TC|RD|RA|   Z    |   RCODE   |
1773    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
1774    //    |                    QDCOUNT                    |
1775    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
1776    //    |                    ANCOUNT                    |
1777    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
1778    //    |                    NSCOUNT                    |
1779    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
1780    //    |                    ARCOUNT                    |
1781    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
1782    //
1783    fn write_header(&mut self, id: u16, flags: u16) {
1784        self.set_short_at(0, id);
1785        self.set_short_at(2, flags);
1786        self.set_short_at(4, self.question_count);
1787        self.set_short_at(6, self.answer_count);
1788        self.set_short_at(8, self.auth_count);
1789        self.set_short_at(10, self.addi_count);
1790    }
1791}
1792
1793/// Encodes a [`DnsOutgoing`] into one or more [`DnsOutPacket`], starting a new
1794/// packet whenever the current one runs out of room.
1795struct PacketBuilder<'a> {
1796    out: &'a DnsOutgoing,
1797
1798    /// Max size of a packet that holds more than one record.
1799    max_size: usize,
1800
1801    /// IP version these packets are bound for, which decides their absolute
1802    /// ceiling: see [`max_pkt_absolute`].
1803    is_ipv4: bool,
1804
1805    finished: Vec<DnsOutPacket>,
1806    current: DnsOutPacket,
1807}
1808
1809impl<'a> PacketBuilder<'a> {
1810    fn new(out: &'a DnsOutgoing, max_size: usize, is_ipv4: bool) -> Self {
1811        Self {
1812            out,
1813            max_size,
1814            is_ipv4,
1815            finished: Vec::new(),
1816            current: DnsOutPacket::new(max_size),
1817        }
1818    }
1819
1820    /// Writes one question or record into the current packet, starting a new
1821    /// packet if it does not fit in the current one.
1822    ///
1823    /// An item that cannot be encoded at all is skipped, leaving the packet as
1824    /// it was. Sections are written in message order, so an item that spills
1825    /// never lands ahead of one already written.
1826    fn add<F>(&mut self, section: Section, write: F)
1827    where
1828        F: Fn(&mut DnsOutPacket) -> WriteResult,
1829    {
1830        match write(&mut self.current) {
1831            Ok(()) => {
1832                self.current.bump(section);
1833                return;
1834            }
1835            // The item can never be encoded: skip it.
1836            Err(WriteError::NameTooLong) => return,
1837            Err(WriteError::PacketFull) => {}
1838        }
1839
1840        // Packet is full. Flush the current and create a new one.
1841        if !self.current.is_empty() {
1842            self.flush();
1843
1844            match write(&mut self.current) {
1845                Ok(()) => {
1846                    self.current.bump(section);
1847                    return;
1848                }
1849                Err(WriteError::NameTooLong) => return,
1850                Err(WriteError::PacketFull) => {}
1851            }
1852        }
1853
1854        // Packet is still full. A question such big is not legitimate.
1855        if matches!(section, Section::Question) {
1856            return;
1857        }
1858
1859        // Packet is still full. We will send this single record.
1860
1861        // RFC 6762 section 17:
1862        // "a record too large for one MTU-sized packet SHOULD be sent alone, in a
1863        // single IP datagram".
1864        self.current.max_size = max_pkt_absolute(self.is_ipv4);
1865
1866        if write(&mut self.current).is_ok() {
1867            self.current.bump(section);
1868            self.flush();
1869        } else {
1870            // Too big even for the hard ceiling: skip the record and carry on.
1871            self.current.max_size = self.max_size;
1872            debug!(
1873                "Record too big for absolute max size, skipping: {:?}",
1874                section
1875            );
1876        }
1877    }
1878
1879    /// Finishes the current packet and starts a new empty one.
1880    fn flush(&mut self) {
1881        self.current
1882            .write_header(self.out.wire_id(), self.out.flags);
1883
1884        let next = DnsOutPacket::new(self.max_size);
1885        self.finished
1886            .push(std::mem::replace(&mut self.current, next));
1887    }
1888
1889    fn finish(mut self) -> Vec<DnsOutPacket> {
1890        // Always produce at least one packet, even an empty one, but never leave a
1891        // trailing empty packet behind a full one.
1892        if !self.current.is_empty() || self.finished.is_empty() {
1893            self.flush();
1894        }
1895
1896        let mut packets = self.finished;
1897
1898        /*
1899        RFC 6762 section 7.2: https://datatracker.ietf.org/doc/html/rfc6762#section-7.2
1900        ...
1901            When a Multicast DNS querier sends a query to which it already knows some
1902            answers, it ... sets the TC (Truncated) bit in the header ... [so that the
1903            responder knows] to wait for the remaining known answers before responding.
1904         */
1905        if self.out.is_query() {
1906            if let Some((_last, rest)) = packets.split_last_mut() {
1907                for packet in rest {
1908                    packet.set_truncated();
1909                }
1910            }
1911        }
1912
1913        packets
1914    }
1915}
1916
1917/// Representation of one outgoing DNS message that could be sent in one or more packet(s).
1918#[derive(Debug)]
1919pub struct DnsOutgoing {
1920    flags: u16,
1921    id: u16,
1922    multicast: bool,
1923    questions: Vec<DnsQuestion>,
1924    answers: Vec<(DnsRecordBox, u64)>,
1925    authorities: Vec<DnsRecordBox>,
1926    additionals: Vec<DnsRecordBox>,
1927    known_answer_count: i64, // for internal maintenance only
1928}
1929
1930impl DnsOutgoing {
1931    pub fn new(flags: u16) -> Self {
1932        Self {
1933            flags,
1934            id: 0,
1935            multicast: true,
1936            questions: Vec::new(),
1937            answers: Vec::new(),
1938            authorities: Vec::new(),
1939            additionals: Vec::new(),
1940            known_answer_count: 0,
1941        }
1942    }
1943
1944    pub fn questions(&self) -> &[DnsQuestion] {
1945        &self.questions
1946    }
1947
1948    /// For testing purposes only.
1949    pub(crate) fn _answers(&self) -> &[(DnsRecordBox, u64)] {
1950        &self.answers
1951    }
1952
1953    pub fn answers_count(&self) -> usize {
1954        self.answers.len()
1955    }
1956
1957    pub fn authorities(&self) -> &[DnsRecordBox] {
1958        &self.authorities
1959    }
1960
1961    pub fn additionals(&self) -> &[DnsRecordBox] {
1962        &self.additionals
1963    }
1964
1965    pub fn known_answer_count(&self) -> i64 {
1966        self.known_answer_count
1967    }
1968
1969    pub fn set_id(&mut self, id: u16) {
1970        self.id = id;
1971    }
1972
1973    /// The id to put in the header, always 0 for multicast.
1974    const fn wire_id(&self) -> u16 {
1975        if self.multicast {
1976            0
1977        } else {
1978            self.id
1979        }
1980    }
1981
1982    pub const fn is_query(&self) -> bool {
1983        (self.flags & FLAGS_QR_MASK) == FLAGS_QR_QUERY
1984    }
1985
1986    // Adds an additional answer
1987
1988    // From: RFC 6763, DNS-Based Service Discovery, February 2013
1989
1990    // 12.  DNS Additional Record Generation
1991
1992    //    DNS has an efficiency feature whereby a DNS server may place
1993    //    additional records in the additional section of the DNS message.
1994    //    These additional records are records that the client did not
1995    //    explicitly request, but the server has reasonable grounds to expect
1996    //    that the client might request them shortly, so including them can
1997    //    save the client from having to issue additional queries.
1998
1999    //    This section recommends which additional records SHOULD be generated
2000    //    to improve network efficiency, for both Unicast and Multicast DNS-SD
2001    //    responses.
2002
2003    // 12.1.  PTR Records
2004
2005    //    When including a DNS-SD Service Instance Enumeration or Selective
2006    //    Instance Enumeration (subtype) PTR record in a response packet, the
2007    //    server/responder SHOULD include the following additional records:
2008
2009    //    o  The SRV record(s) named in the PTR rdata.
2010    //    o  The TXT record(s) named in the PTR rdata.
2011    //    o  All address records (type "A" and "AAAA") named in the SRV rdata.
2012
2013    // 12.2.  SRV Records
2014
2015    //    When including an SRV record in a response packet, the
2016    //    server/responder SHOULD include the following additional records:
2017
2018    //    o  All address records (type "A" and "AAAA") named in the SRV rdata.
2019    pub fn add_additional_answer(&mut self, answer: impl DnsRecordExt + 'static) {
2020        trace!("add_additional_answer: {:?}", &answer);
2021        self.additionals.push(answer.boxed());
2022    }
2023
2024    /// A workaround as Rust doesn't allow us to pass DnsRecordBox in as `impl DnsRecordExt`
2025    pub fn add_answer_box(&mut self, answer_box: DnsRecordBox) {
2026        self.answers.push((answer_box, 0));
2027    }
2028
2029    pub fn add_authority(&mut self, record: DnsRecordBox) {
2030        self.authorities.push(record);
2031    }
2032
2033    /// Retains only the answers for which `keep` returns true.
2034    pub(crate) fn retain_answers<F>(&mut self, mut keep: F)
2035    where
2036        F: FnMut(&DnsRecordBox) -> bool,
2037    {
2038        self.answers.retain(|(record, _)| keep(record));
2039    }
2040
2041    /// Retains only the additional records for which `keep` returns true.
2042    pub(crate) fn retain_additionals<F>(&mut self, mut keep: F)
2043    where
2044        F: FnMut(&DnsRecordBox) -> bool,
2045    {
2046        self.additionals.retain(|record| keep(record));
2047    }
2048
2049    /// Returns true if `answer` is added to the outgoing msg.
2050    /// Returns false if `answer` was not added as it expired or suppressed by the incoming `msg`.
2051    pub fn add_answer(
2052        &mut self,
2053        msg: &DnsIncoming,
2054        answer: impl DnsRecordExt + Send + 'static,
2055    ) -> bool {
2056        trace!("Check for add_answer");
2057        if answer.suppressed_by(msg) {
2058            trace!("my answer is suppressed by incoming msg");
2059            self.known_answer_count += 1;
2060            return false;
2061        }
2062
2063        self.add_answer_at_time(answer, 0)
2064    }
2065
2066    /// Returns true if `answer` is added to the outgoing msg.
2067    /// Returns false if the answer is expired `now` hence not added.
2068    /// If `now` is 0, do not check if the answer expires.
2069    pub fn add_answer_at_time(
2070        &mut self,
2071        answer: impl DnsRecordExt + Send + 'static,
2072        now: u64,
2073    ) -> bool {
2074        if now == 0 || !answer.get_record().is_expired(now) {
2075            trace!("add_answer push: {:?}", &answer);
2076            self.answers.push((answer.boxed(), now));
2077            return true;
2078        }
2079        false
2080    }
2081
2082    /// Adds a PTR answer for `service` along with recommended additional records
2083    /// (SRV, TXT, and address records) per [RFC 6763 Section 12.1].
2084    ///
2085    /// Resolves any name conflicts via `dns_registry` and selects addresses
2086    /// matching the given interface. Does nothing if no addresses are available
2087    /// on `intf` or if the PTR answer is suppressed by known-answer entries in `msg`.
2088    ///
2089    /// [RFC 6763 Section 12.1]: https://tools.ietf.org/html/rfc6763#section-12.1
2090    pub(crate) fn add_answer_with_additionals(
2091        &mut self,
2092        msg: &DnsIncoming,
2093        service: &ServiceInfo,
2094        intf: &MyIntf,
2095        dns_registry: &DnsRegistry,
2096        is_ipv4: bool,
2097    ) {
2098        let intf_addrs = if is_ipv4 {
2099            service.get_addrs_on_my_intf_v4(intf)
2100        } else {
2101            service.get_addrs_on_my_intf_v6(intf)
2102        };
2103        if intf_addrs.is_empty() {
2104            trace!("No addrs on LAN of intf {:?}", intf);
2105            return;
2106        }
2107
2108        // check if we changed our name due to conflicts.
2109        let service_fullname = dns_registry.resolve_name(service.get_fullname());
2110        let hostname = dns_registry.resolve_name(service.get_hostname());
2111
2112        let ptr_added = self.add_answer(
2113            msg,
2114            DnsPointer::new(
2115                service.get_type(),
2116                RRType::PTR,
2117                CLASS_IN,
2118                service.get_other_ttl(),
2119                service_fullname.to_string(),
2120            ),
2121        );
2122
2123        if !ptr_added {
2124            trace!("answer was not added for msg {:?}", msg);
2125            return;
2126        }
2127
2128        if let Some(sub) = service.get_subtype() {
2129            trace!("Adding subdomain {}", sub);
2130            self.add_additional_answer(DnsPointer::new(
2131                sub,
2132                RRType::PTR,
2133                CLASS_IN,
2134                service.get_other_ttl(),
2135                service_fullname.to_string(),
2136            ));
2137        }
2138
2139        // Add recommended additional answers according to
2140        // https://tools.ietf.org/html/rfc6763#section-12.1.
2141        self.add_additional_answer(DnsSrv::new(
2142            service_fullname,
2143            CLASS_IN | CLASS_CACHE_FLUSH,
2144            service.get_host_ttl(),
2145            service.get_priority(),
2146            service.get_weight(),
2147            service.get_port(),
2148            hostname.to_string(),
2149        ));
2150
2151        self.add_additional_answer(DnsTxt::new(
2152            service_fullname,
2153            CLASS_IN | CLASS_CACHE_FLUSH,
2154            service.get_other_ttl(),
2155            service.generate_txt(),
2156        ));
2157
2158        for address in intf_addrs {
2159            self.add_additional_answer(DnsAddress::new(
2160                hostname,
2161                ip_address_rr_type(&address),
2162                CLASS_IN | CLASS_CACHE_FLUSH,
2163                service.get_host_ttl(),
2164                address,
2165                intf.into(),
2166            ));
2167        }
2168    }
2169
2170    pub fn add_question(&mut self, name: &str, qtype: RRType) {
2171        let q = DnsQuestion {
2172            entry: DnsEntry::new(name.to_string(), qtype, CLASS_IN),
2173        };
2174        self.questions.push(q);
2175    }
2176
2177    /// Clear the cache-flush (unique) bit on every answer and additional
2178    /// record. Required for RFC 6762 §6.7 (Legacy Unicast Responses) and
2179    /// §10.2 — a legacy resolver doesn't know about the cache-flush bit
2180    /// and may misinterpret responses where it is set.
2181    pub fn clear_cache_flush_bits(&mut self) {
2182        for (rec, _) in &mut self.answers {
2183            rec.get_record_mut().entry.cache_flush = false;
2184        }
2185        for rec in &mut self.additionals {
2186            rec.get_record_mut().entry.cache_flush = false;
2187        }
2188        for rec in &mut self.authorities {
2189            rec.get_record_mut().entry.cache_flush = false;
2190        }
2191    }
2192
2193    /// Returns a list of actual DNS packet data to be sent on the wire, each no
2194    /// bigger than `max_size`, over the IP version given by `is_ipv4`.
2195    ///
2196    /// Most callers want [`MAX_PKT_DEFAULT`] for `max_size`.
2197    pub fn to_data_on_wire(&self, max_size: usize, is_ipv4: bool) -> Vec<Vec<u8>> {
2198        let packet_list = self.to_packets(max_size, is_ipv4);
2199        packet_list.into_iter().map(|p| p.data).collect()
2200    }
2201
2202    /// Encode self into one or more packets, each no bigger than `max_size`.
2203    ///
2204    /// Questions and records are written in message order and spill into a new
2205    /// packet whenever the current one is full, so none is dropped for lack of
2206    /// room. The one exception is a single record too big to fit in an otherwise
2207    /// empty packet: it is sent alone in an oversized packet, per RFC 6762
2208    /// section 17.
2209    ///
2210    /// `is_ipv4` tells which IP version the packets are bound for, and so how big
2211    /// that lone oversized packet may get: see [`max_pkt_absolute`]. A record too
2212    /// big even for that could not be sent at all, and is dropped.
2213    ///
2214    /// `max_size` must be no bigger than [`MAX_PKT_ABSOLUTE_IPV6`], the RFC 6762
2215    /// section 17 ceiling that is legal over either IP version;
2216    /// [`ServiceDaemon::set_max_packet_size`](crate::ServiceDaemon::set_max_packet_size)
2217    /// caps what it accepts. Most callers want [`MAX_PKT_DEFAULT`].
2218    pub fn to_packets(&self, max_size: usize, is_ipv4: bool) -> Vec<DnsOutPacket> {
2219        debug_assert!(
2220            max_size <= MAX_PKT_ABSOLUTE_IPV6,
2221            "max_size {} exceeds the RFC 6762 section 17 ceiling",
2222            max_size
2223        );
2224        let mut builder = PacketBuilder::new(self, max_size, is_ipv4);
2225
2226        for question in self.questions.iter() {
2227            builder.add(Section::Question, |packet| packet.write_question(question));
2228        }
2229
2230        for (answer, time) in self.answers.iter() {
2231            builder.add(Section::Answer, |packet| {
2232                packet.write_record(answer.as_ref(), *time)
2233            });
2234        }
2235
2236        for auth in self.authorities.iter() {
2237            builder.add(Section::Authority, |packet| {
2238                packet.write_record(auth.as_ref(), 0)
2239            });
2240        }
2241
2242        for addi in self.additionals.iter() {
2243            builder.add(Section::Additional, |packet| {
2244                packet.write_record(addi.as_ref(), 0)
2245            });
2246        }
2247
2248        builder.finish()
2249    }
2250}
2251
2252/// An incoming DNS message. It could be a query or a response.
2253#[derive(Debug)]
2254pub struct DnsIncoming {
2255    offset: usize,
2256    data: Vec<u8>,
2257    questions: Vec<DnsQuestion>,
2258    answers: Vec<DnsRecordBox>,
2259    authorities: Vec<DnsRecordBox>,
2260    additional: Vec<DnsRecordBox>,
2261    id: u16,
2262    flags: u16,
2263    num_questions: u16,
2264    num_answers: u16,
2265    num_authorities: u16,
2266    num_additionals: u16,
2267    interface_id: InterfaceId,
2268}
2269
2270impl DnsIncoming {
2271    pub fn new(data: Vec<u8>, interface_id: InterfaceId) -> Result<Self> {
2272        let mut incoming = Self {
2273            offset: 0,
2274            data,
2275            questions: Vec::new(),
2276            answers: Vec::new(),
2277            authorities: Vec::new(),
2278            additional: Vec::new(),
2279            id: 0,
2280            flags: 0,
2281            num_questions: 0,
2282            num_answers: 0,
2283            num_authorities: 0,
2284            num_additionals: 0,
2285            interface_id,
2286        };
2287
2288        /*
2289        RFC 1035 section 4.1: https://datatracker.ietf.org/doc/html/rfc1035#section-4.1
2290        ...
2291        All communications inside of the domain protocol are carried in a single
2292        format called a message.  The top level format of message is divided
2293        into 5 sections (some of which are empty in certain cases) shown below:
2294
2295            +---------------------+
2296            |        Header       |
2297            +---------------------+
2298            |       Question      | the question for the name server
2299            +---------------------+
2300            |        Answer       | RRs answering the question
2301            +---------------------+
2302            |      Authority      | RRs pointing toward an authority
2303            +---------------------+
2304            |      Additional     | RRs holding additional information
2305            +---------------------+
2306         */
2307        incoming.read_header()?;
2308        incoming.read_questions()?;
2309        incoming.read_answers()?;
2310        incoming.read_authorities()?;
2311        incoming.read_additional()?;
2312
2313        Ok(incoming)
2314    }
2315
2316    pub fn id(&self) -> u16 {
2317        self.id
2318    }
2319
2320    pub fn questions(&self) -> &[DnsQuestion] {
2321        &self.questions
2322    }
2323
2324    pub fn answers(&self) -> &[DnsRecordBox] {
2325        &self.answers
2326    }
2327
2328    pub fn authorities(&self) -> &[DnsRecordBox] {
2329        &self.authorities
2330    }
2331
2332    pub fn additionals(&self) -> &[DnsRecordBox] {
2333        &self.additional
2334    }
2335
2336    pub fn answers_mut(&mut self) -> &mut Vec<DnsRecordBox> {
2337        &mut self.answers
2338    }
2339
2340    pub fn authorities_mut(&mut self) -> &mut Vec<DnsRecordBox> {
2341        &mut self.authorities
2342    }
2343
2344    pub fn additionals_mut(&mut self) -> &mut Vec<DnsRecordBox> {
2345        &mut self.additional
2346    }
2347
2348    pub fn all_records(self) -> impl Iterator<Item = DnsRecordBox> {
2349        self.answers
2350            .into_iter()
2351            .chain(self.authorities)
2352            .chain(self.additional)
2353    }
2354
2355    pub fn num_additionals(&self) -> u16 {
2356        self.num_additionals
2357    }
2358
2359    pub fn num_authorities(&self) -> u16 {
2360        self.num_authorities
2361    }
2362
2363    pub fn num_questions(&self) -> u16 {
2364        self.num_questions
2365    }
2366
2367    pub const fn is_query(&self) -> bool {
2368        (self.flags & FLAGS_QR_MASK) == FLAGS_QR_QUERY
2369    }
2370
2371    pub const fn is_response(&self) -> bool {
2372        (self.flags & FLAGS_QR_MASK) == FLAGS_QR_RESPONSE
2373    }
2374
2375    fn read_header(&mut self) -> Result<()> {
2376        if self.data.len() < MSG_HEADER_LEN {
2377            return Err(e_fmt!(
2378                "DNS incoming: header is too short: {} bytes",
2379                self.data.len()
2380            ));
2381        }
2382
2383        let data = &self.data[0..];
2384        self.id = u16_from_be_slice(&data[..2]);
2385        self.flags = u16_from_be_slice(&data[2..4]);
2386        self.num_questions = u16_from_be_slice(&data[4..6]);
2387        self.num_answers = u16_from_be_slice(&data[6..8]);
2388        self.num_authorities = u16_from_be_slice(&data[8..10]);
2389        self.num_additionals = u16_from_be_slice(&data[10..12]);
2390
2391        self.offset = MSG_HEADER_LEN;
2392
2393        trace!(
2394            "read_header: id {}, {} questions {} answers {} authorities {} additionals",
2395            self.id,
2396            self.num_questions,
2397            self.num_answers,
2398            self.num_authorities,
2399            self.num_additionals
2400        );
2401        Ok(())
2402    }
2403
2404    fn read_questions(&mut self) -> Result<()> {
2405        trace!("read_questions: {}", &self.num_questions);
2406        for i in 0..self.num_questions {
2407            let name = self.read_name()?;
2408
2409            let data = &self.data[self.offset..];
2410            if data.len() < 4 {
2411                return Err(Error::Msg(format!(
2412                    "DNS incoming: question idx {} too short: {}",
2413                    i,
2414                    data.len()
2415                )));
2416            }
2417            let ty = u16_from_be_slice(&data[..2]);
2418            let class = u16_from_be_slice(&data[2..4]);
2419            self.offset += 4;
2420
2421            let Some(rr_type) = RRType::from_u16(ty) else {
2422                return Err(Error::Msg(format!(
2423                    "DNS incoming: question idx {i} qtype unknown: {ty}",
2424                )));
2425            };
2426
2427            self.questions.push(DnsQuestion {
2428                entry: DnsEntry::new(name, rr_type, class),
2429            });
2430        }
2431        Ok(())
2432    }
2433
2434    fn read_answers(&mut self) -> Result<()> {
2435        self.answers = self.read_rr_records(self.num_answers)?;
2436        Ok(())
2437    }
2438
2439    fn read_authorities(&mut self) -> Result<()> {
2440        self.authorities = self.read_rr_records(self.num_authorities)?;
2441        Ok(())
2442    }
2443
2444    fn read_additional(&mut self) -> Result<()> {
2445        self.additional = self.read_rr_records(self.num_additionals)?;
2446        Ok(())
2447    }
2448
2449    /// Decodes a sequence of RR records (in answers, authorities and additionals).
2450    fn read_rr_records(&mut self, count: u16) -> Result<Vec<DnsRecordBox>> {
2451        trace!("read_rr_records: {}", count);
2452        let mut rr_records = Vec::new();
2453
2454        // RFC 1035: https://datatracker.ietf.org/doc/html/rfc1035#section-3.2.1
2455        //
2456        // All RRs have the same top level format shown below:
2457        //                               1  1  1  1  1  1
2458        // 0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
2459        // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
2460        // |                                               |
2461        // /                                               /
2462        // /                      NAME                     /
2463        // |                                               |
2464        // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
2465        // |                      TYPE                     |
2466        // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
2467        // |                     CLASS                     |
2468        // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
2469        // |                      TTL                      |
2470        // |                                               |
2471        // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
2472        // |                   RDLENGTH                    |
2473        // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
2474        // /                     RDATA                     /
2475        // /                                               /
2476        // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
2477
2478        // Muse have at least TYPE, CLASS, TTL, RDLENGTH fields: 10 bytes.
2479        const RR_HEADER_REMAIN: usize = 10;
2480
2481        for _ in 0..count {
2482            let name = self.read_name()?;
2483            let slice = &self.data[self.offset..];
2484
2485            if slice.len() < RR_HEADER_REMAIN {
2486                return Err(Error::Msg(format!(
2487                    "read_others: RR '{}' is too short after name: {} bytes",
2488                    &name,
2489                    slice.len()
2490                )));
2491            }
2492
2493            let ty = u16_from_be_slice(&slice[..2]);
2494            let class = u16_from_be_slice(&slice[2..4]);
2495            let mut ttl = u32_from_be_slice(&slice[4..8]);
2496            if ttl == 0 && self.is_response() {
2497                // RFC 6762 section 10.1:
2498                // "...Queriers receiving a Multicast DNS response with a TTL of zero SHOULD
2499                // NOT immediately delete the record from the cache, but instead record
2500                // a TTL of 1 and then delete the record one second later."
2501                // See https://datatracker.ietf.org/doc/html/rfc6762#section-10.1
2502
2503                ttl = 1;
2504            }
2505            let rdata_len = u16_from_be_slice(&slice[8..10]) as usize;
2506            self.offset += RR_HEADER_REMAIN;
2507            let next_offset = self.offset + rdata_len;
2508
2509            // Sanity check for RDATA length.
2510            if next_offset > self.data.len() {
2511                return Err(Error::Msg(format!(
2512                    "RR {name} RDATA length {rdata_len} is invalid: remain data len: {}",
2513                    self.data.len() - self.offset
2514                )));
2515            }
2516
2517            // decode RDATA based on the record type.
2518            let rec: Option<DnsRecordBox> = match RRType::from_u16(ty) {
2519                None => None,
2520
2521                Some(rr_type) => match rr_type {
2522                    RRType::CNAME | RRType::PTR => {
2523                        Some(DnsPointer::new(&name, rr_type, class, ttl, self.read_name()?).boxed())
2524                    }
2525                    RRType::TXT => {
2526                        Some(DnsTxt::new(&name, class, ttl, self.read_vec(rdata_len)?).boxed())
2527                    }
2528                    RRType::SRV => Some(
2529                        DnsSrv::new(
2530                            &name,
2531                            class,
2532                            ttl,
2533                            self.read_u16()?,
2534                            self.read_u16()?,
2535                            self.read_u16()?,
2536                            self.read_name()?,
2537                        )
2538                        .boxed(),
2539                    ),
2540                    RRType::HINFO => Some(
2541                        DnsHostInfo::new(
2542                            &name,
2543                            rr_type,
2544                            class,
2545                            ttl,
2546                            self.read_char_string()?,
2547                            self.read_char_string()?,
2548                        )
2549                        .boxed(),
2550                    ),
2551                    RRType::A => Some(
2552                        DnsAddress::new(
2553                            &name,
2554                            rr_type,
2555                            class,
2556                            ttl,
2557                            self.read_ipv4()?.into(),
2558                            self.interface_id.clone(),
2559                        )
2560                        .boxed(),
2561                    ),
2562                    RRType::AAAA => Some(
2563                        DnsAddress::new(
2564                            &name,
2565                            rr_type,
2566                            class,
2567                            ttl,
2568                            self.read_ipv6()?.into(),
2569                            self.interface_id.clone(),
2570                        )
2571                        .boxed(),
2572                    ),
2573                    RRType::NSEC => Some(
2574                        DnsNSec::new(
2575                            &name,
2576                            class,
2577                            ttl,
2578                            self.read_name()?,
2579                            self.read_type_bitmap()?,
2580                        )
2581                        .boxed(),
2582                    ),
2583                    _ => None,
2584                },
2585            };
2586
2587            if let Some(record) = rec {
2588                trace!("read_rr_records: {:?}", &record);
2589                rr_records.push(record);
2590            } else {
2591                trace!("Unsupported DNS record type: {} name: {}", ty, &name);
2592                self.offset += rdata_len;
2593            }
2594
2595            // sanity check.
2596            if self.offset != next_offset {
2597                return Err(Error::Msg(format!(
2598                    "read_rr_records: decode offset error for RData type {} offset: {} expected offset: {}",
2599                    ty, self.offset, next_offset,
2600                )));
2601            }
2602        }
2603
2604        Ok(rr_records)
2605    }
2606
2607    fn read_char_string(&mut self) -> Result<String> {
2608        let length = self.data[self.offset];
2609        self.offset += 1;
2610        self.read_string(length as usize)
2611    }
2612
2613    fn read_u16(&mut self) -> Result<u16> {
2614        let slice = &self.data[self.offset..];
2615        if slice.len() < U16_SIZE {
2616            return Err(Error::Msg(format!(
2617                "read_u16: slice len is only {}",
2618                slice.len()
2619            )));
2620        }
2621        let num = u16_from_be_slice(&slice[..U16_SIZE]);
2622        self.offset += U16_SIZE;
2623        Ok(num)
2624    }
2625
2626    /// Reads the "Type Bit Map" block for a DNS NSEC record.
2627    fn read_type_bitmap(&mut self) -> Result<Vec<u8>> {
2628        // From RFC 6762: 6.1.  Negative Responses
2629        // https://datatracker.ietf.org/doc/html/rfc6762#section-6.1
2630        //   o The Type Bit Map block number is 0.
2631        //   o The Type Bit Map block length byte is a value in the range 1-32.
2632        //   o The Type Bit Map data is 1-32 bytes, as indicated by length
2633        //     byte.
2634
2635        // Sanity check: at least 2 bytes to read.
2636        if self.data.len() < self.offset + 2 {
2637            return Err(Error::Msg(format!(
2638                "DnsIncoming is too short: {} at NSEC Type Bit Map offset {}",
2639                self.data.len(),
2640                self.offset
2641            )));
2642        }
2643
2644        let block_num = self.data[self.offset];
2645        self.offset += 1;
2646        if block_num != 0 {
2647            return Err(Error::Msg(format!(
2648                "NSEC block number is not 0: {block_num}"
2649            )));
2650        }
2651
2652        let block_len = self.data[self.offset] as usize;
2653        if !(1..=32).contains(&block_len) {
2654            return Err(Error::Msg(format!(
2655                "NSEC block length must be in the range 1-32: {block_len}"
2656            )));
2657        }
2658        self.offset += 1;
2659
2660        let end = self.offset + block_len;
2661        if end > self.data.len() {
2662            return Err(Error::Msg(format!(
2663                "NSEC block overflow: {} over RData len {}",
2664                end,
2665                self.data.len()
2666            )));
2667        }
2668        let bitmap = self.data[self.offset..end].to_vec();
2669        self.offset += block_len;
2670
2671        Ok(bitmap)
2672    }
2673
2674    fn read_vec(&mut self, length: usize) -> Result<Vec<u8>> {
2675        if self.data.len() < self.offset + length {
2676            return Err(e_fmt!(
2677                "DNS Incoming: not enough data to read a chunk of data"
2678            ));
2679        }
2680
2681        let v = self.data[self.offset..self.offset + length].to_vec();
2682        self.offset += length;
2683        Ok(v)
2684    }
2685
2686    fn read_ipv4(&mut self) -> Result<Ipv4Addr> {
2687        if self.data.len() < self.offset + 4 {
2688            return Err(e_fmt!("DNS Incoming: not enough data to read an IPV4"));
2689        }
2690
2691        let bytes: [u8; 4] = self.data[self.offset..self.offset + 4]
2692            .try_into()
2693            .map_err(|_| e_fmt!("DNS incoming: Not enough bytes for reading an IPV4"))?;
2694        self.offset += bytes.len();
2695        Ok(Ipv4Addr::from(bytes))
2696    }
2697
2698    fn read_ipv6(&mut self) -> Result<Ipv6Addr> {
2699        if self.data.len() < self.offset + 16 {
2700            return Err(e_fmt!("DNS Incoming: not enough data to read an IPV6"));
2701        }
2702
2703        let bytes: [u8; 16] = self.data[self.offset..self.offset + 16]
2704            .try_into()
2705            .map_err(|_| e_fmt!("DNS incoming: Not enough bytes for reading an IPV6"))?;
2706        self.offset += bytes.len();
2707        Ok(Ipv6Addr::from(bytes))
2708    }
2709
2710    fn read_string(&mut self, length: usize) -> Result<String> {
2711        if self.data.len() < self.offset + length {
2712            return Err(e_fmt!("DNS Incoming: not enough data to read a string"));
2713        }
2714
2715        let s = str::from_utf8(&self.data[self.offset..self.offset + length])
2716            .map_err(|e| Error::Msg(e.to_string()))?;
2717        self.offset += length;
2718        Ok(s.to_string())
2719    }
2720
2721    /// Reads a domain name at the current location of `self.data`.
2722    ///
2723    /// See https://datatracker.ietf.org/doc/html/rfc1035#section-3.1 for
2724    /// domain name encoding.
2725    fn read_name(&mut self) -> Result<String> {
2726        let data = &self.data[..];
2727        let start_offset = self.offset;
2728        let mut offset = start_offset;
2729        let mut name = "".to_string();
2730        let mut at_end = false;
2731
2732        // From RFC1035:
2733        // "...Domain names in messages are expressed in terms of a sequence of labels.
2734        // Each label is represented as a one octet length field followed by that
2735        // number of octets."
2736        //
2737        // "...The compression scheme allows a domain name in a message to be
2738        // represented as either:
2739        // - a sequence of labels ending in a zero octet
2740        // - a pointer
2741        // - a sequence of labels ending with a pointer"
2742        loop {
2743            if offset >= data.len() {
2744                return Err(Error::Msg(format!(
2745                    "read_name: offset: {} data len {}. DnsIncoming: {:?}",
2746                    offset,
2747                    data.len(),
2748                    self
2749                )));
2750            }
2751            let length = data[offset];
2752
2753            // From RFC1035:
2754            // "...Since every domain name ends with the null label of
2755            // the root, a domain name is terminated by a length byte of zero."
2756            if length == 0 {
2757                if !at_end {
2758                    self.offset = offset + 1;
2759                }
2760                break; // The end of the name
2761            }
2762
2763            // Check the first 2 bits for possible "Message compression".
2764            match length & 0xC0 {
2765                0x00 => {
2766                    // regular utf8 string with length
2767                    offset += 1;
2768                    let ending = offset + length as usize;
2769
2770                    // Never read beyond the whole data length.
2771                    if ending > data.len() {
2772                        return Err(Error::Msg(format!(
2773                            "read_name: ending {} exceeds data length {}",
2774                            ending,
2775                            data.len()
2776                        )));
2777                    }
2778
2779                    name += str::from_utf8(&data[offset..ending])
2780                        .map_err(|e| Error::Msg(format!("read_name: from_utf8: {e}")))?;
2781                    name += ".";
2782                    offset += length as usize;
2783                }
2784                0xC0 => {
2785                    // Message compression.
2786                    // See https://datatracker.ietf.org/doc/html/rfc1035#section-4.1.4
2787                    let slice = &data[offset..];
2788                    if slice.len() < U16_SIZE {
2789                        return Err(Error::Msg(format!(
2790                            "read_name: u16 slice len is only {}",
2791                            slice.len()
2792                        )));
2793                    }
2794                    let pointer = (u16_from_be_slice(slice) ^ 0xC000) as usize;
2795                    if pointer >= start_offset {
2796                        // Error: could trigger an infinite loop.
2797                        return Err(Error::Msg(format!(
2798                            "Invalid name compression: pointer {} must be less than the start offset {}",
2799                            &pointer, &start_offset
2800                        )));
2801                    }
2802
2803                    // A pointer marks the end of a domain name.
2804                    if !at_end {
2805                        self.offset = offset + U16_SIZE;
2806                        at_end = true;
2807                    }
2808                    offset = pointer;
2809                }
2810                _ => {
2811                    return Err(Error::Msg(format!(
2812                        "Bad name with invalid length: 0x{:x} offset {}, data (so far): {:x?}",
2813                        length,
2814                        offset,
2815                        &data[..offset]
2816                    )));
2817                }
2818            };
2819        }
2820
2821        Ok(name)
2822    }
2823}
2824
2825const fn u16_from_be_slice(bytes: &[u8]) -> u16 {
2826    let u8_array: [u8; 2] = [bytes[0], bytes[1]];
2827    u16::from_be_bytes(u8_array)
2828}
2829
2830const fn u32_from_be_slice(s: &[u8]) -> u32 {
2831    let u8_array: [u8; 4] = [s[0], s[1], s[2], s[3]];
2832    u32::from_be_bytes(u8_array)
2833}
2834
2835/// Returns the UNIX time in millis at which this record will have expired
2836/// by a certain percentage.
2837const fn get_expiration_time(created: u64, ttl: u32, percent: u32) -> u64 {
2838    // 'created' is in millis, 'ttl' is in seconds, hence:
2839    // ttl * 1000 * (percent / 100) => ttl * percent * 10
2840    created + (ttl as u64 * percent as u64 * 10)
2841}
2842
2843#[cfg(test)]
2844mod tests {
2845    use super::{
2846        DnsAddress, DnsHostInfo, DnsIncoming, DnsOutPacket, DnsOutgoing, DnsPointer, DnsTxt,
2847        RRType, CLASS_CACHE_FLUSH, CLASS_IN, FLAGS_QR_QUERY, FLAGS_QR_RESPONSE, FLAGS_TC,
2848        MAX_PKT_ABSOLUTE_IPV6, MAX_PKT_DEFAULT, MSG_HEADER_LEN,
2849    };
2850    use crate::InterfaceId;
2851    use std::collections::HashMap;
2852    use std::net::{IpAddr, Ipv4Addr};
2853
2854    /// The `is_ipv4` argument of `to_packets`. IPv6 has the smaller of the two
2855    /// absolute ceilings, so it is the stricter one to encode for.
2856    const IPV6: bool = false;
2857
2858    #[test]
2859    fn test_dns_outgoing_serialization_empty() {
2860        let out = DnsOutgoing::new(0);
2861        let packets = out.to_packets(MAX_PKT_DEFAULT, IPV6);
2862        assert_eq!(packets.len(), 1);
2863        assert_eq!(packets[0].as_bytes(), &[0; 12]);
2864        let expected_names = HashMap::new();
2865        assert_eq!(&packets[0].names, &expected_names);
2866    }
2867
2868    #[test]
2869    fn test_dns_outgoing_serialization_question() {
2870        let mut out = DnsOutgoing::new(0);
2871        out.add_question("123.test", RRType::A);
2872        let packets = out.to_packets(MAX_PKT_DEFAULT, IPV6);
2873        assert_eq!(packets.len(), 1);
2874        assert_eq!(
2875            packets[0].as_bytes(),
2876            &[
2877                0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, // Header
2878                // Payload
2879                3, 49, 50, 51, 4, 116, 101, 115, 116, 0, 0, 1, 0, 1,
2880            ]
2881        );
2882        let mut expected_names = HashMap::new();
2883        expected_names.insert("123.test".to_string(), 12);
2884        expected_names.insert("test".to_string(), 16);
2885        assert_eq!(&packets[0].names, &expected_names);
2886    }
2887
2888    #[test]
2889    fn test_dns_outgoing_serialization_question_with_authority() {
2890        let mut out = DnsOutgoing::new(0);
2891        out.add_question("123.test", RRType::ANY);
2892        out.add_authority(Box::new(DnsTxt::new(
2893            "124.test",
2894            CLASS_IN,
2895            0x00112233,
2896            b"help".to_vec(),
2897        )));
2898        out.add_authority(Box::new(DnsHostInfo::new(
2899            "124.test",
2900            RRType::CNAME,
2901            CLASS_IN,
2902            0x00112233,
2903            "arm".to_string(),
2904            "linux".to_string(),
2905        )));
2906        let packets = out.to_packets(MAX_PKT_DEFAULT, IPV6);
2907        assert_eq!(packets.len(), 1);
2908        assert_eq!(
2909            packets[0].as_bytes(),
2910            &[
2911                0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, // Header
2912                // Payload
2913                3, 49, 50, 51, 4, 116, 101, 115, 116, 0, 0, 255, 0, 1, 3, 49, 50, 52, 192, 16, 0,
2914                16, 0, 1, 0, 17, 34, 51, 0, 4, 104, 101, 108, 112, 192, 26, 0, 5, 0, 1, 0, 17, 34,
2915                51, 0, 8, 97, 114, 109, 108, 105, 110, 117, 120,
2916            ]
2917        );
2918        let mut expected_names = HashMap::new();
2919        expected_names.insert("123.test".to_string(), 12);
2920        expected_names.insert("test".to_string(), 16);
2921        expected_names.insert("124.test".to_string(), 26);
2922        assert_eq!(&packets[0].names, &expected_names);
2923    }
2924
2925    #[test]
2926    fn test_dns_outgoing_serialization_additional_answer() {
2927        let mut out = DnsOutgoing::new(0);
2928        out.add_additional_answer(DnsAddress::new(
2929            "test.local",
2930            RRType::A,
2931            CLASS_IN | CLASS_CACHE_FLUSH,
2932            0xdead_beef,
2933            IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
2934            InterfaceId::default(),
2935        ));
2936        let packets = out.to_packets(MAX_PKT_DEFAULT, IPV6);
2937        assert_eq!(packets.len(), 1);
2938        assert_eq!(
2939            packets[0].as_bytes(),
2940            &[
2941                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, // Header
2942                // Payload
2943                4, 116, 101, 115, 116, 5, 108, 111, 99, 97, 108, 0, 0, 1, 128, 1, 222, 173, 190,
2944                239, 0, 4, 127, 0, 0, 1,
2945            ]
2946        );
2947        let mut expected_names = HashMap::new();
2948        expected_names.insert("test.local".to_string(), 12);
2949        expected_names.insert("local".to_string(), 17);
2950        assert_eq!(&packets[0].names, &expected_names);
2951    }
2952
2953    #[test]
2954    fn test_dns_outgoing_serialization_answer_at_time() {
2955        let mut out = DnsOutgoing::new(0);
2956        out.add_answer_at_time(
2957            DnsPointer::new(
2958                "test",
2959                RRType::PTR,
2960                CLASS_IN,
2961                0xaaaa5555,
2962                "test-service".to_string(),
2963            ),
2964            0,
2965        );
2966        let packets = out.to_packets(MAX_PKT_DEFAULT, IPV6);
2967        assert_eq!(packets.len(), 1);
2968        assert_eq!(
2969            packets[0].as_bytes(),
2970            &[
2971                0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, // Header
2972                // Payload
2973                4, 116, 101, 115, 116, 0, 0, 12, 0, 1, 170, 170, 85, 85, 0, 14, 12, 116, 101, 115,
2974                116, 45, 115, 101, 114, 118, 105, 99, 101, 0,
2975            ]
2976        );
2977
2978        let mut out = DnsOutgoing::new(0);
2979        out.add_answer_at_time(
2980            DnsPointer::new(
2981                "test",
2982                RRType::CNAME,
2983                CLASS_IN,
2984                0xaaaa5555,
2985                "test-service.local".to_string(),
2986            ),
2987            0,
2988        );
2989        out.add_answer_at_time(
2990            DnsPointer::new(
2991                "test",
2992                RRType::AAAA,
2993                CLASS_IN,
2994                0xffffffff,
2995                "test-service.local".to_string(),
2996            ),
2997            0,
2998        );
2999        let packets = out.to_packets(MAX_PKT_DEFAULT, IPV6);
3000        assert_eq!(packets.len(), 1);
3001        assert_eq!(
3002            packets[0].as_bytes(),
3003            &[
3004                0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, // Header
3005                // Payload
3006                4, 116, 101, 115, 116, 0, 0, 5, 0, 1, 170, 170, 85, 85, 0, 20, 12, 116, 101, 115,
3007                116, 45, 115, 101, 114, 118, 105, 99, 101, 5, 108, 111, 99, 97, 108, 0, 192, 12, 0,
3008                28, 0, 1, 255, 255, 255, 255, 0, 2, 192, 28,
3009            ]
3010        );
3011        let mut expected_names = HashMap::new();
3012        expected_names.insert("test".to_string(), 12);
3013        expected_names.insert("test-service.local".to_string(), 28);
3014        expected_names.insert("local".to_string(), 41);
3015        assert_eq!(&packets[0].names, &expected_names);
3016    }
3017
3018    /// A question whose name has a label longer than 63 bytes cannot be
3019    /// encoded. It must be skipped, not panic. (Note the question count in the
3020    /// header must reflect the questions actually written.)
3021    #[test]
3022    fn test_dns_outgoing_question_label_too_long() {
3023        let long_label = "a".repeat(64);
3024        let mut out = DnsOutgoing::new(0);
3025        out.add_question(&format!("{long_label}.local"), RRType::PTR);
3026        out.add_question("123.test", RRType::A);
3027
3028        let packets = out.to_packets(MAX_PKT_DEFAULT, IPV6);
3029        assert_eq!(packets.len(), 1);
3030        assert_eq!(
3031            packets[0].as_bytes(),
3032            &[
3033                0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, // Header: 1 question
3034                // Payload: only "123.test" made it in.
3035                3, 49, 50, 51, 4, 116, 101, 115, 116, 0, 0, 1, 0, 1,
3036            ]
3037        );
3038
3039        // The rolled back name must not leave a stale compression offset behind.
3040        let mut expected_names = HashMap::new();
3041        expected_names.insert("123.test".to_string(), 12);
3042        expected_names.insert("test".to_string(), 16);
3043        assert_eq!(&packets[0].names, &expected_names);
3044    }
3045
3046    /// A record whose rdata carries an unencodable name (here a PTR alias) is
3047    /// dropped as a whole, leaving the rest of the packet intact.
3048    #[test]
3049    fn test_dns_outgoing_record_label_too_long() {
3050        let long_label = "a".repeat(64);
3051        let mut out = DnsOutgoing::new(0);
3052        out.add_answer_at_time(
3053            DnsPointer::new(
3054                "_test._tcp.local.",
3055                RRType::PTR,
3056                CLASS_IN,
3057                0,
3058                format!("{long_label}._test._tcp.local."),
3059            ),
3060            0,
3061        );
3062        out.add_answer_at_time(
3063            DnsPointer::new(
3064                "_test._tcp.local.",
3065                RRType::PTR,
3066                CLASS_IN,
3067                0,
3068                "ok._test._tcp.local.".to_string(),
3069            ),
3070            0,
3071        );
3072
3073        let packets = out.to_packets(MAX_PKT_DEFAULT, IPV6);
3074        assert_eq!(packets.len(), 1);
3075
3076        // Header answer count is 1: the first answer was dropped.
3077        assert_eq!(&packets[0].as_bytes()[6..8], &[0, 1]);
3078
3079        // Re-parsing must succeed and yield only the good answer.
3080        let incoming = DnsIncoming::new(
3081            packets[0].as_bytes().to_vec(),
3082            InterfaceId {
3083                name: "test".to_string(),
3084                index: 1,
3085            },
3086        )
3087        .unwrap();
3088        assert_eq!(incoming.answers().len(), 1);
3089    }
3090
3091    /// A name learned from the network can hold a label that ends with a
3092    /// backslash, which escapes the following label separator. Unescaping such
3093    /// a name on the way out merges two 63-byte labels into a 127-byte one.
3094    /// This used to panic the daemon thread. See issue #483.
3095    #[test]
3096    fn test_incoming_name_with_merged_labels_does_not_panic() {
3097        // A query with one question: "aa..a\" + "bb..b", 63 bytes each.
3098        let mut data: Vec<u8> = vec![0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0];
3099        data.push(63);
3100        data.extend(vec![b'a'; 62]);
3101        data.push(b'\\');
3102        data.push(63);
3103        data.extend(vec![b'b'; 63]);
3104        data.push(0);
3105        data.extend([0, 12, 0, 1]); // PTR, IN
3106
3107        let incoming = DnsIncoming::new(
3108            data,
3109            InterfaceId {
3110                name: "test".to_string(),
3111                index: 1,
3112            },
3113        )
3114        .unwrap();
3115        let name = incoming.questions()[0].entry.name.clone();
3116
3117        // The two labels merged: the trailing backslash escaped the separator.
3118        assert!(name.starts_with("aaa"));
3119        assert!(name.contains("\\.bbb"));
3120
3121        // Re-emitting it must drop the question rather than panic.
3122        let mut out = DnsOutgoing::new(0);
3123        out.add_question(&name, RRType::PTR);
3124        let packets = out.to_packets(MAX_PKT_DEFAULT, IPV6);
3125        assert_eq!(packets.len(), 1);
3126        assert_eq!(packets[0].as_bytes(), &[0; MSG_HEADER_LEN]);
3127    }
3128
3129    fn test_interface_id() -> InterfaceId {
3130        InterfaceId {
3131            name: "test".to_string(),
3132            index: 1,
3133        }
3134    }
3135
3136    /// The "flags" field of a finished packet.
3137    fn packet_flags(packet: &DnsOutPacket) -> u16 {
3138        let bytes = packet.as_bytes();
3139        u16::from_be_bytes([bytes[2], bytes[3]])
3140    }
3141
3142    fn ptr_answer(index: usize) -> DnsPointer {
3143        DnsPointer::new(
3144            "_spill._tcp.local.",
3145            RRType::PTR,
3146            CLASS_IN,
3147            4500,
3148            format!("instance-{index:04}._spill._tcp.local."),
3149        )
3150    }
3151
3152    /// Re-parses each packet and returns the total number of answers found, which
3153    /// checks the header counts against what each packet actually holds.
3154    fn parsed_answer_count(packets: &[DnsOutPacket]) -> usize {
3155        packets
3156            .iter()
3157            .map(|packet: &DnsOutPacket| {
3158                let parsed = DnsIncoming::new(packet.as_bytes().to_vec(), test_interface_id())
3159                    .expect("each packet must parse on its own");
3160                assert!(
3161                    !parsed.answers().is_empty(),
3162                    "a spilled packet must not be empty"
3163                );
3164                parsed.answers().len()
3165            })
3166            .sum()
3167    }
3168
3169    /// A response too big for one packet spills into more packets. Every record
3170    /// must survive: before, records that did not fit were silently dropped.
3171    #[test]
3172    fn test_dns_outgoing_response_spills_into_packets() {
3173        const ANSWER_COUNT: usize = 100;
3174
3175        let mut out = DnsOutgoing::new(FLAGS_QR_RESPONSE);
3176        for i in 0..ANSWER_COUNT {
3177            out.add_answer_at_time(ptr_answer(i), 0);
3178        }
3179
3180        let packets = out.to_packets(MAX_PKT_DEFAULT, IPV6);
3181        assert!(
3182            packets.len() > 1,
3183            "{} answers should not fit in one packet",
3184            ANSWER_COUNT
3185        );
3186
3187        for packet in &packets {
3188            assert!(
3189                packet.size() <= MAX_PKT_DEFAULT,
3190                "packet of {} bytes exceeds the limit",
3191                packet.size()
3192            );
3193
3194            // A multi-packet response is a series of independent responses: unlike
3195            // a query's known answers, it does not use the TC bit.
3196            assert_eq!(packet_flags(packet) & FLAGS_TC, 0);
3197        }
3198
3199        assert_eq!(parsed_answer_count(&packets), ANSWER_COUNT);
3200    }
3201
3202    /// RFC 6762 section 7.2: a querier sending known answers in more than one
3203    /// packet sets the TC bit in every packet but the last.
3204    #[test]
3205    fn test_dns_outgoing_query_truncation_bit() {
3206        let mut out = DnsOutgoing::new(FLAGS_QR_QUERY);
3207        out.add_question("_spill._tcp.local.", RRType::PTR);
3208        for i in 0..100 {
3209            out.add_answer_box(Box::new(ptr_answer(i)));
3210        }
3211
3212        let packets = out.to_packets(MAX_PKT_DEFAULT, IPV6);
3213        assert!(
3214            packets.len() > 1,
3215            "known answers should not fit in one packet"
3216        );
3217
3218        let (last, rest) = packets.split_last().expect("at least one packet");
3219        for packet in rest {
3220            assert_ne!(
3221                packet_flags(packet) & FLAGS_TC,
3222                0,
3223                "a packet with more known answers to follow must set TC"
3224            );
3225        }
3226        assert_eq!(
3227            packet_flags(last) & FLAGS_TC,
3228            0,
3229            "the last packet must not set TC"
3230        );
3231
3232        // The question goes in the first packet only, and no answer is lost.
3233        assert_eq!(packets[0].as_bytes()[4..6], 1u16.to_be_bytes());
3234        for packet in rest.iter().skip(1) {
3235            assert_eq!(packet.as_bytes()[4..6], [0, 0]);
3236        }
3237        assert_eq!(parsed_answer_count(&packets), 100);
3238    }
3239
3240    /// RFC 6762 section 17: a record too large for one MTU-sized packet is sent
3241    /// alone in an oversized packet, rather than dropped. It must be alone, since
3242    /// a fragmented packet "MUST NOT contain more than one resource record".
3243    #[test]
3244    fn test_dns_outgoing_oversized_record_sent_alone() {
3245        let mut out = DnsOutgoing::new(FLAGS_QR_RESPONSE);
3246        out.add_answer_at_time(ptr_answer(0), 0);
3247        out.add_answer_at_time(
3248            DnsTxt::new("big._spill._tcp.local.", CLASS_IN, 4500, vec![b'x'; 2000]),
3249            0,
3250        );
3251        out.add_answer_at_time(ptr_answer(1), 0);
3252
3253        let packets = out.to_packets(MAX_PKT_DEFAULT, IPV6);
3254        assert_eq!(packets.len(), 3, "the big record needs a packet to itself");
3255
3256        assert!(packets[0].size() <= MAX_PKT_DEFAULT);
3257        assert!(
3258            packets[1].size() > MAX_PKT_DEFAULT,
3259            "the oversized record must not be dropped"
3260        );
3261        // Still small enough that the send path will let it out.
3262        assert!(packets[1].size() <= MAX_PKT_ABSOLUTE_IPV6);
3263        assert!(packets[2].size() <= MAX_PKT_DEFAULT);
3264
3265        // One record per packet here, the middle one being the big TXT.
3266        let parsed = DnsIncoming::new(packets[1].as_bytes().to_vec(), test_interface_id()).unwrap();
3267        assert_eq!(parsed.answers().len(), 1);
3268        assert_eq!(parsed.answers()[0].get_name(), "big._spill._tcp.local.");
3269        assert_eq!(parsed_answer_count(&packets), 3);
3270    }
3271
3272    /// A record over the RFC 6762 section 17 ceiling could not go out on the wire
3273    /// even in a packet of its own, so it is dropped while its neighbors survive.
3274    #[test]
3275    fn test_dns_outgoing_record_over_absolute_ceiling_dropped() {
3276        let mut out = DnsOutgoing::new(FLAGS_QR_RESPONSE);
3277        out.add_answer_at_time(ptr_answer(0), 0);
3278        out.add_answer_at_time(
3279            DnsTxt::new(
3280                "huge._spill._tcp.local.",
3281                CLASS_IN,
3282                4500,
3283                vec![b'x'; MAX_PKT_ABSOLUTE_IPV6],
3284            ),
3285            0,
3286        );
3287        out.add_answer_at_time(ptr_answer(1), 0);
3288
3289        let packets = out.to_packets(MAX_PKT_DEFAULT, IPV6);
3290        for packet in &packets {
3291            assert!(
3292                packet.size() <= MAX_PKT_ABSOLUTE_IPV6,
3293                "an unsendable packet must never be generated"
3294            );
3295        }
3296        assert_eq!(
3297            parsed_answer_count(&packets),
3298            2,
3299            "only the huge record is dropped"
3300        );
3301    }
3302
3303    /// Authorities and additionals spill too, and stay in their own sections.
3304    #[test]
3305    fn test_dns_outgoing_all_sections_spill() {
3306        let mut out = DnsOutgoing::new(FLAGS_QR_RESPONSE);
3307        for i in 0..40 {
3308            out.add_answer_at_time(ptr_answer(i), 0);
3309        }
3310        for i in 40..80 {
3311            out.add_authority(Box::new(ptr_answer(i)));
3312        }
3313        for i in 80..120 {
3314            out.add_additional_answer(ptr_answer(i));
3315        }
3316
3317        let packets = out.to_packets(MAX_PKT_DEFAULT, IPV6);
3318        assert!(packets.len() > 1);
3319
3320        let mut answers = 0;
3321        let mut authorities = 0;
3322        let mut additionals = 0;
3323        for packet in &packets {
3324            assert!(packet.size() <= MAX_PKT_DEFAULT);
3325            let parsed = DnsIncoming::new(packet.as_bytes().to_vec(), test_interface_id()).unwrap();
3326            answers += parsed.answers().len();
3327            authorities += parsed.authorities().len();
3328            additionals += parsed.additionals().len();
3329        }
3330
3331        assert_eq!(answers, 40);
3332        assert_eq!(authorities, 40);
3333        assert_eq!(additionals, 40);
3334    }
3335}