1use crate::layer::field::FieldError;
7use crate::layer::field::MacAddress;
8
9#[derive(Debug, Clone, PartialEq)]
11pub enum EdnsOption {
12 NSID(Vec<u8>),
14
15 DAU(Vec<u8>),
17
18 DHU(Vec<u8>),
20
21 N3U(Vec<u8>),
23
24 ClientSubnet {
26 family: u16, source_prefix: u8,
28 scope_prefix: u8,
29 address: Vec<u8>,
30 },
31
32 Cookie {
34 client: Vec<u8>, server: Vec<u8>, },
37
38 ExtendedDnsError { info_code: u16, extra_text: String },
40
41 Owner {
43 version: u8,
44 seq: u8,
45 primary_mac: MacAddress,
46 },
47
48 Unknown { code: u16, data: Vec<u8> },
50}
51
52pub mod option_code {
54 pub const LLQ: u16 = 1;
55 pub const UL: u16 = 2;
56 pub const NSID: u16 = 3;
57 pub const OWNER: u16 = 4;
58 pub const DAU: u16 = 5;
59 pub const DHU: u16 = 6;
60 pub const N3U: u16 = 7;
61 pub const CLIENT_SUBNET: u16 = 8;
62 pub const COOKIE: u16 = 10;
63 pub const TCP_KEEPALIVE: u16 = 11;
64 pub const PADDING: u16 = 12;
65 pub const CHAIN: u16 = 13;
66 pub const KEY_TAG: u16 = 14;
67 pub const EXTENDED_DNS_ERROR: u16 = 15;
68}
69
70pub mod ede_code {
72 pub const OTHER: u16 = 0;
73 pub const UNSUPPORTED_DNSKEY_ALGORITHM: u16 = 1;
74 pub const UNSUPPORTED_DS_DIGEST_TYPE: u16 = 2;
75 pub const STALE_ANSWER: u16 = 3;
76 pub const FORGED_ANSWER: u16 = 4;
77 pub const DNSSEC_INDETERMINATE: u16 = 5;
78 pub const DNSSEC_BOGUS: u16 = 6;
79 pub const SIGNATURE_EXPIRED: u16 = 7;
80 pub const SIGNATURE_NOT_YET_VALID: u16 = 8;
81 pub const DNSKEY_MISSING: u16 = 9;
82 pub const RRSIGS_MISSING: u16 = 10;
83 pub const NO_ZONE_KEY_BIT_SET: u16 = 11;
84 pub const NSEC_MISSING: u16 = 12;
85 pub const CACHED_ERROR: u16 = 13;
86 pub const NOT_READY: u16 = 14;
87 pub const BLOCKED: u16 = 15;
88 pub const CENSORED: u16 = 16;
89 pub const FILTERED: u16 = 17;
90 pub const PROHIBITED: u16 = 18;
91 pub const STALE_NXDOMAIN_ANSWER: u16 = 19;
92 pub const NOT_AUTHORITATIVE: u16 = 20;
93 pub const NOT_SUPPORTED: u16 = 21;
94 pub const NO_REACHABLE_AUTHORITY: u16 = 22;
95 pub const NETWORK_ERROR: u16 = 23;
96 pub const INVALID_DATA: u16 = 24;
97}
98
99impl EdnsOption {
100 pub fn parse(code: u16, data: &[u8]) -> Result<Self, FieldError> {
103 match code {
104 option_code::NSID => Ok(EdnsOption::NSID(data.to_vec())),
105
106 option_code::DAU => Ok(EdnsOption::DAU(data.to_vec())),
107
108 option_code::DHU => Ok(EdnsOption::DHU(data.to_vec())),
109
110 option_code::N3U => Ok(EdnsOption::N3U(data.to_vec())),
111
112 option_code::CLIENT_SUBNET => {
113 if data.len() < 4 {
114 return Err(FieldError::BufferTooShort {
115 offset: 0,
116 need: 4,
117 have: data.len(),
118 });
119 }
120 let family = u16::from_be_bytes([data[0], data[1]]);
121 let source_prefix = data[2];
122 let scope_prefix = data[3];
123 let address = data[4..].to_vec();
124 Ok(EdnsOption::ClientSubnet {
125 family,
126 source_prefix,
127 scope_prefix,
128 address,
129 })
130 }
131
132 option_code::COOKIE => {
133 if data.len() < 8 {
134 return Err(FieldError::BufferTooShort {
135 offset: 0,
136 need: 8,
137 have: data.len(),
138 });
139 }
140 let client = data[..8].to_vec();
141 let server = if data.len() > 8 {
142 data[8..].to_vec()
143 } else {
144 Vec::new()
145 };
146 Ok(EdnsOption::Cookie { client, server })
147 }
148
149 option_code::EXTENDED_DNS_ERROR => {
150 if data.len() < 2 {
151 return Err(FieldError::BufferTooShort {
152 offset: 0,
153 need: 2,
154 have: data.len(),
155 });
156 }
157 let info_code = u16::from_be_bytes([data[0], data[1]]);
158 let extra_text = if data.len() > 2 {
159 String::from_utf8_lossy(&data[2..]).into_owned()
160 } else {
161 String::new()
162 };
163 Ok(EdnsOption::ExtendedDnsError {
164 info_code,
165 extra_text,
166 })
167 }
168
169 option_code::OWNER => {
170 if data.len() < 8 {
171 return Err(FieldError::BufferTooShort {
172 offset: 0,
173 need: 8,
174 have: data.len(),
175 });
176 }
177 let version = data[0];
178 let seq = data[1];
179 let primary_mac =
180 MacAddress::new([data[2], data[3], data[4], data[5], data[6], data[7]]);
181 Ok(EdnsOption::Owner {
182 version,
183 seq,
184 primary_mac,
185 })
186 }
187
188 _ => Ok(EdnsOption::Unknown {
189 code,
190 data: data.to_vec(),
191 }),
192 }
193 }
194
195 pub fn code(&self) -> u16 {
197 match self {
198 EdnsOption::NSID(_) => option_code::NSID,
199 EdnsOption::DAU(_) => option_code::DAU,
200 EdnsOption::DHU(_) => option_code::DHU,
201 EdnsOption::N3U(_) => option_code::N3U,
202 EdnsOption::ClientSubnet { .. } => option_code::CLIENT_SUBNET,
203 EdnsOption::Cookie { .. } => option_code::COOKIE,
204 EdnsOption::ExtendedDnsError { .. } => option_code::EXTENDED_DNS_ERROR,
205 EdnsOption::Owner { .. } => option_code::OWNER,
206 EdnsOption::Unknown { code, .. } => *code,
207 }
208 }
209
210 pub fn build_data(&self) -> Vec<u8> {
212 match self {
213 EdnsOption::NSID(data) => data.clone(),
214 EdnsOption::DAU(data) => data.clone(),
215 EdnsOption::DHU(data) => data.clone(),
216 EdnsOption::N3U(data) => data.clone(),
217 EdnsOption::ClientSubnet {
218 family,
219 source_prefix,
220 scope_prefix,
221 address,
222 } => {
223 let mut out = Vec::with_capacity(4 + address.len());
224 out.extend_from_slice(&family.to_be_bytes());
225 out.push(*source_prefix);
226 out.push(*scope_prefix);
227 out.extend_from_slice(address);
228 out
229 }
230 EdnsOption::Cookie { client, server } => {
231 let mut out = Vec::with_capacity(client.len() + server.len());
232 out.extend_from_slice(client);
233 out.extend_from_slice(server);
234 out
235 }
236 EdnsOption::ExtendedDnsError {
237 info_code,
238 extra_text,
239 } => {
240 let mut out = Vec::with_capacity(2 + extra_text.len());
241 out.extend_from_slice(&info_code.to_be_bytes());
242 out.extend_from_slice(extra_text.as_bytes());
243 out
244 }
245 EdnsOption::Owner {
246 version,
247 seq,
248 primary_mac,
249 } => {
250 let mut out = vec![*version, *seq];
251 out.extend_from_slice(primary_mac.as_bytes());
252 out
253 }
254 EdnsOption::Unknown { data, .. } => data.clone(),
255 }
256 }
257
258 pub fn build(&self) -> Vec<u8> {
260 let data = self.build_data();
261 let mut out = Vec::with_capacity(4 + data.len());
262 out.extend_from_slice(&self.code().to_be_bytes());
263 out.extend_from_slice(&(data.len() as u16).to_be_bytes());
264 out.extend_from_slice(&data);
265 out
266 }
267
268 pub fn parse_all(data: &[u8]) -> Result<Vec<Self>, FieldError> {
270 let mut options = Vec::new();
271 let mut pos = 0;
272
273 while pos < data.len() {
274 if pos + 4 > data.len() {
275 return Err(FieldError::BufferTooShort {
276 offset: pos,
277 need: 4,
278 have: data.len(),
279 });
280 }
281 let code = u16::from_be_bytes([data[pos], data[pos + 1]]);
282 let opt_len = u16::from_be_bytes([data[pos + 2], data[pos + 3]]) as usize;
283 pos += 4;
284
285 if pos + opt_len > data.len() {
286 return Err(FieldError::BufferTooShort {
287 offset: pos,
288 need: opt_len,
289 have: data.len() - pos,
290 });
291 }
292
293 let option = Self::parse(code, &data[pos..pos + opt_len])?;
294 options.push(option);
295 pos += opt_len;
296 }
297
298 Ok(options)
299 }
300
301 pub fn summary(&self) -> String {
303 match self {
304 EdnsOption::NSID(data) => format!("NSID: {:?}", String::from_utf8_lossy(data)),
305 EdnsOption::DAU(algs) => format!("DAU: {:?}", algs),
306 EdnsOption::DHU(algs) => format!("DHU: {:?}", algs),
307 EdnsOption::N3U(algs) => format!("N3U: {:?}", algs),
308 EdnsOption::ClientSubnet {
309 family,
310 source_prefix,
311 scope_prefix,
312 ..
313 } => {
314 format!(
315 "ClientSubnet: family={} source=/{} scope=/{}",
316 family, source_prefix, scope_prefix
317 )
318 }
319 EdnsOption::Cookie { client, server } => {
320 format!("Cookie: client={} server={}", hex(client), hex(server))
321 }
322 EdnsOption::ExtendedDnsError {
323 info_code,
324 extra_text,
325 } => {
326 format!("EDE: code={} text={:?}", info_code, extra_text)
327 }
328 EdnsOption::Owner {
329 version,
330 seq,
331 primary_mac,
332 } => {
333 format!("Owner: v={} seq={} mac={}", version, seq, primary_mac)
334 }
335 EdnsOption::Unknown { code, data } => {
336 format!("Option({}): {} bytes", code, data.len())
337 }
338 }
339 }
340}
341
342fn hex(data: &[u8]) -> String {
343 data.iter().map(|b| format!("{:02x}", b)).collect()
344}
345
346#[cfg(test)]
347mod tests {
348 use super::*;
349
350 #[test]
351 fn test_client_subnet_parse() {
352 let data = vec![0x00, 0x01, 24, 0, 192, 168, 1];
354 let opt = EdnsOption::parse(option_code::CLIENT_SUBNET, &data).unwrap();
355 match opt {
356 EdnsOption::ClientSubnet {
357 family,
358 source_prefix,
359 scope_prefix,
360 address,
361 } => {
362 assert_eq!(family, 1);
363 assert_eq!(source_prefix, 24);
364 assert_eq!(scope_prefix, 0);
365 assert_eq!(address, vec![192, 168, 1]);
366 }
367 _ => panic!("wrong variant"),
368 }
369 }
370
371 #[test]
372 fn test_client_subnet_roundtrip() {
373 let opt = EdnsOption::ClientSubnet {
374 family: 1,
375 source_prefix: 24,
376 scope_prefix: 0,
377 address: vec![192, 168, 1],
378 };
379 let built = opt.build();
380 assert_eq!(built[0..2], [0x00, 0x08]); assert_eq!(built[2..4], [0x00, 0x07]); let parsed = EdnsOption::parse(option_code::CLIENT_SUBNET, &built[4..]).unwrap();
383 assert_eq!(parsed, opt);
384 }
385
386 #[test]
387 fn test_cookie_parse() {
388 let mut data = vec![0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]; data.extend_from_slice(&[0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18]); let opt = EdnsOption::parse(option_code::COOKIE, &data).unwrap();
391 match &opt {
392 EdnsOption::Cookie { client, server } => {
393 assert_eq!(client.len(), 8);
394 assert_eq!(server.len(), 8);
395 }
396 _ => panic!("wrong variant"),
397 }
398 }
399
400 #[test]
401 fn test_extended_dns_error_parse() {
402 let mut data = vec![0x00, 0x06]; data.extend_from_slice(b"signature verification failed");
404 let opt = EdnsOption::parse(option_code::EXTENDED_DNS_ERROR, &data).unwrap();
405 match opt {
406 EdnsOption::ExtendedDnsError {
407 info_code,
408 extra_text,
409 } => {
410 assert_eq!(info_code, 6);
411 assert_eq!(extra_text, "signature verification failed");
412 }
413 _ => panic!("wrong variant"),
414 }
415 }
416
417 #[test]
418 fn test_dau_roundtrip() {
419 let opt = EdnsOption::DAU(vec![8, 10, 13, 14]); let built = opt.build();
421 let parsed_opts = EdnsOption::parse_all(&built).unwrap();
422 assert_eq!(parsed_opts.len(), 1);
423 assert_eq!(parsed_opts[0], opt);
424 }
425
426 #[test]
427 fn test_parse_multiple_options() {
428 let opt1 = EdnsOption::DAU(vec![8, 10]);
429 let opt2 = EdnsOption::DHU(vec![1, 2]);
430 let mut data = opt1.build();
431 data.extend_from_slice(&opt2.build());
432
433 let parsed = EdnsOption::parse_all(&data).unwrap();
434 assert_eq!(parsed.len(), 2);
435 assert_eq!(parsed[0], opt1);
436 assert_eq!(parsed[1], opt2);
437 }
438
439 #[test]
440 fn test_unknown_option() {
441 let opt = EdnsOption::parse(9999, &[0x01, 0x02, 0x03]).unwrap();
442 match opt {
443 EdnsOption::Unknown { code, data } => {
444 assert_eq!(code, 9999);
445 assert_eq!(data, vec![1, 2, 3]);
446 }
447 _ => panic!("wrong variant"),
448 }
449 }
450}