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