Skip to main content

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