mdns_parser/
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::trace;
9
10use std::{
11    any::Any,
12    cmp,
13    collections::HashMap,
14    convert::TryInto,
15    fmt,
16    net::{IpAddr, Ipv4Addr, Ipv6Addr},
17    str,
18    time::SystemTime,
19};
20
21/// DNS resource record types, stored as `u16`. Can do `as u16` when needed.
22///
23/// See [RFC 1035 section 3.2.2](https://datatracker.ietf.org/doc/html/rfc1035#section-3.2.2)
24#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
25#[non_exhaustive]
26#[repr(u16)]
27pub enum RRType {
28    /// DNS record type for IPv4 address
29    A = 1,
30
31    /// DNS record type for Canonical Name
32    CNAME = 5,
33
34    /// DNS record type for Pointer
35    PTR = 12,
36
37    /// DNS record type for Host Info
38    HINFO = 13,
39
40    /// DNS record type for Text (properties)
41    TXT = 16,
42
43    /// DNS record type for IPv6 address
44    AAAA = 28,
45
46    /// DNS record type for Service
47    SRV = 33,
48
49    /// DNS record type for Negative Responses
50    NSEC = 47,
51
52    /// DNS record type for any records (wildcard)
53    ANY = 255,
54}
55
56impl RRType {
57    /// Converts `u16` into `RRType` if possible.
58    pub const fn from_u16(value: u16) -> Option<RRType> {
59        match value {
60            1 => Some(RRType::A),
61            5 => Some(RRType::CNAME),
62            12 => Some(RRType::PTR),
63            13 => Some(RRType::HINFO),
64            16 => Some(RRType::TXT),
65            28 => Some(RRType::AAAA),
66            33 => Some(RRType::SRV),
67            47 => Some(RRType::NSEC),
68            255 => Some(RRType::ANY),
69            _ => None,
70        }
71    }
72}
73
74impl fmt::Display for RRType {
75    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76        match self {
77            RRType::A => write!(f, "TYPE_A"),
78            RRType::CNAME => write!(f, "TYPE_CNAME"),
79            RRType::PTR => write!(f, "TYPE_PTR"),
80            RRType::HINFO => write!(f, "TYPE_HINFO"),
81            RRType::TXT => write!(f, "TYPE_TXT"),
82            RRType::AAAA => write!(f, "TYPE_AAAA"),
83            RRType::SRV => write!(f, "TYPE_SRV"),
84            RRType::NSEC => write!(f, "TYPE_NSEC"),
85            RRType::ANY => write!(f, "TYPE_ANY"),
86        }
87    }
88}
89
90/// The class value for the Internet.
91pub const CLASS_IN: u16 = 1;
92pub const CLASS_MASK: u16 = 0x7FFF;
93
94/// Cache-flush bit: the most significant bit of the rrclass field of the resource record.  
95pub const CLASS_CACHE_FLUSH: u16 = 0x8000;
96
97/// Max size of UDP datagram payload.
98///
99/// It is calculated as: 9000 bytes - IP header 20 bytes - UDP header 8 bytes.
100/// Reference: [RFC6762 section 17](https://datatracker.ietf.org/doc/html/rfc6762#section-17)
101pub const MAX_MSG_ABSOLUTE: usize = 8972;
102
103const MSG_HEADER_LEN: usize = 12;
104
105// Definitions for DNS message header "flags" field
106//
107// The "flags" field is 16-bit long, in this format:
108// (RFC 1035 section 4.1.1)
109//
110//   0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
111// |QR|   Opcode  |AA|TC|RD|RA|   Z    |   RCODE   |
112//
113pub const FLAGS_QR_MASK: u16 = 0x8000; // mask for query/response bit
114
115/// Flag bit to indicate a query
116pub const FLAGS_QR_QUERY: u16 = 0x0000;
117
118/// Flag bit to indicate a response
119pub const FLAGS_QR_RESPONSE: u16 = 0x8000;
120
121/// Flag bit for Authoritative Answer
122pub const FLAGS_AA: u16 = 0x0400;
123
124/// mask for TC(Truncated) bit
125///
126/// 2024-08-10: currently this flag is only supported on the querier side,
127///             not supported on the responder side. I.e. the responder only
128///             handles the first packet and ignore this bit. Since the
129///             additional packets have 0 questions, the processing of them
130///             is no-op.
131///             In practice, this means the responder supports Known-Answer
132///             only with single packet, not multi-packet. The querier supports
133///             both single packet and multi-packet.
134pub const FLAGS_TC: u16 = 0x0200;
135
136/// A convenience type alias for DNS record trait objects.
137pub type DnsRecordBox = Box<dyn DnsRecordExt>;
138
139impl Clone for DnsRecordBox {
140    fn clone(&self) -> Self {
141        self.clone_box()
142    }
143}
144
145const U16_SIZE: usize = 2;
146
147/// Returns `RRType` for a given IP address.
148#[inline]
149pub const fn ip_address_rr_type(address: &IpAddr) -> RRType {
150    match address {
151        IpAddr::V4(_) => RRType::A,
152        IpAddr::V6(_) => RRType::AAAA,
153    }
154}
155
156#[derive(Eq, PartialEq, Debug, Clone)]
157pub struct DnsEntry {
158    pub(crate) name: String, // always lower case.
159    pub(crate) ty: RRType,
160    class: u16,
161    cache_flush: bool,
162}
163
164impl DnsEntry {
165    const fn new(name: String, ty: RRType, class: u16) -> Self {
166        Self {
167            name,
168            ty,
169            class: class & CLASS_MASK,
170            cache_flush: (class & CLASS_CACHE_FLUSH) != 0,
171        }
172    }
173}
174
175/// Common methods for all DNS entries:  questions and resource records.
176pub trait DnsEntryExt: fmt::Debug {
177    fn entry_name(&self) -> &str;
178
179    fn entry_type(&self) -> RRType;
180}
181
182/// A DNS question entry
183#[derive(Debug)]
184pub struct DnsQuestion {
185    pub(crate) entry: DnsEntry,
186}
187
188impl DnsEntryExt for DnsQuestion {
189    fn entry_name(&self) -> &str {
190        &self.entry.name
191    }
192
193    fn entry_type(&self) -> RRType {
194        self.entry.ty
195    }
196}
197
198/// A DNS Resource Record - like a DNS entry, but has a TTL.
199/// RFC: https://www.rfc-editor.org/rfc/rfc1035#section-3.2.1
200///      https://www.rfc-editor.org/rfc/rfc1035#section-4.1.3
201#[derive(Debug, Clone)]
202pub struct DnsRecord {
203    pub(crate) entry: DnsEntry,
204    ttl: u32,     // in seconds, 0 means this record should not be cached
205    created: u64, // UNIX time in millis
206    expires: u64, // expires at this UNIX time in millis
207
208    /// Support re-query an instance before its PTR record expires.
209    /// See https://datatracker.ietf.org/doc/html/rfc6762#section-5.2
210    refresh: u64, // UNIX time in millis
211
212    /// If conflict resolution decides to change the name, this is the new one.
213    new_name: Option<String>,
214}
215
216impl DnsRecord {
217    fn new(name: &str, ty: RRType, class: u16, ttl: u32) -> Self {
218        let created = current_time_millis();
219
220        // From RFC 6762 section 5.2:
221        // "... The querier should plan to issue a query at 80% of the record
222        // lifetime, and then if no answer is received, at 85%, 90%, and 95%."
223        let refresh = get_expiration_time(created, ttl, 80);
224
225        let expires = get_expiration_time(created, ttl, 100);
226
227        Self {
228            entry: DnsEntry::new(name.to_string(), ty, class),
229            ttl,
230            created,
231            expires,
232            refresh,
233            new_name: None,
234        }
235    }
236
237    pub const fn get_ttl(&self) -> u32 {
238        self.ttl
239    }
240
241    pub const fn get_expire_time(&self) -> u64 {
242        self.expires
243    }
244
245    pub const fn get_refresh_time(&self) -> u64 {
246        self.refresh
247    }
248
249    pub const fn is_expired(&self, now: u64) -> bool {
250        now >= self.expires
251    }
252
253    pub const fn refresh_due(&self, now: u64) -> bool {
254        now >= self.refresh
255    }
256
257    /// Returns whether `now` (in millis) has passed half of TTL.
258    pub fn halflife_passed(&self, now: u64) -> bool {
259        let halflife = get_expiration_time(self.created, self.ttl, 50);
260        now > halflife
261    }
262
263    pub fn is_unique(&self) -> bool {
264        self.entry.cache_flush
265    }
266
267    /// Updates the refresh time to be the same as the expire time so that
268    /// this record will not refresh again and will just expire.
269    pub fn refresh_no_more(&mut self) {
270        self.refresh = get_expiration_time(self.created, self.ttl, 100);
271    }
272
273    /// Returns if this record is due for refresh. If yes, `refresh` time is updated.
274    pub fn refresh_maybe(&mut self, now: u64) -> bool {
275        if self.is_expired(now) || !self.refresh_due(now) {
276            return false;
277        }
278
279        trace!(
280            "{} qtype {} is due to refresh",
281            &self.entry.name,
282            self.entry.ty
283        );
284
285        // From RFC 6762 section 5.2:
286        // "... The querier should plan to issue a query at 80% of the record
287        // lifetime, and then if no answer is received, at 85%, 90%, and 95%."
288        //
289        // If the answer is received in time, 'refresh' will be reset outside
290        // this function, back to 80% of the new TTL.
291        if self.refresh == get_expiration_time(self.created, self.ttl, 80) {
292            self.refresh = get_expiration_time(self.created, self.ttl, 85);
293        } else if self.refresh == get_expiration_time(self.created, self.ttl, 85) {
294            self.refresh = get_expiration_time(self.created, self.ttl, 90);
295        } else if self.refresh == get_expiration_time(self.created, self.ttl, 90) {
296            self.refresh = get_expiration_time(self.created, self.ttl, 95);
297        } else {
298            self.refresh_no_more();
299        }
300
301        true
302    }
303
304    /// Returns the remaining TTL in seconds
305    fn get_remaining_ttl(&self, now: u64) -> u32 {
306        let remaining_millis = get_expiration_time(self.created, self.ttl, 100) - now;
307        cmp::max(0, remaining_millis / 1000) as u32
308    }
309
310    /// Return the absolute time for this record being created
311    pub const fn get_created(&self) -> u64 {
312        self.created
313    }
314
315    /// Set the absolute expiration time in millis
316    fn set_expire(&mut self, expire_at: u64) {
317        self.expires = expire_at;
318    }
319
320    fn reset_ttl(&mut self, other: &Self) {
321        self.ttl = other.ttl;
322        self.created = other.created;
323        self.refresh = get_expiration_time(self.created, self.ttl, 80);
324        self.expires = get_expiration_time(self.created, self.ttl, 100);
325    }
326
327    /// Modify TTL to reflect the remaining life time from `now`.
328    pub fn update_ttl(&mut self, now: u64) {
329        if now > self.created {
330            let elapsed = now - self.created;
331            self.ttl -= (elapsed / 1000) as u32;
332        }
333    }
334
335    pub fn set_new_name(&mut self, new_name: String) {
336        if new_name == self.entry.name {
337            self.new_name = None;
338        } else {
339            self.new_name = Some(new_name);
340        }
341    }
342
343    pub fn get_new_name(&self) -> Option<&str> {
344        self.new_name.as_deref()
345    }
346
347    /// Return the new name if exists, otherwise the regular name in DnsEntry.
348    pub(crate) fn get_name(&self) -> &str {
349        self.new_name.as_deref().unwrap_or(&self.entry.name)
350    }
351
352    pub fn get_original_name(&self) -> &str {
353        &self.entry.name
354    }
355}
356
357impl PartialEq for DnsRecord {
358    fn eq(&self, other: &Self) -> bool {
359        self.entry == other.entry
360    }
361}
362
363/// Common methods for DNS resource records.
364pub trait DnsRecordExt: fmt::Debug {
365    fn get_record(&self) -> &DnsRecord;
366    fn get_record_mut(&mut self) -> &mut DnsRecord;
367    fn write(&self, packet: &mut DnsOutPacket);
368    fn any(&self) -> &dyn Any;
369
370    /// Returns whether `other` record is considered the same except TTL.
371    fn matches(&self, other: &dyn DnsRecordExt) -> bool;
372
373    /// Returns whether `other` record has the same rdata.
374    fn rrdata_match(&self, other: &dyn DnsRecordExt) -> bool;
375
376    /// Returns the result based on a byte-level comparison of `rdata`.
377    /// If `other` is not valid, returns `Greater`.
378    fn compare_rdata(&self, other: &dyn DnsRecordExt) -> cmp::Ordering;
379
380    /// Returns the result based on "lexicographically later" defined below.
381    fn compare(&self, other: &dyn DnsRecordExt) -> cmp::Ordering {
382        /*
383        RFC 6762: https://datatracker.ietf.org/doc/html/rfc6762#section-8.2
384
385        ... The determination of "lexicographically later" is performed by first
386        comparing the record class (excluding the cache-flush bit described
387        in Section 10.2), then the record type, then raw comparison of the
388        binary content of the rdata without regard for meaning or structure.
389        If the record classes differ, then the numerically greater class is
390        considered "lexicographically later".  Otherwise, if the record types
391        differ, then the numerically greater type is considered
392        "lexicographically later".  If the rrtype and rrclass both match,
393        then the rdata is compared. ...
394        */
395        match self.get_class().cmp(&other.get_class()) {
396            cmp::Ordering::Equal => match self.get_type().cmp(&other.get_type()) {
397                cmp::Ordering::Equal => self.compare_rdata(other),
398                not_equal => not_equal,
399            },
400            not_equal => not_equal,
401        }
402    }
403
404    /// Returns a human-readable string of rdata.
405    fn rdata_print(&self) -> String;
406
407    /// Returns the class only, excluding class_flush / unique bit.
408    fn get_class(&self) -> u16 {
409        self.get_record().entry.class
410    }
411
412    fn get_cache_flush(&self) -> bool {
413        self.get_record().entry.cache_flush
414    }
415
416    /// Return the new name if exists, otherwise the regular name in DnsEntry.
417    fn get_name(&self) -> &str {
418        self.get_record().get_name()
419    }
420
421    fn get_type(&self) -> RRType {
422        self.get_record().entry.ty
423    }
424
425    /// Resets TTL using `other` record.
426    /// `self.refresh` and `self.expires` are also reset.
427    fn reset_ttl(&mut self, other: &dyn DnsRecordExt) {
428        self.get_record_mut().reset_ttl(other.get_record());
429    }
430
431    fn get_created(&self) -> u64 {
432        self.get_record().get_created()
433    }
434
435    fn get_expire(&self) -> u64 {
436        self.get_record().get_expire_time()
437    }
438
439    fn set_expire(&mut self, expire_at: u64) {
440        self.get_record_mut().set_expire(expire_at);
441    }
442
443    /// Set expire as `expire_at` if it is sooner than the current `expire`.
444    fn set_expire_sooner(&mut self, expire_at: u64) {
445        if expire_at < self.get_expire() {
446            self.get_record_mut().set_expire(expire_at);
447        }
448    }
449
450    /// Given `now`, if the record is due to refresh, this method updates the refresh time
451    /// and returns the new refresh time. Otherwise, returns None.
452    fn updated_refresh_time(&mut self, now: u64) -> Option<u64> {
453        if self.get_record_mut().refresh_maybe(now) {
454            Some(self.get_record().get_refresh_time())
455        } else {
456            None
457        }
458    }
459
460    /// Returns true if another record has matched content,
461    /// and if its TTL is at least half of this record's.
462    fn suppressed_by_answer(&self, other: &dyn DnsRecordExt) -> bool {
463        self.matches(other) && (other.get_record().ttl > self.get_record().ttl / 2)
464    }
465
466    /// Required by RFC 6762 Section 7.1: Known-Answer Suppression.
467    fn suppressed_by(&self, msg: &DnsIncoming) -> bool {
468        for answer in msg.answers.iter() {
469            if self.suppressed_by_answer(answer.as_ref()) {
470                return true;
471            }
472        }
473        false
474    }
475
476    fn clone_box(&self) -> DnsRecordBox;
477}
478
479/// Resource Record for IPv4 address or IPv6 address.
480#[derive(Debug, Clone)]
481pub struct DnsAddress {
482    pub(crate) record: DnsRecord,
483    address: IpAddr,
484}
485
486impl DnsAddress {
487    pub fn new(name: &str, ty: RRType, class: u16, ttl: u32, address: IpAddr) -> Self {
488        let record = DnsRecord::new(name, ty, class, ttl);
489        Self { record, address }
490    }
491
492    pub fn address(&self) -> IpAddr {
493        self.address
494    }
495}
496
497impl DnsRecordExt for DnsAddress {
498    fn get_record(&self) -> &DnsRecord {
499        &self.record
500    }
501
502    fn get_record_mut(&mut self) -> &mut DnsRecord {
503        &mut self.record
504    }
505
506    fn write(&self, packet: &mut DnsOutPacket) {
507        match self.address {
508            IpAddr::V4(addr) => packet.write_bytes(addr.octets().as_ref()),
509            IpAddr::V6(addr) => packet.write_bytes(addr.octets().as_ref()),
510        };
511    }
512
513    fn any(&self) -> &dyn Any {
514        self
515    }
516
517    fn matches(&self, other: &dyn DnsRecordExt) -> bool {
518        if let Some(other_a) = other.any().downcast_ref::<Self>() {
519            return self.address == other_a.address && self.record.entry == other_a.record.entry;
520        }
521        false
522    }
523
524    fn rrdata_match(&self, other: &dyn DnsRecordExt) -> bool {
525        if let Some(other_a) = other.any().downcast_ref::<Self>() {
526            return self.address == other_a.address;
527        }
528        false
529    }
530
531    fn compare_rdata(&self, other: &dyn DnsRecordExt) -> cmp::Ordering {
532        if let Some(other_a) = other.any().downcast_ref::<Self>() {
533            self.address.cmp(&other_a.address)
534        } else {
535            cmp::Ordering::Greater
536        }
537    }
538
539    fn rdata_print(&self) -> String {
540        format!("{}", self.address)
541    }
542
543    fn clone_box(&self) -> DnsRecordBox {
544        Box::new(self.clone())
545    }
546}
547
548/// Resource Record for a DNS pointer
549#[derive(Debug, Clone)]
550pub struct DnsPointer {
551    record: DnsRecord,
552    alias: String, // the full name of Service Instance
553}
554
555impl DnsPointer {
556    pub fn new(name: &str, ty: RRType, class: u16, ttl: u32, alias: String) -> Self {
557        let record = DnsRecord::new(name, ty, class, ttl);
558        Self { record, alias }
559    }
560
561    pub fn alias(&self) -> &str {
562        &self.alias
563    }
564}
565
566impl DnsRecordExt for DnsPointer {
567    fn get_record(&self) -> &DnsRecord {
568        &self.record
569    }
570
571    fn get_record_mut(&mut self) -> &mut DnsRecord {
572        &mut self.record
573    }
574
575    fn write(&self, packet: &mut DnsOutPacket) {
576        packet.write_name(&self.alias);
577    }
578
579    fn any(&self) -> &dyn Any {
580        self
581    }
582
583    fn matches(&self, other: &dyn DnsRecordExt) -> bool {
584        if let Some(other_ptr) = other.any().downcast_ref::<Self>() {
585            return self.alias == other_ptr.alias && self.record.entry == other_ptr.record.entry;
586        }
587        false
588    }
589
590    fn rrdata_match(&self, other: &dyn DnsRecordExt) -> bool {
591        if let Some(other_ptr) = other.any().downcast_ref::<Self>() {
592            return self.alias == other_ptr.alias;
593        }
594        false
595    }
596
597    fn compare_rdata(&self, other: &dyn DnsRecordExt) -> cmp::Ordering {
598        if let Some(other_ptr) = other.any().downcast_ref::<Self>() {
599            self.alias.cmp(&other_ptr.alias)
600        } else {
601            cmp::Ordering::Greater
602        }
603    }
604
605    fn rdata_print(&self) -> String {
606        self.alias.clone()
607    }
608
609    fn clone_box(&self) -> DnsRecordBox {
610        Box::new(self.clone())
611    }
612}
613
614/// Resource Record for a DNS service.
615#[derive(Debug, Clone)]
616pub struct DnsSrv {
617    pub(crate) record: DnsRecord,
618    pub(crate) priority: u16, // lower number means higher priority. Should be 0 in common cases.
619    pub(crate) weight: u16,   // Should be 0 in common cases
620    host: String,
621    port: u16,
622}
623
624impl DnsSrv {
625    pub fn new(
626        name: &str,
627        class: u16,
628        ttl: u32,
629        priority: u16,
630        weight: u16,
631        port: u16,
632        host: String,
633    ) -> Self {
634        let record = DnsRecord::new(name, RRType::SRV, class, ttl);
635        Self {
636            record,
637            priority,
638            weight,
639            host,
640            port,
641        }
642    }
643
644    pub fn host(&self) -> &str {
645        &self.host
646    }
647
648    pub fn port(&self) -> u16 {
649        self.port
650    }
651
652    pub fn set_host(&mut self, host: String) {
653        self.host = host;
654    }
655}
656
657impl DnsRecordExt for DnsSrv {
658    fn get_record(&self) -> &DnsRecord {
659        &self.record
660    }
661
662    fn get_record_mut(&mut self) -> &mut DnsRecord {
663        &mut self.record
664    }
665
666    fn write(&self, packet: &mut DnsOutPacket) {
667        packet.write_short(self.priority);
668        packet.write_short(self.weight);
669        packet.write_short(self.port);
670        packet.write_name(&self.host);
671    }
672
673    fn any(&self) -> &dyn Any {
674        self
675    }
676
677    fn matches(&self, other: &dyn DnsRecordExt) -> bool {
678        if let Some(other_svc) = other.any().downcast_ref::<Self>() {
679            return self.host == other_svc.host
680                && self.port == other_svc.port
681                && self.weight == other_svc.weight
682                && self.priority == other_svc.priority
683                && self.record.entry == other_svc.record.entry;
684        }
685        false
686    }
687
688    fn rrdata_match(&self, other: &dyn DnsRecordExt) -> bool {
689        if let Some(other_srv) = other.any().downcast_ref::<Self>() {
690            return self.host == other_srv.host
691                && self.port == other_srv.port
692                && self.weight == other_srv.weight
693                && self.priority == other_srv.priority;
694        }
695        false
696    }
697
698    fn compare_rdata(&self, other: &dyn DnsRecordExt) -> cmp::Ordering {
699        let Some(other_srv) = other.any().downcast_ref::<Self>() else {
700            return cmp::Ordering::Greater;
701        };
702
703        // 1. compare `priority`
704        match self
705            .priority
706            .to_be_bytes()
707            .cmp(&other_srv.priority.to_be_bytes())
708        {
709            cmp::Ordering::Equal => {
710                // 2. compare `weight`
711                match self
712                    .weight
713                    .to_be_bytes()
714                    .cmp(&other_srv.weight.to_be_bytes())
715                {
716                    cmp::Ordering::Equal => {
717                        // 3. compare `port`.
718                        match self.port.to_be_bytes().cmp(&other_srv.port.to_be_bytes()) {
719                            cmp::Ordering::Equal => self.host.cmp(&other_srv.host),
720                            not_equal => not_equal,
721                        }
722                    }
723                    not_equal => not_equal,
724                }
725            }
726            not_equal => not_equal,
727        }
728    }
729
730    fn rdata_print(&self) -> String {
731        format!(
732            "priority: {}, weight: {}, port: {}, host: {}",
733            self.priority, self.weight, self.port, self.host
734        )
735    }
736
737    fn clone_box(&self) -> DnsRecordBox {
738        Box::new(self.clone())
739    }
740}
741
742/// Resource Record for a DNS TXT record.
743///
744/// From [RFC 6763 section 6]:
745///
746/// The format of each constituent string within the DNS TXT record is a
747/// single length byte, followed by 0-255 bytes of text data.
748///
749/// DNS-SD uses DNS TXT records to store arbitrary key/value pairs
750///    conveying additional information about the named service.  Each
751///    key/value pair is encoded as its own constituent string within the
752///    DNS TXT record, in the form "key=value" (without the quotation
753///    marks).  Everything up to the first '=' character is the key (Section
754///    6.4).  Everything after the first '=' character to the end of the
755///    string (including subsequent '=' characters, if any) is the value
756#[derive(Clone)]
757pub struct DnsTxt {
758    pub(crate) record: DnsRecord,
759    text: Vec<u8>,
760}
761
762impl DnsTxt {
763    pub fn new(name: &str, class: u16, ttl: u32, text: Vec<u8>) -> Self {
764        let record = DnsRecord::new(name, RRType::TXT, class, ttl);
765        Self { record, text }
766    }
767
768    pub fn text(&self) -> &[u8] {
769        &self.text
770    }
771}
772
773impl DnsRecordExt for DnsTxt {
774    fn get_record(&self) -> &DnsRecord {
775        &self.record
776    }
777
778    fn get_record_mut(&mut self) -> &mut DnsRecord {
779        &mut self.record
780    }
781
782    fn write(&self, packet: &mut DnsOutPacket) {
783        packet.write_bytes(&self.text);
784    }
785
786    fn any(&self) -> &dyn Any {
787        self
788    }
789
790    fn matches(&self, other: &dyn DnsRecordExt) -> bool {
791        if let Some(other_txt) = other.any().downcast_ref::<Self>() {
792            return self.text == other_txt.text && self.record.entry == other_txt.record.entry;
793        }
794        false
795    }
796
797    fn rrdata_match(&self, other: &dyn DnsRecordExt) -> bool {
798        if let Some(other_txt) = other.any().downcast_ref::<Self>() {
799            return self.text == other_txt.text;
800        }
801        false
802    }
803
804    fn compare_rdata(&self, other: &dyn DnsRecordExt) -> cmp::Ordering {
805        if let Some(other_txt) = other.any().downcast_ref::<Self>() {
806            self.text.cmp(&other_txt.text)
807        } else {
808            cmp::Ordering::Greater
809        }
810    }
811
812    fn rdata_print(&self) -> String {
813        format!("{:?}", decode_txt(&self.text))
814    }
815
816    fn clone_box(&self) -> DnsRecordBox {
817        Box::new(self.clone())
818    }
819}
820
821impl fmt::Debug for DnsTxt {
822    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
823        let properties = decode_txt(&self.text);
824        write!(
825            f,
826            "DnsTxt {{ record: {:?}, text: {:?} }}",
827            self.record, properties
828        )
829    }
830}
831
832// Convert from DNS TXT record content to key/value pairs
833fn decode_txt(txt: &[u8]) -> Vec<TxtProperty> {
834    let mut properties = Vec::new();
835    let mut offset = 0;
836    while offset < txt.len() {
837        let length = txt[offset] as usize;
838        if length == 0 {
839            break; // reached the end
840        }
841        offset += 1; // move over the length byte
842
843        let offset_end = offset + length;
844        if offset_end > txt.len() {
845            trace!("ERROR: DNS TXT: size given for property is out of range. (offset={}, length={}, offset_end={}, record length={})", offset, length, offset_end, txt.len());
846            break; // Skipping the rest of the record content, as the size for this property would already be out of range.
847        }
848        let kv_bytes = &txt[offset..offset_end];
849
850        // split key and val using the first `=`
851        let (k, v) = kv_bytes.iter().position(|&x| x == b'=').map_or_else(
852            || (kv_bytes.to_vec(), None),
853            |idx| (kv_bytes[..idx].to_vec(), Some(kv_bytes[idx + 1..].to_vec())),
854        );
855
856        // Make sure the key can be stored in UTF-8.
857        match String::from_utf8(k) {
858            Ok(k_string) => {
859                properties.push(TxtProperty {
860                    key: k_string,
861                    val: v,
862                });
863            }
864            Err(e) => trace!("ERROR: convert to String from key: {}", e),
865        }
866
867        offset += length;
868    }
869
870    properties
871}
872
873/// Represents a property in a TXT record.
874#[derive(Clone, PartialEq, Eq)]
875pub struct TxtProperty {
876    /// The name of the property. The original cases are kept.
877    key: String,
878
879    /// RFC 6763 says values are bytes, not necessarily UTF-8.
880    /// It is also possible that there is no value, in which case
881    /// the key is a boolean key.
882    val: Option<Vec<u8>>,
883}
884
885impl TxtProperty {
886    /// Returns the key of a property.
887    pub fn key(&self) -> &str {
888        &self.key
889    }
890
891    /// Returns the value of a property, which could be `None`.
892    ///
893    /// To obtain a `&str` of the value, use `val_str()` instead.
894    pub fn val(&self) -> Option<&[u8]> {
895        self.val.as_deref()
896    }
897
898    /// Returns the value of a property as str.
899    pub fn val_str(&self) -> &str {
900        self.val
901            .as_ref()
902            .map_or("", |v| std::str::from_utf8(&v[..]).unwrap_or_default())
903    }
904}
905
906/// Supports constructing from a tuple.
907impl<K, V> From<&(K, V)> for TxtProperty
908where
909    K: ToString,
910    V: ToString,
911{
912    fn from(prop: &(K, V)) -> Self {
913        Self {
914            key: prop.0.to_string(),
915            val: Some(prop.1.to_string().into_bytes()),
916        }
917    }
918}
919
920impl<K, V> From<(K, V)> for TxtProperty
921where
922    K: ToString,
923    V: AsRef<[u8]>,
924{
925    fn from(prop: (K, V)) -> Self {
926        Self {
927            key: prop.0.to_string(),
928            val: Some(prop.1.as_ref().into()),
929        }
930    }
931}
932
933/// Support a property that has no value.
934impl From<&str> for TxtProperty {
935    fn from(key: &str) -> Self {
936        Self {
937            key: key.to_string(),
938            val: None,
939        }
940    }
941}
942
943impl fmt::Display for TxtProperty {
944    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
945        write!(f, "{}={}", self.key, self.val_str())
946    }
947}
948
949/// Mimic the default debug output for a struct, with a twist:
950/// - If self.var is UTF-8, will output it as a string in double quotes.
951/// - If self.var is not UTF-8, will output its bytes as in hex.
952impl fmt::Debug for TxtProperty {
953    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
954        let val_string = self.val.as_ref().map_or_else(
955            || "None".to_string(),
956            |v| {
957                std::str::from_utf8(&v[..]).map_or_else(
958                    |_| format!("Some({})", u8_slice_to_hex(&v[..])),
959                    |s| format!("Some(\"{}\")", s),
960                )
961            },
962        );
963
964        write!(
965            f,
966            "TxtProperty {{key: \"{}\", val: {}}}",
967            &self.key, &val_string,
968        )
969    }
970}
971
972const HEX_TABLE: [char; 16] = [
973    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
974];
975
976/// Create a hex string from `slice`, with a "0x" prefix.
977///
978/// For example, [1u8, 2u8] -> "0x0102"
979fn u8_slice_to_hex(slice: &[u8]) -> String {
980    let mut hex = String::with_capacity(slice.len() * 2 + 2);
981    hex.push_str("0x");
982    for b in slice {
983        hex.push(HEX_TABLE[(b >> 4) as usize]);
984        hex.push(HEX_TABLE[(b & 0x0F) as usize]);
985    }
986    hex
987}
988
989/// A DNS host information record
990#[derive(Debug, Clone)]
991struct DnsHostInfo {
992    record: DnsRecord,
993    cpu: String,
994    os: String,
995}
996
997impl DnsHostInfo {
998    fn new(name: &str, ty: RRType, class: u16, ttl: u32, cpu: String, os: String) -> Self {
999        let record = DnsRecord::new(name, ty, class, ttl);
1000        Self { record, cpu, os }
1001    }
1002}
1003
1004impl DnsRecordExt for DnsHostInfo {
1005    fn get_record(&self) -> &DnsRecord {
1006        &self.record
1007    }
1008
1009    fn get_record_mut(&mut self) -> &mut DnsRecord {
1010        &mut self.record
1011    }
1012
1013    fn write(&self, packet: &mut DnsOutPacket) {
1014        println!("writing HInfo: cpu {} os {}", &self.cpu, &self.os);
1015        packet.write_bytes(self.cpu.as_bytes());
1016        packet.write_bytes(self.os.as_bytes());
1017    }
1018
1019    fn any(&self) -> &dyn Any {
1020        self
1021    }
1022
1023    fn matches(&self, other: &dyn DnsRecordExt) -> bool {
1024        if let Some(other_hinfo) = other.any().downcast_ref::<Self>() {
1025            return self.cpu == other_hinfo.cpu
1026                && self.os == other_hinfo.os
1027                && self.record.entry == other_hinfo.record.entry;
1028        }
1029        false
1030    }
1031
1032    fn rrdata_match(&self, other: &dyn DnsRecordExt) -> bool {
1033        if let Some(other_hinfo) = other.any().downcast_ref::<Self>() {
1034            return self.cpu == other_hinfo.cpu && self.os == other_hinfo.os;
1035        }
1036        false
1037    }
1038
1039    fn compare_rdata(&self, other: &dyn DnsRecordExt) -> cmp::Ordering {
1040        if let Some(other_hinfo) = other.any().downcast_ref::<Self>() {
1041            match self.cpu.cmp(&other_hinfo.cpu) {
1042                cmp::Ordering::Equal => self.os.cmp(&other_hinfo.os),
1043                ordering => ordering,
1044            }
1045        } else {
1046            cmp::Ordering::Greater
1047        }
1048    }
1049
1050    fn rdata_print(&self) -> String {
1051        format!("cpu: {}, os: {}", self.cpu, self.os)
1052    }
1053
1054    fn clone_box(&self) -> DnsRecordBox {
1055        Box::new(self.clone())
1056    }
1057}
1058
1059/// Resource Record for negative responses
1060///
1061/// [RFC4034 section 4.1](https://datatracker.ietf.org/doc/html/rfc4034#section-4.1)
1062/// and
1063/// [RFC6762 section 6.1](https://datatracker.ietf.org/doc/html/rfc6762#section-6.1)
1064#[derive(Debug, Clone)]
1065pub struct DnsNSec {
1066    record: DnsRecord,
1067    next_domain: String,
1068    type_bitmap: Vec<u8>,
1069}
1070
1071impl DnsNSec {
1072    pub fn new(
1073        name: &str,
1074        class: u16,
1075        ttl: u32,
1076        next_domain: String,
1077        type_bitmap: Vec<u8>,
1078    ) -> Self {
1079        let record = DnsRecord::new(name, RRType::NSEC, class, ttl);
1080        Self {
1081            record,
1082            next_domain,
1083            type_bitmap,
1084        }
1085    }
1086
1087    /// Returns the types marked by `type_bitmap`
1088    pub fn types(&self) -> Vec<u16> {
1089        // From RFC 4034: 4.1.2 The Type Bit Maps Field
1090        // https://datatracker.ietf.org/doc/html/rfc4034#section-4.1.2
1091        //
1092        // Each bitmap encodes the low-order 8 bits of RR types within the
1093        // window block, in network bit order.  The first bit is bit 0.  For
1094        // window block 0, bit 1 corresponds to RR type 1 (A), bit 2 corresponds
1095        // to RR type 2 (NS), and so forth.
1096
1097        let mut bit_num = 0;
1098        let mut results = Vec::new();
1099
1100        for byte in self.type_bitmap.iter() {
1101            let mut bit_mask: u8 = 0x80; // for bit 0 in network bit order
1102
1103            // check every bit in this byte, one by one.
1104            for _ in 0..8 {
1105                if (byte & bit_mask) != 0 {
1106                    results.push(bit_num);
1107                }
1108                bit_num += 1;
1109                bit_mask >>= 1; // mask for the next bit
1110            }
1111        }
1112        results
1113    }
1114}
1115
1116impl DnsRecordExt for DnsNSec {
1117    fn get_record(&self) -> &DnsRecord {
1118        &self.record
1119    }
1120
1121    fn get_record_mut(&mut self) -> &mut DnsRecord {
1122        &mut self.record
1123    }
1124
1125    fn write(&self, packet: &mut DnsOutPacket) {
1126        packet.write_bytes(self.next_domain.as_bytes());
1127        packet.write_bytes(&self.type_bitmap);
1128    }
1129
1130    fn any(&self) -> &dyn Any {
1131        self
1132    }
1133
1134    fn matches(&self, other: &dyn DnsRecordExt) -> bool {
1135        if let Some(other_record) = other.any().downcast_ref::<Self>() {
1136            return self.next_domain == other_record.next_domain
1137                && self.type_bitmap == other_record.type_bitmap
1138                && self.record.entry == other_record.record.entry;
1139        }
1140        false
1141    }
1142
1143    fn rrdata_match(&self, other: &dyn DnsRecordExt) -> bool {
1144        if let Some(other_record) = other.any().downcast_ref::<Self>() {
1145            return self.next_domain == other_record.next_domain
1146                && self.type_bitmap == other_record.type_bitmap;
1147        }
1148        false
1149    }
1150
1151    fn compare_rdata(&self, other: &dyn DnsRecordExt) -> cmp::Ordering {
1152        if let Some(other_nsec) = other.any().downcast_ref::<Self>() {
1153            match self.next_domain.cmp(&other_nsec.next_domain) {
1154                cmp::Ordering::Equal => self.type_bitmap.cmp(&other_nsec.type_bitmap),
1155                ordering => ordering,
1156            }
1157        } else {
1158            cmp::Ordering::Greater
1159        }
1160    }
1161
1162    fn rdata_print(&self) -> String {
1163        format!(
1164            "next_domain: {}, type_bitmap len: {}",
1165            self.next_domain,
1166            self.type_bitmap.len()
1167        )
1168    }
1169
1170    fn clone_box(&self) -> DnsRecordBox {
1171        Box::new(self.clone())
1172    }
1173}
1174
1175#[derive(PartialEq)]
1176enum PacketState {
1177    Init = 0,
1178    Finished = 1,
1179}
1180
1181/// A single packet for outgoing DNS message.
1182pub struct DnsOutPacket {
1183    /// All bytes in `data` concatenated is the actual packet on the wire.
1184    data: Vec<Vec<u8>>,
1185
1186    /// Current logical size of the packet. It starts with the size of the mandatory header.
1187    size: usize,
1188
1189    /// An internal state, not defined by DNS.
1190    state: PacketState,
1191
1192    /// k: name, v: offset
1193    names: HashMap<String, u16>,
1194}
1195
1196impl DnsOutPacket {
1197    fn new() -> Self {
1198        Self {
1199            data: Vec::new(),
1200            size: MSG_HEADER_LEN, // Header is mandatory.
1201            state: PacketState::Init,
1202            names: HashMap::new(),
1203        }
1204    }
1205
1206    pub fn size(&self) -> usize {
1207        self.size
1208    }
1209
1210    pub fn to_bytes(&self) -> Vec<u8> {
1211        self.data.concat()
1212    }
1213
1214    fn write_question(&mut self, question: &DnsQuestion) {
1215        self.write_name(&question.entry.name);
1216        self.write_short(question.entry.ty as u16);
1217        self.write_short(question.entry.class);
1218    }
1219
1220    /// Writes a record (answer, authoritative answer, additional)
1221    /// Returns false if the packet exceeds the max size with this record, nothing is written to the packet.
1222    /// otherwise returns true.
1223    fn write_record(&mut self, record_ext: &dyn DnsRecordExt, now: u64) -> bool {
1224        let start_data_length = self.data.len();
1225        let start_size = self.size;
1226
1227        let record = record_ext.get_record();
1228        self.write_name(record.get_name());
1229        self.write_short(record.entry.ty as u16);
1230        if record.entry.cache_flush {
1231            // check "multicast"
1232            self.write_short(record.entry.class | CLASS_CACHE_FLUSH);
1233        } else {
1234            self.write_short(record.entry.class);
1235        }
1236
1237        if now == 0 {
1238            self.write_u32(record.ttl);
1239        } else {
1240            self.write_u32(record.get_remaining_ttl(now));
1241        }
1242
1243        let index = self.data.len();
1244
1245        // Adjust size for the short we will write before this record
1246        self.size += 2;
1247        record_ext.write(self);
1248        self.size -= 2;
1249
1250        let length: usize = self.data[index..].iter().map(|x| x.len()).sum();
1251        self.insert_short(index, length as u16);
1252
1253        if self.size > MAX_MSG_ABSOLUTE {
1254            self.data.truncate(start_data_length);
1255            self.size = start_size;
1256            self.state = PacketState::Finished;
1257            return false;
1258        }
1259
1260        true
1261    }
1262
1263    pub(crate) fn insert_short(&mut self, index: usize, value: u16) {
1264        self.data.insert(index, value.to_be_bytes().to_vec());
1265        self.size += 2;
1266    }
1267
1268    // Write name to packet
1269    //
1270    // [RFC1035]
1271    // 4.1.4. Message compression
1272    //
1273    // In order to reduce the size of messages, the domain system utilizes a
1274    // compression scheme which eliminates the repetition of domain names in a
1275    // message.  In this scheme, an entire domain name or a list of labels at
1276    // the end of a domain name is replaced with a pointer to a prior occurrence
1277    // of the same name.
1278    // The pointer takes the form of a two octet sequence:
1279    //     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
1280    //     | 1  1|                OFFSET                   |
1281    //     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
1282    // The first two bits are ones.  This allows a pointer to be distinguished
1283    // from a label, since the label must begin with two zero bits because
1284    // labels are restricted to 63 octets or less.  (The 10 and 01 combinations
1285    // are reserved for future use.)  The OFFSET field specifies an offset from
1286    // the start of the message (i.e., the first octet of the ID field in the
1287    // domain header).  A zero offset specifies the first byte of the ID field,
1288    // etc.
1289    fn write_name(&mut self, name: &str) {
1290        // ignore the ending "." if exists
1291        let end = name.len();
1292        let end = if end > 0 && &name[end - 1..] == "." {
1293            end - 1
1294        } else {
1295            end
1296        };
1297
1298        let mut here = 0;
1299        while here < end {
1300            const POINTER_MASK: u16 = 0xC000;
1301            let remaining = &name[here..end];
1302
1303            // Check if 'remaining' already appeared in this message
1304            match self.names.get(remaining) {
1305                Some(offset) => {
1306                    let pointer = *offset | POINTER_MASK;
1307                    self.write_short(pointer);
1308                    // println!(
1309                    //     "written pointer {} ({}) for {}",
1310                    //     pointer,
1311                    //     pointer ^ POINTER_MASK,
1312                    //     remaining
1313                    // );
1314                    break;
1315                }
1316                None => {
1317                    // Remember the remaining parts so we can point to it
1318                    self.names.insert(remaining.to_string(), self.size as u16);
1319                    // println!("set offset {} for {}", self.size, remaining);
1320
1321                    // Find the current label to write into the packet
1322                    let stop = remaining.find('.').map_or(end, |i| here + i);
1323                    let label = &name[here..stop];
1324                    self.write_utf8(label);
1325
1326                    here = stop + 1; // move past the current label
1327                }
1328            }
1329
1330            if here >= end {
1331                self.write_byte(0); // name ends with 0 if not using a pointer
1332            }
1333        }
1334    }
1335
1336    fn write_utf8(&mut self, utf: &str) {
1337        assert!(utf.len() < 64);
1338        self.write_byte(utf.len() as u8);
1339        self.write_bytes(utf.as_bytes());
1340    }
1341
1342    fn write_bytes(&mut self, s: &[u8]) {
1343        self.data.push(s.to_vec());
1344        self.size += s.len();
1345    }
1346
1347    fn write_u32(&mut self, int: u32) {
1348        self.data.push(int.to_be_bytes().to_vec());
1349        self.size += 4;
1350    }
1351
1352    fn write_short(&mut self, short: u16) {
1353        self.data.push(short.to_be_bytes().to_vec());
1354        self.size += 2;
1355    }
1356
1357    fn write_byte(&mut self, byte: u8) {
1358        self.data.push(vec![byte]);
1359        self.size += 1;
1360    }
1361
1362    /// Writes the header fields and finish the packet.
1363    /// This function should be only called when finishing a packet.
1364    ///
1365    /// The header format is based on RFC 1035 section 4.1.1:
1366    /// https://datatracker.ietf.org/doc/html/rfc1035#section-4.1.1
1367    //
1368    //                                  1  1  1  1  1  1
1369    //    0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
1370    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
1371    //    |                      ID                       |
1372    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
1373    //    |QR|   Opcode  |AA|TC|RD|RA|   Z    |   RCODE   |
1374    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
1375    //    |                    QDCOUNT                    |
1376    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
1377    //    |                    ANCOUNT                    |
1378    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
1379    //    |                    NSCOUNT                    |
1380    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
1381    //    |                    ARCOUNT                    |
1382    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
1383    //
1384    fn write_header(
1385        &mut self,
1386        id: u16,
1387        flags: u16,
1388        q_count: u16,
1389        a_count: u16,
1390        auth_count: u16,
1391        addi_count: u16,
1392    ) {
1393        self.insert_short(0, addi_count);
1394        self.insert_short(0, auth_count);
1395        self.insert_short(0, a_count);
1396        self.insert_short(0, q_count);
1397        self.insert_short(0, flags);
1398        self.insert_short(0, id);
1399
1400        // Adjust the size as it was already initialized to include the header.
1401        self.size -= MSG_HEADER_LEN;
1402
1403        self.state = PacketState::Finished;
1404    }
1405}
1406
1407/// Representation of one outgoing DNS message that could be sent in one or more packet(s).
1408pub struct DnsOutgoing {
1409    flags: u16,
1410    id: u16,
1411    multicast: bool,
1412    questions: Vec<DnsQuestion>,
1413    answers: Vec<(DnsRecordBox, u64)>,
1414    authorities: Vec<DnsRecordBox>,
1415    additionals: Vec<DnsRecordBox>,
1416    known_answer_count: i64, // for internal maintenance only
1417}
1418
1419impl DnsOutgoing {
1420    pub fn new(flags: u16) -> Self {
1421        Self {
1422            flags,
1423            id: 0,
1424            multicast: true,
1425            questions: Vec::new(),
1426            answers: Vec::new(),
1427            authorities: Vec::new(),
1428            additionals: Vec::new(),
1429            known_answer_count: 0,
1430        }
1431    }
1432
1433    pub fn questions(&self) -> &[DnsQuestion] {
1434        &self.questions
1435    }
1436
1437    pub fn answers_count(&self) -> usize {
1438        self.answers.len()
1439    }
1440
1441    pub fn authorities(&self) -> &[DnsRecordBox] {
1442        &self.authorities
1443    }
1444
1445    pub fn additionals(&self) -> &[DnsRecordBox] {
1446        &self.additionals
1447    }
1448
1449    pub fn known_answer_count(&self) -> i64 {
1450        self.known_answer_count
1451    }
1452
1453    pub fn set_id(&mut self, id: u16) {
1454        self.id = id;
1455    }
1456
1457    pub const fn is_query(&self) -> bool {
1458        (self.flags & FLAGS_QR_MASK) == FLAGS_QR_QUERY
1459    }
1460
1461    const fn is_response(&self) -> bool {
1462        (self.flags & FLAGS_QR_MASK) == FLAGS_QR_RESPONSE
1463    }
1464
1465    // Adds an additional answer
1466
1467    // From: RFC 6763, DNS-Based Service Discovery, February 2013
1468
1469    // 12.  DNS Additional Record Generation
1470
1471    //    DNS has an efficiency feature whereby a DNS server may place
1472    //    additional records in the additional section of the DNS message.
1473    //    These additional records are records that the client did not
1474    //    explicitly request, but the server has reasonable grounds to expect
1475    //    that the client might request them shortly, so including them can
1476    //    save the client from having to issue additional queries.
1477
1478    //    This section recommends which additional records SHOULD be generated
1479    //    to improve network efficiency, for both Unicast and Multicast DNS-SD
1480    //    responses.
1481
1482    // 12.1.  PTR Records
1483
1484    //    When including a DNS-SD Service Instance Enumeration or Selective
1485    //    Instance Enumeration (subtype) PTR record in a response packet, the
1486    //    server/responder SHOULD include the following additional records:
1487
1488    //    o  The SRV record(s) named in the PTR rdata.
1489    //    o  The TXT record(s) named in the PTR rdata.
1490    //    o  All address records (type "A" and "AAAA") named in the SRV rdata.
1491
1492    // 12.2.  SRV Records
1493
1494    //    When including an SRV record in a response packet, the
1495    //    server/responder SHOULD include the following additional records:
1496
1497    //    o  All address records (type "A" and "AAAA") named in the SRV rdata.
1498    pub fn add_additional_answer(&mut self, answer: impl DnsRecordExt + 'static) {
1499        trace!("add_additional_answer: {:?}", &answer);
1500        self.additionals.push(Box::new(answer));
1501    }
1502
1503    /// A workaround as Rust doesn't allow us to pass DnsRecordBox in as `impl DnsRecordExt`
1504    pub fn add_answer_box(&mut self, answer_box: DnsRecordBox) {
1505        self.answers.push((answer_box, 0));
1506    }
1507
1508    pub fn add_authority(&mut self, record: DnsRecordBox) {
1509        self.authorities.push(record);
1510    }
1511
1512    /// Returns true if `answer` is added to the outgoing msg.
1513    /// Returns false if `answer` was not added as it expired or suppressed by the incoming `msg`.
1514    pub fn add_answer(
1515        &mut self,
1516        msg: &DnsIncoming,
1517        answer: impl DnsRecordExt + Send + 'static,
1518    ) -> bool {
1519        trace!("Check for add_answer");
1520        if answer.suppressed_by(msg) {
1521            trace!("my answer is suppressed by incoming msg");
1522            self.known_answer_count += 1;
1523            return false;
1524        }
1525
1526        self.add_answer_at_time(answer, 0)
1527    }
1528
1529    /// Returns true if `answer` is added to the outgoing msg.
1530    /// Returns false if the answer is expired `now` hence not added.
1531    /// If `now` is 0, do not check if the answer expires.
1532    pub fn add_answer_at_time(
1533        &mut self,
1534        answer: impl DnsRecordExt + Send + 'static,
1535        now: u64,
1536    ) -> bool {
1537        if now == 0 || !answer.get_record().is_expired(now) {
1538            trace!("add_answer push: {:?}", &answer);
1539            self.answers.push((Box::new(answer), now));
1540            return true;
1541        }
1542        false
1543    }
1544
1545    pub fn add_question(&mut self, name: &str, qtype: RRType) {
1546        let q = DnsQuestion {
1547            entry: DnsEntry::new(name.to_string(), qtype, CLASS_IN),
1548        };
1549        self.questions.push(q);
1550    }
1551
1552    /// Returns a list of actual DNS packet data to be sent on the wire.
1553    pub fn to_data_on_wire(&self) -> Vec<Vec<u8>> {
1554        let packet_list = self.to_packets();
1555        packet_list.iter().map(|p| p.data.concat()).collect()
1556    }
1557
1558    /// Encode self into one or more packets.
1559    pub fn to_packets(&self) -> Vec<DnsOutPacket> {
1560        let mut packet_list = Vec::new();
1561        let mut packet = DnsOutPacket::new();
1562
1563        let mut question_count = self.questions.len() as u16;
1564        let mut answer_count = 0;
1565        let mut auth_count = 0;
1566        let mut addi_count = 0;
1567        let id = if self.multicast { 0 } else { self.id };
1568
1569        for question in self.questions.iter() {
1570            packet.write_question(question);
1571        }
1572
1573        for (answer, time) in self.answers.iter() {
1574            if packet.write_record(answer.as_ref(), *time) {
1575                answer_count += 1;
1576            }
1577        }
1578
1579        for auth in self.authorities.iter() {
1580            auth_count += u16::from(packet.write_record(auth.as_ref(), 0));
1581        }
1582
1583        for addi in self.additionals.iter() {
1584            if packet.write_record(addi.as_ref(), 0) {
1585                addi_count += 1;
1586                continue;
1587            }
1588
1589            // No more processing for response packets.
1590            if self.is_response() {
1591                break;
1592            }
1593
1594            // For query, the current packet exceeds its max size due to known answers,
1595            // need to truncate.
1596
1597            // finish the current packet first.
1598            packet.write_header(
1599                id,
1600                self.flags | FLAGS_TC,
1601                question_count,
1602                answer_count,
1603                auth_count,
1604                addi_count,
1605            );
1606
1607            packet_list.push(packet);
1608
1609            // create a new packet and reset counts.
1610            packet = DnsOutPacket::new();
1611            packet.write_record(addi.as_ref(), 0);
1612
1613            question_count = 0;
1614            answer_count = 0;
1615            auth_count = 0;
1616            addi_count = 1;
1617        }
1618
1619        packet.write_header(
1620            id,
1621            self.flags,
1622            question_count,
1623            answer_count,
1624            auth_count,
1625            addi_count,
1626        );
1627
1628        packet_list.push(packet);
1629        packet_list
1630    }
1631}
1632
1633/// An incoming DNS message. It could be a query or a response.
1634#[derive(Debug)]
1635pub struct DnsIncoming {
1636    offset: usize,
1637    data: Vec<u8>,
1638    questions: Vec<DnsQuestion>,
1639    answers: Vec<DnsRecordBox>,
1640    authorities: Vec<DnsRecordBox>,
1641    additional: Vec<DnsRecordBox>,
1642    id: u16,
1643    flags: u16,
1644    num_questions: u16,
1645    num_answers: u16,
1646    num_authorities: u16,
1647    num_additionals: u16,
1648}
1649
1650impl DnsIncoming {
1651    pub fn new(data: Vec<u8>) -> Result<Self> {
1652        let mut incoming = Self {
1653            offset: 0,
1654            data,
1655            questions: Vec::new(),
1656            answers: Vec::new(),
1657            authorities: Vec::new(),
1658            additional: Vec::new(),
1659            id: 0,
1660            flags: 0,
1661            num_questions: 0,
1662            num_answers: 0,
1663            num_authorities: 0,
1664            num_additionals: 0,
1665        };
1666
1667        /*
1668        RFC 1035 section 4.1: https://datatracker.ietf.org/doc/html/rfc1035#section-4.1
1669        ...
1670        All communications inside of the domain protocol are carried in a single
1671        format called a message.  The top level format of message is divided
1672        into 5 sections (some of which are empty in certain cases) shown below:
1673
1674            +---------------------+
1675            |        Header       |
1676            +---------------------+
1677            |       Question      | the question for the name server
1678            +---------------------+
1679            |        Answer       | RRs answering the question
1680            +---------------------+
1681            |      Authority      | RRs pointing toward an authority
1682            +---------------------+
1683            |      Additional     | RRs holding additional information
1684            +---------------------+
1685         */
1686        incoming.read_header()?;
1687        incoming.read_questions()?;
1688        incoming.read_answers()?;
1689        incoming.read_authorities()?;
1690        incoming.read_additional()?;
1691
1692        Ok(incoming)
1693    }
1694
1695    pub fn id(&self) -> u16 {
1696        self.id
1697    }
1698
1699    pub fn questions(&self) -> &[DnsQuestion] {
1700        &self.questions
1701    }
1702
1703    pub fn answers(&self) -> &[DnsRecordBox] {
1704        &self.answers
1705    }
1706
1707    pub fn authorities(&self) -> &[DnsRecordBox] {
1708        &self.authorities
1709    }
1710
1711    pub fn additionals(&self) -> &[DnsRecordBox] {
1712        &self.additional
1713    }
1714
1715    pub fn answers_mut(&mut self) -> &mut Vec<DnsRecordBox> {
1716        &mut self.answers
1717    }
1718
1719    pub fn authorities_mut(&mut self) -> &mut Vec<DnsRecordBox> {
1720        &mut self.authorities
1721    }
1722
1723    pub fn additionals_mut(&mut self) -> &mut Vec<DnsRecordBox> {
1724        &mut self.additional
1725    }
1726
1727    pub fn all_records(self) -> impl Iterator<Item = DnsRecordBox> {
1728        self.answers
1729            .into_iter()
1730            .chain(self.authorities)
1731            .chain(self.additional)
1732    }
1733
1734    pub fn num_additionals(&self) -> u16 {
1735        self.num_additionals
1736    }
1737
1738    pub fn num_authorities(&self) -> u16 {
1739        self.num_authorities
1740    }
1741
1742    pub fn num_questions(&self) -> u16 {
1743        self.num_questions
1744    }
1745
1746    pub const fn is_query(&self) -> bool {
1747        (self.flags & FLAGS_QR_MASK) == FLAGS_QR_QUERY
1748    }
1749
1750    pub const fn is_response(&self) -> bool {
1751        (self.flags & FLAGS_QR_MASK) == FLAGS_QR_RESPONSE
1752    }
1753
1754    fn read_header(&mut self) -> Result<()> {
1755        if self.data.len() < MSG_HEADER_LEN {
1756            return Err(Error::Msg(format!(
1757                "DNS incoming: header is too short: {} bytes",
1758                self.data.len()
1759            )));
1760        }
1761
1762        let data = &self.data[0..];
1763        self.id = u16_from_be_slice(&data[..2]);
1764        self.flags = u16_from_be_slice(&data[2..4]);
1765        self.num_questions = u16_from_be_slice(&data[4..6]);
1766        self.num_answers = u16_from_be_slice(&data[6..8]);
1767        self.num_authorities = u16_from_be_slice(&data[8..10]);
1768        self.num_additionals = u16_from_be_slice(&data[10..12]);
1769
1770        self.offset = MSG_HEADER_LEN;
1771
1772        trace!(
1773            "read_header: id {}, {} questions {} answers {} authorities {} additionals",
1774            self.id,
1775            self.num_questions,
1776            self.num_answers,
1777            self.num_authorities,
1778            self.num_additionals
1779        );
1780        Ok(())
1781    }
1782
1783    fn read_questions(&mut self) -> Result<()> {
1784        trace!("read_questions: {}", &self.num_questions);
1785        for i in 0..self.num_questions {
1786            let name = self.read_name()?;
1787
1788            let data = &self.data[self.offset..];
1789            if data.len() < 4 {
1790                return Err(Error::Msg(format!(
1791                    "DNS incoming: question idx {} too short: {}",
1792                    i,
1793                    data.len()
1794                )));
1795            }
1796            let ty = u16_from_be_slice(&data[..2]);
1797            let class = u16_from_be_slice(&data[2..4]);
1798            self.offset += 4;
1799
1800            let Some(rr_type) = RRType::from_u16(ty) else {
1801                return Err(Error::Msg(format!(
1802                    "DNS incoming: question idx {} qtype unknown: {}",
1803                    i, ty
1804                )));
1805            };
1806
1807            self.questions.push(DnsQuestion {
1808                entry: DnsEntry::new(name, rr_type, class),
1809            });
1810        }
1811        Ok(())
1812    }
1813
1814    fn read_answers(&mut self) -> Result<()> {
1815        self.answers = self.read_rr_records(self.num_answers)?;
1816        Ok(())
1817    }
1818
1819    fn read_authorities(&mut self) -> Result<()> {
1820        self.authorities = self.read_rr_records(self.num_authorities)?;
1821        Ok(())
1822    }
1823
1824    fn read_additional(&mut self) -> Result<()> {
1825        self.additional = self.read_rr_records(self.num_additionals)?;
1826        Ok(())
1827    }
1828
1829    /// Decodes a sequence of RR records (in answers, authorities and additionals).
1830    fn read_rr_records(&mut self, count: u16) -> Result<Vec<DnsRecordBox>> {
1831        trace!("read_rr_records: {}", count);
1832        let mut rr_records = Vec::new();
1833
1834        // RFC 1035: https://datatracker.ietf.org/doc/html/rfc1035#section-3.2.1
1835        //
1836        // All RRs have the same top level format shown below:
1837        //                               1  1  1  1  1  1
1838        // 0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
1839        // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
1840        // |                                               |
1841        // /                                               /
1842        // /                      NAME                     /
1843        // |                                               |
1844        // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
1845        // |                      TYPE                     |
1846        // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
1847        // |                     CLASS                     |
1848        // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
1849        // |                      TTL                      |
1850        // |                                               |
1851        // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
1852        // |                   RDLENGTH                    |
1853        // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
1854        // /                     RDATA                     /
1855        // /                                               /
1856        // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
1857
1858        // Muse have at least TYPE, CLASS, TTL, RDLENGTH fields: 10 bytes.
1859        const RR_HEADER_REMAIN: usize = 10;
1860
1861        for _ in 0..count {
1862            let name = self.read_name()?;
1863            let slice = &self.data[self.offset..];
1864
1865            if slice.len() < RR_HEADER_REMAIN {
1866                return Err(Error::Msg(format!(
1867                    "read_others: RR '{}' is too short after name: {} bytes",
1868                    &name,
1869                    slice.len()
1870                )));
1871            }
1872
1873            let ty = u16_from_be_slice(&slice[..2]);
1874            let class = u16_from_be_slice(&slice[2..4]);
1875            let mut ttl = u32_from_be_slice(&slice[4..8]);
1876            if ttl == 0 && self.is_response() {
1877                // RFC 6762 section 10.1:
1878                // "...Queriers receiving a Multicast DNS response with a TTL of zero SHOULD
1879                // NOT immediately delete the record from the cache, but instead record
1880                // a TTL of 1 and then delete the record one second later."
1881                // See https://datatracker.ietf.org/doc/html/rfc6762#section-10.1
1882
1883                ttl = 1;
1884            }
1885            let rdata_len = u16_from_be_slice(&slice[8..10]) as usize;
1886            self.offset += RR_HEADER_REMAIN;
1887            let next_offset = self.offset + rdata_len;
1888
1889            // Sanity check for RDATA length.
1890            if next_offset > self.data.len() {
1891                return Err(Error::Msg(format!(
1892                    "RR {name} RDATA length {rdata_len} is invalid: remain data len: {}",
1893                    self.data.len() - self.offset
1894                )));
1895            }
1896
1897            // decode RDATA based on the record type.
1898            let rec: Option<DnsRecordBox> = match RRType::from_u16(ty) {
1899                None => None,
1900
1901                Some(rr_type) => match rr_type {
1902                    RRType::CNAME | RRType::PTR => Some(Box::new(DnsPointer::new(
1903                        &name,
1904                        rr_type,
1905                        class,
1906                        ttl,
1907                        self.read_name()?,
1908                    ))),
1909                    RRType::TXT => Some(Box::new(DnsTxt::new(
1910                        &name,
1911                        class,
1912                        ttl,
1913                        self.read_vec(rdata_len),
1914                    ))),
1915                    RRType::SRV => Some(Box::new(DnsSrv::new(
1916                        &name,
1917                        class,
1918                        ttl,
1919                        self.read_u16()?,
1920                        self.read_u16()?,
1921                        self.read_u16()?,
1922                        self.read_name()?,
1923                    ))),
1924                    RRType::HINFO => Some(Box::new(DnsHostInfo::new(
1925                        &name,
1926                        rr_type,
1927                        class,
1928                        ttl,
1929                        self.read_char_string(),
1930                        self.read_char_string(),
1931                    ))),
1932                    RRType::A => Some(Box::new(DnsAddress::new(
1933                        &name,
1934                        rr_type,
1935                        class,
1936                        ttl,
1937                        self.read_ipv4().into(),
1938                    ))),
1939                    RRType::AAAA => Some(Box::new(DnsAddress::new(
1940                        &name,
1941                        rr_type,
1942                        class,
1943                        ttl,
1944                        self.read_ipv6().into(),
1945                    ))),
1946                    RRType::NSEC => Some(Box::new(DnsNSec::new(
1947                        &name,
1948                        class,
1949                        ttl,
1950                        self.read_name()?,
1951                        self.read_type_bitmap()?,
1952                    ))),
1953                    _ => None,
1954                },
1955            };
1956
1957            if let Some(record) = rec {
1958                trace!("read_rr_records: {:?}", &record);
1959                rr_records.push(record);
1960            } else {
1961                trace!("Unsupported DNS record type: {} name: {}", ty, &name);
1962                self.offset += rdata_len;
1963            }
1964
1965            // sanity check.
1966            if self.offset != next_offset {
1967                return Err(Error::Msg(format!(
1968                    "read_rr_records: decode offset error for RData type {} offset: {} expected offset: {}",
1969                    ty, self.offset, next_offset,
1970                )));
1971            }
1972        }
1973
1974        Ok(rr_records)
1975    }
1976
1977    fn read_char_string(&mut self) -> String {
1978        let length = self.data[self.offset];
1979        self.offset += 1;
1980        self.read_string(length as usize)
1981    }
1982
1983    fn read_u16(&mut self) -> Result<u16> {
1984        let slice = &self.data[self.offset..];
1985        if slice.len() < U16_SIZE {
1986            return Err(Error::Msg(format!(
1987                "read_u16: slice len is only {}",
1988                slice.len()
1989            )));
1990        }
1991        let num = u16_from_be_slice(&slice[..U16_SIZE]);
1992        self.offset += U16_SIZE;
1993        Ok(num)
1994    }
1995
1996    /// Reads the "Type Bit Map" block for a DNS NSEC record.
1997    fn read_type_bitmap(&mut self) -> Result<Vec<u8>> {
1998        // From RFC 6762: 6.1.  Negative Responses
1999        // https://datatracker.ietf.org/doc/html/rfc6762#section-6.1
2000        //   o The Type Bit Map block number is 0.
2001        //   o The Type Bit Map block length byte is a value in the range 1-32.
2002        //   o The Type Bit Map data is 1-32 bytes, as indicated by length
2003        //     byte.
2004
2005        // Sanity check: at least 2 bytes to read.
2006        if self.data.len() < self.offset + 2 {
2007            return Err(Error::Msg(format!(
2008                "DnsIncoming is too short: {} at NSEC Type Bit Map offset {}",
2009                self.data.len(),
2010                self.offset
2011            )));
2012        }
2013
2014        let block_num = self.data[self.offset];
2015        self.offset += 1;
2016        if block_num != 0 {
2017            return Err(Error::Msg(format!(
2018                "NSEC block number is not 0: {}",
2019                block_num
2020            )));
2021        }
2022
2023        let block_len = self.data[self.offset] as usize;
2024        if !(1..=32).contains(&block_len) {
2025            return Err(Error::Msg(format!(
2026                "NSEC block length must be in the range 1-32: {}",
2027                block_len
2028            )));
2029        }
2030        self.offset += 1;
2031
2032        let end = self.offset + block_len;
2033        if end > self.data.len() {
2034            return Err(Error::Msg(format!(
2035                "NSEC block overflow: {} over RData len {}",
2036                end,
2037                self.data.len()
2038            )));
2039        }
2040        let bitmap = self.data[self.offset..end].to_vec();
2041        self.offset += block_len;
2042
2043        Ok(bitmap)
2044    }
2045
2046    fn read_vec(&mut self, length: usize) -> Vec<u8> {
2047        let v = self.data[self.offset..self.offset + length].to_vec();
2048        self.offset += length;
2049        v
2050    }
2051
2052    fn read_ipv4(&mut self) -> Ipv4Addr {
2053        let bytes: [u8; 4] = (&self.data)[self.offset..self.offset + 4]
2054            .try_into()
2055            .unwrap();
2056        self.offset += bytes.len();
2057        Ipv4Addr::from(bytes)
2058    }
2059
2060    fn read_ipv6(&mut self) -> Ipv6Addr {
2061        let bytes: [u8; 16] = (&self.data)[self.offset..self.offset + 16]
2062            .try_into()
2063            .unwrap();
2064        self.offset += bytes.len();
2065        Ipv6Addr::from(bytes)
2066    }
2067
2068    fn read_string(&mut self, length: usize) -> String {
2069        let s = str::from_utf8(&self.data[self.offset..self.offset + length]).unwrap();
2070        self.offset += length;
2071        s.to_string()
2072    }
2073
2074    /// Reads a domain name at the current location of `self.data`.
2075    ///
2076    /// See https://datatracker.ietf.org/doc/html/rfc1035#section-3.1 for
2077    /// domain name encoding.
2078    fn read_name(&mut self) -> Result<String> {
2079        let data = &self.data[..];
2080        let start_offset = self.offset;
2081        let mut offset = start_offset;
2082        let mut name = "".to_string();
2083        let mut at_end = false;
2084
2085        // From RFC1035:
2086        // "...Domain names in messages are expressed in terms of a sequence of labels.
2087        // Each label is represented as a one octet length field followed by that
2088        // number of octets."
2089        //
2090        // "...The compression scheme allows a domain name in a message to be
2091        // represented as either:
2092        // - a sequence of labels ending in a zero octet
2093        // - a pointer
2094        // - a sequence of labels ending with a pointer"
2095        loop {
2096            if offset >= data.len() {
2097                return Err(Error::Msg(format!(
2098                    "read_name: offset: {} data len {}. DnsIncoming: {:?}",
2099                    offset,
2100                    data.len(),
2101                    self
2102                )));
2103            }
2104            let length = data[offset];
2105
2106            // From RFC1035:
2107            // "...Since every domain name ends with the null label of
2108            // the root, a domain name is terminated by a length byte of zero."
2109            if length == 0 {
2110                if !at_end {
2111                    self.offset = offset + 1;
2112                }
2113                break; // The end of the name
2114            }
2115
2116            // Check the first 2 bits for possible "Message compression".
2117            match length & 0xC0 {
2118                0x00 => {
2119                    // regular utf8 string with length
2120                    offset += 1;
2121                    let ending = offset + length as usize;
2122
2123                    // Never read beyond the whole data length.
2124                    if ending > data.len() {
2125                        return Err(Error::Msg(format!(
2126                            "read_name: ending {} exceeds data length {}",
2127                            ending,
2128                            data.len()
2129                        )));
2130                    }
2131
2132                    name += str::from_utf8(&data[offset..ending])
2133                        .map_err(|e| Error::Msg(format!("read_name: from_utf8: {}", e)))?;
2134                    name += ".";
2135                    offset += length as usize;
2136                }
2137                0xC0 => {
2138                    // Message compression.
2139                    // See https://datatracker.ietf.org/doc/html/rfc1035#section-4.1.4
2140                    let slice = &data[offset..];
2141                    if slice.len() < U16_SIZE {
2142                        return Err(Error::Msg(format!(
2143                            "read_name: u16 slice len is only {}",
2144                            slice.len()
2145                        )));
2146                    }
2147                    let pointer = (u16_from_be_slice(slice) ^ 0xC000) as usize;
2148                    if pointer >= start_offset {
2149                        // Error: could trigger an infinite loop.
2150                        return Err(Error::Msg(format!(
2151                            "Invalid name compression: pointer {} must be less than the start offset {}",
2152                            &pointer, &start_offset
2153                        )));
2154                    }
2155
2156                    // A pointer marks the end of a domain name.
2157                    if !at_end {
2158                        self.offset = offset + U16_SIZE;
2159                        at_end = true;
2160                    }
2161                    offset = pointer;
2162                }
2163                _ => {
2164                    return Err(Error::Msg(format!(
2165                        "Bad name with invalid length: 0x{:x} offset {}, data (so far): {:x?}",
2166                        length,
2167                        offset,
2168                        &data[..offset]
2169                    )));
2170                }
2171            };
2172        }
2173
2174        Ok(name)
2175    }
2176}
2177
2178/// Returns UNIX time in millis
2179fn current_time_millis() -> u64 {
2180    SystemTime::now()
2181        .duration_since(SystemTime::UNIX_EPOCH)
2182        .expect("failed to get current UNIX time")
2183        .as_millis() as u64
2184}
2185
2186const fn u16_from_be_slice(bytes: &[u8]) -> u16 {
2187    let u8_array: [u8; 2] = [bytes[0], bytes[1]];
2188    u16::from_be_bytes(u8_array)
2189}
2190
2191const fn u32_from_be_slice(s: &[u8]) -> u32 {
2192    let u8_array: [u8; 4] = [s[0], s[1], s[2], s[3]];
2193    u32::from_be_bytes(u8_array)
2194}
2195
2196/// Returns the UNIX time in millis at which this record will have expired
2197/// by a certain percentage.
2198const fn get_expiration_time(created: u64, ttl: u32, percent: u32) -> u64 {
2199    // 'created' is in millis, 'ttl' is in seconds, hence:
2200    // ttl * 1000 * (percent / 100) => ttl * percent * 10
2201    created + (ttl * percent * 10) as u64
2202}
2203
2204/// A basic error type from this library.
2205#[derive(Clone, Debug, PartialEq, Eq)]
2206#[non_exhaustive]
2207pub enum Error {
2208    /// Like a classic EAGAIN. The receiver should retry.
2209    Again,
2210
2211    /// A generic error message.
2212    Msg(String),
2213
2214    /// Error during parsing of ip address
2215    ParseIpAddr(String),
2216}
2217
2218impl fmt::Display for Error {
2219    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2220        match self {
2221            Self::Msg(s) => write!(f, "{}", s),
2222            Self::ParseIpAddr(s) => write!(f, "parsing of ip addr failed, reason: {}", s),
2223            Self::Again => write!(f, "try again"),
2224        }
2225    }
2226}
2227
2228impl std::error::Error for Error {}
2229
2230/// One and only `Result` type from this library crate.
2231pub type Result<T> = core::result::Result<T, Error>;