simple_dns/dns/
question.rs

1use std::{
2    collections::HashMap,
3    convert::{TryFrom, TryInto},
4};
5
6use super::{name::Label, Name, WireFormat, QCLASS, QTYPE};
7
8/// Question represents a query in the DNS Packet
9#[derive(Debug, Clone)]
10pub struct Question<'a> {
11    /// a [Name](`Name`)  to query for
12    pub qname: Name<'a>,
13    /// a [QTYPE](`QTYPE`) which specifies the type of the query.
14    pub qtype: QTYPE,
15    /// a [QCLASS](`QCLASS`) whire specifics the class of the query, For Example: IN
16    pub qclass: QCLASS,
17    /// indicates if the queries prefers a unicast response.  
18    /// MDNS related, See [RFC 6762](https://tools.ietf.org/html/rfc6762#section-5.4)
19    pub unicast_response: bool,
20}
21
22impl<'a> Question<'a> {
23    /// Creates a new question
24    pub fn new(qname: Name<'a>, qtype: QTYPE, qclass: QCLASS, unicast_response: bool) -> Self {
25        Self {
26            qname,
27            qtype,
28            qclass,
29            unicast_response,
30        }
31    }
32
33    /// Transforms the inner data into its owned type
34    pub fn into_owned<'b>(self) -> Question<'b> {
35        Question {
36            qname: self.qname.into_owned(),
37            qtype: self.qtype,
38            qclass: self.qclass,
39            unicast_response: self.unicast_response,
40        }
41    }
42
43    fn write_common<T: std::io::Write>(&self, out: &mut T) -> crate::Result<()> {
44        let qclass: u16 = match self.unicast_response {
45            true => Into::<u16>::into(self.qclass) | 0x8000,
46            false => self.qclass.into(),
47        };
48
49        out.write_all(&Into::<u16>::into(self.qtype).to_be_bytes())?;
50        out.write_all(&qclass.to_be_bytes())
51            .map_err(crate::SimpleDnsError::from)
52    }
53}
54
55impl<'a> WireFormat<'a> for Question<'a> {
56    fn parse(data: &'a [u8], position: &mut usize) -> crate::Result<Self> {
57        let qname = Name::parse(data, position)?;
58        if *position + 4 > data.len() {
59            return Err(crate::SimpleDnsError::InsufficientData);
60        }
61
62        let qtype = u16::from_be_bytes(data[*position..*position + 2].try_into()?);
63        let qclass = u16::from_be_bytes(data[*position + 2..*position + 4].try_into()?);
64
65        *position += 4;
66
67        Ok(Self {
68            qname,
69            qtype: QTYPE::try_from(qtype)?,
70            qclass: QCLASS::try_from(qclass & 0x7FFF)?,
71            unicast_response: qclass & 0x8000 == 0x8000,
72        })
73    }
74
75    fn len(&self) -> usize {
76        self.qname.len() + 4
77    }
78
79    fn write_to<T: std::io::Write>(&self, out: &mut T) -> crate::Result<()> {
80        self.qname.write_to(out)?;
81        self.write_common(out)
82    }
83
84    fn write_compressed_to<T: std::io::Write + std::io::Seek>(
85        &'a self,
86        out: &mut T,
87        name_refs: &mut HashMap<&'a [Label<'a>], usize>,
88    ) -> crate::Result<()> {
89        self.qname.write_compressed_to(out, name_refs)?;
90        self.write_common(out)
91    }
92}
93
94#[cfg(test)]
95mod tests {
96    use crate::{CLASS, TYPE};
97
98    use super::*;
99    use std::convert::TryInto;
100
101    #[test]
102    fn parse_question() {
103        let bytes = b"\x00\x00\x04_srv\x04_udp\x05local\x00\x00\x10\x00\x01";
104        let question = Question::parse(bytes, &mut 2);
105
106        assert!(question.is_ok());
107        let question = question.unwrap();
108
109        assert_eq!(QCLASS::CLASS(CLASS::IN), question.qclass);
110        assert_eq!(QTYPE::TYPE(TYPE::TXT), question.qtype);
111        assert!(!question.unicast_response);
112    }
113
114    #[test]
115    fn append_to_vec() {
116        let question = Question::new(
117            "_srv._udp.local".try_into().unwrap(),
118            TYPE::TXT.into(),
119            CLASS::IN.into(),
120            false,
121        );
122        let mut bytes = Vec::new();
123        question.write_to(&mut bytes).unwrap();
124
125        assert_eq!(b"\x04_srv\x04_udp\x05local\x00\x00\x10\x00\x01", &bytes[..]);
126        assert_eq!(bytes.len(), question.len());
127    }
128
129    #[test]
130    fn unicast_response() {
131        let mut bytes = Vec::new();
132        Question::new(
133            "x.local".try_into().unwrap(),
134            TYPE::TXT.into(),
135            CLASS::IN.into(),
136            true,
137        )
138        .write_to(&mut bytes)
139        .unwrap();
140        let parsed = Question::parse(&bytes, &mut 0).unwrap();
141
142        assert!(parsed.unicast_response);
143    }
144}