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}