trust_dns_proto/rr/record_type.rs
1// Copyright 2015-2021 Benjamin Fry <benjaminfry@me.com>
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://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 definitions
9#![allow(clippy::use_self)]
10
11use std::cmp::Ordering;
12use std::convert::From;
13use std::fmt;
14use std::fmt::{Display, Formatter};
15use std::str::FromStr;
16
17#[cfg(feature = "serde-config")]
18use serde::{Deserialize, Serialize};
19
20use crate::error::*;
21use crate::serialize::binary::*;
22
23// TODO: adopt proper restrictions on usage: https://tools.ietf.org/html/rfc6895 section 3.1
24// add the data TYPEs, QTYPEs, and Meta-TYPEs
25//
26
27/// The type of the resource record.
28///
29/// This specifies the type of data in the RData field of the Resource Record
30#[cfg_attr(feature = "serde-config", derive(Deserialize, Serialize))]
31#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
32#[allow(dead_code)]
33#[non_exhaustive]
34pub enum RecordType {
35 /// [RFC 1035](https://tools.ietf.org/html/rfc1035) IPv4 Address record
36 A,
37 /// [RFC 3596](https://tools.ietf.org/html/rfc3596) IPv6 address record
38 AAAA,
39 /// [ANAME draft-ietf-dnsop-aname](https://tools.ietf.org/html/draft-ietf-dnsop-aname-04)
40 ANAME,
41 // AFSDB, // 18 RFC 1183 AFS database record
42 /// [RFC 1035](https://tools.ietf.org/html/rfc1035) All cached records, aka ANY
43 ANY,
44 // APL, // 42 RFC 3123 Address Prefix List
45 /// [RFC 1035](https://tools.ietf.org/html/rfc1035) Authoritative Zone Transfer
46 AXFR,
47 /// [RFC 6844](https://tools.ietf.org/html/rfc6844) Certification Authority Authorization
48 CAA,
49 /// [RFC 7344](https://tools.ietf.org/html/rfc7344) Child DS
50 CDS,
51 /// [RFC 7344](https://tools.ietf.org/html/rfc7344) Child DNSKEY
52 CDNSKEY,
53 // CERT, // 37 RFC 4398 Certificate record
54 /// [RFC 1035](https://tools.ietf.org/html/rfc1035) Canonical name record
55 CNAME,
56 // DHCID, // 49 RFC 4701 DHCP identifier
57 // DLV, // 32769 RFC 4431 DNSSEC Lookaside Validation record
58 // DNAME, // 39 RFC 2672 Delegation Name
59 /// [RFC 7477](https://tools.ietf.org/html/rfc4034) Child-to-parent synchronization record
60 CSYNC,
61 /// [RFC 4034](https://tools.ietf.org/html/rfc4034) DNS Key record: RSASHA256 and RSASHA512, RFC5702
62 DNSKEY,
63 /// [RFC 4034](https://tools.ietf.org/html/rfc4034) Delegation signer: RSASHA256 and RSASHA512, RFC5702
64 DS,
65 /// [RFC 1035](https://tools.ietf.org/html/rfc1035) host information
66 HINFO,
67 // HIP, // 55 RFC 5205 Host Identity Protocol
68 /// [RFC draft-ietf-dnsop-svcb-https-03](https://tools.ietf.org/html/draft-ietf-dnsop-svcb-httpssvc-03) DNS SVCB and HTTPS RRs
69 HTTPS,
70 // IPSECKEY, // 45 RFC 4025 IPsec Key
71 /// [RFC 1996](https://tools.ietf.org/html/rfc1996) Incremental Zone Transfer
72 IXFR,
73 // KX, // 36 RFC 2230 Key eXchanger record
74 /// [RFC 2535](https://tools.ietf.org/html/rfc2535) and [RFC 2930](https://tools.ietf.org/html/rfc2930) Key record
75 KEY,
76 // LOC, // 29 RFC 1876 Location record
77 /// [RFC 1035](https://tools.ietf.org/html/rfc1035) Mail exchange record
78 MX,
79 /// [RFC 3403](https://tools.ietf.org/html/rfc3403) Naming Authority Pointer
80 NAPTR,
81 /// [RFC 1035](https://tools.ietf.org/html/rfc1035) Name server record
82 NS,
83 /// [RFC 4034](https://tools.ietf.org/html/rfc4034) Next-Secure record
84 NSEC,
85 /// [RFC 5155](https://tools.ietf.org/html/rfc5155) NSEC record version 3
86 NSEC3,
87 /// [RFC 5155](https://tools.ietf.org/html/rfc5155) NSEC3 parameters
88 NSEC3PARAM,
89 /// [RFC 1035](https://tools.ietf.org/html/rfc1035) Null server record, for testing
90 NULL,
91 /// [RFC 7929](https://tools.ietf.org/html/rfc7929) OpenPGP public key
92 OPENPGPKEY,
93 /// [RFC 6891](https://tools.ietf.org/html/rfc6891) Option
94 OPT,
95 /// [RFC 1035](https://tools.ietf.org/html/rfc1035) Pointer record
96 PTR,
97 // RP, // 17 RFC 1183 Responsible person
98 /// [RFC 4034](https://tools.ietf.org/html/rfc4034) DNSSEC signature: RSASHA256 and RSASHA512, RFC5702
99 RRSIG,
100 /// [RFC 2535](https://tools.ietf.org/html/rfc2535) (and [RFC 2931](https://tools.ietf.org/html/rfc2931)) Signature, to support [RFC 2137](https://tools.ietf.org/html/rfc2137) Update.
101 SIG,
102 /// [RFC 1035](https://tools.ietf.org/html/rfc1035) and [RFC 2308](https://tools.ietf.org/html/rfc2308) Start of [a zone of] authority record
103 SOA,
104 /// [RFC 2782](https://tools.ietf.org/html/rfc2782) Service locator
105 SRV,
106 /// [RFC 4255](https://tools.ietf.org/html/rfc4255) SSH Public Key Fingerprint
107 SSHFP,
108 /// [RFC draft-ietf-dnsop-svcb-https-03](https://tools.ietf.org/html/draft-ietf-dnsop-svcb-httpssvc-03) DNS SVCB and HTTPS RRs
109 SVCB,
110 // TA, // 32768 N/A DNSSEC Trust Authorities
111 // TKEY, // 249 RFC 2930 Secret key record
112 /// [RFC 6698](https://tools.ietf.org/html/rfc6698) TLSA certificate association
113 TLSA,
114 /// [RFC 8945](https://tools.ietf.org/html/rfc8945) Transaction Signature
115 TSIG,
116 /// [RFC 1035](https://tools.ietf.org/html/rfc1035) Text record
117 TXT,
118 /// Unknown Record type, or unsupported
119 Unknown(u16),
120
121 /// This corresponds to a record type of 0, unspecified
122 ZERO,
123}
124
125impl RecordType {
126 /// Returns true if this is an ANY
127 #[inline]
128 pub fn is_any(self) -> bool {
129 self == Self::ANY
130 }
131
132 /// Returns true if this is a CNAME
133 #[inline]
134 pub fn is_cname(self) -> bool {
135 self == Self::CNAME
136 }
137
138 /// Returns true if this is an NS
139 #[inline]
140 pub fn is_ns(self) -> bool {
141 self == Self::NS
142 }
143
144 /// Returns true if this is an SOA
145 #[inline]
146 pub fn is_soa(self) -> bool {
147 self == Self::SOA
148 }
149
150 /// Returns true if this is an SRV
151 #[inline]
152 pub fn is_srv(self) -> bool {
153 self == Self::SRV
154 }
155
156 /// Returns true if this is an A or an AAAA record
157 #[inline]
158 pub fn is_ip_addr(self) -> bool {
159 matches!(self, Self::A | Self::AAAA)
160 }
161
162 /// Returns true if this is a DNSSEC RecordType
163 #[inline]
164 pub fn is_dnssec(self) -> bool {
165 matches!(
166 self,
167 Self::DNSKEY
168 | Self::CDNSKEY
169 | Self::CDS
170 | Self::DS
171 | Self::KEY
172 | Self::NSEC
173 | Self::NSEC3
174 | Self::NSEC3PARAM
175 | Self::RRSIG
176 | Self::SIG
177 | Self::TSIG
178 )
179 }
180
181 /// Returns true if this is a Zero (unspecified) RecordType
182 #[inline]
183 pub fn is_zero(self) -> bool {
184 self == Self::ZERO
185 }
186}
187
188impl FromStr for RecordType {
189 type Err = ProtoError;
190
191 /// Convert `&str` to `RecordType`
192 ///
193 /// ```
194 /// use std::str::FromStr;
195 /// use trust_dns_proto::rr::record_type::RecordType;
196 ///
197 /// let var: RecordType = RecordType::from_str("A").unwrap();
198 /// assert_eq!(RecordType::A, var);
199 /// ```
200 fn from_str(str: &str) -> ProtoResult<Self> {
201 // TODO missing stuff?
202 debug_assert!(str.chars().all(|x| char::is_digit(x, 36)));
203 match str {
204 "A" => Ok(Self::A),
205 "AAAA" => Ok(Self::AAAA),
206 "ANAME" => Ok(Self::ANAME),
207 "AXFR" => Ok(Self::AXFR),
208 "CAA" => Ok(Self::CAA),
209 "CDNSKEY" => Ok(Self::CDNSKEY),
210 "CDS" => Ok(Self::CDS),
211 "CNAME" => Ok(Self::CNAME),
212 "CSYNC" => Ok(Self::CSYNC),
213 "DNSKEY" => Ok(Self::DNSKEY),
214 "DS" => Ok(Self::DS),
215 "HINFO" => Ok(Self::HINFO),
216 "HTTPS" => Ok(Self::HTTPS),
217 "KEY" => Ok(Self::KEY),
218 "MX" => Ok(Self::MX),
219 "NAPTR" => Ok(Self::NAPTR),
220 "NSEC" => Ok(Self::NSEC),
221 "NSEC3" => Ok(Self::NSEC3),
222 "NSEC3PARAM" => Ok(Self::NSEC3PARAM),
223 "NS" => Ok(Self::NS),
224 "NULL" => Ok(Self::NULL),
225 "OPENPGPKEY" => Ok(Self::OPENPGPKEY),
226 "PTR" => Ok(Self::PTR),
227 "RRSIG" => Ok(Self::RRSIG),
228 "SIG" => Ok(Self::SIG),
229 "SOA" => Ok(Self::SOA),
230 "SRV" => Ok(Self::SRV),
231 "SSHFP" => Ok(Self::SSHFP),
232 "SVCB" => Ok(Self::SVCB),
233 "TLSA" => Ok(Self::TLSA),
234 "TXT" => Ok(Self::TXT),
235 "TSIG" => Ok(Self::TSIG),
236 "ANY" | "*" => Ok(Self::ANY),
237 _ => Err(ProtoErrorKind::UnknownRecordTypeStr(str.to_string()).into()),
238 }
239 }
240}
241
242impl From<u16> for RecordType {
243 /// Convert from `u16` to `RecordType`
244 ///
245 /// ```
246 /// use trust_dns_proto::rr::record_type::RecordType;
247 ///
248 /// let var = RecordType::from(1);
249 /// assert_eq!(RecordType::A, var);
250 /// ```
251 fn from(value: u16) -> Self {
252 match value {
253 1 => Self::A,
254 28 => Self::AAAA,
255 // TODO: wrong value here, see https://github.com/bluejekyll/trust-dns/issues/723
256 65305 => Self::ANAME,
257 255 => Self::ANY,
258 251 => Self::IXFR,
259 252 => Self::AXFR,
260 257 => Self::CAA,
261 59 => Self::CDS,
262 60 => Self::CDNSKEY,
263 5 => Self::CNAME,
264 62 => Self::CSYNC,
265 48 => Self::DNSKEY,
266 43 => Self::DS,
267 13 => Self::HINFO,
268 65 => Self::HTTPS,
269 25 => Self::KEY,
270 15 => Self::MX,
271 35 => Self::NAPTR,
272 2 => Self::NS,
273 47 => Self::NSEC,
274 50 => Self::NSEC3,
275 51 => Self::NSEC3PARAM,
276 10 => Self::NULL,
277 61 => Self::OPENPGPKEY,
278 41 => Self::OPT,
279 12 => Self::PTR,
280 46 => Self::RRSIG,
281 24 => Self::SIG,
282 6 => Self::SOA,
283 33 => Self::SRV,
284 44 => Self::SSHFP,
285 64 => Self::SVCB,
286 52 => Self::TLSA,
287 250 => Self::TSIG,
288 16 => Self::TXT,
289 0 => Self::ZERO,
290 // all unknown record types
291 _ => Self::Unknown(value),
292 }
293 }
294}
295
296impl BinEncodable for RecordType {
297 fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
298 encoder.emit_u16((*self).into())
299 }
300}
301
302impl<'r> BinDecodable<'r> for RecordType {
303 fn read(decoder: &mut BinDecoder<'_>) -> ProtoResult<Self> {
304 Ok(decoder
305 .read_u16()
306 .map(
307 Restrict::unverified, /*RecordType is safe with any u16*/
308 )
309 .map(Self::from)?)
310 }
311}
312
313// TODO make these a macro...
314
315/// Convert from `RecordType` to `&str`
316///
317/// ```
318/// use std::convert::From;
319/// use trust_dns_proto::rr::record_type::RecordType;
320///
321/// let var: &'static str = From::from(RecordType::A);
322/// assert_eq!("A", var);
323///
324/// let var: &'static str = RecordType::A.into();
325/// assert_eq!("A", var);
326/// ```
327impl From<RecordType> for &'static str {
328 fn from(rt: RecordType) -> &'static str {
329 match rt {
330 RecordType::A => "A",
331 RecordType::AAAA => "AAAA",
332 RecordType::ANAME => "ANAME",
333 RecordType::ANY => "ANY",
334 RecordType::AXFR => "AXFR",
335 RecordType::CAA => "CAA",
336 RecordType::CDNSKEY => "CDNSKEY",
337 RecordType::CDS => "CDS",
338 RecordType::CNAME => "CNAME",
339 RecordType::CSYNC => "CSYNC",
340 RecordType::DNSKEY => "DNSKEY",
341 RecordType::DS => "DS",
342 RecordType::HINFO => "HINFO",
343 RecordType::HTTPS => "HTTPS",
344 RecordType::KEY => "KEY",
345 RecordType::IXFR => "IXFR",
346 RecordType::MX => "MX",
347 RecordType::NAPTR => "NAPTR",
348 RecordType::NS => "NS",
349 RecordType::NSEC => "NSEC",
350 RecordType::NSEC3 => "NSEC3",
351 RecordType::NSEC3PARAM => "NSEC3PARAM",
352 RecordType::NULL => "NULL",
353 RecordType::OPENPGPKEY => "OPENPGPKEY",
354 RecordType::OPT => "OPT",
355 RecordType::PTR => "PTR",
356 RecordType::RRSIG => "RRSIG",
357 RecordType::SIG => "SIG",
358 RecordType::SOA => "SOA",
359 RecordType::SRV => "SRV",
360 RecordType::SSHFP => "SSHFP",
361 RecordType::SVCB => "SVCB",
362 RecordType::TLSA => "TLSA",
363 RecordType::TSIG => "TSIG",
364 RecordType::TXT => "TXT",
365 RecordType::ZERO => "ZERO",
366 RecordType::Unknown(_) => "Unknown",
367 }
368 }
369}
370
371/// Convert from `RecordType` to `u16`
372///
373/// ```
374/// use std::convert::From;
375/// use trust_dns_proto::rr::record_type::RecordType;
376///
377/// let var: u16 = RecordType::A.into();
378/// assert_eq!(1, var);
379/// ```
380impl From<RecordType> for u16 {
381 fn from(rt: RecordType) -> Self {
382 match rt {
383 RecordType::A => 1,
384 RecordType::AAAA => 28,
385 // TODO: wrong value here, see https://github.com/bluejekyll/trust-dns/issues/723
386 RecordType::ANAME => 65305,
387 RecordType::ANY => 255,
388 RecordType::AXFR => 252,
389 RecordType::CAA => 257,
390 RecordType::CDNSKEY => 60,
391 RecordType::CDS => 59,
392 RecordType::CNAME => 5,
393 RecordType::CSYNC => 62,
394 RecordType::DNSKEY => 48,
395 RecordType::DS => 43,
396 RecordType::HINFO => 13,
397 RecordType::HTTPS => 65,
398 RecordType::KEY => 25,
399 RecordType::IXFR => 251,
400 RecordType::MX => 15,
401 RecordType::NAPTR => 35,
402 RecordType::NS => 2,
403 RecordType::NSEC => 47,
404 RecordType::NSEC3 => 50,
405 RecordType::NSEC3PARAM => 51,
406 RecordType::NULL => 10,
407 RecordType::OPENPGPKEY => 61,
408 RecordType::OPT => 41,
409 RecordType::PTR => 12,
410 RecordType::RRSIG => 46,
411 RecordType::SIG => 24,
412 RecordType::SOA => 6,
413 RecordType::SRV => 33,
414 RecordType::SSHFP => 44,
415 RecordType::SVCB => 64,
416 RecordType::TLSA => 52,
417 RecordType::TSIG => 250,
418 RecordType::TXT => 16,
419 RecordType::ZERO => 0,
420 RecordType::Unknown(code) => code,
421 }
422 }
423}
424
425/// [Canonical DNS Name Order](https://tools.ietf.org/html/rfc4034#section-6)
426impl PartialOrd<Self> for RecordType {
427 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
428 Some(self.cmp(other))
429 }
430}
431
432/// [Canonical DNS Name Order](https://tools.ietf.org/html/rfc4034#section-6)
433impl Ord for RecordType {
434 fn cmp(&self, other: &Self) -> Ordering {
435 u16::from(*self).cmp(&u16::from(*other))
436 }
437}
438
439impl Display for RecordType {
440 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
441 f.write_str(Into::<&str>::into(*self))
442 }
443}
444
445#[cfg(test)]
446mod tests {
447 #![allow(clippy::dbg_macro, clippy::print_stdout)]
448
449 use super::*;
450
451 #[test]
452 fn test_order() {
453 let ordered = vec![
454 RecordType::A,
455 RecordType::NS,
456 RecordType::CNAME,
457 RecordType::SOA,
458 RecordType::NULL,
459 RecordType::PTR,
460 RecordType::HINFO,
461 RecordType::MX,
462 RecordType::TXT,
463 RecordType::AAAA,
464 RecordType::SRV,
465 RecordType::CSYNC,
466 RecordType::AXFR,
467 RecordType::ANY,
468 ];
469
470 let mut unordered = vec![
471 RecordType::ANY,
472 RecordType::NULL,
473 RecordType::AXFR,
474 RecordType::A,
475 RecordType::NS,
476 RecordType::SOA,
477 RecordType::SRV,
478 RecordType::PTR,
479 RecordType::MX,
480 RecordType::CNAME,
481 RecordType::TXT,
482 RecordType::AAAA,
483 RecordType::HINFO,
484 RecordType::CSYNC,
485 ];
486
487 unordered.sort();
488
489 for rtype in unordered.clone() {
490 println!("u16 for {:?}: {}", rtype, u16::from(rtype));
491 }
492
493 assert_eq!(ordered, unordered);
494 }
495
496 /// Check that all record type names parse into unique `RecordType` instances,
497 /// and can be converted back into the same name.
498 #[test]
499 fn test_record_type_parse() {
500 let record_names = &[
501 "A",
502 "AAAA",
503 "ANAME",
504 "CAA",
505 "CNAME",
506 "CSYNC",
507 "HINFO",
508 "NULL",
509 "MX",
510 "NAPTR",
511 "NS",
512 "OPENPGPKEY",
513 "PTR",
514 "SOA",
515 "SRV",
516 "SSHFP",
517 "TLSA",
518 "TXT",
519 "ANY",
520 "AXFR",
521 ];
522
523 #[cfg(feature = "dnssec")]
524 let dnssec_record_names = &[
525 "CDNSKEY",
526 "CDS",
527 "DNSKEY",
528 "DS",
529 "KEY",
530 "NSEC",
531 "NSEC3",
532 "NSEC3PARAM",
533 "RRSIG",
534 "SIG",
535 "TSIG",
536 ];
537 #[cfg(not(feature = "dnssec"))]
538 let dnssec_record_names = &[];
539
540 let mut rtypes = std::collections::HashSet::new();
541 for name in record_names.iter().chain(dnssec_record_names) {
542 let rtype: RecordType = name.parse().unwrap();
543 assert_eq!(rtype.to_string().to_ascii_uppercase().as_str(), *name);
544 assert!(rtypes.insert(rtype));
545 }
546 }
547}