dns_parser_revived/
parser.rs

1use byteorder::{BigEndian, ByteOrder};
2
3use crate::rdata::opt::Record as Opt;
4use crate::{Class, RData, ResourceRecord, Type};
5use crate::{Error, Header, Name, Packet, QueryClass, QueryType, Question};
6
7const OPT_RR_START: [u8; 3] = [0, 0, 41];
8
9impl Packet<'_> {
10    /// Parse a full DNS Packet and return a structure that has all the
11    /// data borrowed from the passed buffer.
12    pub fn parse(data: &[u8]) -> Result<Packet, Error> {
13        let header = Header::parse(data)?;
14        let mut offset = Header::size();
15        let mut questions = Vec::with_capacity(header.questions as usize);
16        for _ in 0..header.questions {
17            let name = Name::scan(&data[offset..], data)?;
18            offset += name.byte_len();
19            if offset + 4 > data.len() {
20                return Err(Error::UnexpectedEOF);
21            }
22            let qtype = QueryType::parse(BigEndian::read_u16(&data[offset..offset + 2]))?;
23            offset += 2;
24
25            let (prefer_unicast, qclass) =
26                parse_qclass_code(BigEndian::read_u16(&data[offset..offset + 2]))?;
27            offset += 2;
28
29            questions.push(Question {
30                qname: name,
31                qtype,
32                prefer_unicast,
33                qclass,
34            });
35        }
36        let mut answers = Vec::with_capacity(header.answers as usize);
37        for _ in 0..header.answers {
38            answers.push(parse_record(data, &mut offset)?);
39        }
40        let mut nameservers = Vec::with_capacity(header.nameservers as usize);
41        for _ in 0..header.nameservers {
42            nameservers.push(parse_record(data, &mut offset)?);
43        }
44        let mut additional = Vec::with_capacity(header.additional as usize);
45        let mut opt = None;
46        for _ in 0..header.additional {
47            if offset + 3 <= data.len() && data[offset..offset + 3] == OPT_RR_START {
48                if opt.is_none() {
49                    opt = Some(parse_opt_record(data, &mut offset)?);
50                } else {
51                    return Err(Error::AdditionalOPT);
52                }
53            } else {
54                additional.push(parse_record(data, &mut offset)?);
55            }
56        }
57        Ok(Packet {
58            header,
59            questions,
60            answers,
61            nameservers,
62            additional,
63            opt,
64        })
65    }
66}
67
68fn parse_qclass_code(value: u16) -> Result<(bool, QueryClass), Error> {
69    let prefer_unicast = value & 0x8000 == 0x8000;
70    let qclass_code = value & 0x7FFF;
71
72    let qclass = QueryClass::parse(qclass_code)?;
73    Ok((prefer_unicast, qclass))
74}
75
76fn parse_class_code(value: u16) -> Result<(bool, Class), Error> {
77    let is_unique = value & 0x8000 == 0x8000;
78    let class_code = value & 0x7FFF;
79
80    let cls = Class::parse(class_code)?;
81    Ok((is_unique, cls))
82}
83
84// Generic function to parse answer, nameservers, and additional records.
85fn parse_record<'a>(data: &'a [u8], offset: &mut usize) -> Result<ResourceRecord<'a>, Error> {
86    let name = Name::scan(&data[*offset..], data)?;
87    *offset += name.byte_len();
88    if *offset + 10 > data.len() {
89        return Err(Error::UnexpectedEOF);
90    }
91    let typ = Type::parse(BigEndian::read_u16(&data[*offset..*offset + 2]))?;
92    *offset += 2;
93
94    let class_code = BigEndian::read_u16(&data[*offset..*offset + 2]);
95    let (multicast_unique, cls) = parse_class_code(class_code)?;
96    *offset += 2;
97
98    let mut ttl = BigEndian::read_u32(&data[*offset..*offset + 4]);
99    if ttl > i32::MAX as u32 {
100        ttl = 0;
101    }
102    *offset += 4;
103    let rdlen = BigEndian::read_u16(&data[*offset..*offset + 2]) as usize;
104    *offset += 2;
105    if *offset + rdlen > data.len() {
106        return Err(Error::UnexpectedEOF);
107    }
108    let data = RData::parse(typ, &data[*offset..*offset + rdlen], data)?;
109    *offset += rdlen;
110    Ok(ResourceRecord {
111        name,
112        multicast_unique,
113        cls,
114        ttl,
115        data,
116    })
117}
118
119// Function to parse an RFC 6891 OPT Pseudo RR
120fn parse_opt_record<'a>(data: &'a [u8], offset: &mut usize) -> Result<Opt<'a>, Error> {
121    if *offset + 11 > data.len() {
122        return Err(Error::UnexpectedEOF);
123    }
124    *offset += 1;
125    let typ = Type::parse(BigEndian::read_u16(&data[*offset..*offset + 2]))?;
126    if typ != Type::OPT {
127        return Err(Error::InvalidType(typ as u16));
128    }
129    *offset += 2;
130    let udp = BigEndian::read_u16(&data[*offset..*offset + 2]);
131    *offset += 2;
132    let extrcode = data[*offset];
133    *offset += 1;
134    let version = data[*offset];
135    *offset += 1;
136    let flags = BigEndian::read_u16(&data[*offset..*offset + 2]);
137    *offset += 2;
138    let rdlen = BigEndian::read_u16(&data[*offset..*offset + 2]) as usize;
139    *offset += 2;
140    if *offset + rdlen > data.len() {
141        return Err(Error::UnexpectedEOF);
142    }
143    let data = RData::parse(typ, &data[*offset..*offset + rdlen], data)?;
144    *offset += rdlen;
145
146    Ok(Opt {
147        udp,
148        extrcode,
149        version,
150        flags,
151        data,
152    })
153}
154
155#[cfg(test)]
156mod test {
157
158    use crate::Class as C;
159    use crate::Opcode::*;
160    use crate::QueryClass as QC;
161    use crate::QueryType as QT;
162    use crate::RData;
163    use crate::ResponseCode::NoError;
164    use crate::{Header, Packet};
165    use std::net::Ipv4Addr;
166
167    #[test]
168    fn parse_example_query() {
169        let query = b"\x06%\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\
170                      \x07example\x03com\x00\x00\x01\x00\x01";
171        let packet = Packet::parse(query).unwrap();
172        assert_eq!(
173            packet.header,
174            Header {
175                id: 1573,
176                query: true,
177                opcode: StandardQuery,
178                authoritative: false,
179                truncated: false,
180                recursion_desired: true,
181                recursion_available: false,
182                authenticated_data: false,
183                checking_disabled: false,
184                response_code: NoError,
185                questions: 1,
186                answers: 0,
187                nameservers: 0,
188                additional: 0,
189            }
190        );
191        assert_eq!(packet.questions.len(), 1);
192        assert_eq!(packet.questions[0].qtype, QT::A);
193        assert_eq!(packet.questions[0].qclass, QC::IN);
194        assert_eq!(&packet.questions[0].qname.to_string()[..], "example.com");
195        assert_eq!(packet.answers.len(), 0);
196    }
197
198    #[test]
199    fn parse_example_response() {
200        let response = b"\x06%\x81\x80\x00\x01\x00\x01\x00\x00\x00\x00\
201                         \x07example\x03com\x00\x00\x01\x00\x01\
202                         \xc0\x0c\x00\x01\x00\x01\x00\x00\x04\xf8\
203                         \x00\x04]\xb8\xd8\"";
204        let packet = Packet::parse(response).unwrap();
205        assert_eq!(
206            packet.header,
207            Header {
208                id: 1573,
209                query: false,
210                opcode: StandardQuery,
211                authoritative: false,
212                truncated: false,
213                recursion_desired: true,
214                recursion_available: true,
215                authenticated_data: false,
216                checking_disabled: false,
217                response_code: NoError,
218                questions: 1,
219                answers: 1,
220                nameservers: 0,
221                additional: 0,
222            }
223        );
224        assert_eq!(packet.questions.len(), 1);
225        assert_eq!(packet.questions[0].qtype, QT::A);
226        assert_eq!(packet.questions[0].qclass, QC::IN);
227        assert_eq!(&packet.questions[0].qname.to_string()[..], "example.com");
228        assert_eq!(packet.answers.len(), 1);
229        assert_eq!(&packet.answers[0].name.to_string()[..], "example.com");
230        assert!(!packet.answers[0].multicast_unique);
231        assert_eq!(packet.answers[0].cls, C::IN);
232        assert_eq!(packet.answers[0].ttl, 1272);
233        match packet.answers[0].data {
234            RData::A(addr) => {
235                assert_eq!(addr.0, Ipv4Addr::new(93, 184, 216, 34));
236            }
237            ref x => panic!("Wrong rdata {:?}", x),
238        }
239    }
240
241    #[test]
242    fn parse_response_with_multicast_unique() {
243        let response = b"\x06%\x81\x80\x00\x01\x00\x01\x00\x00\x00\x00\
244                         \x07example\x03com\x00\x00\x01\x00\x01\
245                         \xc0\x0c\x00\x01\x80\x01\x00\x00\x04\xf8\
246                         \x00\x04]\xb8\xd8\"";
247        let packet = Packet::parse(response).unwrap();
248
249        assert_eq!(packet.answers.len(), 1);
250        assert!(packet.answers[0].multicast_unique);
251        assert_eq!(packet.answers[0].cls, C::IN);
252    }
253
254    #[test]
255    fn parse_additional_record_response() {
256        let response = b"\x4a\xf0\x81\x80\x00\x01\x00\x01\x00\x01\x00\x01\
257                          \x03www\x05skype\x03com\x00\x00\x01\x00\x01\
258                          \xc0\x0c\x00\x05\x00\x01\x00\x00\x0e\x10\
259                          \x00\x1c\x07\x6c\x69\x76\x65\x63\x6d\x73\x0e\x74\
260                          \x72\x61\x66\x66\x69\x63\x6d\x61\x6e\x61\x67\x65\
261                          \x72\x03\x6e\x65\x74\x00\
262                          \xc0\x42\x00\x02\x00\x01\x00\x01\xd5\xd3\x00\x11\
263                          \x01\x67\x0c\x67\x74\x6c\x64\x2d\x73\x65\x72\x76\x65\x72\x73\
264                          \xc0\x42\
265                          \x01\x61\xc0\x55\x00\x01\x00\x01\x00\x00\xa3\x1c\
266                          \x00\x04\xc0\x05\x06\x1e";
267        let packet = Packet::parse(response).unwrap();
268        assert_eq!(
269            packet.header,
270            Header {
271                id: 19184,
272                query: false,
273                opcode: StandardQuery,
274                authoritative: false,
275                truncated: false,
276                recursion_desired: true,
277                recursion_available: true,
278                authenticated_data: false,
279                checking_disabled: false,
280                response_code: NoError,
281                questions: 1,
282                answers: 1,
283                nameservers: 1,
284                additional: 1,
285            }
286        );
287        assert_eq!(packet.questions.len(), 1);
288        assert_eq!(packet.questions[0].qtype, QT::A);
289        assert_eq!(packet.questions[0].qclass, QC::IN);
290        assert_eq!(&packet.questions[0].qname.to_string()[..], "www.skype.com");
291        assert_eq!(packet.answers.len(), 1);
292        assert_eq!(&packet.answers[0].name.to_string()[..], "www.skype.com");
293        assert_eq!(packet.answers[0].cls, C::IN);
294        assert_eq!(packet.answers[0].ttl, 3600);
295        match packet.answers[0].data {
296            RData::CNAME(cname) => {
297                assert_eq!(&cname.0.to_string()[..], "livecms.trafficmanager.net");
298            }
299            ref x => panic!("Wrong rdata {:?}", x),
300        }
301        assert_eq!(packet.nameservers.len(), 1);
302        assert_eq!(&packet.nameservers[0].name.to_string()[..], "net");
303        assert_eq!(packet.nameservers[0].cls, C::IN);
304        assert_eq!(packet.nameservers[0].ttl, 120275);
305        match packet.nameservers[0].data {
306            RData::NS(ns) => {
307                assert_eq!(&ns.0.to_string()[..], "g.gtld-servers.net");
308            }
309            ref x => panic!("Wrong rdata {:?}", x),
310        }
311        assert_eq!(packet.additional.len(), 1);
312        assert_eq!(
313            &packet.additional[0].name.to_string()[..],
314            "a.gtld-servers.net"
315        );
316        assert_eq!(packet.additional[0].cls, C::IN);
317        assert_eq!(packet.additional[0].ttl, 41756);
318        match packet.additional[0].data {
319            RData::A(addr) => {
320                assert_eq!(addr.0, Ipv4Addr::new(192, 5, 6, 30));
321            }
322            ref x => panic!("Wrong rdata {:?}", x),
323        }
324    }
325
326    #[test]
327    fn parse_multiple_answers() {
328        let response = b"\x9d\xe9\x81\x80\x00\x01\x00\x06\x00\x00\x00\x00\
329            \x06google\x03com\x00\x00\x01\x00\x01\xc0\x0c\
330            \x00\x01\x00\x01\x00\x00\x00\xef\x00\x04@\xe9\
331            \xa4d\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\xef\
332            \x00\x04@\xe9\xa4\x8b\xc0\x0c\x00\x01\x00\x01\
333            \x00\x00\x00\xef\x00\x04@\xe9\xa4q\xc0\x0c\x00\
334            \x01\x00\x01\x00\x00\x00\xef\x00\x04@\xe9\xa4f\
335            \xc0\x0c\x00\x01\x00\x01\x00\x00\x00\xef\x00\x04@\
336            \xe9\xa4e\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\xef\
337            \x00\x04@\xe9\xa4\x8a";
338        let packet = Packet::parse(response).unwrap();
339        assert_eq!(
340            packet.header,
341            Header {
342                id: 40425,
343                query: false,
344                opcode: StandardQuery,
345                authoritative: false,
346                truncated: false,
347                recursion_desired: true,
348                recursion_available: true,
349                authenticated_data: false,
350                checking_disabled: false,
351                response_code: NoError,
352                questions: 1,
353                answers: 6,
354                nameservers: 0,
355                additional: 0,
356            }
357        );
358        assert_eq!(packet.questions.len(), 1);
359        assert_eq!(packet.questions[0].qtype, QT::A);
360        assert_eq!(packet.questions[0].qclass, QC::IN);
361        assert_eq!(&packet.questions[0].qname.to_string()[..], "google.com");
362        assert_eq!(packet.answers.len(), 6);
363        let ips = [
364            Ipv4Addr::new(64, 233, 164, 100),
365            Ipv4Addr::new(64, 233, 164, 139),
366            Ipv4Addr::new(64, 233, 164, 113),
367            Ipv4Addr::new(64, 233, 164, 102),
368            Ipv4Addr::new(64, 233, 164, 101),
369            Ipv4Addr::new(64, 233, 164, 138),
370        ];
371        for (i, ip) in ips.iter().enumerate() {
372            assert_eq!(&packet.answers[i].name.to_string()[..], "google.com");
373            assert_eq!(packet.answers[i].cls, C::IN);
374            assert_eq!(packet.answers[i].ttl, 239);
375            match packet.answers[i].data {
376                RData::A(addr) => {
377                    assert_eq!(addr.0, *ip);
378                }
379                ref x => panic!("Wrong rdata {:?}", x),
380            }
381        }
382    }
383
384    #[test]
385    fn parse_srv_query() {
386        let query = b"[\xd9\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\
387            \x0c_xmpp-server\x04_tcp\x05gmail\x03com\x00\x00!\x00\x01";
388        let packet = Packet::parse(query).unwrap();
389        assert_eq!(
390            packet.header,
391            Header {
392                id: 23513,
393                query: true,
394                opcode: StandardQuery,
395                authoritative: false,
396                truncated: false,
397                recursion_desired: true,
398                recursion_available: false,
399                authenticated_data: false,
400                checking_disabled: false,
401                response_code: NoError,
402                questions: 1,
403                answers: 0,
404                nameservers: 0,
405                additional: 0,
406            }
407        );
408        assert_eq!(packet.questions.len(), 1);
409        assert_eq!(packet.questions[0].qtype, QT::SRV);
410        assert_eq!(packet.questions[0].qclass, QC::IN);
411        assert!(!packet.questions[0].prefer_unicast);
412        assert_eq!(
413            &packet.questions[0].qname.to_string()[..],
414            "_xmpp-server._tcp.gmail.com"
415        );
416        assert_eq!(packet.answers.len(), 0);
417    }
418
419    #[test]
420    fn parse_multicast_prefer_unicast_query() {
421        let query = b"\x06%\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\
422                      \x07example\x03com\x00\x00\x01\x80\x01";
423        let packet = Packet::parse(query).unwrap();
424
425        assert_eq!(packet.questions.len(), 1);
426        assert_eq!(packet.questions[0].qtype, QT::A);
427        assert_eq!(packet.questions[0].qclass, QC::IN);
428        assert!(packet.questions[0].prefer_unicast);
429    }
430
431    #[test]
432    fn parse_example_query_edns() {
433        let query = b"\x95\xce\x01\x00\x00\x01\x00\x00\x00\x00\x00\x01\
434            \x06google\x03com\x00\x00\x01\x00\
435            \x01\x00\x00\x29\x10\x00\x00\x00\x00\x00\x00\x00";
436        let packet = Packet::parse(query).unwrap();
437        assert_eq!(
438            packet.header,
439            Header {
440                id: 38350,
441                query: true,
442                opcode: StandardQuery,
443                authoritative: false,
444                truncated: false,
445                recursion_desired: true,
446                recursion_available: false,
447                authenticated_data: false,
448                checking_disabled: false,
449                response_code: NoError,
450                questions: 1,
451                answers: 0,
452                nameservers: 0,
453                additional: 1,
454            }
455        );
456        assert_eq!(packet.questions.len(), 1);
457        assert_eq!(packet.questions[0].qtype, QT::A);
458        assert_eq!(packet.questions[0].qclass, QC::IN);
459        assert_eq!(&packet.questions[0].qname.to_string()[..], "google.com");
460        assert_eq!(packet.answers.len(), 0);
461        match packet.opt {
462            Some(opt) => {
463                assert_eq!(opt.udp, 4096);
464                assert_eq!(opt.extrcode, 0);
465                assert_eq!(opt.version, 0);
466                assert_eq!(opt.flags, 0);
467            }
468            None => panic!("Missing OPT RR"),
469        }
470    }
471}