simple_dns/dns/rdata/
opt.rs1use crate::{
2 dns::{header::Header, WireFormat},
3 RCODE,
4};
5use std::borrow::Cow;
6
7use super::RR;
8
9pub mod masks {
10 pub const RCODE_MASK: u32 = 0b0000_0000_0000_0000_0000_0000_1111_1111;
11 pub const VERSION_MASK: u32 = 0b0000_0000_0000_0000_1111_1111_0000_0000;
12}
13
14#[derive(Debug, PartialEq, Eq, Hash, Clone)]
21pub struct OPT<'a> {
22 pub opt_codes: Vec<OPTCode<'a>>,
24 pub udp_packet_size: u16,
26
27 pub version: u8,
29}
30
31impl<'a> RR for OPT<'a> {
32 const TYPE_CODE: u16 = 41;
33}
34
35impl<'a> WireFormat<'a> for OPT<'a> {
36 fn parse(data: &'a [u8], position: &mut usize) -> crate::Result<Self>
37 where
38 Self: Sized,
39 {
40 if *position + 10 > data.len() {
41 return Err(crate::SimpleDnsError::InsufficientData);
42 }
43
44 let udp_packet_size = u16::from_be_bytes(data[*position + 2..*position + 4].try_into()?);
46 let ttl = u32::from_be_bytes(data[*position + 4..*position + 8].try_into()?);
48 let version = ((ttl & masks::VERSION_MASK) >> masks::VERSION_MASK.trailing_zeros()) as u8;
49
50 *position += 10;
51
52 let mut opt_codes = Vec::new();
53 while *position < data.len() {
54 if *position + 4 > data.len() {
55 return Err(crate::SimpleDnsError::InsufficientData);
56 }
57
58 let code = u16::from_be_bytes(data[*position..*position + 2].try_into()?);
59 let length =
60 u16::from_be_bytes(data[*position + 2..*position + 4].try_into()?) as usize;
61
62 if *position + 4 + length > data.len() {
63 return Err(crate::SimpleDnsError::InsufficientData);
64 }
65
66 let inner_data = Cow::Borrowed(&data[*position + 4..*position + 4 + length]);
67 opt_codes.push(OPTCode {
68 code,
69 data: inner_data,
70 });
71
72 *position += 4 + length;
73 }
74
75 Ok(Self {
76 opt_codes,
77 udp_packet_size,
78 version,
79 })
80 }
81
82 fn write_to<T: std::io::Write>(&self, out: &mut T) -> crate::Result<()> {
83 for code in self.opt_codes.iter() {
84 out.write_all(&code.code.to_be_bytes())?;
85 out.write_all(&(code.data.len() as u16).to_be_bytes())?;
86 out.write_all(&code.data)?;
87 }
88
89 Ok(())
90 }
91
92 fn len(&self) -> usize {
93 self.opt_codes.iter().map(|o| o.data.len() + 4).sum()
94 }
95}
96
97impl<'a> OPT<'a> {
98 pub(crate) fn extract_rcode_from_ttl(ttl: u32, header: &Header) -> RCODE {
99 let mut rcode = (ttl & masks::RCODE_MASK) << 4;
100 rcode |= header.response_code as u32;
101 RCODE::from(rcode as u16)
102 }
103
104 pub(crate) fn encode_ttl(&self, header: &Header) -> u32 {
105 let mut ttl: u32 = (header.response_code as u32 & masks::RCODE_MASK) >> 4;
106 ttl |= (self.version as u32) << masks::VERSION_MASK.trailing_zeros();
107 ttl
108 }
109 pub fn into_owned<'b>(self) -> OPT<'b> {
111 OPT {
112 udp_packet_size: self.udp_packet_size,
114 version: self.version,
115 opt_codes: self.opt_codes.into_iter().map(|o| o.into_owned()).collect(),
116 }
117 }
118}
119
120#[derive(Debug, PartialEq, Eq, Hash, Clone)]
122pub struct OPTCode<'a> {
123 pub code: u16,
126 pub data: Cow<'a, [u8]>,
128}
129
130impl<'a> OPTCode<'a> {
131 pub fn into_owned<'b>(self) -> OPTCode<'b> {
133 OPTCode {
134 code: self.code,
135 data: self.data.into_owned().into(),
136 }
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 use crate::{rdata::RData, Name, ResourceRecord};
143
144 use super::*;
145
146 #[test]
147 fn parse_and_write_opt_empty() {
148 let header = Header::new_reply(1, crate::OPCODE::StandardQuery);
149
150 let opt = OPT {
151 udp_packet_size: 500,
152 version: 2,
153 opt_codes: Vec::new(),
154 };
155 let opt_rr = ResourceRecord {
156 ttl: opt.encode_ttl(&header),
157 name: Name::new_unchecked("."),
158 class: crate::CLASS::IN,
159 cache_flush: false,
160 rdata: RData::OPT(opt),
161 };
162
163 let mut data = Vec::new();
164 assert!(opt_rr.write_to(&mut data).is_ok());
165
166 let opt = match ResourceRecord::parse(&data, &mut 0)
167 .expect("failed to parse")
168 .rdata
169 {
170 RData::OPT(rdata) => rdata,
171 _ => unreachable!(),
172 };
173
174 assert_eq!(data.len(), opt_rr.len());
175 assert_eq!(500, opt.udp_packet_size);
176 assert_eq!(2, opt.version);
177 assert!(opt.opt_codes.is_empty());
178 }
179
180 #[test]
181 fn parse_and_write_opt() {
182 let header = Header::new_reply(1, crate::OPCODE::StandardQuery);
183
184 let opt = OPT {
185 udp_packet_size: 500,
186 version: 2,
187 opt_codes: vec![
188 OPTCode {
189 code: 1,
190 data: Cow::Owned(vec![255, 255]),
191 },
192 OPTCode {
193 code: 2,
194 data: Cow::Owned(vec![255, 255, 255]),
195 },
196 ],
197 };
198
199 let opt_rr = ResourceRecord {
200 ttl: opt.encode_ttl(&header),
201 name: Name::new_unchecked("."),
202 class: crate::CLASS::IN,
203 cache_flush: false,
204 rdata: RData::OPT(opt),
205 };
206
207 let mut data = Vec::new();
208 assert!(opt_rr.write_to(&mut data).is_ok());
209
210 let mut opt = match ResourceRecord::parse(&data, &mut 0)
211 .expect("failed to parse")
212 .rdata
213 {
214 RData::OPT(rdata) => rdata,
215 _ => unreachable!(),
216 };
217
218 assert_eq!(data.len(), opt_rr.len());
219 assert_eq!(500, opt.udp_packet_size);
220 assert_eq!(2, opt.version);
221 assert_eq!(2, opt.opt_codes.len());
222
223 let opt_code = opt.opt_codes.pop().unwrap();
224 assert_eq!(2, opt_code.code);
225 assert_eq!(vec![255, 255, 255], *opt_code.data);
226
227 let opt_code = opt.opt_codes.pop().unwrap();
228 assert_eq!(1, opt_code.code);
229 assert_eq!(vec![255, 255], *opt_code.data);
230 }
231
232 }