hickory_proto/rr/rdata/opt.rs
1// Copyright 2015-2023 Benjamin Fry <benjaminfry@me.com>
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// https://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// https://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8//! option record for passing protocol options between the client and server
9#![allow(clippy::use_self)]
10
11use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
12use std::str::FromStr;
13use std::{collections::HashMap, fmt};
14
15#[cfg(feature = "serde-config")]
16use serde::{Deserialize, Serialize};
17
18use tracing::warn;
19
20use crate::{
21 error::{ProtoError, ProtoErrorKind, ProtoResult},
22 rr::{RData, RecordData, RecordDataDecodable, RecordType},
23 serialize::binary::{BinDecodable, BinDecoder, BinEncodable, BinEncoder, Restrict},
24};
25
26#[cfg(feature = "dnssec")]
27use crate::rr::dnssec::SupportedAlgorithms;
28
29/// The OPT record type is used for ExtendedDNS records.
30///
31/// These allow for additional information to be associated with the DNS request that otherwise
32/// would require changes to the DNS protocol.
33///
34/// [RFC 6891, EDNS(0) Extensions, April 2013](https://tools.ietf.org/html/rfc6891#section-6)
35///
36/// ```text
37/// 6.1. OPT Record Definition
38///
39/// 6.1.1. Basic Elements
40///
41/// An OPT pseudo-RR (sometimes called a meta-RR) MAY be added to the
42/// additional data section of a request.
43///
44/// The OPT RR has RR type 41.
45///
46/// If an OPT record is present in a received request, compliant
47/// responders MUST include an OPT record in their respective responses.
48///
49/// An OPT record does not carry any DNS data. It is used only to
50/// contain control information pertaining to the question-and-answer
51/// sequence of a specific transaction. OPT RRs MUST NOT be cached,
52/// forwarded, or stored in or loaded from Zone Files.
53///
54/// The OPT RR MAY be placed anywhere within the additional data section.
55/// When an OPT RR is included within any DNS message, it MUST be the
56/// only OPT RR in that message. If a query message with more than one
57/// OPT RR is received, a FORMERR (RCODE=1) MUST be returned. The
58/// placement flexibility for the OPT RR does not override the need for
59/// the TSIG or SIG(0) RRs to be the last in the additional section
60/// whenever they are present.
61///
62/// 6.1.2. Wire Format
63///
64/// An OPT RR has a fixed part and a variable set of options expressed as
65/// {attribute, value} pairs. The fixed part holds some DNS metadata,
66/// and also a small collection of basic extension elements that we
67/// expect to be so popular that it would be a waste of wire space to
68/// encode them as {attribute, value} pairs.
69///
70/// The fixed part of an OPT RR is structured as follows:
71///
72/// +------------+--------------+------------------------------+
73/// | Field Name | Field Type | Description |
74/// +------------+--------------+------------------------------+
75/// | NAME | domain name | MUST be 0 (root domain) |
76/// | TYPE | u_int16_t | OPT (41) |
77/// | CLASS | u_int16_t | requestor's UDP payload size |
78/// | TTL | u_int32_t | extended RCODE and flags |
79/// | RDLEN | u_int16_t | length of all RDATA |
80/// | RDATA | octet stream | {attribute,value} pairs |
81/// +------------+--------------+------------------------------+
82///
83/// OPT RR Format
84///
85/// The variable part of an OPT RR may contain zero or more options in
86/// the RDATA. Each option MUST be treated as a bit field. Each option
87/// is encoded as:
88///
89/// +0 (MSB) +1 (LSB)
90/// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
91/// 0: | OPTION-CODE |
92/// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
93/// 2: | OPTION-LENGTH |
94/// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
95/// 4: | |
96/// / OPTION-DATA /
97/// / /
98/// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
99///
100/// OPTION-CODE
101/// Assigned by the Expert Review process as defined by the DNSEXT
102/// working group and the IESG.
103///
104/// OPTION-LENGTH
105/// Size (in octets) of OPTION-DATA.
106///
107/// OPTION-DATA
108/// Varies per OPTION-CODE. MUST be treated as a bit field.
109///
110/// The order of appearance of option tuples is not defined. If one
111/// option modifies the behaviour of another or multiple options are
112/// related to one another in some way, they have the same effect
113/// regardless of ordering in the RDATA wire encoding.
114///
115/// Any OPTION-CODE values not understood by a responder or requestor
116/// MUST be ignored. Specifications of such options might wish to
117/// include some kind of signaled acknowledgement. For example, an
118/// option specification might say that if a responder sees and supports
119/// option XYZ, it MUST include option XYZ in its response.
120///
121/// 6.1.3. OPT Record TTL Field Use
122///
123/// The extended RCODE and flags, which OPT stores in the RR Time to Live
124/// (TTL) field, are structured as follows:
125///
126/// +0 (MSB) +1 (LSB)
127/// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
128/// 0: | EXTENDED-RCODE | VERSION |
129/// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
130/// 2: | DO| Z |
131/// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
132///
133/// EXTENDED-RCODE
134/// Forms the upper 8 bits of extended 12-bit RCODE (together with the
135/// 4 bits defined in [RFC1035]. Note that EXTENDED-RCODE value 0
136/// indicates that an unextended RCODE is in use (values 0 through
137/// 15).
138///
139/// VERSION
140/// Indicates the implementation level of the setter. Full
141/// conformance with this specification is indicated by version '0'.
142/// Requestors are encouraged to set this to the lowest implemented
143/// level capable of expressing a transaction, to minimise the
144/// responder and network load of discovering the greatest common
145/// implementation level between requestor and responder. A
146/// requestor's version numbering strategy MAY ideally be a run-time
147/// configuration option.
148/// If a responder does not implement the VERSION level of the
149/// request, then it MUST respond with RCODE=BADVERS. All responses
150/// MUST be limited in format to the VERSION level of the request, but
151/// the VERSION of each response SHOULD be the highest implementation
152/// level of the responder. In this way, a requestor will learn the
153/// implementation level of a responder as a side effect of every
154/// response, including error responses and including RCODE=BADVERS.
155///
156/// 6.1.4. Flags
157///
158/// DO
159/// DNSSEC OK bit as defined by [RFC3225].
160///
161/// Z
162/// Set to zero by senders and ignored by receivers, unless modified
163/// in a subsequent specification.
164/// ```
165#[cfg_attr(feature = "serde-config", derive(Deserialize, Serialize))]
166#[derive(Default, Debug, PartialEq, Eq, Clone)]
167pub struct OPT {
168 options: HashMap<EdnsCode, EdnsOption>,
169}
170
171impl OPT {
172 /// Creates a new OPT record data.
173 ///
174 /// # Arguments
175 ///
176 /// * `options` - A map of the codes and record types
177 ///
178 /// # Return value
179 ///
180 /// The newly created OPT data
181 pub fn new(options: HashMap<EdnsCode, EdnsOption>) -> Self {
182 Self { options }
183 }
184
185 #[deprecated(note = "Please use as_ref() or as_mut() for shared/mutable references")]
186 /// The entire map of options
187 pub fn options(&self) -> &HashMap<EdnsCode, EdnsOption> {
188 &self.options
189 }
190
191 /// Get a single option based on the code
192 pub fn get(&self, code: EdnsCode) -> Option<&EdnsOption> {
193 self.options.get(&code)
194 }
195
196 /// Insert a new option, the key is derived from the `EdnsOption`
197 pub fn insert(&mut self, option: EdnsOption) {
198 self.options.insert((&option).into(), option);
199 }
200
201 /// Remove an option, the key is derived from the `EdnsOption`
202 pub fn remove(&mut self, option: EdnsCode) {
203 self.options.remove(&option);
204 }
205}
206
207impl AsMut<HashMap<EdnsCode, EdnsOption>> for OPT {
208 fn as_mut(&mut self) -> &mut HashMap<EdnsCode, EdnsOption> {
209 &mut self.options
210 }
211}
212
213impl AsRef<HashMap<EdnsCode, EdnsOption>> for OPT {
214 fn as_ref(&self) -> &HashMap<EdnsCode, EdnsOption> {
215 &self.options
216 }
217}
218
219impl BinEncodable for OPT {
220 fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
221 for (edns_code, edns_option) in self.as_ref().iter() {
222 encoder.emit_u16(u16::from(*edns_code))?;
223 encoder.emit_u16(edns_option.len())?;
224 edns_option.emit(encoder)?
225 }
226 Ok(())
227 }
228}
229
230impl<'r> RecordDataDecodable<'r> for OPT {
231 fn read_data(decoder: &mut BinDecoder<'r>, length: Restrict<u16>) -> ProtoResult<Self> {
232 let mut state: OptReadState = OptReadState::ReadCode;
233 let mut options: HashMap<EdnsCode, EdnsOption> = HashMap::new();
234 let start_idx = decoder.index();
235
236 // There is no unsafe direct use of the rdata length after this point
237 let rdata_length = length.map(|u| u as usize).unverified(/*rdata length usage is bounded*/);
238 while rdata_length > decoder.index() - start_idx {
239 match state {
240 OptReadState::ReadCode => {
241 state = OptReadState::Code {
242 code: EdnsCode::from(
243 decoder.read_u16()?.unverified(/*EdnsCode is verified as safe*/),
244 ),
245 };
246 }
247 OptReadState::Code { code } => {
248 let length = decoder
249 .read_u16()?
250 .map(|u| u as usize)
251 .verify_unwrap(|u| *u <= rdata_length)
252 .map_err(|_| ProtoError::from("OPT value length exceeds rdata length"))?;
253 // If we know that the length is 0, we can avoid the `OptReadState::Data` state
254 // and directly add the option to the map.
255 // The data state does not process 0-length correctly, since it always reads at
256 // least 1 byte, thus making the length check fail.
257 state = if length == 0 {
258 options.insert(code, (code, &[] as &[u8]).try_into()?);
259 OptReadState::ReadCode
260 } else {
261 OptReadState::Data {
262 code,
263 length,
264 // TODO: this can be replaced with decoder.read_vec(), right?
265 // the current version allows for malformed opt to be skipped...
266 collected: Vec::<u8>::with_capacity(length),
267 }
268 };
269 }
270 OptReadState::Data {
271 code,
272 length,
273 mut collected,
274 } => {
275 // TODO: can this be replaced by read_slice()?
276 collected.push(decoder.pop()?.unverified(/*byte array is safe*/));
277 if length == collected.len() {
278 options.insert(code, (code, &collected as &[u8]).try_into()?);
279 state = OptReadState::ReadCode;
280 } else {
281 state = OptReadState::Data {
282 code,
283 length,
284 collected,
285 };
286 }
287 }
288 }
289 }
290
291 if state != OptReadState::ReadCode {
292 // there was some problem parsing the data for the options, ignoring them
293 // TODO: should we ignore all of the EDNS data in this case?
294 warn!("incomplete or poorly formatted EDNS options: {:?}", state);
295 options.clear();
296 }
297
298 // the record data is stored as unstructured data, the expectation is that this will be processed after initial parsing.
299 Ok(Self::new(options))
300 }
301}
302
303impl RecordData for OPT {
304 fn try_from_rdata(data: RData) -> Result<Self, RData> {
305 match data {
306 RData::OPT(csync) => Ok(csync),
307 _ => Err(data),
308 }
309 }
310
311 fn try_borrow(data: &RData) -> Option<&Self> {
312 match data {
313 RData::OPT(csync) => Some(csync),
314 _ => None,
315 }
316 }
317
318 fn record_type(&self) -> RecordType {
319 RecordType::OPT
320 }
321
322 fn into_rdata(self) -> RData {
323 RData::OPT(self)
324 }
325}
326
327impl fmt::Display for OPT {
328 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
329 fmt::Debug::fmt(self, f)
330 }
331}
332
333#[derive(Debug, PartialEq, Eq)]
334enum OptReadState {
335 ReadCode,
336 Code {
337 code: EdnsCode,
338 }, // expect LSB for the opt code, store the high byte
339 Data {
340 code: EdnsCode,
341 length: usize,
342 collected: Vec<u8>,
343 }, // expect the data for the option
344}
345
346/// The code of the EDNS data option
347#[cfg_attr(feature = "serde-config", derive(Deserialize, Serialize))]
348#[derive(Hash, Debug, Copy, Clone, PartialEq, Eq)]
349#[non_exhaustive]
350pub enum EdnsCode {
351 /// [RFC 6891, Reserved](https://tools.ietf.org/html/rfc6891)
352 Zero,
353
354 /// [RFC 8764l, Apple's Long-Lived Queries, Optional](https://tools.ietf.org/html/rfc8764)
355 LLQ,
356
357 /// [UL On-hold](https://files.dns-sd.org/draft-sekar-dns-ul.txt)
358 UL,
359
360 /// [RFC 5001, NSID](https://tools.ietf.org/html/rfc5001)
361 NSID,
362 // 4 Reserved [draft-cheshire-edns0-owner-option] -EXPIRED-
363 /// [RFC 6975, DNSSEC Algorithm Understood](https://tools.ietf.org/html/rfc6975)
364 DAU,
365
366 /// [RFC 6975, DS Hash Understood](https://tools.ietf.org/html/rfc6975)
367 DHU,
368
369 /// [RFC 6975, NSEC3 Hash Understood](https://tools.ietf.org/html/rfc6975)
370 N3U,
371
372 /// [RFC 7871, Client Subnet, Optional](https://tools.ietf.org/html/rfc7871)
373 Subnet,
374
375 /// [RFC 7314, EDNS EXPIRE, Optional](https://tools.ietf.org/html/rfc7314)
376 Expire,
377
378 /// [RFC 7873, DNS Cookies](https://tools.ietf.org/html/rfc7873)
379 Cookie,
380
381 /// [RFC 7828, edns-tcp-keepalive](https://tools.ietf.org/html/rfc7828)
382 Keepalive,
383
384 /// [RFC 7830, The EDNS(0) Padding](https://tools.ietf.org/html/rfc7830)
385 Padding,
386
387 /// [RFC 7901, CHAIN Query Requests in DNS, Optional](https://tools.ietf.org/html/rfc7901)
388 Chain,
389
390 /// Unknown, used to deal with unknown or unsupported codes
391 Unknown(u16),
392}
393
394// TODO: implement a macro to perform these inversions
395impl From<u16> for EdnsCode {
396 fn from(value: u16) -> Self {
397 match value {
398 0 => Self::Zero,
399 1 => Self::LLQ,
400 2 => Self::UL,
401 3 => Self::NSID,
402 // 4 Reserved [draft-cheshire-edns0-owner-option] -EXPIRED-
403 5 => Self::DAU,
404 6 => Self::DHU,
405 7 => Self::N3U,
406 8 => Self::Subnet,
407 9 => Self::Expire,
408 10 => Self::Cookie,
409 11 => Self::Keepalive,
410 12 => Self::Padding,
411 13 => Self::Chain,
412 _ => Self::Unknown(value),
413 }
414 }
415}
416
417impl From<EdnsCode> for u16 {
418 fn from(value: EdnsCode) -> Self {
419 match value {
420 EdnsCode::Zero => 0,
421 EdnsCode::LLQ => 1,
422 EdnsCode::UL => 2,
423 EdnsCode::NSID => 3,
424 // 4 Reserved [draft-cheshire-edns0-owner-option] -EXPIRED-
425 EdnsCode::DAU => 5,
426 EdnsCode::DHU => 6,
427 EdnsCode::N3U => 7,
428 EdnsCode::Subnet => 8,
429 EdnsCode::Expire => 9,
430 EdnsCode::Cookie => 10,
431 EdnsCode::Keepalive => 11,
432 EdnsCode::Padding => 12,
433 EdnsCode::Chain => 13,
434 EdnsCode::Unknown(value) => value,
435 }
436 }
437}
438
439/// options used to pass information about capabilities between client and server
440///
441/// `note: Not all EdnsOptions are supported at this time.`
442///
443/// <https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-13>
444#[cfg_attr(feature = "serde-config", derive(Deserialize, Serialize))]
445#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Hash)]
446#[non_exhaustive]
447pub enum EdnsOption {
448 /// [RFC 6975, DNSSEC Algorithm Understood](https://tools.ietf.org/html/rfc6975)
449 #[cfg(feature = "dnssec")]
450 #[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))]
451 DAU(SupportedAlgorithms),
452
453 /// [RFC 6975, DS Hash Understood](https://tools.ietf.org/html/rfc6975)
454 #[cfg(feature = "dnssec")]
455 #[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))]
456 DHU(SupportedAlgorithms),
457
458 /// [RFC 6975, NSEC3 Hash Understood](https://tools.ietf.org/html/rfc6975)
459 #[cfg(feature = "dnssec")]
460 #[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))]
461 N3U(SupportedAlgorithms),
462
463 /// [RFC 7871, Client Subnet, Optional](https://tools.ietf.org/html/rfc7871)
464 Subnet(ClientSubnet),
465
466 /// Unknown, used to deal with unknown or unsupported codes
467 Unknown(u16, Vec<u8>),
468}
469
470impl EdnsOption {
471 /// Returns the length in bytes of the EdnsOption
472 pub fn len(&self) -> u16 {
473 match *self {
474 #[cfg(feature = "dnssec")]
475 EdnsOption::DAU(ref algorithms)
476 | EdnsOption::DHU(ref algorithms)
477 | EdnsOption::N3U(ref algorithms) => algorithms.len(),
478 EdnsOption::Subnet(ref subnet) => subnet.len(),
479 EdnsOption::Unknown(_, ref data) => data.len() as u16, // TODO: should we verify?
480 }
481 }
482
483 /// Returns `true` if the length in bytes of the EdnsOption is 0
484 pub fn is_empty(&self) -> bool {
485 match *self {
486 #[cfg(feature = "dnssec")]
487 EdnsOption::DAU(ref algorithms)
488 | EdnsOption::DHU(ref algorithms)
489 | EdnsOption::N3U(ref algorithms) => algorithms.is_empty(),
490 EdnsOption::Subnet(ref subnet) => subnet.is_empty(),
491 EdnsOption::Unknown(_, ref data) => data.is_empty(),
492 }
493 }
494}
495
496impl BinEncodable for EdnsOption {
497 fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
498 match *self {
499 #[cfg(feature = "dnssec")]
500 EdnsOption::DAU(ref algorithms)
501 | EdnsOption::DHU(ref algorithms)
502 | EdnsOption::N3U(ref algorithms) => algorithms.emit(encoder),
503 EdnsOption::Subnet(ref subnet) => subnet.emit(encoder),
504 EdnsOption::Unknown(_, ref data) => encoder.emit_vec(data), // gah, clone needed or make a crazy api.
505 }
506 }
507}
508
509/// only the supported extensions are listed right now.
510impl<'a> TryFrom<(EdnsCode, &'a [u8])> for EdnsOption {
511 type Error = ProtoError;
512
513 #[allow(clippy::match_single_binding)]
514 fn try_from(value: (EdnsCode, &'a [u8])) -> Result<Self, Self::Error> {
515 Ok(match value.0 {
516 #[cfg(feature = "dnssec")]
517 EdnsCode::DAU => Self::DAU(value.1.into()),
518 #[cfg(feature = "dnssec")]
519 EdnsCode::DHU => Self::DHU(value.1.into()),
520 #[cfg(feature = "dnssec")]
521 EdnsCode::N3U => Self::N3U(value.1.into()),
522 EdnsCode::Subnet => Self::Subnet(value.1.try_into()?),
523 _ => Self::Unknown(value.0.into(), value.1.to_vec()),
524 })
525 }
526}
527
528impl<'a> TryFrom<&'a EdnsOption> for Vec<u8> {
529 type Error = ProtoError;
530
531 fn try_from(value: &'a EdnsOption) -> Result<Self, Self::Error> {
532 Ok(match *value {
533 #[cfg(feature = "dnssec")]
534 EdnsOption::DAU(ref algorithms)
535 | EdnsOption::DHU(ref algorithms)
536 | EdnsOption::N3U(ref algorithms) => algorithms.into(),
537 EdnsOption::Subnet(ref subnet) => subnet.try_into()?,
538 EdnsOption::Unknown(_, ref data) => data.clone(), // gah, clone needed or make a crazy api.
539 })
540 }
541}
542
543impl<'a> From<&'a EdnsOption> for EdnsCode {
544 fn from(value: &'a EdnsOption) -> Self {
545 match *value {
546 #[cfg(feature = "dnssec")]
547 EdnsOption::DAU(..) => Self::DAU,
548 #[cfg(feature = "dnssec")]
549 EdnsOption::DHU(..) => Self::DHU,
550 #[cfg(feature = "dnssec")]
551 EdnsOption::N3U(..) => Self::N3U,
552 EdnsOption::Subnet(..) => Self::Subnet,
553 EdnsOption::Unknown(code, _) => code.into(),
554 }
555 }
556}
557
558/// [RFC 7871, Client Subnet, Optional](https://tools.ietf.org/html/rfc7871)
559///
560/// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
561/// 0: | FAMILY |
562/// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
563/// 2: | SOURCE PREFIX-LENGTH | SCOPE PREFIX-LENGTH |
564/// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
565/// 4: | ADDRESS... /
566/// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
567///
568/// o FAMILY, 2 octets, indicates the family of the address contained in
569/// the option, using address family codes as assigned by IANA in
570/// Address Family Numbers [Address_Family_Numbers].
571/// o SOURCE PREFIX-LENGTH, an unsigned octet representing the leftmost
572/// number of significant bits of ADDRESS to be used for the lookup.
573/// In responses, it mirrors the same value as in the queries.
574/// o SCOPE PREFIX-LENGTH, an unsigned octet representing the leftmost
575/// number of significant bits of ADDRESS that the response covers.
576/// In queries, it MUST be set to 0.
577/// o ADDRESS, variable number of octets, contains either an IPv4 or
578/// IPv6 address, depending on FAMILY, which MUST be truncated to the
579/// number of bits indicated by the SOURCE PREFIX-LENGTH field,
580/// padding with 0 bits to pad to the end of the last octet needed.
581/// o A server receiving an ECS option that uses either too few or too
582/// many ADDRESS octets, or that has non-zero ADDRESS bits set beyond
583/// SOURCE PREFIX-LENGTH, SHOULD return FORMERR to reject the packet,
584/// as a signal to the software developer making the request to fix
585/// their implementation.
586#[cfg_attr(feature = "serde-config", derive(Deserialize, Serialize))]
587#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)]
588pub struct ClientSubnet {
589 address: IpAddr,
590 source_prefix: u8,
591 scope_prefix: u8,
592}
593
594impl ClientSubnet {
595 /// Construct a new EcsOption with the address, source_prefix and scope_prefix.
596 pub fn new(address: IpAddr, source_prefix: u8, scope_prefix: u8) -> Self {
597 Self {
598 address,
599 source_prefix,
600 scope_prefix,
601 }
602 }
603
604 /// Returns the length in bytes of the EdnsOption
605 pub fn len(&self) -> u16 {
606 // FAMILY: 2 octets
607 // SOURCE PREFIX-LENGTH: 1 octets
608 // SCOPE PREFIX-LENGTH: 1 octets
609 // ADDRESS: runcated to the number of bits indicated by the SOURCE PREFIX-LENGTH field
610 2 + 1 + 1 + self.addr_len()
611 }
612
613 /// Returns `true` if the length in bytes of the EcsOption is 0
614 #[inline]
615 pub fn is_empty(&self) -> bool {
616 false
617 }
618
619 fn addr_len(&self) -> u16 {
620 let source_prefix = self.source_prefix as u16;
621 source_prefix / 8 + if source_prefix % 8 > 0 { 1 } else { 0 }
622 }
623}
624
625impl BinEncodable for ClientSubnet {
626 fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
627 let address = self.address;
628 let source_prefix = self.source_prefix;
629 let scope_prefix = self.scope_prefix;
630
631 let addr_len = self.addr_len();
632
633 match address {
634 IpAddr::V4(ip) => {
635 encoder.emit_u16(1)?; // FAMILY: IPv4
636 encoder.emit_u8(source_prefix)?;
637 encoder.emit_u8(scope_prefix)?;
638 let octets = ip.octets();
639 let addr_len = addr_len as usize;
640 if addr_len <= octets.len() {
641 encoder.emit_vec(&octets[0..addr_len])?
642 } else {
643 return Err(ProtoErrorKind::Message(
644 "Invalid addr length for encode EcsOption",
645 )
646 .into());
647 }
648 }
649 IpAddr::V6(ip) => {
650 encoder.emit_u16(2)?; // FAMILY: IPv6
651 encoder.emit_u8(source_prefix)?;
652 encoder.emit_u8(scope_prefix)?;
653 let octets = ip.octets();
654 let addr_len = addr_len as usize;
655 if addr_len <= octets.len() {
656 encoder.emit_vec(&octets[0..addr_len])?
657 } else {
658 return Err(ProtoErrorKind::Message(
659 "Invalid addr length for encode EcsOption",
660 )
661 .into());
662 }
663 }
664 }
665 Ok(())
666 }
667}
668
669impl<'a> BinDecodable<'a> for ClientSubnet {
670 fn read(decoder: &mut BinDecoder<'a>) -> ProtoResult<Self> {
671 let family = decoder.read_u16()?.unverified();
672
673 match family {
674 1 => {
675 // ipv4
676 let source_prefix = decoder.read_u8()?.unverified();
677 let scope_prefix = decoder.read_u8()?.unverified();
678 let addr_len =
679 (source_prefix / 8 + if source_prefix % 8 > 0 { 1 } else { 0 }) as usize;
680 let mut octets = Ipv4Addr::UNSPECIFIED.octets();
681 if addr_len > octets.len() {
682 return Err(ProtoErrorKind::Message("Invalid address length").into());
683 }
684 for octet in octets.iter_mut().take(addr_len) {
685 *octet = decoder.read_u8()?.unverified();
686 }
687 Ok(Self {
688 address: IpAddr::from(octets),
689 source_prefix,
690 scope_prefix,
691 })
692 }
693 2 => {
694 // ipv6
695 let source_prefix = decoder.read_u8()?.unverified();
696 let scope_prefix = decoder.read_u8()?.unverified();
697 let addr_len =
698 (source_prefix / 8 + if source_prefix % 8 > 0 { 1 } else { 0 }) as usize;
699 let mut octets = Ipv6Addr::UNSPECIFIED.octets();
700 if addr_len > octets.len() {
701 return Err(ProtoErrorKind::Message("Invalid address length").into());
702 }
703 for octet in octets.iter_mut().take(addr_len) {
704 *octet = decoder.read_u8()?.unverified();
705 }
706
707 Ok(Self {
708 address: IpAddr::from(octets),
709 source_prefix,
710 scope_prefix,
711 })
712 }
713 _ => Err(ProtoErrorKind::Message("Invalid family type.").into()),
714 }
715 }
716}
717
718impl<'a> TryFrom<&'a ClientSubnet> for Vec<u8> {
719 type Error = ProtoError;
720
721 fn try_from(value: &'a ClientSubnet) -> Result<Self, Self::Error> {
722 let mut bytes = Self::with_capacity(value.len() as usize); // today this is less than 8
723 let mut encoder = BinEncoder::new(&mut bytes);
724 value.emit(&mut encoder)?;
725 bytes.shrink_to_fit();
726 Ok(bytes)
727 }
728}
729
730impl<'a> TryFrom<&'a [u8]> for ClientSubnet {
731 type Error = ProtoError;
732
733 fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
734 let mut decoder = BinDecoder::new(value);
735 Self::read(&mut decoder)
736 }
737}
738
739impl From<ipnet::IpNet> for ClientSubnet {
740 fn from(net: ipnet::IpNet) -> Self {
741 Self {
742 address: net.addr(),
743 source_prefix: net.prefix_len(),
744 scope_prefix: Default::default(),
745 }
746 }
747}
748
749impl FromStr for ClientSubnet {
750 type Err = ipnet::AddrParseError;
751
752 fn from_str(s: &str) -> Result<Self, Self::Err> {
753 ipnet::IpNet::from_str(s).map(ClientSubnet::from)
754 }
755}
756
757#[cfg(test)]
758mod tests {
759 #![allow(clippy::dbg_macro, clippy::print_stdout)]
760
761 use super::*;
762
763 #[test]
764 #[cfg(feature = "dnssec")]
765 fn test() {
766 let mut rdata = OPT::default();
767 rdata.insert(EdnsOption::DAU(SupportedAlgorithms::all()));
768
769 let mut bytes = Vec::new();
770 let mut encoder: BinEncoder<'_> = BinEncoder::new(&mut bytes);
771 assert!(rdata.emit(&mut encoder).is_ok());
772 let bytes = encoder.into_bytes();
773
774 println!("bytes: {bytes:?}");
775
776 let mut decoder: BinDecoder<'_> = BinDecoder::new(bytes);
777 let restrict = Restrict::new(bytes.len() as u16);
778 let read_rdata = OPT::read_data(&mut decoder, restrict).expect("Decoding error");
779 assert_eq!(rdata, read_rdata);
780 }
781
782 #[test]
783 fn test_read_empty_option_at_end_of_opt() {
784 let bytes: Vec<u8> = vec![
785 0x00, 0x0a, 0x00, 0x08, 0x0b, 0x64, 0xb4, 0xdc, 0xd7, 0xb0, 0xcc, 0x8f, 0x00, 0x08,
786 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00,
787 ];
788
789 let mut decoder: BinDecoder<'_> = BinDecoder::new(&bytes);
790 let read_rdata = OPT::read_data(&mut decoder, Restrict::new(bytes.len() as u16));
791 assert!(
792 read_rdata.is_ok(),
793 "error decoding: {:?}",
794 read_rdata.unwrap_err()
795 );
796
797 let opt = read_rdata.unwrap();
798 let mut options = HashMap::default();
799 options.insert(
800 EdnsCode::Subnet,
801 EdnsOption::Subnet("0.0.0.0/0".parse().unwrap()),
802 );
803 options.insert(
804 EdnsCode::Cookie,
805 EdnsOption::Unknown(10, vec![0x0b, 0x64, 0xb4, 0xdc, 0xd7, 0xb0, 0xcc, 0x8f]),
806 );
807 options.insert(EdnsCode::Keepalive, EdnsOption::Unknown(11, vec![]));
808 let options = OPT::new(options);
809 assert_eq!(opt, options);
810 }
811
812 #[test]
813 fn test_write_client_subnet() {
814 let expected_bytes: Vec<u8> = vec![0x00, 0x01, 0x18, 0x00, 0xac, 0x01, 0x01];
815 let ecs: ClientSubnet = "172.1.1.1/24".parse().unwrap();
816 let bytes = Vec::<u8>::try_from(&ecs).unwrap();
817 println!("bytes: {bytes:?}");
818 assert_eq!(bytes, expected_bytes);
819 }
820
821 #[test]
822 fn test_read_client_subnet() {
823 let bytes: Vec<u8> = vec![0x00, 0x01, 0x18, 0x00, 0xac, 0x01, 0x01];
824 let ecs = ClientSubnet::try_from(bytes.as_slice()).unwrap();
825 assert_eq!(ecs, "172.1.1.0/24".parse().unwrap());
826 }
827}