mtop_client/dns/
core.rs

1use crate::core::MtopError;
2use std::fmt::{self, Display};
3use std::str::FromStr;
4
5#[derive(Debug, Copy, Clone, Eq, PartialEq)]
6#[repr(u16)]
7pub enum RecordType {
8    A,
9    NS,
10    CNAME,
11    SOA,
12    TXT,
13    AAAA,
14    SRV,
15    OPT,
16    Unknown(u16),
17}
18
19impl RecordType {
20    pub fn size(&self) -> usize {
21        2
22    }
23}
24
25impl From<u16> for RecordType {
26    fn from(value: u16) -> Self {
27        match value {
28            1 => Self::A,
29            2 => Self::NS,
30            5 => Self::CNAME,
31            6 => Self::SOA,
32            16 => Self::TXT,
33            28 => Self::AAAA,
34            33 => Self::SRV,
35            41 => Self::OPT,
36            v => Self::Unknown(v),
37        }
38    }
39}
40
41impl From<RecordType> for u16 {
42    fn from(value: RecordType) -> Self {
43        match value {
44            RecordType::A => 1,
45            RecordType::NS => 2,
46            RecordType::CNAME => 5,
47            RecordType::SOA => 6,
48            RecordType::TXT => 16,
49            RecordType::AAAA => 28,
50            RecordType::SRV => 33,
51            RecordType::OPT => 41,
52            RecordType::Unknown(c) => c,
53        }
54    }
55}
56
57impl Display for RecordType {
58    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59        match self {
60            RecordType::A => write!(f, "A"),
61            RecordType::NS => write!(f, "NS"),
62            RecordType::CNAME => write!(f, "CNAME"),
63            RecordType::SOA => write!(f, "SOA"),
64            RecordType::TXT => write!(f, "TXT"),
65            RecordType::AAAA => write!(f, "AAAA"),
66            RecordType::SRV => write!(f, "SRV"),
67            RecordType::OPT => write!(f, "OPT"),
68            RecordType::Unknown(t) => write!(f, "Unknown({})", t),
69        }
70    }
71}
72
73impl FromStr for RecordType {
74    type Err = MtopError;
75
76    fn from_str(s: &str) -> Result<Self, Self::Err> {
77        let s = s.to_uppercase();
78        match s.as_ref() {
79            "A" => Ok(RecordType::A),
80            "NS" => Ok(RecordType::NS),
81            "CNAME" => Ok(RecordType::CNAME),
82            "SOA" => Ok(RecordType::SOA),
83            "TXT" => Ok(RecordType::TXT),
84            "AAAA" => Ok(RecordType::AAAA),
85            "SRV" => Ok(RecordType::SRV),
86            "OPT" => Ok(RecordType::OPT),
87            v => Err(MtopError::configuration(format!("unknown record type '{}'", v))),
88        }
89    }
90}
91
92#[derive(Debug, Copy, Clone, Eq, PartialEq)]
93#[repr(u16)]
94pub enum RecordClass {
95    INET,
96    CHAOS,
97    HESIOD,
98    NONE,
99    ANY,
100    Unknown(u16),
101}
102
103impl RecordClass {
104    pub fn size(&self) -> usize {
105        2
106    }
107}
108
109impl From<u16> for RecordClass {
110    fn from(value: u16) -> Self {
111        match value {
112            1 => Self::INET,
113            3 => Self::CHAOS,
114            4 => Self::HESIOD,
115            254 => Self::NONE,
116            255 => Self::ANY,
117            v => Self::Unknown(v),
118        }
119    }
120}
121
122impl From<RecordClass> for u16 {
123    fn from(value: RecordClass) -> Self {
124        match value {
125            RecordClass::INET => 1,
126            RecordClass::CHAOS => 3,
127            RecordClass::HESIOD => 4,
128            RecordClass::NONE => 254,
129            RecordClass::ANY => 255,
130            RecordClass::Unknown(c) => c,
131        }
132    }
133}
134
135impl Display for RecordClass {
136    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137        match self {
138            RecordClass::INET => write!(f, "INET"),
139            RecordClass::HESIOD => write!(f, "HESIOD"),
140            RecordClass::CHAOS => write!(f, "CHAOS"),
141            RecordClass::NONE => write!(f, "NONE"),
142            RecordClass::ANY => write!(f, "ANY"),
143            RecordClass::Unknown(c) => write!(f, "Unknown({})", c),
144        }
145    }
146}
147
148impl FromStr for RecordClass {
149    type Err = MtopError;
150
151    fn from_str(s: &str) -> Result<Self, Self::Err> {
152        let s = s.to_uppercase();
153        match s.as_ref() {
154            "INET" => Ok(RecordClass::INET),
155            "HESIOD" => Ok(RecordClass::HESIOD),
156            "CHAOS" => Ok(RecordClass::CHAOS),
157            "NONE" => Ok(RecordClass::NONE),
158            "ANY" => Ok(RecordClass::ANY),
159            v => Err(MtopError::configuration(format!("unknown record class '{}'", v))),
160        }
161    }
162}
163
164#[cfg(test)]
165mod test {
166    use super::{RecordClass, RecordType};
167    use std::str::FromStr;
168
169    #[test]
170    fn test_record_type_from_u16() {
171        assert_eq!(RecordType::A, RecordType::from(1));
172        assert_eq!(RecordType::NS, RecordType::from(2));
173        assert_eq!(RecordType::CNAME, RecordType::from(5));
174        assert_eq!(RecordType::SOA, RecordType::from(6));
175        assert_eq!(RecordType::TXT, RecordType::from(16));
176        assert_eq!(RecordType::AAAA, RecordType::from(28));
177        assert_eq!(RecordType::SRV, RecordType::from(33));
178        assert_eq!(RecordType::OPT, RecordType::from(41));
179        assert_eq!(RecordType::Unknown(999), RecordType::from(999));
180    }
181
182    #[test]
183    fn test_record_type_to_u16() {
184        assert_eq!(1_u16, RecordType::A.into());
185        assert_eq!(2_u16, RecordType::NS.into());
186        assert_eq!(5_u16, RecordType::CNAME.into());
187        assert_eq!(6_u16, RecordType::SOA.into());
188        assert_eq!(16_u16, RecordType::TXT.into());
189        assert_eq!(28_u16, RecordType::AAAA.into());
190        assert_eq!(33_u16, RecordType::SRV.into());
191        assert_eq!(41_u16, RecordType::OPT.into());
192        assert_eq!(999_u16, RecordType::Unknown(999).into());
193    }
194
195    #[test]
196    fn test_record_type_display() {
197        assert_eq!("A", RecordType::A.to_string());
198        assert_eq!("NS", RecordType::NS.to_string());
199        assert_eq!("CNAME", RecordType::CNAME.to_string());
200        assert_eq!("SOA", RecordType::SOA.to_string());
201        assert_eq!("TXT", RecordType::TXT.to_string());
202        assert_eq!("AAAA", RecordType::AAAA.to_string());
203        assert_eq!("SRV", RecordType::SRV.to_string());
204        assert_eq!("OPT", RecordType::OPT.to_string());
205        assert_eq!("Unknown(999)", RecordType::Unknown(999).to_string());
206    }
207
208    #[test]
209    fn test_record_type_from_str() {
210        assert_eq!(RecordType::A, RecordType::from_str("A").unwrap());
211        assert_eq!(RecordType::NS, RecordType::from_str("NS").unwrap());
212        assert_eq!(RecordType::CNAME, RecordType::from_str("CNAME").unwrap());
213        assert_eq!(RecordType::SOA, RecordType::from_str("SOA").unwrap());
214        assert_eq!(RecordType::TXT, RecordType::from_str("TXT").unwrap());
215        assert_eq!(RecordType::AAAA, RecordType::from_str("AAAA").unwrap());
216        assert_eq!(RecordType::SRV, RecordType::from_str("SRV").unwrap());
217        assert_eq!(RecordType::OPT, RecordType::from_str("OPT").unwrap());
218        assert!(RecordType::from_str("BOGUS").is_err());
219    }
220
221    #[test]
222    fn test_record_class_from_u16() {
223        assert_eq!(RecordClass::INET, RecordClass::from(1));
224        assert_eq!(RecordClass::CHAOS, RecordClass::from(3));
225        assert_eq!(RecordClass::HESIOD, RecordClass::from(4));
226        assert_eq!(RecordClass::NONE, RecordClass::from(254));
227        assert_eq!(RecordClass::ANY, RecordClass::from(255));
228        assert_eq!(RecordClass::Unknown(512), RecordClass::from(512));
229    }
230
231    #[test]
232    fn test_record_class_to_u16() {
233        assert_eq!(1_u16, RecordClass::INET.into());
234        assert_eq!(3_u16, RecordClass::CHAOS.into());
235        assert_eq!(4_u16, RecordClass::HESIOD.into());
236        assert_eq!(254_u16, RecordClass::NONE.into());
237        assert_eq!(255_u16, RecordClass::ANY.into());
238        assert_eq!(512_u16, RecordClass::Unknown(512).into());
239    }
240
241    #[test]
242    fn test_record_class_display() {
243        assert_eq!("INET", RecordClass::INET.to_string());
244        assert_eq!("CHAOS", RecordClass::CHAOS.to_string());
245        assert_eq!("HESIOD", RecordClass::HESIOD.to_string());
246        assert_eq!("NONE", RecordClass::NONE.to_string());
247        assert_eq!("ANY", RecordClass::ANY.to_string());
248        assert_eq!("Unknown(512)", RecordClass::Unknown(512).to_string());
249    }
250
251    #[test]
252    fn test_record_class_from_str() {
253        assert_eq!(RecordClass::INET, RecordClass::from_str("INET").unwrap());
254        assert_eq!(RecordClass::CHAOS, RecordClass::from_str("CHAOS").unwrap());
255        assert_eq!(RecordClass::HESIOD, RecordClass::from_str("HESIOD").unwrap());
256        assert_eq!(RecordClass::NONE, RecordClass::from_str("NONE").unwrap());
257        assert_eq!(RecordClass::ANY, RecordClass::from_str("ANY").unwrap());
258        assert!(RecordClass::from_str("BOGUS").is_err());
259    }
260}