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