1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
use byteorder::{BigEndian, ByteOrder};
use {Error, ResponseCode, Opcode};
mod flag {
pub const QUERY: u16 = 0b1000_0000_0000_0000;
pub const OPCODE_MASK: u16 = 0b0111_1000_0000_0000;
pub const AUTHORITATIVE: u16 = 0b0000_0100_0000_0000;
pub const TRUNCATED: u16 = 0b0000_0010_0000_0000;
pub const RECURSION_DESIRED: u16 = 0b0000_0001_0000_0000;
pub const RECURSION_AVAILABLE: u16 = 0b0000_0000_1000_0000;
pub const RESERVED_MASK: u16 = 0b0000_0000_0111_0000;
pub const RESPONSE_CODE_MASK: u16 = 0b0000_0000_0000_1111;
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct Header {
pub id: u16,
pub query: bool,
pub opcode: Opcode,
pub authoritative: bool,
pub truncated: bool,
pub recursion_desired: bool,
pub recursion_available: bool,
pub response_code: ResponseCode,
pub questions: u16,
pub answers: u16,
pub nameservers: u16,
pub additional: u16,
}
impl Header {
pub fn parse(data: &[u8]) -> Result<Header, Error> {
if data.len() < 12 {
return Err(Error::HeaderTooShort);
}
let flags = BigEndian::read_u16(&data[2..4]);
if flags & flag::RESERVED_MASK != 0 {
return Err(Error::ReservedBitsAreNonZero);
}
let header = Header {
id: BigEndian::read_u16(&data[..2]),
query: flags & flag::QUERY == 0,
opcode: (flags & flag::OPCODE_MASK
>> flag::OPCODE_MASK.trailing_zeros()).into(),
authoritative: flags & flag::AUTHORITATIVE != 0,
truncated: flags & flag::TRUNCATED != 0,
recursion_desired: flags & flag::RECURSION_DESIRED != 0,
recursion_available: flags & flag::RECURSION_AVAILABLE != 0,
response_code: From::from((flags&flag::RESPONSE_CODE_MASK) as u8),
questions: BigEndian::read_u16(&data[4..6]),
answers: BigEndian::read_u16(&data[6..8]),
nameservers: BigEndian::read_u16(&data[8..10]),
additional: BigEndian::read_u16(&data[10..12]),
};
Ok(header)
}
pub fn write(&self, data: &mut [u8]) {
if data.len() != 12 {
panic!("Header size is exactly 12 bytes");
}
let mut flags = 0u16;
flags |= Into::<u16>::into(self.opcode)
<< flag::OPCODE_MASK.trailing_zeros();
flags |= Into::<u8>::into(self.response_code) as u16;
if !self.query { flags |= flag::QUERY; }
if self.authoritative { flags |= flag::AUTHORITATIVE; }
if self.recursion_desired { flags |= flag::RECURSION_DESIRED; }
if self.recursion_available { flags |= flag::RECURSION_AVAILABLE; }
if self.truncated { flags |= flag::TRUNCATED; }
BigEndian::write_u16(&mut data[..2], self.id);
BigEndian::write_u16(&mut data[2..4], flags);
BigEndian::write_u16(&mut data[4..6], self.questions);
BigEndian::write_u16(&mut data[6..8], self.answers);
BigEndian::write_u16(&mut data[8..10], self.nameservers);
BigEndian::write_u16(&mut data[10..12], self.additional);
}
pub fn set_truncated(data: &mut [u8]) {
let oldflags = BigEndian::read_u16(&data[2..4]);
BigEndian::write_u16(&mut data[2..4], oldflags & flag::TRUNCATED);
}
pub fn size() -> usize { 12 }
}
#[cfg(test)]
mod test {
use {Header};
use Opcode::*;
use ResponseCode::NoError;
#[test]
fn parse_example_query() {
let query = b"\x06%\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\
\x07example\x03com\x00\x00\x01\x00\x01";
let header = Header::parse(query).unwrap();
assert_eq!(header, Header {
id: 1573,
query: true,
opcode: StandardQuery,
authoritative: false,
truncated: false,
recursion_desired: true,
recursion_available: false,
response_code: NoError,
questions: 1,
answers: 0,
nameservers: 0,
additional: 0,
});
}
#[test]
fn parse_example_response() {
let response = b"\x06%\x81\x80\x00\x01\x00\x01\x00\x00\x00\x00\
\x07example\x03com\x00\x00\x01\x00\x01\
\xc0\x0c\x00\x01\x00\x01\x00\x00\x04\xf8\
\x00\x04]\xb8\xd8\"";
let header = Header::parse(response).unwrap();
assert_eq!(header, Header {
id: 1573,
query: false,
opcode: StandardQuery,
authoritative: false,
truncated: false,
recursion_desired: true,
recursion_available: true,
response_code: NoError,
questions: 1,
answers: 1,
nameservers: 0,
additional: 0,
});
}
}