hickory_proto/rr/rdata/
name.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
// Copyright 2015-2023 Benjamin Fry <benjaminfry@me.com>
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// https://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// https://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

//! Record type for all cname like records.
//!
//! A generic struct for all {*}NAME pointer RData records, CNAME, NS, and PTR. Here is the text for
//! CNAME from RFC 1035, Domain Implementation and Specification, November 1987:
//!
//! [RFC 1035, DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION, November 1987](https://tools.ietf.org/html/rfc1035)
//!
//! ```text
//! 3.3.1. CNAME RDATA format
//!
//!     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
//!     /                     CNAME                     /
//!     /                                               /
//!     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
//!
//! where:
//!
//! CNAME           A <domain-name> which specifies the canonical or primary
//!                 name for the owner.  The owner name is an alias.
//!
//! CNAME RRs cause no additional section processing, but name servers may
//! choose to restart the query at the canonical name in certain cases.  See
//! the description of name server logic in [RFC-1034] for details.
//! ```

use std::{fmt, ops::Deref};

#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

use crate::{
    error::ProtoResult,
    rr::{domain::Name, RData, RecordData, RecordType},
    serialize::binary::*,
};

/// Read the RData from the given Decoder
pub fn read(decoder: &mut BinDecoder<'_>) -> ProtoResult<Name> {
    Name::read(decoder)
}

/// [RFC 4034](https://tools.ietf.org/html/rfc4034#section-6), DNSSEC Resource Records, March 2005
///
/// This is accurate for all currently known name records.
///
/// ```text
/// 6.2.  Canonical RR Form
///
///    For the purposes of DNS security, the canonical form of an RR is the
///    wire format of the RR where:
///
///    ...
///
///    3.  if the type of the RR is NS, MD, MF, CNAME, SOA, MB, MG, MR, PTR,
///        HINFO, MINFO, MX, HINFO, RP, AFSDB, RT, SIG, PX, NXT, NAPTR, KX,
///        SRV, DNAME, A6, RRSIG, or (rfc6840 removes NSEC), all uppercase
///        US-ASCII letters in the DNS names contained within the RDATA are replaced
///        by the corresponding lowercase US-ASCII letters;
/// ```
pub fn emit(encoder: &mut BinEncoder<'_>, name_data: &Name) -> ProtoResult<()> {
    let is_canonical_names = encoder.is_canonical_names();

    // to_lowercase for rfc4034 and rfc6840
    name_data.emit_with_lowercase(encoder, is_canonical_names)?;
    Ok(())
}

macro_rules! name_rdata {
    ($name: ident) => {
        #[doc = stringify!(new type for the RecordData of $name)]
        #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
        #[derive(Debug, PartialEq, Eq, Hash, Clone)]
        pub struct $name(pub Name);

        impl BinEncodable for $name {
            fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
                emit(encoder, &self.0)
            }
        }

        impl<'r> BinDecodable<'r> for $name {
            fn read(decoder: &mut BinDecoder<'r>) -> ProtoResult<Self> {
                Name::read(decoder).map(Self)
            }
        }

        impl RecordData for $name {
            fn try_from_rdata(data: RData) -> Result<Self, RData> {
                match data {
                    RData::$name(data) => Ok(data),
                    _ => Err(data),
                }
            }

            fn try_borrow(data: &RData) -> Option<&Self> {
                match data {
                    RData::$name(data) => Some(data),
                    _ => None,
                }
            }

            fn record_type(&self) -> RecordType {
                RecordType::$name
            }

            fn into_rdata(self) -> RData {
                RData::$name(self)
            }
        }

        impl Deref for $name {
            type Target = Name;

            fn deref(&self) -> &Self::Target {
                &self.0
            }
        }

        impl fmt::Display for $name {
            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
                write!(f, "{}", self.0)
            }
        }
    };
}

name_rdata!(CNAME);
name_rdata!(NS);
name_rdata!(PTR);
name_rdata!(ANAME);

#[cfg(test)]
mod tests {

    use super::*;

    #[test]
    fn test_it_to_string_should_not_stack_overflow() {
        assert_eq!(PTR("abc.com".parse().unwrap()).to_string(), "abc.com");
    }

    #[test]
    fn test() {
        #![allow(clippy::dbg_macro, clippy::print_stdout)]

        let rdata = Name::from_ascii("WWW.example.com.").unwrap();

        let mut bytes = Vec::new();
        let mut encoder: BinEncoder<'_> = BinEncoder::new(&mut bytes);
        assert!(emit(&mut encoder, &rdata).is_ok());
        let bytes = encoder.into_bytes();

        println!("bytes: {bytes:?}");

        let mut decoder: BinDecoder<'_> = BinDecoder::new(bytes);
        let read_rdata = read(&mut decoder).expect("Decoding error");
        assert_eq!(rdata, read_rdata);
    }
}