hickory_proto/rr/rdata/name.rs
1// Copyright 2015-2023 Benjamin Fry <benjaminfry@me.com>
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// https://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// https://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8//! Record type for all cname like records.
9//!
10//! A generic struct for all {*}NAME pointer RData records, CNAME, NS, and PTR. Here is the text for
11//! CNAME from RFC 1035, Domain Implementation and Specification, November 1987:
12//!
13//! [RFC 1035, DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION, November 1987](https://tools.ietf.org/html/rfc1035)
14//!
15//! ```text
16//! 3.3.1. CNAME RDATA format
17//!
18//! +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
19//! / CNAME /
20//! / /
21//! +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
22//!
23//! where:
24//!
25//! CNAME A <domain-name> which specifies the canonical or primary
26//! name for the owner. The owner name is an alias.
27//!
28//! CNAME RRs cause no additional section processing, but name servers may
29//! choose to restart the query at the canonical name in certain cases. See
30//! the description of name server logic in [RFC-1034] for details.
31//! ```
32
33use std::{fmt, ops::Deref};
34
35#[cfg(feature = "serde-config")]
36use serde::{Deserialize, Serialize};
37
38use crate::{
39 error::ProtoResult,
40 rr::{domain::Name, RData, RecordData, RecordType},
41 serialize::binary::*,
42};
43
44/// Read the RData from the given Decoder
45pub fn read(decoder: &mut BinDecoder<'_>) -> ProtoResult<Name> {
46 Name::read(decoder)
47}
48
49/// [RFC 4034](https://tools.ietf.org/html/rfc4034#section-6), DNSSEC Resource Records, March 2005
50///
51/// This is accurate for all currently known name records.
52///
53/// ```text
54/// 6.2. Canonical RR Form
55///
56/// For the purposes of DNS security, the canonical form of an RR is the
57/// wire format of the RR where:
58///
59/// ...
60///
61/// 3. if the type of the RR is NS, MD, MF, CNAME, SOA, MB, MG, MR, PTR,
62/// HINFO, MINFO, MX, HINFO, RP, AFSDB, RT, SIG, PX, NXT, NAPTR, KX,
63/// SRV, DNAME, A6, RRSIG, or (rfc6840 removes NSEC), all uppercase
64/// US-ASCII letters in the DNS names contained within the RDATA are replaced
65/// by the corresponding lowercase US-ASCII letters;
66/// ```
67pub fn emit(encoder: &mut BinEncoder<'_>, name_data: &Name) -> ProtoResult<()> {
68 let is_canonical_names = encoder.is_canonical_names();
69
70 // to_lowercase for rfc4034 and rfc6840
71 name_data.emit_with_lowercase(encoder, is_canonical_names)?;
72 Ok(())
73}
74
75macro_rules! name_rdata {
76 ($name: ident) => {
77 #[doc = stringify!(new type for the RecordData of $name)]
78 #[cfg_attr(feature = "serde-config", derive(Deserialize, Serialize))]
79 #[derive(Debug, PartialEq, Eq, Hash, Clone)]
80 pub struct $name(pub Name);
81
82 impl BinEncodable for $name {
83 fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
84 emit(encoder, &self.0)
85 }
86 }
87
88 impl<'r> BinDecodable<'r> for $name {
89 fn read(decoder: &mut BinDecoder<'r>) -> ProtoResult<Self> {
90 Name::read(decoder).map(Self)
91 }
92 }
93
94 impl RecordData for $name {
95 fn try_from_rdata(data: RData) -> Result<Self, RData> {
96 match data {
97 RData::$name(data) => Ok(data),
98 _ => Err(data),
99 }
100 }
101
102 fn try_borrow(data: &RData) -> Option<&Self> {
103 match data {
104 RData::$name(data) => Some(data),
105 _ => None,
106 }
107 }
108
109 fn record_type(&self) -> RecordType {
110 RecordType::$name
111 }
112
113 fn into_rdata(self) -> RData {
114 RData::$name(self)
115 }
116 }
117
118 impl Deref for $name {
119 type Target = Name;
120
121 fn deref(&self) -> &Self::Target {
122 &self.0
123 }
124 }
125
126 impl fmt::Display for $name {
127 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
128 write!(f, "{}", self.0)
129 }
130 }
131 };
132}
133
134name_rdata!(CNAME);
135name_rdata!(NS);
136name_rdata!(PTR);
137name_rdata!(ANAME);
138
139#[cfg(test)]
140mod tests {
141
142 use super::*;
143
144 #[test]
145 fn test_it_to_string_should_not_stack_overflow() {
146 assert_eq!(PTR("abc.com".parse().unwrap()).to_string(), "abc.com");
147 }
148
149 #[test]
150 fn test() {
151 #![allow(clippy::dbg_macro, clippy::print_stdout)]
152
153 let rdata = Name::from_ascii("WWW.example.com.").unwrap();
154
155 let mut bytes = Vec::new();
156 let mut encoder: BinEncoder<'_> = BinEncoder::new(&mut bytes);
157 assert!(emit(&mut encoder, &rdata).is_ok());
158 let bytes = encoder.into_bytes();
159
160 println!("bytes: {bytes:?}");
161
162 let mut decoder: BinDecoder<'_> = BinDecoder::new(bytes);
163 let read_rdata = read(&mut decoder).expect("Decoding error");
164 assert_eq!(rdata, read_rdata);
165 }
166}