1use std::{
2 fmt::Display,
3 io,
4 net::{Ipv4Addr, Ipv6Addr},
5};
6
7use bytes::Buf;
8use endpoint::{EndpointAddr, be_endpoint_addr_compat};
9use nom::{
10 Parser,
11 bytes::streaming::take,
12 combinator::map,
13 number::streaming::{be_u16, be_u32, be_u128},
14};
15use ptr::{Ptr, be_ptr};
16use srv::{Srv, be_srv};
17use txt::Txt;
18
19use super::name::{Name, be_name};
20
21pub mod endpoint;
22pub mod ptr;
23pub mod srv;
24pub mod txt;
25#[derive(Debug, PartialEq, Eq, Hash, Clone)]
47pub struct ResourceRecord {
48 pub(crate) name: Name,
49 pub(crate) typ: Type,
50 pub(crate) multicast_unique: bool,
54 pub(crate) cls: Class,
55 pub(crate) ttl: u32,
56 pub(crate) data: RData,
57}
58
59impl ResourceRecord {
60 pub fn data(&self) -> &RData {
61 &self.data
62 }
63
64 pub fn name(&self) -> Name {
65 self.name.clone()
66 }
67
68 pub fn typ(&self) -> Type {
69 self.typ
70 }
71
72 pub fn cls(&self) -> Class {
73 self.cls
74 }
75
76 pub fn ttl(&self) -> u32 {
77 self.ttl
78 }
79}
80
81#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
83pub enum Class {
84 IN = 1,
86 CS = 2,
89 CH = 3,
91 HS = 4,
93}
94
95impl TryFrom<u16> for Class {
96 type Error = io::Error;
97
98 fn try_from(value: u16) -> Result<Self, Self::Error> {
99 let value = value & 0x7FFF; let class = match value {
101 1 => Self::IN,
102 2 => Self::CS,
103 3 => Self::CH,
104 4 => Self::HS,
105 _ => {
106 return Err(io::Error::new(
107 io::ErrorKind::InvalidInput,
108 format!("Unknown record class: {value}"),
109 ));
110 }
111 };
112 Ok(class)
113 }
114}
115
116impl From<Class> for u16 {
117 fn from(value: Class) -> Self {
118 match value {
119 Class::IN => 1,
120 Class::CS => 2,
121 Class::CH => 3,
122 Class::HS => 4,
123 }
124 }
125}
126
127#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
132pub enum Type {
133 A,
135 Ns,
137 #[allow(clippy::upper_case_acronyms)]
139 AAAA,
140 Cname,
142 Txt,
144 Srv,
146 Ptr,
148 E,
150}
151
152impl TryFrom<u16> for Type {
153 type Error = io::Error;
154
155 fn try_from(value: u16) -> Result<Self, Self::Error> {
156 let typ = match value {
157 1 => Self::A,
158 2 => Self::Ns,
159 28 => Self::AAAA,
160 5 => Self::Cname,
161 16 => Self::Txt,
162 33 => Self::Srv,
163 12 => Self::Ptr,
164 266 => Self::E,
165 267..=269 => Self::E,
167 _ => {
168 return Err(io::Error::new(
169 io::ErrorKind::InvalidInput,
170 format!("Unknown record type: {value}"),
171 ));
172 }
173 };
174 Ok(typ)
175 }
176}
177
178impl From<Type> for u16 {
179 fn from(value: Type) -> Self {
180 match value {
181 Type::A => 1,
182 Type::Ns => 2,
183 Type::AAAA => 28,
184 Type::Cname => 5,
185 Type::Txt => 16,
186 Type::Srv => 33,
187 Type::Ptr => 12,
188 Type::E => 266,
189 }
190 }
191}
192
193#[derive(Debug, PartialEq, Eq, Hash, Clone)]
194pub enum RData {
195 A(Ipv4Addr),
196 AAAA(Ipv6Addr),
197 CName(Name),
198 Txt(Txt),
199 Srv(Srv),
200 Ptr(Ptr),
201 E(EndpointAddr),
202}
203
204impl Display for RData {
205 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
206 match self {
207 RData::A(ip) => write!(f, "{ip}"),
208 RData::AAAA(ip) => write!(f, "{ip}"),
209 RData::CName(name) => write!(f, "CName({name})"),
210 RData::Txt(txt) => write!(f, "{txt:?})"),
211 RData::Srv(srv) => write!(f, "{srv:?}"),
212 RData::Ptr(ptr) => write!(f, "{ptr:?}"),
213 RData::E(e) => write!(f, "{e}"),
214 }
215 }
216}
217
218pub fn be_record<'a>(input: &'a [u8], origin: &'a [u8]) -> nom::IResult<&'a [u8], ResourceRecord> {
219 let (remain, name) = be_name(input, origin)?;
220 let (remain, typ) = be_u16(remain)?;
221 let (remain, cls) = be_u16(remain)?;
222 let (remain, ttl) = be_u32(remain)?;
223 let (mut remain, rdlen) = be_u16(remain)?;
224
225 let Ok(typ) = Type::try_from(typ) else {
226 if remain.len() < rdlen as usize {
227 return Err(nom::Err::Incomplete(nom::Needed::new(
228 rdlen as usize - remain.len(),
229 )));
230 }
231 remain.advance(rdlen as usize);
232 return Err(nom::Err::Error(nom::error::make_error(
233 remain,
234 nom::error::ErrorKind::Alt,
235 )));
236 };
237
238 if remain.len() < rdlen as usize {
239 return Err(nom::Err::Incomplete(nom::Needed::new(
240 rdlen as usize - remain.len(),
241 )));
242 }
243 let (remain_after_rdata, rdata_bytes) = take(rdlen)(remain)?;
244 let (rdata_remain, rdata) = be_rdata(rdata_bytes, origin, typ, rdlen)?;
245 if !rdata_remain.is_empty() {
246 return Err(nom::Err::Error(nom::error::make_error(
247 remain_after_rdata,
248 nom::error::ErrorKind::Eof,
249 )));
250 }
251 let mut remain = remain_after_rdata;
252
253 let multicast_unique = cls & 0x8000 == 0x8000;
254 let cls = cls & 0x7FFF;
255 let Ok(cls) = Class::try_from(cls) else {
256 if remain.len() < rdlen as usize {
257 return Err(nom::Err::Incomplete(nom::Needed::new(
258 rdlen as usize - remain.len(),
259 )));
260 }
261 remain.advance(rdlen as usize);
262 return Err(nom::Err::Error(nom::error::make_error(
263 remain,
264 nom::error::ErrorKind::Alt,
265 )));
266 };
267
268 Ok((
269 remain,
270 ResourceRecord {
271 name,
272 typ,
273 multicast_unique,
274 cls,
275 ttl,
276 data: rdata,
277 },
278 ))
279}
280
281fn be_rdata<'a>(
282 input: &'a [u8],
283 origin: &'a [u8],
284 typ: Type,
285 rdlen: u16,
286) -> nom::IResult<&'a [u8], RData> {
287 match typ {
288 Type::A => map(be_u32, |ip| RData::A(Ipv4Addr::from(ip))).parse(input),
289 Type::AAAA => map(be_u128, |ip| RData::AAAA(Ipv6Addr::from(ip))).parse(input),
290 Type::Cname => be_name(input, origin).map(|(remain, name)| (remain, RData::CName(name))),
291 Type::Txt => map(take(rdlen), |txt: &[u8]| RData::Txt(Txt::new(txt.to_vec()))).parse(input),
292 Type::Srv => {
293 let (remain, srv) = be_srv(input, origin)?;
294 Ok((remain, RData::Srv(srv)))
295 }
296 Type::Ptr => be_ptr(input, origin).map(|(remain, ptr)| (remain, RData::Ptr(ptr))),
297 Type::Ns => be_name(input, origin).map(|(remain, name)| (remain, RData::CName(name))),
298 Type::E => be_endpoint_addr_compat(input, rdlen).map(|(remain, e)| (remain, RData::E(e))),
299 }
300}
301
302#[cfg(test)]
303mod test {
304 use super::*;
305
306 fn record_prefix_for_a(name: &[u8], rdlen: u16) -> Vec<u8> {
307 let mut buf = Vec::new();
308 buf.extend_from_slice(name);
309 buf.extend_from_slice(&1u16.to_be_bytes());
310 buf.extend_from_slice(&1u16.to_be_bytes());
311 buf.extend_from_slice(&0u32.to_be_bytes());
312 buf.extend_from_slice(&rdlen.to_be_bytes());
313 buf
314 }
315
316 #[test]
317 fn parse_record_incomplete_rdata_returns_incomplete() {
318 let name = b"\x07example\x03com\x00";
319 let mut buf = record_prefix_for_a(name, 4);
320 buf.extend_from_slice(&[127, 0, 0]);
321 let ret = be_record(&buf, &buf);
322 assert!(matches!(ret, Err(nom::Err::Incomplete(_))));
323 }
324
325 #[test]
326 fn parse_record_extra_rdata_bytes_is_error() {
327 let name = b"\x07example\x03com\x00";
328 let mut buf = record_prefix_for_a(name, 5);
329 buf.extend_from_slice(&[127, 0, 0, 1, 9]);
330 let ret = be_record(&buf, &buf);
331 assert!(matches!(ret, Err(nom::Err::Error(_))));
332 }
333}