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