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