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}