1 use byteorder::{BigEndian, ByteOrder};
2 
3 use {Error, ResponseCode, Opcode};
4 
5 mod flag {
6     pub const QUERY:               u16 = 0b1000_0000_0000_0000;
7     pub const OPCODE_MASK:         u16 = 0b0111_1000_0000_0000;
8     pub const AUTHORITATIVE:       u16 = 0b0000_0100_0000_0000;
9     pub const TRUNCATED:           u16 = 0b0000_0010_0000_0000;
10     pub const RECURSION_DESIRED:   u16 = 0b0000_0001_0000_0000;
11     pub const RECURSION_AVAILABLE: u16 = 0b0000_0000_1000_0000;
12     pub const AUTHENTICATED_DATA:  u16 = 0b0000_0000_0010_0000;
13     pub const CHECKING_DISABLED:   u16 = 0b0000_0000_0001_0000;
14     pub const RESERVED_MASK:       u16 = 0b0000_0000_0100_0000;
15     pub const RESPONSE_CODE_MASK:  u16 = 0b0000_0000_0000_1111;
16 }
17 
18 /// Represents parsed header of the packet
19 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
20 #[allow(missing_docs)] // fields are from the spec I think
21 pub struct Header {
22     pub id: u16,
23     pub query: bool,
24     pub opcode: Opcode,
25     pub authoritative: bool,
26     pub truncated: bool,
27     pub recursion_desired: bool,
28     pub recursion_available: bool,
29     pub authenticated_data: bool,
30     pub checking_disabled: bool,
31     pub response_code: ResponseCode,
32     pub questions: u16,
33     pub answers: u16,
34     pub nameservers: u16,
35     pub additional: u16,
36 }
37 
38 impl Header {
39     /// Parse the header into a header structure
parse(data: &[u8]) -> Result<Header, Error>40     pub fn parse(data: &[u8]) -> Result<Header, Error> {
41         if data.len() < 12 {
42             return Err(Error::HeaderTooShort);
43         }
44         let flags = BigEndian::read_u16(&data[2..4]);
45         if flags & flag::RESERVED_MASK != 0 {
46             return Err(Error::ReservedBitsAreNonZero);
47         }
48         let header = Header {
49             id: BigEndian::read_u16(&data[..2]),
50             query: flags & flag::QUERY == 0,
51             opcode: ((flags & flag::OPCODE_MASK)
52                      >> flag::OPCODE_MASK.trailing_zeros()).into(),
53             authoritative: flags & flag::AUTHORITATIVE != 0,
54             truncated: flags & flag::TRUNCATED != 0,
55             recursion_desired: flags & flag::RECURSION_DESIRED != 0,
56             recursion_available: flags & flag::RECURSION_AVAILABLE != 0,
57             authenticated_data: flags & flag::AUTHENTICATED_DATA != 0,
58             checking_disabled: flags & flag::CHECKING_DISABLED != 0,
59             response_code: From::from((flags&flag::RESPONSE_CODE_MASK) as u8),
60             questions: BigEndian::read_u16(&data[4..6]),
61             answers: BigEndian::read_u16(&data[6..8]),
62             nameservers: BigEndian::read_u16(&data[8..10]),
63             additional: BigEndian::read_u16(&data[10..12]),
64         };
65         Ok(header)
66     }
67     /// Write a header to a buffer slice
68     ///
69     /// # Panics
70     ///
71     /// When buffer size is not exactly 12 bytes
write(&self, data: &mut [u8])72     pub fn write(&self, data: &mut [u8]) {
73         if data.len() != 12 {
74             panic!("Header size is exactly 12 bytes");
75         }
76         let mut flags = 0u16;
77         flags |= Into::<u16>::into(self.opcode)
78             << flag::OPCODE_MASK.trailing_zeros();
79         flags |= Into::<u8>::into(self.response_code) as u16;
80         if !self.query { flags |= flag::QUERY; }
81         if self.authoritative { flags |= flag::AUTHORITATIVE; }
82         if self.recursion_desired { flags |= flag::RECURSION_DESIRED; }
83         if self.recursion_available { flags |= flag::RECURSION_AVAILABLE; }
84         if self.truncated { flags |= flag::TRUNCATED; }
85         BigEndian::write_u16(&mut data[..2], self.id);
86         BigEndian::write_u16(&mut data[2..4], flags);
87         BigEndian::write_u16(&mut data[4..6], self.questions);
88         BigEndian::write_u16(&mut data[6..8], self.answers);
89         BigEndian::write_u16(&mut data[8..10], self.nameservers);
90         BigEndian::write_u16(&mut data[10..12], self.additional);
91     }
92     /// Set "truncated flag" in the raw data
93     // shouldn't this method be non-public?
set_truncated(data: &mut [u8])94     pub fn set_truncated(data: &mut [u8]) {
95         let oldflags = BigEndian::read_u16(&data[2..4]);
96         BigEndian::write_u16(&mut data[2..4], oldflags & flag::TRUNCATED);
97     }
98     /// Returns a size of the header (always 12 bytes)
size() -> usize99     pub fn size() -> usize { 12 }
100 }
101 
102 
103 #[cfg(test)]
104 mod test {
105 
106     use {Header};
107     use Opcode::*;
108     use ResponseCode::NoError;
109 
110     #[test]
parse_example_query()111     fn parse_example_query() {
112         let query = b"\x06%\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\
113                       \x07example\x03com\x00\x00\x01\x00\x01";
114         let header = Header::parse(query).unwrap();
115         assert_eq!(header, Header {
116             id: 1573,
117             query: true,
118             opcode: StandardQuery,
119             authoritative: false,
120             truncated: false,
121             recursion_desired: true,
122             recursion_available: false,
123             authenticated_data: false,
124             checking_disabled: false,
125             response_code: NoError,
126             questions: 1,
127             answers: 0,
128             nameservers: 0,
129             additional: 0,
130         });
131     }
132 
133     #[test]
parse_example_response()134     fn parse_example_response() {
135         let response = b"\x06%\x81\x80\x00\x01\x00\x01\x00\x00\x00\x00\
136                          \x07example\x03com\x00\x00\x01\x00\x01\
137                          \xc0\x0c\x00\x01\x00\x01\x00\x00\x04\xf8\
138                          \x00\x04]\xb8\xd8\"";
139         let header = Header::parse(response).unwrap();
140         assert_eq!(header, Header {
141             id: 1573,
142             query: false,
143             opcode: StandardQuery,
144             authoritative: false,
145             truncated: false,
146             recursion_desired: true,
147             recursion_available: true,
148             authenticated_data: false,
149             checking_disabled: false,
150             response_code: NoError,
151             questions: 1,
152             answers: 1,
153             nameservers: 0,
154             additional: 0,
155         });
156     }
157 
158     #[test]
parse_query_with_ad_set()159     fn parse_query_with_ad_set() {
160         let query = b"\x06%\x01\x20\x00\x01\x00\x00\x00\x00\x00\x00\
161                       \x07example\x03com\x00\x00\x01\x00\x01";
162         let header = Header::parse(query).unwrap();
163         assert_eq!(header, Header {
164             id: 1573,
165             query: true,
166             opcode: StandardQuery,
167             authoritative: false,
168             truncated: false,
169             recursion_desired: true,
170             recursion_available: false,
171             authenticated_data: true,
172             checking_disabled: false,
173             response_code: NoError,
174             questions: 1,
175             answers: 0,
176             nameservers: 0,
177             additional: 0,
178         });
179     }
180 
181     #[test]
parse_query_with_cd_set()182     fn parse_query_with_cd_set() {
183         let query = b"\x06%\x01\x10\x00\x01\x00\x00\x00\x00\x00\x00\
184                       \x07example\x03com\x00\x00\x01\x00\x01";
185         let header = Header::parse(query).unwrap();
186         assert_eq!(header, Header {
187             id: 1573,
188             query: true,
189             opcode: StandardQuery,
190             authoritative: false,
191             truncated: false,
192             recursion_desired: true,
193             recursion_available: false,
194             authenticated_data: false,
195             checking_disabled: true,
196             response_code: NoError,
197             questions: 1,
198             answers: 0,
199             nameservers: 0,
200             additional: 0,
201         });
202     }
203 }
204