hickory_proto/rr/rdata/
svcb.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//! SVCB records, see [RFC 9460 SVCB and HTTPS Resource Records, Nov 2023](https://datatracker.ietf.org/doc/html/rfc9460)
9#![allow(clippy::use_self)]
10
11use alloc::{string::String, vec::Vec};
12use core::{
13    cmp::{Ord, Ordering, PartialOrd},
14    convert::TryFrom,
15    fmt,
16};
17
18#[cfg(feature = "serde")]
19use serde::{Deserialize, Serialize};
20
21use enum_as_inner::EnumAsInner;
22
23use crate::{
24    error::{ProtoError, ProtoErrorKind, ProtoResult},
25    rr::{
26        Name, RData, RecordData, RecordDataDecodable, RecordType,
27        rdata::{A, AAAA},
28    },
29    serialize::binary::{
30        BinDecodable, BinDecoder, BinEncodable, BinEncoder, Restrict, RestrictedMath,
31    },
32};
33
34///  [RFC 9460 SVCB and HTTPS Resource Records, Nov 2023](https://datatracker.ietf.org/doc/html/rfc9460#section-2.2)
35///
36/// ```text
37/// 2.2.  RDATA wire format
38///
39///   The RDATA for the SVCB RR consists of:
40///
41///   *  a 2 octet field for SvcPriority as an integer in network byte
42///      order.
43///   *  the uncompressed, fully-qualified TargetName, represented as a
44///      sequence of length-prefixed labels as in Section 3.1 of [RFC1035].
45///   *  the SvcParams, consuming the remainder of the record (so smaller
46///      than 65535 octets and constrained by the RDATA and DNS message
47///      sizes).
48///
49///   When the list of SvcParams is non-empty (ServiceMode), it contains a
50///   series of SvcParamKey=SvcParamValue pairs, represented as:
51///
52///   *  a 2 octet field containing the SvcParamKey as an integer in
53///      network byte order.  (See Section 14.3.2 for the defined values.)
54///   *  a 2 octet field containing the length of the SvcParamValue as an
55///      integer between 0 and 65535 in network byte order
56///   *  an octet string of this length whose contents are the SvcParamValue
57///      in a format determined by the SvcParamKey
58///
59///   SvcParamKeys SHALL appear in increasing numeric order.
60///
61///   Clients MUST consider an RR malformed if:
62///
63///   *  the end of the RDATA occurs within a SvcParam.
64///   *  SvcParamKeys are not in strictly increasing numeric order.
65///   *  the SvcParamValue for an SvcParamKey does not have the expected
66///      format.
67///
68///   Note that the second condition implies that there are no duplicate
69///   SvcParamKeys.
70///
71///   If any RRs are malformed, the client MUST reject the entire RRSet and
72///   fall back to non-SVCB connection establishment.
73/// ```
74#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
75#[derive(Debug, PartialEq, Eq, Hash, Clone)]
76pub struct SVCB {
77    svc_priority: u16,
78    target_name: Name,
79    svc_params: Vec<(SvcParamKey, SvcParamValue)>,
80}
81
82impl SVCB {
83    /// Create a new SVCB record from parts
84    ///
85    /// It is up to the caller to validate the data going into the record
86    pub fn new(
87        svc_priority: u16,
88        target_name: Name,
89        svc_params: Vec<(SvcParamKey, SvcParamValue)>,
90    ) -> Self {
91        Self {
92            svc_priority,
93            target_name,
94            svc_params,
95        }
96    }
97
98    ///  [RFC 9460 SVCB and HTTPS Resource Records, Nov 2023](https://datatracker.ietf.org/doc/html/rfc9460#section-2.4.1)
99    /// ```text
100    /// 2.4.1.  SvcPriority
101    ///
102    ///   When SvcPriority is 0 the SVCB record is in AliasMode
103    ///   (Section 2.4.2).  Otherwise, it is in ServiceMode (Section 2.4.3).
104    ///
105    ///   Within a SVCB RRSet, all RRs SHOULD have the same Mode.  If an RRSet
106    ///   contains a record in AliasMode, the recipient MUST ignore any
107    ///   ServiceMode records in the set.
108    ///
109    ///   RRSets are explicitly unordered collections, so the SvcPriority field
110    ///   is used to impose an ordering on SVCB RRs.  A smaller SvcPriority indicates
111    ///   that the domain owner recommends the use of this record over ServiceMode
112    ///   RRs with a larger SvcPriority value.
113    ///
114    ///   When receiving an RRSet containing multiple SVCB records with the
115    ///   same SvcPriority value, clients SHOULD apply a random shuffle within
116    ///   a priority level to the records before using them, to ensure uniform
117    ///   load-balancing.
118    /// ```
119    pub fn svc_priority(&self) -> u16 {
120        self.svc_priority
121    }
122
123    ///  [RFC 9460 SVCB and HTTPS Resource Records, Nov 2023](https://datatracker.ietf.org/doc/html/rfc9460#section-2.5)
124    /// ```text
125    /// 2.5.  Special handling of "." in TargetName
126    ///
127    ///   If TargetName has the value "." (represented in the wire format as a
128    ///    zero-length label), special rules apply.
129    ///
130    /// 2.5.1.  AliasMode
131    ///
132    ///    For AliasMode SVCB RRs, a TargetName of "." indicates that the
133    ///    service is not available or does not exist.  This indication is
134    ///    advisory: clients encountering this indication MAY ignore it and
135    ///    attempt to connect without the use of SVCB.
136    ///
137    /// 2.5.2.  ServiceMode
138    ///
139    ///    For ServiceMode SVCB RRs, if TargetName has the value ".", then the
140    ///    owner name of this record MUST be used as the effective TargetName.
141    ///    If the record has a wildcard owner name in the zone file, the recipient
142    ///    SHALL use the response's synthesized owner name as the effective TargetName.
143    ///
144    ///    Here, for example, "svc2.example.net" is the effective TargetName:
145    ///
146    ///    example.com.      7200  IN HTTPS 0 svc.example.net.
147    ///    svc.example.net.  7200  IN CNAME svc2.example.net.
148    ///    svc2.example.net. 7200  IN HTTPS 1 . port=8002
149    ///    svc2.example.net. 300   IN A     192.0.2.2
150    ///    svc2.example.net. 300   IN AAAA  2001:db8::2
151    /// ```
152    pub fn target_name(&self) -> &Name {
153        &self.target_name
154    }
155
156    /// See [`SvcParamKey`] for details on each parameter
157    pub fn svc_params(&self) -> &[(SvcParamKey, SvcParamValue)] {
158        &self.svc_params
159    }
160}
161
162///  [RFC 9460 SVCB and HTTPS Resource Records, Nov 2023](https://datatracker.ietf.org/doc/html/rfc9460#section-14.3.2)
163///
164/// ```text
165/// 14.3.2.  Initial contents
166///
167///   The "Service Binding (SVCB) Parameter Registry" shall initially be
168///   populated with the registrations below:
169///
170///   +=============+=================+======================+===========+
171///   | Number      | Name            | Meaning              | Reference |
172///   +=============+=================+======================+===========+
173///   | 0           | mandatory       | Mandatory keys in    | (This     |
174///   |             |                 | this RR              | document) |
175///   +-------------+-----------------+----------------------+-----------+
176///   | 1           | alpn            | Additional supported | (This     |
177///   |             |                 | protocols            | document) |
178///   +-------------+-----------------+----------------------+-----------+
179///   | 2           | no-default-alpn | No support for       | (This     |
180///   |             |                 | default protocol     | document) |
181///   +-------------+-----------------+----------------------+-----------+
182///   | 3           | port            | Port for alternative | (This     |
183///   |             |                 | endpoint             | document) |
184///   +-------------+-----------------+----------------------+-----------+
185///   | 4           | ipv4hint        | IPv4 address hints   | (This     |
186///   |             |                 |                      | document) |
187///   +-------------+-----------------+----------------------+-----------+
188///   | 5           | ech             | RESERVED (held for   | N/A       |
189///   |             |                 | ECH)                 |           |
190///   +-------------+-----------------+----------------------+-----------+
191///   | 6           | ipv6hint        | IPv6 address hints   | (This     |
192///   |             |                 |                      | document) |
193///   +-------------+-----------------+----------------------+-----------+
194///   | 65280-65534 | N/A             | Private Use          | (This     |
195///   |             |                 |                      | document) |
196///   +-------------+-----------------+----------------------+-----------+
197///   | 65535       | N/A             | Reserved ("Invalid   | (This     |
198///   |             |                 | key")                | document) |
199///   +-------------+-----------------+----------------------+-----------+
200///
201/// parsing done via:
202///   *  a 2 octet field containing the SvcParamKey as an integer in
203///      network byte order.  (See Section 14.3.2 for the defined values.)
204/// ```
205#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
206#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
207pub enum SvcParamKey {
208    /// Mandatory keys in this RR
209    #[cfg_attr(feature = "serde", serde(rename = "mandatory"))]
210    Mandatory,
211    /// Additional supported protocols
212    #[cfg_attr(feature = "serde", serde(rename = "alpn"))]
213    Alpn,
214    /// No support for default protocol
215    #[cfg_attr(feature = "serde", serde(rename = "no-default-alpn"))]
216    NoDefaultAlpn,
217    /// Port for alternative endpoint
218    #[cfg_attr(feature = "serde", serde(rename = "port"))]
219    Port,
220    /// IPv4 address hints
221    #[cfg_attr(feature = "serde", serde(rename = "ipv4hint"))]
222    Ipv4Hint,
223    /// Encrypted Client Hello configuration list
224    #[cfg_attr(feature = "serde", serde(rename = "ech"))]
225    EchConfigList,
226    /// IPv6 address hints
227    #[cfg_attr(feature = "serde", serde(rename = "ipv6hint"))]
228    Ipv6Hint,
229    /// Private Use
230    Key(u16),
231    /// Reserved ("Invalid key")
232    Key65535,
233    /// Unknown
234    Unknown(u16),
235}
236
237impl From<u16> for SvcParamKey {
238    fn from(val: u16) -> Self {
239        match val {
240            0 => Self::Mandatory,
241            1 => Self::Alpn,
242            2 => Self::NoDefaultAlpn,
243            3 => Self::Port,
244            4 => Self::Ipv4Hint,
245            5 => Self::EchConfigList,
246            6 => Self::Ipv6Hint,
247            65280..=65534 => Self::Key(val),
248            65535 => Self::Key65535,
249            _ => Self::Unknown(val),
250        }
251    }
252}
253
254impl From<SvcParamKey> for u16 {
255    fn from(val: SvcParamKey) -> Self {
256        match val {
257            SvcParamKey::Mandatory => 0,
258            SvcParamKey::Alpn => 1,
259            SvcParamKey::NoDefaultAlpn => 2,
260            SvcParamKey::Port => 3,
261            SvcParamKey::Ipv4Hint => 4,
262            SvcParamKey::EchConfigList => 5,
263            SvcParamKey::Ipv6Hint => 6,
264            SvcParamKey::Key(val) => val,
265            SvcParamKey::Key65535 => 65535,
266            SvcParamKey::Unknown(val) => val,
267        }
268    }
269}
270
271impl<'r> BinDecodable<'r> for SvcParamKey {
272    // a 2 octet field containing the SvcParamKey as an integer in
273    //      network byte order.  (See Section 14.3.2 for the defined values.)
274    fn read(decoder: &mut BinDecoder<'r>) -> ProtoResult<Self> {
275        Ok(decoder.read_u16()?.unverified(/*any u16 is valid*/).into())
276    }
277}
278
279impl BinEncodable for SvcParamKey {
280    // a 2 octet field containing the SvcParamKey as an integer in
281    //      network byte order.  (See Section 14.3.2 for the defined values.)
282    fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
283        encoder.emit_u16((*self).into())
284    }
285}
286
287impl fmt::Display for SvcParamKey {
288    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
289        match self {
290            Self::Mandatory => f.write_str("mandatory")?,
291            Self::Alpn => f.write_str("alpn")?,
292            Self::NoDefaultAlpn => f.write_str("no-default-alpn")?,
293            Self::Port => f.write_str("port")?,
294            Self::Ipv4Hint => f.write_str("ipv4hint")?,
295            Self::EchConfigList => f.write_str("ech")?,
296            Self::Ipv6Hint => f.write_str("ipv6hint")?,
297            Self::Key(val) => write!(f, "key{val}")?,
298            Self::Key65535 => f.write_str("key65535")?,
299            Self::Unknown(val) => write!(f, "unknown{val}")?,
300        }
301
302        Ok(())
303    }
304}
305
306impl core::str::FromStr for SvcParamKey {
307    type Err = ProtoError;
308
309    fn from_str(s: &str) -> Result<Self, Self::Err> {
310        /// keys are in the format of key#, e.g. key12344, with a max value of u16
311        fn parse_unknown_key(key: &str) -> Result<SvcParamKey, ProtoError> {
312            let key_value = key.strip_prefix("key").ok_or_else(|| {
313                ProtoError::from(ProtoErrorKind::Msg(format!(
314                    "bad formatted key ({key}), expected key1234"
315                )))
316            })?;
317
318            Ok(SvcParamKey::Key(u16::from_str(key_value)?))
319        }
320
321        let key = match s {
322            "mandatory" => Self::Mandatory,
323            "alpn" => Self::Alpn,
324            "no-default-alpn" => Self::NoDefaultAlpn,
325            "port" => Self::Port,
326            "ipv4hint" => Self::Ipv4Hint,
327            "ech" => Self::EchConfigList,
328            "ipv6hint" => Self::Ipv6Hint,
329            "key65535" => Self::Key65535,
330            _ => parse_unknown_key(s)?,
331        };
332
333        Ok(key)
334    }
335}
336
337impl Ord for SvcParamKey {
338    fn cmp(&self, other: &Self) -> Ordering {
339        u16::from(*self).cmp(&u16::from(*other))
340    }
341}
342
343impl PartialOrd for SvcParamKey {
344    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
345        Some(self.cmp(other))
346    }
347}
348
349/// Warning, it is currently up to users of this type to validate the data against that expected by the key
350///
351/// ```text
352///   *  a 2 octet field containing the length of the SvcParamValue as an
353///      integer between 0 and 65535 in network byte order (but constrained
354///      by the RDATA and DNS message sizes).
355///   *  an octet string of this length whose contents are in a format
356///      determined by the SvcParamKey.
357/// ```
358#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
359#[derive(Debug, PartialEq, Eq, Hash, Clone, EnumAsInner)]
360pub enum SvcParamValue {
361    ///    In a ServiceMode RR, a SvcParamKey is considered "mandatory" if the
362    ///    RR will not function correctly for clients that ignore this
363    ///    SvcParamKey.  Each SVCB protocol mapping SHOULD specify a set of keys
364    ///    that are "automatically mandatory", i.e. mandatory if they are
365    ///    present in an RR.  The SvcParamKey "mandatory" is used to indicate
366    ///    any mandatory keys for this RR, in addition to any automatically
367    ///    mandatory keys that are present.
368    ///
369    /// see `Mandatory`
370    #[cfg_attr(feature = "serde", serde(rename = "mandatory"))]
371    Mandatory(Mandatory),
372    ///  [RFC 9460 SVCB and HTTPS Resource Records, Nov 2023](https://datatracker.ietf.org/doc/html/rfc9460#section-7.1)
373    ///
374    /// ```text
375    ///    The "alpn" and "no-default-alpn" SvcParamKeys together indicate the
376    ///    set of Application Layer Protocol Negotiation (ALPN) protocol
377    ///    identifiers [Alpn] and associated transport protocols supported by
378    ///    this service endpoint (the "SVCB ALPN set").
379    /// ```
380    #[cfg_attr(feature = "serde", serde(rename = "alpn"))]
381    Alpn(Alpn),
382    /// For "no-default-alpn", the presentation and wire format values MUST
383    ///    be empty.
384    /// See also `Alpn`
385    #[cfg_attr(feature = "serde", serde(rename = "no-default-alpn"))]
386    NoDefaultAlpn,
387    ///  [RFC 9460 SVCB and HTTPS Resource Records, Nov 2023](https://datatracker.ietf.org/doc/html/rfc9460#section-7.2)
388    ///
389    /// ```text
390    ///    7.2.  "port"
391    ///
392    ///   The "port" SvcParamKey defines the TCP or UDP port that should be
393    ///   used to reach this alternative endpoint.  If this key is not present,
394    ///   clients SHALL use the authority endpoint's port number.
395    ///
396    ///   The presentation value of the SvcParamValue is a single decimal
397    ///   integer between 0 and 65535 in ASCII.  Any other value (e.g. an
398    ///   empty value) is a syntax error.  To enable simpler parsing, this
399    ///   SvcParam MUST NOT contain escape sequences.
400    ///
401    ///   The wire format of the SvcParamValue is the corresponding 2 octet
402    ///   numeric value in network byte order.
403    ///
404    ///   If a port-restricting firewall is in place between some client and
405    ///   the service endpoint, changing the port number might cause that
406    ///   client to lose access to the service, so operators should exercise
407    ///   caution when using this SvcParamKey to specify a non-default port.
408    /// ```
409    #[cfg_attr(feature = "serde", serde(rename = "port"))]
410    Port(u16),
411    ///  [RFC 9460 SVCB and HTTPS Resource Records, Nov 2023](https://datatracker.ietf.org/doc/html/rfc9460#section-7.2)
412    ///
413    ///   The "ipv4hint" and "ipv6hint" keys convey IP addresses that clients
414    ///   MAY use to reach the service.  If A and AAAA records for TargetName
415    ///   are locally available, the client SHOULD ignore these hints.
416    ///   Otherwise, clients SHOULD perform A and/or AAAA queries for
417    ///   TargetName as in Section 3, and clients SHOULD use the IP address in
418    ///   those responses for future connections.  Clients MAY opt to terminate
419    ///   any connections using the addresses in hints and instead switch to
420    ///   the addresses in response to the TargetName query.  Failure to use A
421    ///   and/or AAAA response addresses could negatively impact load balancing
422    ///   or other geo-aware features and thereby degrade client performance.
423    ///
424    /// see `IpHint`
425    #[cfg_attr(feature = "serde", serde(rename = "ipv4hint"))]
426    Ipv4Hint(IpHint<A>),
427    /// [draft-ietf-tls-svcb-ech-01 Bootstrapping TLS Encrypted ClientHello with DNS Service Bindings, Sep 2024](https://datatracker.ietf.org/doc/html/draft-ietf-tls-svcb-ech-01)
428    ///
429    /// ```text
430    /// 2.  "SvcParam for ECH configuration"
431    ///
432    ///   The "ech" SvcParamKey is defined for conveying the ECH configuration
433    ///   of an alternative endpoint. It is applicable to all TLS-based protocols
434    ///   (including DTLS [RFC9147] and QUIC version 1 [RFC9001]) unless otherwise
435    ///   specified.
436    /// ```
437    #[cfg_attr(feature = "serde", serde(rename = "ech"))]
438    EchConfigList(EchConfigList),
439    /// See `IpHint`
440    #[cfg_attr(feature = "serde", serde(rename = "ipv6hint"))]
441    Ipv6Hint(IpHint<AAAA>),
442    /// Unparsed network data. Refer to documents on the associated key value
443    ///
444    /// This will be left as is when read off the wire, and encoded in bas64
445    ///    for presentation.
446    Unknown(Unknown),
447}
448
449impl SvcParamValue {
450    // a 2 octet field containing the length of the SvcParamValue as an
451    //      integer between 0 and 65535 in network byte order (but constrained
452    //      by the RDATA and DNS message sizes).
453    fn read(key: SvcParamKey, decoder: &mut BinDecoder<'_>) -> ProtoResult<Self> {
454        let len: usize = decoder
455            .read_u16()?
456            .verify_unwrap(|len| *len as usize <= decoder.len())
457            .map(|len| len as usize)
458            .map_err(|u| {
459                ProtoError::from(format!(
460                    "length of SvcParamValue ({}) exceeds remainder in RDATA ({})",
461                    u,
462                    decoder.len()
463                ))
464            })?;
465
466        let param_data = decoder.read_slice(len)?.unverified(/*verification to be done by individual param types*/);
467        let mut decoder = BinDecoder::new(param_data);
468
469        let value = match key {
470            SvcParamKey::Mandatory => Self::Mandatory(Mandatory::read(&mut decoder)?),
471            SvcParamKey::Alpn => Self::Alpn(Alpn::read(&mut decoder)?),
472            // should always be empty
473            SvcParamKey::NoDefaultAlpn => {
474                if len > 0 {
475                    return Err(ProtoError::from("Alpn expects at least one value"));
476                }
477
478                Self::NoDefaultAlpn
479            }
480            // The wire format of the SvcParamValue is the corresponding 2 octet
481            // numeric value in network byte order.
482            SvcParamKey::Port => {
483                let port = decoder.read_u16()?.unverified(/*all values are legal ports*/);
484                Self::Port(port)
485            }
486            SvcParamKey::Ipv4Hint => Self::Ipv4Hint(IpHint::<A>::read(&mut decoder)?),
487            SvcParamKey::EchConfigList => Self::EchConfigList(EchConfigList::read(&mut decoder)?),
488            SvcParamKey::Ipv6Hint => Self::Ipv6Hint(IpHint::<AAAA>::read(&mut decoder)?),
489            SvcParamKey::Key(_) | SvcParamKey::Key65535 | SvcParamKey::Unknown(_) => {
490                Self::Unknown(Unknown::read(&mut decoder)?)
491            }
492        };
493
494        Ok(value)
495    }
496}
497
498impl BinEncodable for SvcParamValue {
499    // a 2 octet field containing the length of the SvcParamValue as an
500    //      integer between 0 and 65535 in network byte order (but constrained
501    //      by the RDATA and DNS message sizes).
502    fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
503        // set the place for the length...
504        let place = encoder.place::<u16>()?;
505
506        match self {
507            Self::Mandatory(mandatory) => mandatory.emit(encoder)?,
508            Self::Alpn(alpn) => alpn.emit(encoder)?,
509            Self::NoDefaultAlpn => (),
510            Self::Port(port) => encoder.emit_u16(*port)?,
511            Self::Ipv4Hint(ip_hint) => ip_hint.emit(encoder)?,
512            Self::EchConfigList(ech_config) => ech_config.emit(encoder)?,
513            Self::Ipv6Hint(ip_hint) => ip_hint.emit(encoder)?,
514            Self::Unknown(unknown) => unknown.emit(encoder)?,
515        }
516
517        // go back and set the length
518        let len = u16::try_from(encoder.len_since_place(&place))
519            .map_err(|_| ProtoError::from("Total length of SvcParamValue exceeds u16::MAX"))?;
520        place.replace(encoder, len)?;
521
522        Ok(())
523    }
524}
525
526impl fmt::Display for SvcParamValue {
527    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
528        match self {
529            Self::Mandatory(mandatory) => write!(f, "{mandatory}")?,
530            Self::Alpn(alpn) => write!(f, "{alpn}")?,
531            Self::NoDefaultAlpn => (),
532            Self::Port(port) => write!(f, "{port}")?,
533            Self::Ipv4Hint(ip_hint) => write!(f, "{ip_hint}")?,
534            Self::EchConfigList(ech_config) => write!(f, "{ech_config}")?,
535            Self::Ipv6Hint(ip_hint) => write!(f, "{ip_hint}")?,
536            Self::Unknown(unknown) => write!(f, "{unknown}")?,
537        }
538
539        Ok(())
540    }
541}
542
543///  [RFC 9460 SVCB and HTTPS Resource Records, Nov 2023](https://datatracker.ietf.org/doc/html/rfc9460#section-8)
544///
545/// ```text
546/// 8.  ServiceMode RR compatibility and mandatory keys
547///
548///    In a ServiceMode RR, a SvcParamKey is considered "mandatory" if the
549///    RR will not function correctly for clients that ignore this
550///    SvcParamKey.  Each SVCB protocol mapping SHOULD specify a set of keys
551///    that are "automatically mandatory", i.e. mandatory if they are
552///    present in an RR.  The SvcParamKey "mandatory" is used to indicate
553///    any mandatory keys for this RR, in addition to any automatically
554///    mandatory keys that are present.
555///
556///    A ServiceMode RR is considered "compatible" with a client if the
557///    client recognizes all the mandatory keys, and their values indicate
558///    that successful connection establishment is possible. Incompatible RRs
559///    are ignored (see step 5 of the procedure defined in Section 3)
560///
561///    The presentation value SHALL be a comma-separated list
562///    (Appendix A.1) of one or more valid SvcParamKeys, either by their
563///    registered name or in the unknown-key format (Section 2.1).  Keys MAY
564///    appear in any order, but MUST NOT appear more than once.  For self-
565///    consistency (Section 2.4.3), listed keys MUST also appear in the
566///    SvcParams.
567///
568///    To enable simpler parsing, this SvcParamValue MUST NOT contain escape
569///    sequences.
570///
571///    For example, the following is a valid list of SvcParams:
572///
573///    ipv6hint=... key65333=ex1 key65444=ex2 mandatory=key65444,ipv6hint
574///
575///    In wire format, the keys are represented by their numeric values in
576///    network byte order, concatenated in strictly increasing numeric order.
577///
578///    This SvcParamKey is always automatically mandatory, and MUST NOT
579///    appear in its own value-list.  Other automatically mandatory keys
580///    SHOULD NOT appear in the list either.  (Including them wastes space
581///    and otherwise has no effect.)
582/// ```
583#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
584#[derive(Debug, PartialEq, Eq, Hash, Clone)]
585#[repr(transparent)]
586pub struct Mandatory(pub Vec<SvcParamKey>);
587
588impl<'r> BinDecodable<'r> for Mandatory {
589    /// This expects the decoder to be limited to only this field, i.e. the end of input for the decoder
590    ///   is the end of input for the fields
591    ///
592    /// ```text
593    ///    In wire format, the keys are represented by their numeric values in
594    ///    network byte order, concatenated in strictly increasing numeric order.
595    /// ```
596    fn read(decoder: &mut BinDecoder<'r>) -> ProtoResult<Self> {
597        let mut keys = Vec::with_capacity(1);
598
599        while decoder.peek().is_some() {
600            keys.push(SvcParamKey::read(decoder)?);
601        }
602
603        if keys.is_empty() {
604            return Err(ProtoError::from("Mandatory expects at least one value"));
605        }
606
607        Ok(Self(keys))
608    }
609}
610
611impl BinEncodable for Mandatory {
612    /// This expects the decoder to be limited to only this field, i.e. the end of input for the decoder
613    ///   is the end of input for the fields
614    ///
615    /// ```text
616    ///    In wire format, the keys are represented by their numeric values in
617    ///    network byte order, concatenated in strictly increasing numeric order.
618    /// ```
619    fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
620        if self.0.is_empty() {
621            return Err(ProtoError::from("Alpn expects at least one value"));
622        }
623
624        // TODO: order by key value
625        for key in self.0.iter() {
626            key.emit(encoder)?
627        }
628
629        Ok(())
630    }
631}
632
633impl fmt::Display for Mandatory {
634    ///  [RFC 9460 SVCB and HTTPS Resource Records, Nov 2023](https://datatracker.ietf.org/doc/html/rfc9460#section-8)
635    ///
636    ///    The presentation value SHALL be a comma-separated list
637    ///    (Appendix A.1) of one or more valid SvcParamKeys, either by their
638    ///    registered name or in the unknown-key format (Section 2.1).  Keys MAY
639    ///    appear in any order, but MUST NOT appear more than once.  For self-
640    ///    consistency (Section 2.4.3), listed keys MUST also appear in the
641    ///    SvcParams.
642    ///
643    ///    To enable simpler parsing, this SvcParamValue MUST NOT contain escape
644    ///    sequences.
645    ///
646    ///    For example, the following is a valid list of SvcParams:
647    ///
648    ///    ipv6hint=... key65333=ex1 key65444=ex2 mandatory=key65444,ipv6hint
649    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
650        for key in self.0.iter() {
651            // TODO: confirm in the RFC that trailing commas are ok
652            write!(f, "{key},")?;
653        }
654
655        Ok(())
656    }
657}
658
659///  [RFC 9460 SVCB and HTTPS Resource Records, Nov 2023](https://datatracker.ietf.org/doc/html/rfc9460#section-7.1)
660///
661/// ```text
662/// 6.1.  "alpn" and "no-default-alpn"
663///
664///   The "alpn" and "no-default-alpn" SvcParamKeys together indicate the
665///   set of Application Layer Protocol Negotiation (ALPN) protocol
666///   identifiers [ALPN] and associated transport protocols supported by
667///   this service endpoint.
668///
669///   As with Alt-Svc [AltSvc], the ALPN protocol identifier is used to
670///   identify the application protocol and associated suite of protocols
671///   supported by the endpoint (the "protocol suite"). The presence of an
672///   ALPN protocol identifier in the SVCB ALPN set indicates that this
673///   service endpoint, described by TargetName and the other parameters
674///   (e.g., "port"), offers service with the protocol suite associated
675///   with this ALPN identifier.
676///
677///   Clients filter the set of ALPN identifiers to match the protocol suites
678///   they support, and this informs the underlying transport protocol used
679///   (such as QUIC over UDP or TLS over TCP). ALPN protocol identifiers that do
680///   not uniquely identify a protocol suite (e.g., an Identification Sequence
681///   that can be used with both TLS and DTLS) are not compatible with this
682///   SvcParamKey and MUST NOT be included in the SVCB ALPN set.
683///
684///   ALPNs are identified by their registered "Identification Sequence"
685///   ("alpn-id"), which is a sequence of 1-255 octets.
686///
687///   alpn-id = 1*255OCTET
688///
689///   For "alpn", the presentation value SHALL be a comma-separated list
690///   (Appendix A.1) of one or more alpn-ids. Zone-file implementations MAY
691///   disallow the "," and "\" characters in ALPN IDs instead of implementing
692///   the value-list escaping procedure, relying on the opaque key format
693///   (e.g., key1=\002h2) in the event that these characters are needed.
694///
695///   The wire format value for "alpn" consists of at least one "alpn-id"
696///   prefixed by its length as a single octet, and these length-value
697///   pairs are concatenated to form the SvcParamValue.  These pairs MUST
698///   exactly fill the SvcParamValue; otherwise, the SvcParamValue is
699///   malformed.
700///
701///   For "no-default-alpn", the presentation and wire format values MUST
702///   be empty.  When "no-default-alpn" is specified in an RR, "alpn" must
703///   also be specified in order for the RR to be "self-consistent"
704///   (Section 2.4.3).
705///
706///   Each scheme that uses this SvcParamKey defines a "default set" of ALPN
707///   IDs that are supported by nearly all clients and servers; this set MAY
708///   be empty. To determine the SVCB ALPN set, the client starts with the
709///   list of alpn-ids from the "alpn" SvcParamKey, and it adds the default
710///   set unless the "no-default-alpn" SvcParamKey is present.
711///
712///   To establish a connection to the endpoint, clients MUST
713///
714///   1.  Let SVCB-ALPN-Intersection be the set of protocols in the SVCB
715///       ALPN set that the client supports.
716///
717///   2.  Let Intersection-Transports be the set of transports (e.g.  TLS,
718///       DTLS, QUIC) implied by the protocols in SVCB-ALPN-Intersection.
719///
720///   3.  For each transport in Intersection-Transports, construct a
721///       ProtocolNameList containing the Identification Sequences of all
722///       the client's supported ALPN protocols for that transport, without
723///       regard to the SVCB ALPN set.
724///
725///   For example, if the SVCB ALPN set is ["http/1.1", "h3"], and the
726///   client supports HTTP/1.1, HTTP/2, and HTTP/3, the client could
727///   attempt to connect using TLS over TCP with a ProtocolNameList of
728///   ["http/1.1", "h2"], and could also attempt a connection using QUIC,
729///   with a ProtocolNameList of ["h3"].
730///
731///   Once the client has constructed a ClientHello, protocol negotiation
732///   in that handshake proceeds as specified in [ALPN], without regard to
733///   the SVCB ALPN set.
734///
735///   Clients MAY implement a fallback procedure, using a less-preferred
736///   transport if more-preferred transports fail to connect. This fallback
737///   behavior is vulnerable to manipulation by a network attacker who blocks
738///   the more-preferred transports, but it may be necessary for compatibility
739///   with existing networks.
740///
741///   With this procedure in place, an attacker who can modify DNS and
742///   network traffic can prevent a successful transport connection, but
743///   cannot otherwise interfere with ALPN protocol selection.  This
744///   procedure also ensures that each ProtocolNameList includes at least
745///   one protocol from the SVCB ALPN set.
746///
747///   Clients SHOULD NOT attempt connection to a service endpoint whose
748///   SVCB ALPN set does not contain any supported protocols.
749///
750///   To ensure consistency of behavior, clients MAY reject the entire SVCB RRSet
751///   and fall back to basic connection establishment if all of the RRs
752///   indicate "no-default-alpn", even if connection could have succeeded
753///   using a non-default alpn.
754///
755///   Zone operators SHOULD ensure that at least one RR in each RRset supports
756///   the default transports. This enables compatibility with the greatest
757///   number of clients.
758/// ```
759#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
760#[derive(Debug, PartialEq, Eq, Hash, Clone)]
761#[repr(transparent)]
762pub struct Alpn(pub Vec<String>);
763
764impl<'r> BinDecodable<'r> for Alpn {
765    /// This expects the decoder to be limited to only this field, i.e. the end of input for the decoder
766    ///   is the end of input for the fields
767    ///
768    /// ```text
769    ///   The wire format value for "alpn" consists of at least one "alpn-id"
770    ///   prefixed by its length as a single octet, and these length-value
771    ///   pairs are concatenated to form the SvcParamValue.  These pairs MUST
772    ///   exactly fill the SvcParamValue; otherwise, the SvcParamValue is
773    ///   malformed.
774    /// ```
775    fn read(decoder: &mut BinDecoder<'r>) -> ProtoResult<Self> {
776        let mut alpns = Vec::with_capacity(1);
777
778        while decoder.peek().is_some() {
779            let alpn = decoder.read_character_data()?.unverified(/*will rely on string parser*/);
780            let alpn = String::from_utf8(alpn.to_vec())?;
781            alpns.push(alpn);
782        }
783
784        if alpns.is_empty() {
785            return Err(ProtoError::from("Alpn expects at least one value"));
786        }
787
788        Ok(Self(alpns))
789    }
790}
791
792impl BinEncodable for Alpn {
793    ///   The wire format value for "alpn" consists of at least one "alpn-id"
794    ///   prefixed by its length as a single octet, and these length-value
795    ///   pairs are concatenated to form the SvcParamValue.  These pairs MUST
796    ///   exactly fill the SvcParamValue; otherwise, the SvcParamValue is
797    ///   malformed.
798    fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
799        if self.0.is_empty() {
800            return Err(ProtoError::from("Alpn expects at least one value"));
801        }
802
803        for alpn in self.0.iter() {
804            encoder.emit_character_data(alpn)?
805        }
806
807        Ok(())
808    }
809}
810
811impl fmt::Display for Alpn {
812    ///   The presentation value SHALL be a comma-separated list
813    ///   (Appendix A.1) of one or more "alpn-id"s.
814    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
815        for alpn in self.0.iter() {
816            // TODO: confirm in the RFC that trailing commas are ok
817            write!(f, "{alpn},")?;
818        }
819
820        Ok(())
821    }
822}
823
824/// [draft-ietf-tls-svcb-ech-01 Bootstrapping TLS Encrypted ClientHello with DNS Service Bindings, Sep 2024](https://datatracker.ietf.org/doc/html/draft-ietf-tls-svcb-ech-01)
825///
826/// ```text
827/// 2.  "SvcParam for ECH configuration"
828///
829///   The "ech" SvcParamKey is defined for conveying the ECH configuration
830///   of an alternative endpoint. It is applicable to all TLS-based protocols
831///   (including DTLS [RFC9147] and QUIC version 1 [RFC9001]) unless
832///   otherwise specified.
833///
834///   In wire format, the value of the parameter is an ECHConfigList (Section 4 of draft-ietf-tls-esni-18),
835///   including the redundant length prefix. In presentation format, the value is the ECHConfigList
836///   in Base 64 Encoding (Section 4 of [RFC4648]). Base 64 is used here to simplify integration
837///   with TLS server software. To enable simpler parsing, this SvcParam MUST NOT contain escape
838///   sequences.
839/// ```
840#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
841#[derive(PartialEq, Eq, Hash, Clone)]
842#[repr(transparent)]
843pub struct EchConfigList(pub Vec<u8>);
844
845impl<'r> BinDecodable<'r> for EchConfigList {
846    /// In wire format, the value of the parameter is an ECHConfigList (Section 4 of draft-ietf-tls-esni-18),
847    /// including the redundant length prefix. In presentation format, the value is the
848    /// ECHConfigList in Base 64 Encoding (Section 4 of RFC4648).
849    /// Base 64 is used here to simplify integration with TLS server software.
850    /// To enable simpler parsing, this SvcParam MUST NOT contain escape sequences.
851    fn read(decoder: &mut BinDecoder<'r>) -> ProtoResult<Self> {
852        let data =
853            decoder.read_vec(decoder.len())?.unverified(/*up to consumer to validate this data*/);
854
855        Ok(Self(data))
856    }
857}
858
859impl BinEncodable for EchConfigList {
860    /// In wire format, the value of the parameter is an ECHConfigList (Section 4 of draft-ietf-tls-esni-18),
861    /// including the redundant length prefix. In presentation format, the value is the
862    /// ECHConfigList in Base 64 Encoding (Section 4 of RFC4648).
863    /// Base 64 is used here to simplify integration with TLS server software.
864    /// To enable simpler parsing, this SvcParam MUST NOT contain escape sequences.
865    fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
866        encoder.emit_vec(&self.0)?;
867
868        Ok(())
869    }
870}
871
872impl fmt::Display for EchConfigList {
873    /// As the documentation states, the presentation format (what this function outputs) must be a BASE64 encoded string.
874    ///   hickory-dns will encode to BASE64 during formatting of the internal data, and output the BASE64 value.
875    ///
876    /// [draft-ietf-tls-svcb-ech-01 Bootstrapping TLS Encrypted ClientHello with DNS Service Bindings, Sep 2024](https://datatracker.ietf.org/doc/html/draft-ietf-tls-svcb-ech-01)
877    /// ```text
878    ///  In presentation format, the value is the ECHConfigList in Base 64 Encoding
879    ///  (Section 4 of [RFC4648]). Base 64 is used here to simplify integration with
880    ///  TLS server software. To enable simpler parsing, this SvcParam MUST NOT
881    ///  contain escape sequences.
882    /// ```
883    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
884        write!(f, "\"{}\"", data_encoding::BASE64.encode(&self.0))
885    }
886}
887
888impl fmt::Debug for EchConfigList {
889    /// The debug format for EchConfig will output the value in BASE64 like Display, but will the addition of the type-name.
890    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
891        write!(
892            f,
893            "\"EchConfig ({})\"",
894            data_encoding::BASE64.encode(&self.0)
895        )
896    }
897}
898
899///  [RFC 9460 SVCB and HTTPS Resource Records, Nov 2023](https://datatracker.ietf.org/doc/html/rfc9460#section-7.3)
900///
901/// ```text
902///    7.3.  "ipv4hint" and "ipv6hint"
903///
904///   The "ipv4hint" and "ipv6hint" keys convey IP addresses that clients
905///   MAY use to reach the service.  If A and AAAA records for TargetName
906///   are locally available, the client SHOULD ignore these hints.
907///   Otherwise, clients SHOULD perform A and/or AAAA queries for
908///   TargetName as in Section 3, and clients SHOULD use the IP address in
909///   those responses for future connections.  Clients MAY opt to terminate
910///   any connections using the addresses in hints and instead switch to
911///   the addresses in response to the TargetName query.  Failure to use A
912///   and/or AAAA response addresses could negatively impact load balancing
913///   or other geo-aware features and thereby degrade client performance.
914///
915///   The presentation value SHALL be a comma-separated list
916///   (Appendix A.1) of one or more IP addresses of the appropriate family
917///   in standard textual format [RFC5952].  To enable simpler parsing,
918///   this SvcParamValue MUST NOT contain escape sequences.
919///
920///   The wire format for each parameter is a sequence of IP addresses in
921///   network byte order (for the respective address family). Like an A or
922///   AAAA RRSet, the list of addresses represents an unordered collection,
923///   and clients SHOULD pick addresses to use in a random order.  An empty
924///   list of addresses is invalid.
925///
926///   When selecting between IPv4 and IPv6 addresses to use, clients may
927///   use an approach such as Happy Eyeballs [HappyEyeballsV2].  When only
928///   "ipv4hint" is present, NAT64 clients may synthesize IPv6 addresses
929///   as specified in [RFC7050] or ignore the "ipv4hint" key and
930///   wait for AAAA resolution (Section 3). For best performance, server
931///   operators SHOULD include an "ipv6hint" parameter whenever they
932///   include an "ipv4hint" parameter.
933///
934///   These parameters are intended to minimize additional connection
935///   latency when a recursive resolver is not compliant with the
936///   requirements in Section 4, and SHOULD NOT be included if most clients
937///   are using compliant recursive resolvers.  When TargetName is the
938///   origin hostname or the owner name (which can be written as "."),
939///   server operators SHOULD NOT include these hints, because they are
940///   unlikely to convey any performance benefit.
941/// ```
942#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
943#[derive(Debug, PartialEq, Eq, Hash, Clone)]
944#[repr(transparent)]
945pub struct IpHint<T>(pub Vec<T>);
946
947impl<'r, T> BinDecodable<'r> for IpHint<T>
948where
949    T: BinDecodable<'r>,
950{
951    ///   The wire format for each parameter is a sequence of IP addresses in
952    ///   network byte order (for the respective address family). Like an A or
953    ///   AAAA RRSet, the list of addresses represents an unordered collection,
954    ///   and clients SHOULD pick addresses to use in a random order.  An empty
955    ///   list of addresses is invalid.
956    fn read(decoder: &mut BinDecoder<'r>) -> ProtoResult<Self> {
957        let mut ips = Vec::new();
958
959        while decoder.peek().is_some() {
960            ips.push(T::read(decoder)?)
961        }
962
963        Ok(Self(ips))
964    }
965}
966
967impl<T> BinEncodable for IpHint<T>
968where
969    T: BinEncodable,
970{
971    ///   The wire format for each parameter is a sequence of IP addresses in
972    ///   network byte order (for the respective address family). Like an A or
973    ///   AAAA RRSet, the list of addresses represents an unordered collection,
974    ///   and clients SHOULD pick addresses to use in a random order.  An empty
975    ///   list of addresses is invalid.
976    fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
977        for ip in self.0.iter() {
978            ip.emit(encoder)?;
979        }
980
981        Ok(())
982    }
983}
984
985impl<T> fmt::Display for IpHint<T>
986where
987    T: fmt::Display,
988{
989    ///   The presentation value SHALL be a comma-separated list
990    ///   (Appendix A.1) of one or more IP addresses of the appropriate family
991    ///   in standard textual format [RFC 5952](https://tools.ietf.org/html/rfc5952).  To enable simpler parsing,
992    ///   this SvcParamValue MUST NOT contain escape sequences.
993    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
994        for ip in self.0.iter() {
995            write!(f, "{ip},")?;
996        }
997
998        Ok(())
999    }
1000}
1001
1002///  [RFC 9460 SVCB and HTTPS Resource Records, Nov 2023](https://datatracker.ietf.org/doc/html/rfc9460#section-2.1)
1003///
1004/// ```text
1005///   Arbitrary keys can be represented using the unknown-key presentation
1006///   format "keyNNNNN" where NNNNN is the numeric value of the key type
1007///   without leading zeros. A SvcParam in this form SHALL be parsed as specified
1008///   above, and the decoded value SHALL be used as its wire-format encoding.
1009///
1010///   For some SvcParamKeys, the value corresponds to a list or set of
1011///   items.  Presentation formats for such keys SHOULD use a comma-
1012///   separated list (Appendix A.1).
1013///
1014///   SvcParams in presentation format MAY appear in any order, but keys
1015///   MUST NOT be repeated.
1016/// ```
1017#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
1018#[derive(Debug, PartialEq, Eq, Hash, Clone)]
1019#[repr(transparent)]
1020pub struct Unknown(pub Vec<u8>);
1021
1022impl<'r> BinDecodable<'r> for Unknown {
1023    fn read(decoder: &mut BinDecoder<'r>) -> ProtoResult<Self> {
1024        // The passed slice is already length delimited, and we cannot
1025        // assume it's a collection of anything.
1026        let len = decoder.len();
1027
1028        let data = decoder.read_vec(len)?;
1029        let unknowns = data.unverified(/*any data is valid here*/).to_vec();
1030
1031        Ok(Self(unknowns))
1032    }
1033}
1034
1035impl BinEncodable for Unknown {
1036    fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
1037        encoder.emit_vec(&self.0)?;
1038
1039        Ok(())
1040    }
1041}
1042
1043impl fmt::Display for Unknown {
1044    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1045        // TODO: this needs to be properly encoded
1046        write!(f, "\"{}\",", String::from_utf8_lossy(&self.0))?;
1047
1048        Ok(())
1049    }
1050}
1051
1052impl BinEncodable for SVCB {
1053    fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
1054        self.svc_priority.emit(encoder)?;
1055        self.target_name.emit(encoder)?;
1056
1057        let mut last_key: Option<SvcParamKey> = None;
1058        for (key, param) in self.svc_params.iter() {
1059            if let Some(last_key) = last_key {
1060                if key <= &last_key {
1061                    return Err(ProtoError::from("SvcParams out of order"));
1062                }
1063            }
1064
1065            key.emit(encoder)?;
1066            param.emit(encoder)?;
1067
1068            last_key = Some(*key);
1069        }
1070
1071        Ok(())
1072    }
1073}
1074
1075impl RecordDataDecodable<'_> for SVCB {
1076    /// Reads the SVCB record from the decoder.
1077    ///
1078    /// [RFC 9460 SVCB and HTTPS Resource Records, Nov 2023](https://datatracker.ietf.org/doc/html/rfc9460#section-2.2)
1079    ///
1080    /// ```text
1081    ///   Clients MUST consider an RR malformed if:
1082    ///
1083    ///   *  the end of the RDATA occurs within a SvcParam.
1084    ///   *  SvcParamKeys are not in strictly increasing numeric order.
1085    ///   *  the SvcParamValue for an SvcParamKey does not have the expected
1086    ///      format.
1087    ///
1088    ///   Note that the second condition implies that there are no duplicate
1089    ///   SvcParamKeys.
1090    ///
1091    ///   If any RRs are malformed, the client MUST reject the entire RRSet and
1092    ///   fall back to non-SVCB connection establishment.
1093    /// ```
1094    fn read_data(decoder: &mut BinDecoder<'_>, rdata_length: Restrict<u16>) -> ProtoResult<SVCB> {
1095        let start_index = decoder.index();
1096
1097        let svc_priority = decoder.read_u16()?.unverified(/*any u16 is valid*/);
1098        let target_name = Name::read(decoder)?;
1099
1100        let mut remainder_len = rdata_length
1101            .map(|len| len as usize)
1102            .checked_sub(decoder.index() - start_index)
1103            .map_err(|len| format!("Bad length for RDATA of SVCB: {len}"))?
1104            .unverified(); // valid len
1105        let mut svc_params: Vec<(SvcParamKey, SvcParamValue)> = Vec::new();
1106
1107        // must have at least 4 bytes left for the key and the length
1108        while remainder_len >= 4 {
1109            // a 2 octet field containing the SvcParamKey as an integer in
1110            //      network byte order.  (See Section 14.3.2 for the defined values.)
1111            let key = SvcParamKey::read(decoder)?;
1112
1113            // a 2 octet field containing the length of the SvcParamValue as an
1114            //      integer between 0 and 65535 in network byte order (but constrained
1115            //      by the RDATA and DNS message sizes).
1116            let value = SvcParamValue::read(key, decoder)?;
1117
1118            if let Some(last_key) = svc_params.last().map(|(key, _)| key) {
1119                if last_key >= &key {
1120                    return Err(ProtoError::from("SvcParams out of order"));
1121                }
1122            }
1123
1124            svc_params.push((key, value));
1125            remainder_len = rdata_length
1126                .map(|len| len as usize)
1127                .checked_sub(decoder.index() - start_index)
1128                .map_err(|len| format!("Bad length for RDATA of SVCB: {len}"))?
1129                .unverified(); // valid len
1130        }
1131
1132        Ok(Self {
1133            svc_priority,
1134            target_name,
1135            svc_params,
1136        })
1137    }
1138}
1139
1140impl RecordData for SVCB {
1141    fn try_from_rdata(data: RData) -> Result<Self, RData> {
1142        match data {
1143            RData::SVCB(data) => Ok(data),
1144            _ => Err(data),
1145        }
1146    }
1147
1148    fn try_borrow(data: &RData) -> Option<&Self> {
1149        match data {
1150            RData::SVCB(data) => Some(data),
1151            _ => None,
1152        }
1153    }
1154
1155    fn record_type(&self) -> RecordType {
1156        RecordType::SVCB
1157    }
1158
1159    fn into_rdata(self) -> RData {
1160        RData::SVCB(self)
1161    }
1162}
1163
1164/// [RFC 9460 SVCB and HTTPS Resource Records, Nov 2023](https://datatracker.ietf.org/doc/html/rfc9460#section-10.4)
1165///
1166/// ```text
1167/// simple.example. 7200 IN HTTPS 1 . alpn=h3
1168/// pool  7200 IN HTTPS 1 h3pool alpn=h2,h3 ech="123..."
1169///               HTTPS 2 .      alpn=h2 ech="abc..."
1170/// @     7200 IN HTTPS 0 www
1171/// _8765._baz.api.example.com. 7200 IN SVCB 0 svc4-baz.example.net.
1172/// ```
1173impl fmt::Display for SVCB {
1174    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1175        write!(
1176            f,
1177            "{svc_priority} {target_name}",
1178            svc_priority = self.svc_priority,
1179            target_name = self.target_name,
1180        )?;
1181
1182        for (key, param) in self.svc_params.iter() {
1183            write!(f, " {key}={param}")?
1184        }
1185
1186        Ok(())
1187    }
1188}
1189
1190#[cfg(test)]
1191mod tests {
1192    use alloc::string::ToString;
1193
1194    use super::*;
1195
1196    #[test]
1197    fn read_svcb_key() {
1198        assert_eq!(SvcParamKey::Mandatory, 0.into());
1199        assert_eq!(SvcParamKey::Alpn, 1.into());
1200        assert_eq!(SvcParamKey::NoDefaultAlpn, 2.into());
1201        assert_eq!(SvcParamKey::Port, 3.into());
1202        assert_eq!(SvcParamKey::Ipv4Hint, 4.into());
1203        assert_eq!(SvcParamKey::EchConfigList, 5.into());
1204        assert_eq!(SvcParamKey::Ipv6Hint, 6.into());
1205        assert_eq!(SvcParamKey::Key(65280), 65280.into());
1206        assert_eq!(SvcParamKey::Key(65534), 65534.into());
1207        assert_eq!(SvcParamKey::Key65535, 65535.into());
1208        assert_eq!(SvcParamKey::Unknown(65279), 65279.into());
1209    }
1210
1211    #[test]
1212    fn read_svcb_key_to_u16() {
1213        assert_eq!(u16::from(SvcParamKey::Mandatory), 0);
1214        assert_eq!(u16::from(SvcParamKey::Alpn), 1);
1215        assert_eq!(u16::from(SvcParamKey::NoDefaultAlpn), 2);
1216        assert_eq!(u16::from(SvcParamKey::Port), 3);
1217        assert_eq!(u16::from(SvcParamKey::Ipv4Hint), 4);
1218        assert_eq!(u16::from(SvcParamKey::EchConfigList), 5);
1219        assert_eq!(u16::from(SvcParamKey::Ipv6Hint), 6);
1220        assert_eq!(u16::from(SvcParamKey::Key(65280)), 65280);
1221        assert_eq!(u16::from(SvcParamKey::Key(65534)), 65534);
1222        assert_eq!(u16::from(SvcParamKey::Key65535), 65535);
1223        assert_eq!(u16::from(SvcParamKey::Unknown(65279)), 65279);
1224    }
1225
1226    #[track_caller]
1227    fn test_encode_decode(rdata: SVCB) {
1228        let mut bytes = Vec::new();
1229        let mut encoder = BinEncoder::new(&mut bytes);
1230        rdata.emit(&mut encoder).expect("failed to emit SVCB");
1231        let bytes = encoder.into_bytes();
1232
1233        let mut decoder = BinDecoder::new(bytes);
1234        let read_rdata = SVCB::read_data(&mut decoder, Restrict::new(bytes.len() as u16))
1235            .expect("failed to read back");
1236        assert_eq!(rdata, read_rdata);
1237    }
1238
1239    #[test]
1240    fn test_encode_decode_svcb() {
1241        test_encode_decode(SVCB::new(
1242            0,
1243            Name::from_utf8("www.example.com.").unwrap(),
1244            vec![],
1245        ));
1246        test_encode_decode(SVCB::new(
1247            0,
1248            Name::from_utf8(".").unwrap(),
1249            vec![(
1250                SvcParamKey::Alpn,
1251                SvcParamValue::Alpn(Alpn(vec!["h2".to_string()])),
1252            )],
1253        ));
1254        test_encode_decode(SVCB::new(
1255            0,
1256            Name::from_utf8("example.com.").unwrap(),
1257            vec![
1258                (
1259                    SvcParamKey::Mandatory,
1260                    SvcParamValue::Mandatory(Mandatory(vec![SvcParamKey::Alpn])),
1261                ),
1262                (
1263                    SvcParamKey::Alpn,
1264                    SvcParamValue::Alpn(Alpn(vec!["h2".to_string()])),
1265                ),
1266            ],
1267        ));
1268    }
1269
1270    #[test]
1271    #[should_panic]
1272    fn test_encode_decode_svcb_bad_order() {
1273        test_encode_decode(SVCB::new(
1274            0,
1275            Name::from_utf8(".").unwrap(),
1276            vec![
1277                (
1278                    SvcParamKey::Alpn,
1279                    SvcParamValue::Alpn(Alpn(vec!["h2".to_string()])),
1280                ),
1281                (
1282                    SvcParamKey::Mandatory,
1283                    SvcParamValue::Mandatory(Mandatory(vec![SvcParamKey::Alpn])),
1284                ),
1285            ],
1286        ));
1287    }
1288
1289    #[test]
1290    fn test_no_panic() {
1291        const BUF: &[u8] = &[
1292            255, 121, 0, 0, 0, 0, 40, 255, 255, 160, 160, 0, 0, 0, 64, 0, 1, 255, 158, 0, 0, 0, 8,
1293            0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0,
1294        ];
1295        assert!(crate::op::Message::from_vec(BUF).is_err());
1296    }
1297
1298    #[test]
1299    fn test_unrestricted_output_size() {
1300        let svcb = SVCB::new(
1301            8224,
1302            Name::from_utf8(".").unwrap(),
1303            vec![(
1304                SvcParamKey::Unknown(8224),
1305                SvcParamValue::Unknown(Unknown(vec![32; 257])),
1306            )],
1307        );
1308
1309        let mut buf = Vec::new();
1310        let mut encoder = BinEncoder::new(&mut buf);
1311        svcb.emit(&mut encoder).unwrap();
1312    }
1313
1314    #[test]
1315    fn test_unknown_value_round_trip() {
1316        let svcb = SVCB::new(
1317            8224,
1318            Name::from_utf8(".").unwrap(),
1319            vec![(
1320                SvcParamKey::Unknown(8224),
1321                SvcParamValue::Unknown(Unknown(vec![32; 10])),
1322            )],
1323        );
1324
1325        let mut buf = Vec::new();
1326        let mut encoder = BinEncoder::new(&mut buf);
1327        svcb.emit(&mut encoder).unwrap();
1328
1329        let mut decoder = BinDecoder::new(&buf);
1330        let decoded = SVCB::read_data(
1331            &mut decoder,
1332            Restrict::new(u16::try_from(buf.len()).unwrap()),
1333        )
1334        .unwrap();
1335
1336        assert_eq!(svcb, decoded);
1337    }
1338}