Skip to main content

ddns/core/parser/
question.rs

1use std::io;
2
3use nom::number::streaming::be_u16;
4
5use super::name::{Name, be_name};
6
7///
8/// ```text
9/// 0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
10/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
11/// |                                               |
12/// /                     QNAME                     /
13/// /                                               /
14/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
15/// |                     QTYPE                     |
16/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
17/// |                     QCLASS                    |
18/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
19/// ``
20#[derive(Debug, Clone)]
21pub struct Question {
22    pub(crate) name: Name,
23    pub(crate) prefer_unicast: bool,
24    pub(crate) qtype: QueryType,
25    pub(crate) qclass: QueryClass,
26}
27
28impl Question {
29    pub fn name(&self) -> &Name {
30        &self.name
31    }
32
33    pub fn prefer_unicast(&self) -> bool {
34        self.prefer_unicast
35    }
36
37    pub fn qtype(&self) -> QueryType {
38        self.qtype
39    }
40
41    pub fn qclass(&self) -> QueryClass {
42        self.qclass
43    }
44}
45
46#[derive(Debug, PartialEq, Eq, Clone, Copy)]
47pub enum QueryType {
48    /// a host addresss
49    A,
50    /// IPv6 host address (RFC 2782)
51    #[allow(clippy::upper_case_acronyms)]
52    AAAA,
53    /// the canonical name for an alias
54    Cname,
55    /// text strings
56    Txt,
57    /// service record (RFC 2782)
58    Srv,
59    /// a domain name pointer
60    Ptr,
61    /// Unassigned 265-32767
62    /// 266 a ipv4 address,
63    E,
64    /// 267 a ipv6 address,
65    E6,
66    /// 268 a ipv4 relay endpoint,
67    EE,
68    /// 269 a ipv6 relay endpoint,
69    EE6,
70}
71
72impl TryFrom<u16> for QueryType {
73    type Error = io::Error;
74
75    fn try_from(value: u16) -> Result<Self, Self::Error> {
76        let query = match value {
77            1 => Self::A,
78            28 => Self::AAAA,
79            5 => Self::Cname,
80            16 => Self::Txt,
81            33 => Self::Srv,
82            12 => Self::Ptr,
83            266 => Self::E,
84            267 => Self::E6,
85            268 => Self::EE,
86            269 => Self::EE6,
87            _ => {
88                return Err(io::Error::new(
89                    io::ErrorKind::InvalidInput,
90                    format!("Unknown query type {value}"),
91                ));
92            }
93        };
94        Ok(query)
95    }
96}
97
98impl From<QueryType> for u16 {
99    fn from(value: QueryType) -> Self {
100        match value {
101            QueryType::A => 1,
102            QueryType::AAAA => 28,
103            QueryType::Cname => 5,
104            QueryType::Txt => 16,
105            QueryType::Srv => 33,
106            QueryType::Ptr => 12,
107            QueryType::E => 266,
108            QueryType::E6 => 267,
109            QueryType::EE => 268,
110            QueryType::EE6 => 269,
111        }
112    }
113}
114
115/// The QCLASS value according to RFC 1035
116#[derive(Debug, PartialEq, Eq, Clone, Copy)]
117pub enum QueryClass {
118    /// the Internet
119    IN = 1,
120    /// the CSNET class (Obsolete - used only for examples in some obsolete
121    /// RFCs)
122    CS = 2,
123    /// the CHAOS class
124    CH = 3,
125    /// Hesiod [Dyer 87]
126    HS = 4,
127    /// Any class
128    Any = 255,
129}
130
131impl From<QueryClass> for u16 {
132    fn from(query: QueryClass) -> u16 {
133        match query {
134            QueryClass::IN => 1,
135            QueryClass::CS => 2,
136            QueryClass::CH => 3,
137            QueryClass::HS => 4,
138            QueryClass::Any => 255,
139        }
140    }
141}
142
143impl TryFrom<u16> for QueryClass {
144    type Error = io::Error;
145
146    fn try_from(value: u16) -> Result<Self, Self::Error> {
147        let query = match value {
148            1 => Self::IN,
149            2 => Self::CS,
150            3 => Self::CH,
151            4 => Self::HS,
152            255 => Self::Any,
153            _ => {
154                return Err(io::Error::new(
155                    io::ErrorKind::InvalidInput,
156                    format!("Unknown query class {value}"),
157                ));
158            }
159        };
160        Ok(query)
161    }
162}
163
164pub fn be_question<'a>(input: &'a [u8], origin: &'a [u8]) -> nom::IResult<&'a [u8], Question> {
165    let (remain, name) = be_name(input, origin)?;
166    let (remain, qtype) = be_u16(remain)?;
167    let (remain, qclass) = be_u16(remain)?;
168
169    let Ok(qtype) = QueryType::try_from(qtype) else {
170        return Err(nom::Err::Error(nom::error::make_error(
171            remain,
172            nom::error::ErrorKind::Alt,
173        )));
174    };
175    let prefer_unicast = qclass & 0x8000 == 0x8000;
176    let qclass = qclass & 0x7FFF;
177
178    let Ok(qclass) = QueryClass::try_from(qclass) else {
179        return Err(nom::Err::Error(nom::error::make_error(
180            remain,
181            nom::error::ErrorKind::Alt,
182        )));
183    };
184    Ok((
185        remain,
186        Question {
187            name,
188            prefer_unicast,
189            qtype,
190            qclass,
191        },
192    ))
193}