trust_dns_proto/rr/
dns_class.rs

1// Copyright 2015-2017 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//! class of DNS operations, in general always IN for internet
9#![allow(clippy::use_self)]
10
11use std::cmp::Ordering;
12use std::fmt::{self, Display, Formatter};
13use std::str::FromStr;
14
15#[cfg(feature = "serde-config")]
16use serde::{Deserialize, Serialize};
17
18use crate::error::*;
19use crate::serialize::binary::*;
20
21/// The DNS Record class
22#[cfg_attr(feature = "serde-config", derive(Deserialize, Serialize))]
23#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
24#[allow(dead_code)]
25pub enum DNSClass {
26    /// Internet
27    IN,
28    /// Chaos
29    CH,
30    /// Hesiod
31    HS,
32    /// QCLASS NONE
33    NONE,
34    /// QCLASS * (ANY)
35    ANY,
36    /// Special class for OPT Version, it was overloaded for EDNS - RFC 6891
37    /// From the RFC: `Values lower than 512 MUST be treated as equal to 512`
38    OPT(u16),
39}
40
41impl FromStr for DNSClass {
42    type Err = ProtoError;
43
44    /// Convert from `&str` to `DNSClass`
45    ///
46    /// ```
47    /// use std::str::FromStr;
48    /// use trust_dns_proto::rr::dns_class::DNSClass;
49    ///
50    /// let var: DNSClass = DNSClass::from_str("IN").unwrap();
51    /// assert_eq!(DNSClass::IN, var);
52    /// ```
53    fn from_str(str: &str) -> ProtoResult<Self> {
54        debug_assert!(str.chars().all(|x| !char::is_ascii_lowercase(&x)));
55        match str {
56            "IN" => Ok(Self::IN),
57            "CH" => Ok(Self::CH),
58            "HS" => Ok(Self::HS),
59            "NONE" => Ok(Self::NONE),
60            "ANY" | "*" => Ok(Self::ANY),
61            _ => Err(ProtoErrorKind::UnknownDnsClassStr(str.to_string()).into()),
62        }
63    }
64}
65
66impl DNSClass {
67    /// Convert from `u16` to `DNSClass`
68    ///
69    /// ```
70    /// use trust_dns_proto::rr::dns_class::DNSClass;
71    ///
72    /// let var = DNSClass::from_u16(1).unwrap();
73    /// assert_eq!(DNSClass::IN, var);
74    /// ```
75    pub fn from_u16(value: u16) -> ProtoResult<Self> {
76        match value {
77            1 => Ok(Self::IN),
78            3 => Ok(Self::CH),
79            4 => Ok(Self::HS),
80            254 => Ok(Self::NONE),
81            255 => Ok(Self::ANY),
82            _ => Err(ProtoErrorKind::UnknownDnsClassValue(value).into()),
83        }
84    }
85
86    /// Return the OPT version from value
87    pub fn for_opt(value: u16) -> Self {
88        // From RFC 6891: `Values lower than 512 MUST be treated as equal to 512`
89        let value = value.max(512);
90        Self::OPT(value)
91    }
92}
93
94impl BinEncodable for DNSClass {
95    fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
96        encoder.emit_u16((*self).into())
97    }
98}
99
100impl<'r> BinDecodable<'r> for DNSClass {
101    fn read(decoder: &mut BinDecoder<'_>) -> ProtoResult<Self> {
102        Self::from_u16(
103            decoder.read_u16()?.unverified(/*DNSClass is verified as safe in processing this*/),
104        )
105    }
106}
107
108// TODO make these a macro or annotation
109
110/// Convert from `DNSClass` to `&str`
111///
112/// ```
113/// use trust_dns_proto::rr::dns_class::DNSClass;
114///
115/// let var: &'static str = DNSClass::IN.into();
116/// assert_eq!("IN", var);
117/// ```
118impl From<DNSClass> for &'static str {
119    fn from(rt: DNSClass) -> &'static str {
120        match rt {
121            DNSClass::IN => "IN",
122            DNSClass::CH => "CH",
123            DNSClass::HS => "HS",
124            DNSClass::NONE => "NONE",
125            DNSClass::ANY => "ANY",
126            DNSClass::OPT(_) => "OPT",
127        }
128    }
129}
130
131/// Convert from `DNSClass` to `u16`
132///
133/// ```
134/// use trust_dns_proto::rr::dns_class::DNSClass;
135///
136/// let var: u16 = DNSClass::IN.into();
137/// assert_eq!(1, var);
138/// ```
139impl From<DNSClass> for u16 {
140    fn from(rt: DNSClass) -> Self {
141        match rt {
142            DNSClass::IN => 1,
143            DNSClass::CH => 3,
144            DNSClass::HS => 4,
145            DNSClass::NONE => 254,
146            DNSClass::ANY => 255,
147            // see https://tools.ietf.org/html/rfc6891#section-6.1.2
148            DNSClass::OPT(max_payload_len) => max_payload_len.max(512),
149        }
150    }
151}
152
153impl PartialOrd<Self> for DNSClass {
154    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
155        Some(self.cmp(other))
156    }
157}
158
159impl Ord for DNSClass {
160    fn cmp(&self, other: &Self) -> Ordering {
161        u16::from(*self).cmp(&u16::from(*other))
162    }
163}
164
165impl Display for DNSClass {
166    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
167        f.write_str(Into::<&str>::into(*self))
168    }
169}
170
171#[cfg(test)]
172mod tests {
173    use super::*;
174    #[test]
175    fn test_order() {
176        let ordered = vec![
177            DNSClass::IN,
178            DNSClass::CH,
179            DNSClass::HS,
180            DNSClass::NONE,
181            DNSClass::ANY,
182        ];
183        let mut unordered = vec![
184            DNSClass::NONE,
185            DNSClass::HS,
186            DNSClass::CH,
187            DNSClass::IN,
188            DNSClass::ANY,
189        ];
190
191        unordered.sort();
192
193        assert_eq!(unordered, ordered);
194    }
195
196    #[test]
197    fn check_dns_class_parse_wont_panic_with_symbols() {
198        let dns_class = "a-b-c".to_ascii_uppercase().parse::<DNSClass>();
199        assert!(matches!(&dns_class, Err(ProtoError { .. })));
200    }
201}