dns_parser/
parser.rs

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