Repo of no-std crates for my personal embedded projects
1use winnow::binary::{be_u16, be_u32};
2use winnow::{ModalResult, Parser};
3
4use super::label::Label;
5use super::records::Record;
6use crate::encoder::Encoder;
7use crate::{
8 dns::{
9 records::QType,
10 traits::{DnsParse, DnsParseKind, DnsSerialize},
11 },
12 encoder::DnsError,
13};
14
15#[derive(Debug, PartialEq, Eq)]
16pub struct Query<'a> {
17 pub name: Label<'a>,
18 pub qtype: QType,
19 pub qclass: QClass,
20}
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23#[repr(u16)]
24pub enum QClass {
25 IN = 1,
26 Multicast = 32769, // (IN + Cache flush bit)
27 Unknown(u16),
28}
29
30impl<'a> DnsParse<'a> for Query<'a> {
31 fn parse(input: &mut &'a [u8], context: &'a [u8]) -> ModalResult<Self> {
32 let name = Label::parse(input, context)?;
33 let qtype = QType::parse(input, context)?;
34 let qclass = be_u16.map(QClass::from_u16).parse_next(input)?;
35
36 Ok(Query {
37 name,
38 qtype,
39 qclass,
40 })
41 }
42}
43
44impl<'a> DnsSerialize<'a> for Query<'a> {
45 type Error = DnsError;
46
47 fn serialize<'b>(&self, encoder: &mut Encoder<'a, 'b>) -> Result<(), Self::Error> {
48 self.name.serialize(encoder)?;
49 self.qtype.serialize(encoder).ok();
50 encoder.write(&self.qclass.to_u16().to_be_bytes());
51 Ok(())
52 }
53
54 fn size(&self) -> usize {
55 self.name.size() + self.qtype.size() + core::mem::size_of::<QClass>()
56 }
57}
58
59#[derive(Debug, PartialEq, Eq)]
60pub struct Answer<'a> {
61 pub name: Label<'a>,
62 pub atype: QType,
63 pub aclass: QClass,
64 pub ttl: u32,
65 pub record: Record<'a>,
66}
67
68impl QClass {
69 fn from_u16(value: u16) -> Self {
70 match value {
71 1 => QClass::IN,
72 32769 => QClass::Multicast,
73 _ => QClass::Unknown(value),
74 }
75 }
76
77 fn to_u16(self) -> u16 {
78 match self {
79 QClass::IN => 1,
80 QClass::Multicast => 32769,
81 QClass::Unknown(value) => value,
82 }
83 }
84}
85
86impl<'a> DnsParse<'a> for Answer<'a> {
87 fn parse(input: &mut &'a [u8], context: &'a [u8]) -> ModalResult<Self> {
88 let name = Label::parse(input, context)?;
89 let atype = QType::parse(input, context)?;
90 let aclass = be_u16.map(QClass::from_u16).parse_next(input)?;
91
92 let ttl = be_u32.parse_next(input)?;
93 let record = atype.parse_kind(input, context)?;
94
95 Ok(Answer {
96 name,
97 atype,
98 aclass,
99 ttl,
100 record,
101 })
102 }
103}
104
105impl<'a> DnsSerialize<'a> for Answer<'a> {
106 type Error = DnsError;
107
108 fn serialize<'b>(&self, encoder: &mut Encoder<'a, 'b>) -> Result<(), Self::Error> {
109 self.name.serialize(encoder)?;
110 self.atype.serialize(encoder).ok();
111 encoder.write(&self.aclass.to_u16().to_be_bytes());
112 encoder.write(&self.ttl.to_be_bytes());
113 self.record.serialize(encoder)
114 }
115
116 fn size(&self) -> usize {
117 self.name.size()
118 + self.atype.size()
119 + core::mem::size_of::<QClass>()
120 + core::mem::size_of::<u32>()
121 + self.record.size()
122 }
123}
124
125#[cfg(feature = "defmt")]
126impl<'a> defmt::Format for Query<'a> {
127 fn format(&self, fmt: defmt::Formatter) {
128 defmt::write!(
129 fmt,
130 "Query {{ name: {:?}, qtype: {:?}, qclass: {:?} }}",
131 self.name,
132 self.qtype,
133 self.qclass
134 );
135 }
136}
137
138#[cfg(feature = "defmt")]
139impl defmt::Format for QType {
140 fn format(&self, fmt: defmt::Formatter) {
141 let qtype_str = match self {
142 QType::A => "A",
143 QType::AAAA => "AAAA",
144 QType::PTR => "PTR",
145 QType::TXT => "TXT",
146 QType::SRV => "SRV",
147 QType::Any => "Any",
148 QType::Unknown(_) => "Unknown",
149 };
150 defmt::write!(fmt, "QType({=str})", qtype_str);
151 }
152}
153
154#[cfg(feature = "defmt")]
155impl defmt::Format for QClass {
156 fn format(&self, fmt: defmt::Formatter) {
157 let qclass_str = match self {
158 QClass::IN => "IN",
159 QClass::Multicast => "Multicast",
160 QClass::Unknown(_) => "Unknown",
161 };
162 defmt::write!(fmt, "QClass({=str})", qclass_str);
163 }
164}
165
166#[cfg(feature = "defmt")]
167impl<'a> defmt::Format for Answer<'a> {
168 fn format(&self, fmt: defmt::Formatter) {
169 defmt::write!(
170 fmt,
171 "Answer {{ name: {:?}, atype: {:?}, aclass: {:?}, ttl: {}, record: {:?} }}",
172 self.name,
173 self.atype,
174 self.aclass,
175 self.ttl,
176 self.record
177 );
178 }
179}
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184 use crate::dns::records::A;
185 use core::net::Ipv4Addr;
186
187 #[test]
188 fn roundtrip_query() {
189 let name = Label::from("example.local");
190
191 let query = Query {
192 name,
193 qtype: QType::A,
194 qclass: QClass::IN,
195 };
196
197 let mut buffer = [0u8; 256];
198 let mut buffer = Encoder::new(&mut buffer);
199 query.serialize(&mut buffer).unwrap();
200 let buffer = buffer.finish();
201 let parsed_query = Query::parse(&mut &buffer[..], buffer).unwrap();
202
203 assert_eq!(query, parsed_query);
204 }
205
206 #[test]
207 fn roundtrip_answer() {
208 let name = Label::from("example.local");
209
210 let answer: Answer = Answer {
211 name,
212 atype: QType::A,
213 aclass: QClass::IN,
214 ttl: 120,
215 record: Record::A(A {
216 address: Ipv4Addr::new(192, 168, 1, 1),
217 }),
218 };
219
220 let mut buffer = [0u8; 256];
221 let mut buffer = Encoder::new(&mut buffer);
222 answer.serialize(&mut buffer).unwrap();
223 let buffer = buffer.finish();
224 let parsed_answer = Answer::parse(&mut &buffer[..], buffer).unwrap();
225
226 assert_eq!(answer, parsed_answer);
227 }
228}