trust_dns_proto/rr/
resource.rs

1/*
2 * Copyright (C) 2015 Benjamin Fry <benjaminfry@me.com>
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//! resource record implementation
18
19use std::cmp::Ordering;
20use std::fmt;
21
22#[cfg(feature = "serde-config")]
23use serde::{Deserialize, Serialize};
24
25use crate::error::*;
26use crate::rr::dns_class::DNSClass;
27use crate::rr::rdata::NULL;
28#[allow(deprecated)]
29use crate::rr::IntoRecordSet;
30use crate::rr::Name;
31use crate::rr::RData;
32use crate::rr::RecordSet;
33use crate::rr::RecordType;
34use crate::serialize::binary::*;
35
36#[cfg(feature = "mdns")]
37/// From [RFC 6762](https://tools.ietf.org/html/rfc6762#section-10.2)
38/// ```text
39/// The cache-flush bit is the most significant bit of the second
40/// 16-bit word of a resource record in a Resource Record Section of a
41/// Multicast DNS message (the field conventionally referred to as the
42/// rrclass field), and the actual resource record class is the least
43/// significant fifteen bits of this field.
44/// ```
45const MDNS_ENABLE_CACHE_FLUSH: u16 = 1 << 15;
46
47const NULL_RDATA: &RData = &RData::NULL(NULL::new());
48/// Resource records are storage value in DNS, into which all key/value pair data is stored.
49///
50/// [RFC 1035](https://tools.ietf.org/html/rfc1035), DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION, November 1987
51///
52/// ```text
53/// 4.1.3. Resource record format
54///
55/// The answer, authority, and additional sections all share the same
56/// format: a variable number of resource records, where the number of
57/// records is specified in the corresponding count field in the header.
58/// Each resource record has the following format:
59///                                     1  1  1  1  1  1
60///       0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
61///     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
62///     |                                               |
63///     /                                               /
64///     /                      NAME                     /
65///     |                                               |
66///     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
67///     |                      TYPE                     |
68///     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
69///     |                     CLASS                     |
70///     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
71///     |                      TTL                      |
72///     |                                               |
73///     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
74///     |                   RDLENGTH                    |
75///     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
76///     /                     RDATA                     /
77///     /                                               /
78///     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
79///
80/// ```
81#[cfg_attr(feature = "serde-config", derive(Deserialize, Serialize))]
82#[derive(Eq, Debug, Clone)]
83pub struct Record {
84    name_labels: Name,
85    rr_type: RecordType,
86    dns_class: DNSClass,
87    ttl: u32,
88    rdata: Option<RData>,
89    #[cfg(feature = "mdns")]
90    mdns_cache_flush: bool,
91}
92
93impl Default for Record {
94    fn default() -> Self {
95        Self {
96            // TODO: these really should all be Optionals, I was lazy.
97            name_labels: Name::new(),
98            rr_type: RecordType::NULL,
99            dns_class: DNSClass::IN,
100            ttl: 0,
101            rdata: None,
102            #[cfg(feature = "mdns")]
103            mdns_cache_flush: false,
104        }
105    }
106}
107
108impl Record {
109    /// Creates a default record, use the setters to build a more useful object.
110    ///
111    /// There are no optional elements in this object, defaults are an empty name, type A, class IN,
112    /// ttl of 0 and the 0.0.0.0 ip address.
113    pub fn new() -> Self {
114        Self::default()
115    }
116
117    /// Create a record with the specified initial values.
118    ///
119    /// # Arguments
120    ///
121    /// * `name` - name of the resource records
122    /// * `rr_type` - the record type
123    /// * `ttl` - time-to-live is the amount of time this record should be cached before refreshing
124    pub fn with(name: Name, rr_type: RecordType, ttl: u32) -> Self {
125        Self {
126            name_labels: name,
127            rr_type,
128            dns_class: DNSClass::IN,
129            ttl,
130            rdata: None,
131            #[cfg(feature = "mdns")]
132            mdns_cache_flush: false,
133        }
134    }
135
136    /// Create a record with the specified initial values.
137    ///
138    /// # Arguments
139    ///
140    /// * `name` - name of the resource records
141    /// * `ttl` - time-to-live is the amount of time this record should be cached before refreshing
142    /// * `rdata` - record data to associate with the Record
143    pub fn from_rdata(name: Name, ttl: u32, rdata: RData) -> Self {
144        Self {
145            name_labels: name,
146            rr_type: rdata.to_record_type(),
147            dns_class: DNSClass::IN,
148            ttl,
149            rdata: Some(rdata),
150            #[cfg(feature = "mdns")]
151            mdns_cache_flush: false,
152        }
153    }
154
155    /// ```text
156    /// NAME            a domain name to which this resource record pertains.
157    /// ```
158    pub fn set_name(&mut self, name: Name) -> &mut Self {
159        self.name_labels = name;
160        self
161    }
162
163    /// ```text
164    /// TYPE            two octets containing one of the RR type codes.  This
165    ///                 field specifies the meaning of the data in the RDATA
166    ///                 field.
167    /// ```
168    // #[deprecated(note = "use `Record::set_record_type`")]
169    pub fn set_rr_type(&mut self, rr_type: RecordType) -> &mut Self {
170        self.rr_type = rr_type;
171        self
172    }
173
174    /// ```text
175    /// TYPE            two octets containing one of the RR type codes.  This
176    ///                 field specifies the meaning of the data in the RDATA
177    ///                 field.
178    /// ```
179    pub fn set_record_type(&mut self, rr_type: RecordType) -> &mut Self {
180        self.rr_type = rr_type;
181        self
182    }
183
184    /// ```text
185    /// CLASS           two octets which specify the class of the data in the
186    ///                 RDATA field.
187    /// ```
188    pub fn set_dns_class(&mut self, dns_class: DNSClass) -> &mut Self {
189        self.dns_class = dns_class;
190        self
191    }
192
193    /// ```text
194    /// TTL             a 32 bit unsigned integer that specifies the time
195    ///                 interval (in seconds) that the resource record may be
196    ///                 cached before it should be discarded.  Zero values are
197    ///                 interpreted to mean that the RR can only be used for the
198    ///                 transaction in progress, and should not be cached.
199    /// ```
200    pub fn set_ttl(&mut self, ttl: u32) -> &mut Self {
201        self.ttl = ttl;
202        self
203    }
204
205    /// ```text
206    /// RDATA           a variable length string of octets that describes the
207    ///                 resource.  The format of this information varies
208    ///                 according to the TYPE and CLASS of the resource record.
209    ///                 For example, the if the TYPE is A and the CLASS is IN,
210    ///                 the RDATA field is a 4 octet ARPA Internet address.
211    /// ```
212    #[deprecated(note = "use `Record::set_data` instead")]
213    pub fn set_rdata(&mut self, rdata: RData) -> &mut Self {
214        self.rdata = Some(rdata);
215        self
216    }
217
218    /// ```text
219    /// RDATA           a variable length string of octets that describes the
220    ///                 resource.  The format of this information varies
221    ///                 according to the TYPE and CLASS of the resource record.
222    ///                 For example, the if the TYPE is A and the CLASS is IN,
223    ///                 the RDATA field is a 4 octet ARPA Internet address.
224    /// ```
225    #[allow(deprecated)]
226    pub fn set_data(&mut self, rdata: Option<RData>) -> &mut Self {
227        debug_assert!(
228            !(matches!(&rdata, Some(RData::ZERO))
229                && matches!(&rdata, Some(RData::NULL(null)) if null.anything().is_empty())),
230            "pass None rather than ZERO or NULL"
231        );
232        self.rdata = rdata;
233        self
234    }
235
236    /// Changes mDNS cache-flush bit
237    /// See [RFC 6762](https://tools.ietf.org/html/rfc6762#section-10.2)
238    #[cfg(feature = "mdns")]
239    #[cfg_attr(docsrs, doc(cfg(feature = "mdns")))]
240    pub fn set_mdns_cache_flush(&mut self, flag: bool) -> &mut Self {
241        self.mdns_cache_flush = flag;
242        self
243    }
244
245    /// Returns the name of the record
246    pub fn name(&self) -> &Name {
247        &self.name_labels
248    }
249
250    /// Returns the type of the RData in the record
251    // #[deprecated(note = "use `Record::record_type`")]
252    pub fn rr_type(&self) -> RecordType {
253        self.rr_type
254    }
255
256    /// Returns the type of the RecordData in the record
257    pub fn record_type(&self) -> RecordType {
258        self.rr_type
259    }
260
261    /// Returns the DNSClass of the Record, generally IN fro internet
262    pub fn dns_class(&self) -> DNSClass {
263        self.dns_class
264    }
265
266    /// Returns the time-to-live of the record, for caching purposes
267    pub fn ttl(&self) -> u32 {
268        self.ttl
269    }
270
271    /// Returns the Record Data, i.e. the record information
272    #[deprecated(note = "use `Record::data` instead")]
273    pub fn rdata(&self) -> &RData {
274        if let Some(ref rdata) = &self.rdata {
275            rdata
276        } else {
277            NULL_RDATA
278        }
279    }
280
281    /// Returns the Record Data, i.e. the record information
282    pub fn data(&self) -> Option<&RData> {
283        self.rdata.as_ref()
284    }
285
286    /// Returns if the mDNS cache-flush bit is set or not
287    /// See [RFC 6762](https://tools.ietf.org/html/rfc6762#section-10.2)
288    #[cfg(feature = "mdns")]
289    #[cfg_attr(docsrs, doc(cfg(feature = "mdns")))]
290    pub fn mdns_cache_flush(&self) -> bool {
291        self.mdns_cache_flush
292    }
293
294    /// Returns a mutable reference to the Record Data
295    pub fn data_mut(&mut self) -> Option<&mut RData> {
296        self.rdata.as_mut()
297    }
298
299    /// Returns the RData consuming the Record
300    pub fn into_data(self) -> Option<RData> {
301        self.rdata
302    }
303
304    /// Consumes `Record` and returns its components
305    pub fn into_parts(self) -> RecordParts {
306        self.into()
307    }
308}
309
310/// Consumes `Record` giving public access to fields of `Record` so they can
311/// be destructured and taken by value
312pub struct RecordParts {
313    /// label names
314    pub name_labels: Name,
315    /// record type
316    pub rr_type: RecordType,
317    /// dns class
318    pub dns_class: DNSClass,
319    /// time to live
320    pub ttl: u32,
321    /// rdata
322    pub rdata: Option<RData>,
323    /// mDNS cache flush
324    #[cfg(feature = "mdns")]
325    #[cfg_attr(docsrs, doc(cfg(feature = "mdns")))]
326    pub mdns_cache_flush: bool,
327}
328
329impl From<Record> for RecordParts {
330    fn from(record: Record) -> Self {
331        cfg_if::cfg_if! {
332            if #[cfg(feature = "mdns")] {
333                let Record {
334                    name_labels,
335                    rr_type,
336                    dns_class,
337                    ttl,
338                    rdata,
339                    mdns_cache_flush,
340                } = record;
341            } else {
342                let Record {
343                    name_labels,
344                    rr_type,
345                    dns_class,
346                    ttl,
347                    rdata,
348                } = record;
349            }
350        }
351
352        Self {
353            name_labels,
354            rr_type,
355            dns_class,
356            ttl,
357            rdata,
358            #[cfg(feature = "mdns")]
359            mdns_cache_flush,
360        }
361    }
362}
363
364#[allow(deprecated)]
365impl IntoRecordSet for Record {
366    fn into_record_set(self) -> RecordSet {
367        RecordSet::from(self)
368    }
369}
370
371impl BinEncodable for Record {
372    fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
373        self.name_labels.emit(encoder)?;
374        self.rr_type.emit(encoder)?;
375
376        #[cfg(not(feature = "mdns"))]
377        self.dns_class.emit(encoder)?;
378
379        #[cfg(feature = "mdns")]
380        {
381            if self.mdns_cache_flush {
382                encoder.emit_u16(u16::from(self.dns_class()) | MDNS_ENABLE_CACHE_FLUSH)?;
383            } else {
384                self.dns_class.emit(encoder)?;
385            }
386        }
387
388        encoder.emit_u32(self.ttl)?;
389
390        // place the RData length
391        let place = encoder.place::<u16>()?;
392
393        // write the RData
394        //   the None case is handled below by writing `0` for the length of the RData
395        //   this is in turn read as `None` during the `read` operation.
396        if let Some(rdata) = &self.rdata {
397            rdata.emit(encoder)?;
398        }
399
400        // get the length written
401        let len = encoder.len_since_place(&place);
402        assert!(len <= u16::max_value() as usize);
403
404        // replace the location with the length
405        place.replace(encoder, len as u16)?;
406        Ok(())
407    }
408}
409
410impl<'r> BinDecodable<'r> for Record {
411    /// parse a resource record line example:
412    ///  WARNING: the record_bytes is 100% consumed and destroyed in this parsing process
413    fn read(decoder: &mut BinDecoder<'r>) -> ProtoResult<Self> {
414        // NAME            an owner name, i.e., the name of the node to which this
415        //                 resource record pertains.
416        let name_labels: Name = Name::read(decoder)?;
417
418        // TYPE            two octets containing one of the RR TYPE codes.
419        let record_type: RecordType = RecordType::read(decoder)?;
420
421        #[cfg(feature = "mdns")]
422        let mut mdns_cache_flush = false;
423
424        // CLASS           two octets containing one of the RR CLASS codes.
425        let class: DNSClass = if record_type == RecordType::OPT {
426            // verify that the OPT record is Root
427            if !name_labels.is_root() {
428                return Err(ProtoErrorKind::EdnsNameNotRoot(name_labels).into());
429            }
430
431            //  DNS Class is overloaded for OPT records in EDNS - RFC 6891
432            DNSClass::for_opt(
433                decoder.read_u16()?.unverified(/*restricted to a min of 512 in for_opt*/),
434            )
435        } else {
436            #[cfg(not(feature = "mdns"))]
437            {
438                DNSClass::read(decoder)?
439            }
440
441            #[cfg(feature = "mdns")]
442            {
443                let dns_class_value =
444                    decoder.read_u16()?.unverified(/*DNSClass::from_u16 will verify the value*/);
445                if dns_class_value & MDNS_ENABLE_CACHE_FLUSH > 0 {
446                    mdns_cache_flush = true;
447                    DNSClass::from_u16(dns_class_value & !MDNS_ENABLE_CACHE_FLUSH)?
448                } else {
449                    DNSClass::from_u16(dns_class_value)?
450                }
451            }
452        };
453
454        // TTL             a 32 bit signed integer that specifies the time interval
455        //                that the resource record may be cached before the source
456        //                of the information should again be consulted.  Zero
457        //                values are interpreted to mean that the RR can only be
458        //                used for the transaction in progress, and should not be
459        //                cached.  For example, SOA records are always distributed
460        //                with a zero TTL to prohibit caching.  Zero values can
461        //                also be used for extremely volatile data.
462        // note: u32 seems more accurate given that it can only be positive
463        let ttl: u32 = decoder.read_u32()?.unverified(/*any u32 is valid*/);
464
465        // RDLENGTH        an unsigned 16 bit integer that specifies the length in
466        //                octets of the RDATA field.
467        let rd_length = decoder
468            .read_u16()?
469            .verify_unwrap(|u| (*u as usize) <= decoder.len())
470            .map_err(|u| {
471                ProtoError::from(format!(
472                    "rdata length too large for remaining bytes, need: {} remain: {}",
473                    u,
474                    decoder.len()
475                ))
476            })?;
477
478        // this is to handle updates, RFC 2136, which uses 0 to indicate certain aspects of pre-requisites
479        //   Null represents any data.
480        let rdata = if rd_length == 0 {
481            None
482        } else {
483            // RDATA           a variable length string of octets that describes the
484            //                resource.  The format of this information varies
485            //                according to the TYPE and CLASS of the resource record.
486            // Adding restrict to the rdata length because it's used for many calculations later
487            //  and must be validated before hand
488            Some(RData::read(decoder, record_type, Restrict::new(rd_length))?)
489        };
490
491        Ok(Self {
492            name_labels,
493            rr_type: record_type,
494            dns_class: class,
495            ttl,
496            rdata,
497            #[cfg(feature = "mdns")]
498            mdns_cache_flush,
499        })
500    }
501}
502
503/// [RFC 1033](https://tools.ietf.org/html/rfc1033), DOMAIN OPERATIONS GUIDE, November 1987
504///
505/// ```text
506///   RESOURCE RECORDS
507///
508///   Records in the zone data files are called resource records (RRs).
509///   They are specified in RFC-883 and RFC-973.  An RR has a standard
510///   format as shown:
511///
512///           <name>   [<ttl>]   [<class>]   <type>   <data>
513///
514///   The record is divided into fields which are separated by white space.
515///
516///      <name>
517///
518///         The name field defines what domain name applies to the given
519///         RR.  In some cases the name field can be left blank and it will
520///         default to the name field of the previous RR.
521///
522///      <ttl>
523///
524///         TTL stands for Time To Live.  It specifies how long a domain
525///         resolver should cache the RR before it throws it out and asks a
526///         domain server again.  See the section on TTL's.  If you leave
527///         the TTL field blank it will default to the minimum time
528///         specified in the SOA record (described later).
529///
530///      <class>
531///
532///         The class field specifies the protocol group.  If left blank it
533///         will default to the last class specified.
534///
535///      <type>
536///
537///         The type field specifies what type of data is in the RR.  See
538///         the section on types.
539///
540///      <data>
541///
542///         The data field is defined differently for each type and class
543///         of data.  Popular RR data formats are described later.
544/// ```
545impl fmt::Display for Record {
546    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
547        write!(
548            f,
549            "{name} {ttl} {class} {ty}",
550            name = self.name_labels,
551            ttl = self.ttl,
552            class = self.dns_class,
553            ty = self.rr_type,
554        )?;
555
556        if let Some(rdata) = &self.rdata {
557            write!(f, " {rdata}", rdata = rdata)?;
558        }
559
560        Ok(())
561    }
562}
563
564impl PartialEq for Record {
565    /// Equality or records, as defined by
566    ///  [RFC 2136](https://tools.ietf.org/html/rfc2136), DNS Update, April 1997
567    ///
568    /// ```text
569    ///   1.1.1. Two RRs are considered equal if their NAME, CLASS, TYPE,
570    ///   RDLENGTH and RDATA fields are equal.  Note that the time-to-live
571    ///   (TTL) field is explicitly excluded from the comparison.
572    ///
573    ///   1.1.2. The rules for comparison of character strings in names are
574    ///   specified in [RFC1035 2.3.3]. i.e. case insensitive
575    /// ```
576    fn eq(&self, other: &Self) -> bool {
577        // self == other && // the same pointer
578        self.name_labels == other.name_labels
579            && self.rr_type == other.rr_type
580            && self.dns_class == other.dns_class
581            && self.rdata == other.rdata
582    }
583}
584
585/// returns the value of the compare if the items are greater or lesser, but continues on equal
586macro_rules! compare_or_equal {
587    ($x:ident, $y:ident, $z:ident) => {
588        match $x.$z.cmp(&$y.$z) {
589            o @ Ordering::Less | o @ Ordering::Greater => return o,
590            Ordering::Equal => (),
591        }
592    };
593}
594
595impl Ord for Record {
596    /// Canonical ordering as defined by
597    ///  [RFC 4034](https://tools.ietf.org/html/rfc4034#section-6), DNSSEC Resource Records, March 2005
598    ///
599    /// ```text
600    /// 6.2.  Canonical RR Form
601    ///
602    ///    For the purposes of DNS security, the canonical form of an RR is the
603    ///    wire format of the RR where:
604    ///
605    ///    1.  every domain name in the RR is fully expanded (no DNS name
606    ///        compression) and fully qualified;
607    ///
608    ///    2.  all uppercase US-ASCII letters in the owner name of the RR are
609    ///        replaced by the corresponding lowercase US-ASCII letters;
610    ///
611    ///    3.  if the type of the RR is NS, MD, MF, CNAME, SOA, MB, MG, MR, PTR,
612    ///        HINFO, MINFO, MX, HINFO, RP, AFSDB, RT, SIG, PX, NXT, NAPTR, KX,
613    ///        SRV, DNAME, A6, RRSIG, or NSEC, all uppercase US-ASCII letters in
614    ///        the DNS names contained within the RDATA are replaced by the
615    ///        corresponding lowercase US-ASCII letters;
616    ///
617    ///    4.  if the owner name of the RR is a wildcard name, the owner name is
618    ///        in its original unexpanded form, including the "*" label (no
619    ///        wildcard substitution); and
620    ///
621    ///    5.  the RR's TTL is set to its original value as it appears in the
622    ///        originating authoritative zone or the Original TTL field of the
623    ///        covering RRSIG RR.
624    /// ```
625    fn cmp(&self, other: &Self) -> Ordering {
626        // TODO: given that the ordering of Resource Records is dependent on it's binary form and this
627        //  method will be used during insertion sort or similar, we should probably do this
628        //  conversion once somehow and store it separately. Or should the internal storage of all
629        //  resource records be maintained in binary?
630
631        compare_or_equal!(self, other, name_labels);
632        compare_or_equal!(self, other, rr_type);
633        compare_or_equal!(self, other, dns_class);
634        compare_or_equal!(self, other, ttl);
635        compare_or_equal!(self, other, rdata);
636        Ordering::Equal
637    }
638}
639
640impl PartialOrd<Self> for Record {
641    /// Canonical ordering as defined by
642    ///  [RFC 4034](https://tools.ietf.org/html/rfc4034#section-6), DNSSEC Resource Records, March 2005
643    ///
644    /// ```text
645    /// 6.2.  Canonical RR Form
646    ///
647    ///    For the purposes of DNS security, the canonical form of an RR is the
648    ///    wire format of the RR where:
649    ///
650    ///    1.  every domain name in the RR is fully expanded (no DNS name
651    ///        compression) and fully qualified;
652    ///
653    ///    2.  all uppercase US-ASCII letters in the owner name of the RR are
654    ///        replaced by the corresponding lowercase US-ASCII letters;
655    ///
656    ///    3.  if the type of the RR is NS, MD, MF, CNAME, SOA, MB, MG, MR, PTR,
657    ///        HINFO, MINFO, MX, HINFO, RP, AFSDB, RT, SIG, PX, NXT, NAPTR, KX,
658    ///        SRV, DNAME, A6, RRSIG, or NSEC, all uppercase US-ASCII letters in
659    ///        the DNS names contained within the RDATA are replaced by the
660    ///        corresponding lowercase US-ASCII letters;
661    ///
662    ///    4.  if the owner name of the RR is a wildcard name, the owner name is
663    ///        in its original unexpanded form, including the "*" label (no
664    ///        wildcard substitution); and
665    ///
666    ///    5.  the RR's TTL is set to its original value as it appears in the
667    ///        originating authoritative zone or the Original TTL field of the
668    ///        covering RRSIG RR.
669    /// ```
670    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
671        Some(self.cmp(other))
672    }
673}
674
675#[cfg(test)]
676mod tests {
677    #![allow(clippy::dbg_macro, clippy::print_stdout)]
678
679    use std::cmp::Ordering;
680    use std::net::Ipv4Addr;
681    use std::str::FromStr;
682
683    use super::*;
684    use crate::rr::dns_class::DNSClass;
685    use crate::rr::record_data::RData;
686    use crate::rr::record_type::RecordType;
687    use crate::rr::Name;
688    #[allow(clippy::useless_attribute)]
689    #[allow(unused)]
690    use crate::serialize::binary::*;
691
692    #[test]
693    fn test_emit_and_read() {
694        let mut record = Record::new();
695        record
696            .set_name(Name::from_str("www.example.com").unwrap())
697            .set_rr_type(RecordType::A)
698            .set_dns_class(DNSClass::IN)
699            .set_ttl(5)
700            .set_data(Some(RData::A(Ipv4Addr::new(192, 168, 0, 1))));
701
702        let mut vec_bytes: Vec<u8> = Vec::with_capacity(512);
703        {
704            let mut encoder = BinEncoder::new(&mut vec_bytes);
705            record.emit(&mut encoder).unwrap();
706        }
707
708        let mut decoder = BinDecoder::new(&vec_bytes);
709
710        let got = Record::read(&mut decoder).unwrap();
711
712        assert_eq!(got, record);
713    }
714
715    #[test]
716    fn test_order() {
717        let mut record = Record::new();
718        record
719            .set_name(Name::from_str("www.example.com").unwrap())
720            .set_rr_type(RecordType::A)
721            .set_dns_class(DNSClass::IN)
722            .set_ttl(5)
723            .set_data(Some(RData::A(Ipv4Addr::new(192, 168, 0, 1))));
724
725        let mut greater_name = record.clone();
726        greater_name.set_name(Name::from_str("zzz.example.com").unwrap());
727
728        let mut greater_type = record.clone();
729        greater_type.set_rr_type(RecordType::AAAA);
730
731        let mut greater_class = record.clone();
732        greater_class.set_dns_class(DNSClass::NONE);
733
734        let mut greater_rdata = record.clone();
735        greater_rdata.set_data(Some(RData::A(Ipv4Addr::new(192, 168, 0, 255))));
736
737        let compares = vec![
738            (&record, &greater_name),
739            (&record, &greater_type),
740            (&record, &greater_class),
741            (&record, &greater_rdata),
742        ];
743
744        assert_eq!(record.clone(), record.clone());
745        for (r, g) in compares {
746            println!("r, g: {:?}, {:?}", r, g);
747            assert_eq!(r.cmp(g), Ordering::Less);
748        }
749    }
750
751    #[cfg(feature = "mdns")]
752    #[test]
753    fn test_mdns_cache_flush_bit_handling() {
754        const RR_CLASS_OFFSET: usize = 1 /* empty name */ +
755            std::mem::size_of::<u16>() /* rr_type */;
756
757        let mut record = Record::new();
758        record.set_mdns_cache_flush(true);
759
760        let mut vec_bytes: Vec<u8> = Vec::with_capacity(512);
761        {
762            let mut encoder = BinEncoder::new(&mut vec_bytes);
763            record.emit(&mut encoder).unwrap();
764
765            let rr_class_slice = encoder.slice_of(RR_CLASS_OFFSET, RR_CLASS_OFFSET + 2);
766            assert_eq!(rr_class_slice, &[0x80, 0x01]);
767        }
768
769        let mut decoder = BinDecoder::new(&vec_bytes);
770
771        let got = Record::read(&mut decoder).unwrap();
772
773        assert_eq!(got.dns_class(), DNSClass::IN);
774        assert!(got.mdns_cache_flush());
775    }
776}