hickory_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// 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//! 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    /// Unknown DNSClass was parsed
40    Unknown(u16),
41}
42
43impl FromStr for DNSClass {
44    type Err = ProtoError;
45
46    /// Convert from `&str` to `DNSClass`
47    ///
48    /// ```
49    /// use std::str::FromStr;
50    /// use hickory_proto::rr::dns_class::DNSClass;
51    ///
52    /// let var: DNSClass = DNSClass::from_str("IN").unwrap();
53    /// assert_eq!(DNSClass::IN, var);
54    /// ```
55    fn from_str(str: &str) -> ProtoResult<Self> {
56        debug_assert!(str.chars().all(|x| !char::is_ascii_lowercase(&x)));
57        match str {
58            "IN" => Ok(Self::IN),
59            "CH" => Ok(Self::CH),
60            "HS" => Ok(Self::HS),
61            "NONE" => Ok(Self::NONE),
62            "ANY" | "*" => Ok(Self::ANY),
63            _ => Err(ProtoErrorKind::UnknownDnsClassStr(str.to_string()).into()),
64        }
65    }
66}
67
68impl DNSClass {
69    /// Convert from `u16` to `DNSClass`
70    ///
71    /// ```
72    /// use hickory_proto::rr::dns_class::DNSClass;
73    ///
74    /// let var = DNSClass::from_u16(1).unwrap();
75    /// assert_eq!(DNSClass::IN, var);
76    /// ```
77    #[deprecated(note = "use u16::into instead, this is now infallible")]
78    pub fn from_u16(value: u16) -> ProtoResult<Self> {
79        match value {
80            1 => Ok(Self::IN),
81            3 => Ok(Self::CH),
82            4 => Ok(Self::HS),
83            254 => Ok(Self::NONE),
84            255 => Ok(Self::ANY),
85            _ => Ok(Self::Unknown(value)),
86        }
87    }
88
89    /// Return the OPT version from value
90    pub fn for_opt(value: u16) -> Self {
91        // From RFC 6891: `Values lower than 512 MUST be treated as equal to 512`
92        let value = value.max(512);
93        Self::OPT(value)
94    }
95}
96
97impl BinEncodable for DNSClass {
98    fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
99        encoder.emit_u16((*self).into())
100    }
101}
102
103impl BinDecodable<'_> for DNSClass {
104    fn read(decoder: &mut BinDecoder<'_>) -> ProtoResult<Self> {
105        let this = Self::from(
106            decoder.read_u16()?.unverified(/*DNSClass is verified as safe in processing this*/),
107        );
108
109        Ok(this)
110    }
111}
112
113// TODO make these a macro or annotation
114
115/// Convert from `DNSClass` to `&str`
116///
117/// ```
118/// use hickory_proto::rr::dns_class::DNSClass;
119///
120/// let var: &'static str = DNSClass::IN.into();
121/// assert_eq!("IN", var);
122/// ```
123impl From<DNSClass> for &'static str {
124    fn from(rt: DNSClass) -> &'static str {
125        match rt {
126            DNSClass::IN => "IN",
127            DNSClass::CH => "CH",
128            DNSClass::HS => "HS",
129            DNSClass::NONE => "NONE",
130            DNSClass::ANY => "ANY",
131            DNSClass::OPT(_) => "OPT",
132            DNSClass::Unknown(_) => "UNKNOWN",
133        }
134    }
135}
136
137/// Convert from `u16` to `DNSClass`
138///
139/// ```
140/// use hickory_proto::rr::dns_class::DNSClass;
141///
142/// let var: DNSClass = 1u16.into();
143/// assert_eq!(DNSClass::IN, var);
144/// ```
145impl From<u16> for DNSClass {
146    fn from(value: u16) -> Self {
147        match value {
148            1 => Self::IN,
149            3 => Self::CH,
150            4 => Self::HS,
151            254 => Self::NONE,
152            255 => Self::ANY,
153            _ => Self::Unknown(value),
154        }
155    }
156}
157
158/// Convert from `DNSClass` to `u16`
159///
160/// ```
161/// use hickory_proto::rr::dns_class::DNSClass;
162///
163/// let var: u16 = DNSClass::IN.into();
164/// assert_eq!(1, var);
165/// ```
166impl From<DNSClass> for u16 {
167    fn from(rt: DNSClass) -> Self {
168        match rt {
169            DNSClass::IN => 1,
170            DNSClass::CH => 3,
171            DNSClass::HS => 4,
172            DNSClass::NONE => 254,
173            DNSClass::ANY => 255,
174            // see https://tools.ietf.org/html/rfc6891#section-6.1.2
175            DNSClass::OPT(max_payload_len) => max_payload_len.max(512),
176            DNSClass::Unknown(unknown) => unknown,
177        }
178    }
179}
180
181impl PartialOrd<Self> for DNSClass {
182    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
183        Some(self.cmp(other))
184    }
185}
186
187impl Ord for DNSClass {
188    fn cmp(&self, other: &Self) -> Ordering {
189        u16::from(*self).cmp(&u16::from(*other))
190    }
191}
192
193impl Display for DNSClass {
194    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
195        f.write_str(Into::<&str>::into(*self))
196    }
197}
198
199#[cfg(test)]
200mod tests {
201    use super::*;
202    #[test]
203    fn test_order() {
204        let ordered = vec![
205            DNSClass::IN,
206            DNSClass::CH,
207            DNSClass::HS,
208            DNSClass::NONE,
209            DNSClass::ANY,
210        ];
211        let mut unordered = vec![
212            DNSClass::NONE,
213            DNSClass::HS,
214            DNSClass::CH,
215            DNSClass::IN,
216            DNSClass::ANY,
217        ];
218
219        unordered.sort();
220
221        assert_eq!(unordered, ordered);
222    }
223
224    #[test]
225    fn check_dns_class_parse_wont_panic_with_symbols() {
226        let dns_class = "a-b-c".to_ascii_uppercase().parse::<DNSClass>();
227        assert!(matches!(&dns_class, Err(ProtoError { .. })));
228    }
229}