Skip to main content

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::{
12    string::{String, ToString},
13    vec::Vec,
14};
15use core::{
16    cmp::{Ord, Ordering, PartialOrd},
17    convert::TryFrom,
18    fmt,
19    str::FromStr,
20};
21
22#[cfg(feature = "serde")]
23use serde::{Deserialize, Serialize};
24
25use crate::{
26    error::{ProtoError, ProtoResult},
27    rr::{
28        Name, RData, RecordData, RecordDataDecodable, RecordType,
29        rdata::{A, AAAA},
30    },
31    serialize::{
32        binary::{
33            BinDecodable, BinDecoder, BinEncodable, BinEncoder, DecodeError, RDataEncoding,
34            Restrict, RestrictedMath,
35        },
36        txt::{Lexer, ParseError, Token},
37    },
38};
39
40///  [RFC 9460 SVCB and HTTPS Resource Records, Nov 2023](https://datatracker.ietf.org/doc/html/rfc9460#section-2.2)
41///
42/// ```text
43/// 2.2.  RDATA wire format
44///
45///   The RDATA for the SVCB RR consists of:
46///
47///   *  a 2 octet field for SvcPriority as an integer in network byte
48///      order.
49///   *  the uncompressed, fully-qualified TargetName, represented as a
50///      sequence of length-prefixed labels as in Section 3.1 of [RFC1035].
51///   *  the SvcParams, consuming the remainder of the record (so smaller
52///      than 65535 octets and constrained by the RDATA and DNS message
53///      sizes).
54///
55///   When the list of SvcParams is non-empty (ServiceMode), it contains a
56///   series of SvcParamKey=SvcParamValue pairs, represented as:
57///
58///   *  a 2 octet field containing the SvcParamKey as an integer in
59///      network byte order.  (See Section 14.3.2 for the defined values.)
60///   *  a 2 octet field containing the length of the SvcParamValue as an
61///      integer between 0 and 65535 in network byte order
62///   *  an octet string of this length whose contents are the SvcParamValue
63///      in a format determined by the SvcParamKey
64///
65///   SvcParamKeys SHALL appear in increasing numeric order.
66///
67///   Clients MUST consider an RR malformed if:
68///
69///   *  the end of the RDATA occurs within a SvcParam.
70///   *  SvcParamKeys are not in strictly increasing numeric order.
71///   *  the SvcParamValue for an SvcParamKey does not have the expected
72///      format.
73///
74///   Note that the second condition implies that there are no duplicate
75///   SvcParamKeys.
76///
77///   If any RRs are malformed, the client MUST reject the entire RRSet and
78///   fall back to non-SVCB connection establishment.
79/// ```
80#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
81#[derive(Debug, PartialEq, Eq, Hash, Clone)]
82#[non_exhaustive]
83pub struct SVCB {
84    ///  [RFC 9460 SVCB and HTTPS Resource Records, Nov 2023](https://datatracker.ietf.org/doc/html/rfc9460#section-2.4.1)
85    /// ```text
86    /// 2.4.1.  SvcPriority
87    ///
88    ///   When SvcPriority is 0 the SVCB record is in AliasMode
89    ///   (Section 2.4.2).  Otherwise, it is in ServiceMode (Section 2.4.3).
90    ///
91    ///   Within a SVCB RRSet, all RRs SHOULD have the same Mode.  If an RRSet
92    ///   contains a record in AliasMode, the recipient MUST ignore any
93    ///   ServiceMode records in the set.
94    ///
95    ///   RRSets are explicitly unordered collections, so the SvcPriority field
96    ///   is used to impose an ordering on SVCB RRs.  A smaller SvcPriority indicates
97    ///   that the domain owner recommends the use of this record over ServiceMode
98    ///   RRs with a larger SvcPriority value.
99    ///
100    ///   When receiving an RRSet containing multiple SVCB records with the
101    ///   same SvcPriority value, clients SHOULD apply a random shuffle within
102    ///   a priority level to the records before using them, to ensure uniform
103    ///   load-balancing.
104    /// ```
105    pub svc_priority: u16,
106
107    ///  [RFC 9460 SVCB and HTTPS Resource Records, Nov 2023](https://datatracker.ietf.org/doc/html/rfc9460#section-2.5)
108    /// ```text
109    /// 2.5.  Special handling of "." in TargetName
110    ///
111    ///   If TargetName has the value "." (represented in the wire format as a
112    ///    zero-length label), special rules apply.
113    ///
114    /// 2.5.1.  AliasMode
115    ///
116    ///    For AliasMode SVCB RRs, a TargetName of "." indicates that the
117    ///    service is not available or does not exist.  This indication is
118    ///    advisory: clients encountering this indication MAY ignore it and
119    ///    attempt to connect without the use of SVCB.
120    ///
121    /// 2.5.2.  ServiceMode
122    ///
123    ///    For ServiceMode SVCB RRs, if TargetName has the value ".", then the
124    ///    owner name of this record MUST be used as the effective TargetName.
125    ///    If the record has a wildcard owner name in the zone file, the recipient
126    ///    SHALL use the response's synthesized owner name as the effective TargetName.
127    ///
128    ///    Here, for example, "svc2.example.net" is the effective TargetName:
129    ///
130    ///    example.com.      7200  IN HTTPS 0 svc.example.net.
131    ///    svc.example.net.  7200  IN CNAME svc2.example.net.
132    ///    svc2.example.net. 7200  IN HTTPS 1 . port=8002
133    ///    svc2.example.net. 300   IN A     192.0.2.2
134    ///    svc2.example.net. 300   IN AAAA  2001:db8::2
135    /// ```
136    pub target_name: Name,
137
138    /// See [`SvcParamKey`] for details on each parameter
139    pub svc_params: Vec<(SvcParamKey, SvcParamValue)>,
140}
141
142impl SVCB {
143    /// Create a new SVCB record from parts
144    ///
145    /// It is up to the caller to validate the data going into the record
146    pub fn new(
147        svc_priority: u16,
148        target_name: Name,
149        svc_params: Vec<(SvcParamKey, SvcParamValue)>,
150    ) -> Self {
151        Self {
152            svc_priority,
153            target_name,
154            svc_params,
155        }
156    }
157
158    /// [RFC 9460 SVCB and HTTPS Resource Records, Nov 2023](https://datatracker.ietf.org/doc/html/rfc9460#section-2.1)
159    ///
160    /// ```text
161    /// 2.1.  Zone file presentation format
162    ///
163    ///   The presentation format <RDATA> of the record ([RFC1035]) has the form:
164    ///
165    ///   SvcPriority TargetName SvcParams
166    ///
167    ///   The SVCB record is defined specifically within the Internet ("IN")
168    ///   Class ([RFC1035]).
169    ///
170    ///   SvcPriority is a number in the range 0-65535, TargetName is a
171    ///   <domain-name> ([RFC1035], Section 5.1), and the SvcParams are
172    ///   a whitespace-separated list, with each SvcParam consisting of a
173    ///   SvcParamKey=SvcParamValue pair or a standalone SvcParamKey.
174    ///   SvcParamKeys are registered by IANA  (Section 14.3).
175    ///
176    ///   Each SvcParamKey SHALL appear at most once in the SvcParams.  In
177    ///   presentation format, SvcParamKeys are lowercase alphanumeric
178    ///   strings.  Key names should contain 1-63 characters from the ranges
179    ///   "a"-"z", "0"-"9", and "-".  In ABNF [RFC5234],
180    ///
181    ///   alpha-lc      = %x61-7A   ;  a-z
182    ///   SvcParamKey   = 1*63(alpha-lc / DIGIT / "-")
183    ///   SvcParam      = SvcParamKey ["=" SvcParamValue]
184    ///   SvcParamValue = char-string ; See Appendix A.
185    ///   value         = *OCTET ; Value before key-specific parsing
186    ///
187    ///   The SvcParamValue is parsed using the character-string decoding
188    ///   algorithm (Appendix A), producing a value.  The value is then
189    ///   validated and converted into wire-format in a manner specific to each
190    ///   key.
191    ///
192    ///   When the optional "=" and SvcParamValue are omitted, the value is
193    ///   interpreted as empty.
194    ///
195    ///   Arbitrary keys can be represented using the unknown-key presentation
196    ///   format "keyNNNNN" where NNNNN is the numeric value of the key type
197    ///   without leading zeros. A SvcParam in this form SHALL be parsed as
198    ///   specified above, and the decoded value SHALL be used as its wire-format
199    ///   encoding.
200    ///
201    ///   For some SvcParamKeys, the value corresponds to a list or set of
202    ///   items.  Presentation formats for such keys SHOULD use a comma-
203    ///   separated list (Appendix A.1).
204    ///
205    ///   SvcParams in presentation format MAY appear in any order, but keys
206    ///   MUST NOT be repeated.
207    /// ```
208    pub(crate) fn from_tokens<'i, I: Iterator<Item = &'i str>>(
209        mut tokens: I,
210    ) -> Result<Self, ParseError> {
211        // SvcPriority
212        let svc_priority: u16 = tokens
213            .next()
214            .ok_or_else(|| ParseError::MissingToken("SvcPriority".to_string()))
215            .and_then(|s| s.parse().map_err(Into::into))?;
216
217        // svcb target
218        let target_name: Name = tokens
219            .next()
220            .ok_or_else(|| ParseError::MissingToken("Target".to_string()))
221            .and_then(|s| Name::from_str(s).map_err(ParseError::from))?;
222
223        // Loop over all of the service parameters
224        let mut svc_params = Vec::new();
225        for token in tokens {
226            // first need to split the key and (optional) value
227            let mut key_value = token.splitn(2, '=');
228            let key = key_value
229                .next()
230                .ok_or_else(|| ParseError::MissingToken("SVCB SvcbParams missing".to_string()))?;
231
232            // get the value, and remove any quotes
233            let mut value = key_value.next();
234            if let Some(value) = value.as_mut() {
235                if *value == "\"" {
236                    return Err(ParseError::Message(
237                        "SVCB SvcbParams cannot be a single quote",
238                    ));
239                }
240
241                if value.starts_with('"') && value.ends_with('"') {
242                    *value = &value[1..value.len() - 1];
243                }
244            }
245            svc_params.push(into_svc_param(key, value)?);
246        }
247
248        Ok(SVCB::new(svc_priority, target_name, svc_params))
249    }
250}
251
252// first take the param and convert to
253fn into_svc_param(
254    key: &str,
255    value: Option<&str>,
256) -> Result<(SvcParamKey, SvcParamValue), ParseError> {
257    let key = SvcParamKey::from_str(key)?;
258    let value = parse_value(key, value)?;
259
260    Ok((key, value))
261}
262
263fn parse_value(key: SvcParamKey, value: Option<&str>) -> Result<SvcParamValue, ParseError> {
264    match key {
265        SvcParamKey::Mandatory => parse_mandatory(value),
266        SvcParamKey::Alpn => parse_alpn(value),
267        SvcParamKey::NoDefaultAlpn => parse_no_default_alpn(value),
268        SvcParamKey::Port => parse_port(value),
269        SvcParamKey::Ipv4Hint => parse_ipv4_hint(value),
270        SvcParamKey::Ipv6Hint => parse_ipv6_hint(value),
271        SvcParamKey::EchConfigList => parse_ech_config(value),
272        SvcParamKey::Key(_) => parse_unknown(value),
273        SvcParamKey::Key65535 | SvcParamKey::Unknown(_) => Err(ParseError::Message(
274            "Bad Key type or unsupported, see generic key option, e.g. key1234",
275        )),
276    }
277}
278
279fn parse_char_data(value: &str) -> Result<String, ParseError> {
280    let mut lex = Lexer::new(value);
281    let ch_data = lex
282        .next_token()?
283        .ok_or(ParseError::Message("expected character data"))?;
284
285    match ch_data {
286        Token::CharData(data) => Ok(data),
287        _ => Err(ParseError::Message("expected character data")),
288    }
289}
290
291/// [RFC 9460 SVCB and HTTPS Resource Records, Nov 2023](https://datatracker.ietf.org/doc/html/rfc9460#section-8)
292///
293/// ```text
294///   The presentation value SHALL be a comma-separated list
295///   (Appendix A.1) of one or more valid SvcParamKeys, either by their
296///   registered name or in the unknown-key format (Section 2.1).  Keys MAY
297///   appear in any order, but MUST NOT appear more than once.  For self-
298///   consistency (Section 2.4.3), listed keys MUST also appear in the
299///   SvcParams.
300///
301///   To enable simpler parsing, this SvcParamValue MUST NOT contain escape
302///   sequences.
303///
304///   For example, the following is a valid list of SvcParams:
305///
306///   ipv6hint=... key65333=ex1 key65444=ex2 mandatory=key65444,ipv6hint
307/// ```
308///
309/// Currently this does not validate that the mandatory section matches the other keys
310fn parse_mandatory(value: Option<&str>) -> Result<SvcParamValue, ParseError> {
311    let value = value.ok_or(ParseError::Message("expected at least one Mandatory field"))?;
312
313    let mandatories = parse_list::<SvcParamKey>(value)?;
314    Ok(SvcParamValue::Mandatory(Mandatory(mandatories)))
315}
316
317/// [RFC 9460 SVCB and HTTPS Resource Records, Nov 2023](https://datatracker.ietf.org/doc/html/rfc9460#section-7.1.1)
318///
319/// ```text
320///   ALPNs are identified by their registered "Identification Sequence"
321///   (alpn-id), which is a sequence of 1-255 octets.
322///
323///   alpn-id = 1*255OCTET
324///
325///   For "alpn", the presentation value SHALL be a comma-separated list
326///   (Appendix A.1) of one or more alpn-ids.
327/// ```
328///
329/// This does not currently check to see if the ALPN code is legitimate
330fn parse_alpn(value: Option<&str>) -> Result<SvcParamValue, ParseError> {
331    let value = value.ok_or(ParseError::Message("expected at least one ALPN code"))?;
332
333    let alpns = parse_list::<String>(value)?;
334    Ok(SvcParamValue::Alpn(Alpn(alpns)))
335}
336
337/// [RFC 9460 SVCB and HTTPS Resource Records, Nov 2023](https://datatracker.ietf.org/doc/html/rfc9460#section-7.1.1)
338///
339/// ```text
340///   For "no-default-alpn", the presentation and wire format values MUST
341///   be empty.  When "no-default-alpn" is specified in an RR, "alpn" must
342///   also be specified in order for the RR to be "self-consistent"
343///   (Section 2.4.3).
344/// ```
345fn parse_no_default_alpn(value: Option<&str>) -> Result<SvcParamValue, ParseError> {
346    if value.is_some() {
347        return Err(ParseError::Message("no value expected for NoDefaultAlpn"));
348    }
349
350    Ok(SvcParamValue::NoDefaultAlpn)
351}
352
353/// [RFC 9460 SVCB and HTTPS Resource Records, Nov 2023](https://datatracker.ietf.org/doc/html/rfc9460#section-7.2)
354///
355/// ```text
356///   The presentation value of the SvcParamValue is a single decimal
357///   integer between 0 and 65535 in ASCII.  Any other value (e.g. an
358///   empty value) is a syntax error.  To enable simpler parsing, this
359///   SvcParam MUST NOT contain escape sequences.
360/// ```
361fn parse_port(value: Option<&str>) -> Result<SvcParamValue, ParseError> {
362    let value = value.ok_or(ParseError::Message("a port number for the port option"))?;
363
364    let value = parse_char_data(value)?;
365    let port = u16::from_str(&value)?;
366    Ok(SvcParamValue::Port(port))
367}
368
369/// [RFC 9460 SVCB and HTTPS Resource Records, Nov 2023](https://datatracker.ietf.org/doc/html/rfc9460#section-7.3)
370///
371/// ```text
372///   The presentation value SHALL be a comma-separated list
373///   (Appendix A.1) of one or more IP addresses of the appropriate family
374///   in standard textual format [RFC5952].  To enable simpler parsing,
375///   this SvcParamValue MUST NOT contain escape sequences.
376/// ```
377fn parse_ipv4_hint(value: Option<&str>) -> Result<SvcParamValue, ParseError> {
378    let value = value.ok_or(ParseError::Message("expected at least one ipv4 hint"))?;
379
380    let hints = parse_list::<A>(value)?;
381    Ok(SvcParamValue::Ipv4Hint(IpHint(hints)))
382}
383
384/// [RFC 9460 SVCB and HTTPS Resource Records, Nov 2023](https://datatracker.ietf.org/doc/html/rfc9460#section-7.3)
385///
386/// ```text
387///   The presentation value SHALL be a comma-separated list
388///   (Appendix A.1) of one or more IP addresses of the appropriate family
389///   in standard textual format [RFC5952].  To enable simpler parsing,
390///   this SvcParamValue MUST NOT contain escape sequences.
391/// ```
392fn parse_ipv6_hint(value: Option<&str>) -> Result<SvcParamValue, ParseError> {
393    let value = value.ok_or(ParseError::Message("expected at least one ipv6 hint"))?;
394
395    let hints = parse_list::<AAAA>(value)?;
396    Ok(SvcParamValue::Ipv6Hint(IpHint(hints)))
397}
398
399/// As the documentation states, the presentation format (what this function outputs) must be a BASE64 encoded string.
400///   hickory-dns will encode to BASE64 during formatting of the internal data, and output the BASE64 value.
401///
402/// [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)
403/// ```text
404///  In presentation format, the value is the ECHConfigList in Base 64 Encoding
405///  (Section 4 of [RFC4648]). Base 64 is used here to simplify integration with
406///  TLS server software. To enable simpler parsing, this SvcParam MUST NOT
407///  contain escape sequences.
408/// ```
409fn parse_ech_config(value: Option<&str>) -> Result<SvcParamValue, ParseError> {
410    let value = value.ok_or(ParseError::Message(
411        "expected a base64 encoded string for EchConfig",
412    ))?;
413
414    let value = parse_char_data(value)?;
415    let ech_config_bytes = data_encoding::BASE64.decode(value.as_bytes())?;
416    Ok(SvcParamValue::EchConfigList(EchConfigList(
417        ech_config_bytes,
418    )))
419}
420
421///  [RFC 9460 SVCB and HTTPS Resource Records, Nov 2023](https://datatracker.ietf.org/doc/html/rfc9460#section-2.1)
422///
423/// ```text
424///   Arbitrary keys can be represented using the unknown-key presentation
425///   format "keyNNNNN" where NNNNN is the numeric value of the key type
426///   without leading zeros. A SvcParam in this form SHALL be parsed as specified
427///   above, and the decoded value SHALL be used as its wire-format encoding.
428///
429///   For some SvcParamKeys, the value corresponds to a list or set of
430///   items.  Presentation formats for such keys SHOULD use a comma-
431///   separated list (Appendix A.1).
432///
433///   SvcParams in presentation format MAY appear in any order, but keys
434///   MUST NOT be repeated.
435/// ```
436fn parse_unknown(value: Option<&str>) -> Result<SvcParamValue, ParseError> {
437    let unknown: Vec<u8> = if let Some(value) = value {
438        value.as_bytes().to_vec()
439    } else {
440        Vec::new()
441    };
442
443    Ok(SvcParamValue::Unknown(Unknown(unknown)))
444}
445
446fn parse_list<T>(value: &str) -> Result<Vec<T>, ParseError>
447where
448    T: FromStr,
449    T::Err: Into<ParseError>,
450{
451    let mut result = Vec::new();
452    let mut current_value = String::new();
453    let mut escaping = false;
454
455    for c in value.chars() {
456        match (c, escaping) {
457            // End of value
458            (',', false) => {
459                result.push(T::from_str(&parse_char_data(&current_value)?).map_err(Into::into)?);
460                current_value.clear()
461            }
462            // Start of escape sequence
463            ('\\', false) => escaping = true,
464            // Comma inside escape sequence
465            (',', true) => {
466                current_value.push(',');
467                escaping = false
468            }
469            // Regular character inside escape sequence
470            (_, true) => {
471                current_value.push(c);
472                escaping = false
473            }
474            // Regular character
475            (_, false) => current_value.push(c),
476        }
477    }
478
479    // Push the remaining value if there's any
480    if !current_value.is_empty() {
481        result.push(T::from_str(&parse_char_data(&current_value)?).map_err(Into::into)?);
482    }
483
484    Ok(result)
485}
486
487///  [RFC 9460 SVCB and HTTPS Resource Records, Nov 2023](https://datatracker.ietf.org/doc/html/rfc9460#section-14.3.2)
488///
489/// ```text
490/// 14.3.2.  Initial Contents
491///
492///    The "Service Parameter Keys (SvcParamKeys)" registry has been
493///    populated with the following initial registrations:
494///
495///    +===========+=================+================+=========+==========+
496///    |   Number  | Name            | Meaning        |Reference|Change    |
497///    |           |                 |                |         |Controller|
498///    +===========+=================+================+=========+==========+
499///    |     0     | mandatory       | Mandatory      |RFC 9460,|IETF      |
500///    |           |                 | keys in this   |Section 8|          |
501///    |           |                 | RR             |         |          |
502///    +-----------+-----------------+----------------+---------+----------+
503///    |     1     | alpn            | Additional     |RFC 9460,|IETF      |
504///    |           |                 | supported      |Section  |          |
505///    |           |                 | protocols      |7.1      |          |
506///    +-----------+-----------------+----------------+---------+----------+
507///    |     2     | no-default-alpn | No support     |RFC 9460,|IETF      |
508///    |           |                 | for default    |Section  |          |
509///    |           |                 | protocol       |7.1      |          |
510///    +-----------+-----------------+----------------+---------+----------+
511///    |     3     | port            | Port for       |RFC 9460,|IETF      |
512///    |           |                 | alternative    |Section  |          |
513///    |           |                 | endpoint       |7.2      |          |
514///    +-----------+-----------------+----------------+---------+----------+
515///    |     4     | ipv4hint        | IPv4 address   |RFC 9460,|IETF      |
516///    |           |                 | hints          |Section  |          |
517///    |           |                 |                |7.3      |          |
518///    +-----------+-----------------+----------------+---------+----------+
519///    |     5     | ech             | RESERVED       |N/A      |IETF      |
520///    |           |                 | (held for      |         |          |
521///    |           |                 | Encrypted      |         |          |
522///    |           |                 | ClientHello)   |         |          |
523///    +-----------+-----------------+----------------+---------+----------+
524///    |     6     | ipv6hint        | IPv6 address   |RFC 9460,|IETF      |
525///    |           |                 | hints          |Section  |          |
526///    |           |                 |                |7.3      |          |
527///    +-----------+-----------------+----------------+---------+----------+
528///    |65280-65534| N/A             | Reserved for   |RFC 9460 |IETF      |
529///    |           |                 | Private Use    |         |          |
530///    +-----------+-----------------+----------------+---------+----------+
531///    |   65535   | N/A             | Reserved       |RFC 9460 |IETF      |
532///    |           |                 | ("Invalid      |         |          |
533///    |           |                 | key")          |         |          |
534///    +-----------+-----------------+----------------+---------+----------+
535///
536/// parsing done via:
537///   *  a 2 octet field containing the SvcParamKey as an integer in
538///      network byte order.  (See Section 14.3.2 for the defined values.)
539/// ```
540#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
541#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
542pub enum SvcParamKey {
543    /// Mandatory keys in this RR
544    #[cfg_attr(feature = "serde", serde(rename = "mandatory"))]
545    Mandatory,
546    /// Additional supported protocols
547    #[cfg_attr(feature = "serde", serde(rename = "alpn"))]
548    Alpn,
549    /// No support for default protocol
550    #[cfg_attr(feature = "serde", serde(rename = "no-default-alpn"))]
551    NoDefaultAlpn,
552    /// Port for alternative endpoint
553    #[cfg_attr(feature = "serde", serde(rename = "port"))]
554    Port,
555    /// IPv4 address hints
556    #[cfg_attr(feature = "serde", serde(rename = "ipv4hint"))]
557    Ipv4Hint,
558    /// Encrypted Client Hello configuration list
559    #[cfg_attr(feature = "serde", serde(rename = "ech"))]
560    EchConfigList,
561    /// IPv6 address hints
562    #[cfg_attr(feature = "serde", serde(rename = "ipv6hint"))]
563    Ipv6Hint,
564    /// Private Use
565    Key(u16),
566    /// Reserved ("Invalid key")
567    Key65535,
568    /// Unknown
569    Unknown(u16),
570}
571
572impl From<u16> for SvcParamKey {
573    fn from(val: u16) -> Self {
574        match val {
575            0 => Self::Mandatory,
576            1 => Self::Alpn,
577            2 => Self::NoDefaultAlpn,
578            3 => Self::Port,
579            4 => Self::Ipv4Hint,
580            5 => Self::EchConfigList,
581            6 => Self::Ipv6Hint,
582            65280..=65534 => Self::Key(val),
583            65535 => Self::Key65535,
584            _ => Self::Unknown(val),
585        }
586    }
587}
588
589impl From<SvcParamKey> for u16 {
590    fn from(val: SvcParamKey) -> Self {
591        match val {
592            SvcParamKey::Mandatory => 0,
593            SvcParamKey::Alpn => 1,
594            SvcParamKey::NoDefaultAlpn => 2,
595            SvcParamKey::Port => 3,
596            SvcParamKey::Ipv4Hint => 4,
597            SvcParamKey::EchConfigList => 5,
598            SvcParamKey::Ipv6Hint => 6,
599            SvcParamKey::Key(val) => val,
600            SvcParamKey::Key65535 => 65535,
601            SvcParamKey::Unknown(val) => val,
602        }
603    }
604}
605
606impl<'r> BinDecodable<'r> for SvcParamKey {
607    // a 2 octet field containing the SvcParamKey as an integer in
608    //      network byte order.  (See Section 14.3.2 for the defined values.)
609    fn read(decoder: &mut BinDecoder<'r>) -> Result<Self, DecodeError> {
610        Ok(decoder.read_u16()?.unverified(/*any u16 is valid*/).into())
611    }
612}
613
614impl BinEncodable for SvcParamKey {
615    // a 2 octet field containing the SvcParamKey as an integer in
616    //      network byte order.  (See Section 14.3.2 for the defined values.)
617    fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
618        encoder.emit_u16((*self).into())
619    }
620}
621
622impl fmt::Display for SvcParamKey {
623    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
624        match self {
625            Self::Mandatory => f.write_str("mandatory")?,
626            Self::Alpn => f.write_str("alpn")?,
627            Self::NoDefaultAlpn => f.write_str("no-default-alpn")?,
628            Self::Port => f.write_str("port")?,
629            Self::Ipv4Hint => f.write_str("ipv4hint")?,
630            Self::EchConfigList => f.write_str("ech")?,
631            Self::Ipv6Hint => f.write_str("ipv6hint")?,
632            Self::Key(val) => write!(f, "key{val}")?,
633            Self::Key65535 => f.write_str("key65535")?,
634            Self::Unknown(val) => write!(f, "unknown{val}")?,
635        }
636
637        Ok(())
638    }
639}
640
641impl FromStr for SvcParamKey {
642    type Err = ProtoError;
643
644    fn from_str(s: &str) -> Result<Self, Self::Err> {
645        /// keys are in the format of key#, e.g. key12344, with a max value of u16
646        fn parse_unknown_key(key: &str) -> Result<SvcParamKey, ProtoError> {
647            let key_value = key.strip_prefix("key").ok_or_else(|| {
648                ProtoError::Msg(format!("bad formatted key ({key}), expected key1234"))
649            })?;
650
651            Ok(SvcParamKey::Key(u16::from_str(key_value)?))
652        }
653
654        let key = match s {
655            "mandatory" => Self::Mandatory,
656            "alpn" => Self::Alpn,
657            "no-default-alpn" => Self::NoDefaultAlpn,
658            "port" => Self::Port,
659            "ipv4hint" => Self::Ipv4Hint,
660            "ech" => Self::EchConfigList,
661            "ipv6hint" => Self::Ipv6Hint,
662            "key65535" => Self::Key65535,
663            _ => parse_unknown_key(s)?,
664        };
665
666        Ok(key)
667    }
668}
669
670impl Ord for SvcParamKey {
671    fn cmp(&self, other: &Self) -> Ordering {
672        u16::from(*self).cmp(&u16::from(*other))
673    }
674}
675
676impl PartialOrd for SvcParamKey {
677    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
678        Some(self.cmp(other))
679    }
680}
681
682/// Warning, it is currently up to users of this type to validate the data against that expected by the key
683///
684/// ```text
685///   *  a 2 octet field containing the length of the SvcParamValue as an
686///      integer between 0 and 65535 in network byte order (but constrained
687///      by the RDATA and DNS message sizes).
688///   *  an octet string of this length whose contents are in a format
689///      determined by the SvcParamKey.
690/// ```
691#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
692#[derive(Debug, PartialEq, Eq, Hash, Clone)]
693pub enum SvcParamValue {
694    ///    In a ServiceMode RR, a SvcParamKey is considered "mandatory" if the
695    ///    RR will not function correctly for clients that ignore this
696    ///    SvcParamKey.  Each SVCB protocol mapping SHOULD specify a set of keys
697    ///    that are "automatically mandatory", i.e. mandatory if they are
698    ///    present in an RR.  The SvcParamKey "mandatory" is used to indicate
699    ///    any mandatory keys for this RR, in addition to any automatically
700    ///    mandatory keys that are present.
701    ///
702    /// see `Mandatory`
703    #[cfg_attr(feature = "serde", serde(rename = "mandatory"))]
704    Mandatory(Mandatory),
705    ///  [RFC 9460 SVCB and HTTPS Resource Records, Nov 2023](https://datatracker.ietf.org/doc/html/rfc9460#section-7.1)
706    ///
707    /// ```text
708    ///    The "alpn" and "no-default-alpn" SvcParamKeys together indicate the
709    ///    set of Application Layer Protocol Negotiation (ALPN) protocol
710    ///    identifiers [Alpn] and associated transport protocols supported by
711    ///    this service endpoint (the "SVCB ALPN set").
712    /// ```
713    #[cfg_attr(feature = "serde", serde(rename = "alpn"))]
714    Alpn(Alpn),
715    /// For "no-default-alpn", the presentation and wire format values MUST
716    ///    be empty.
717    /// See also `Alpn`
718    #[cfg_attr(feature = "serde", serde(rename = "no-default-alpn"))]
719    NoDefaultAlpn,
720    ///  [RFC 9460 SVCB and HTTPS Resource Records, Nov 2023](https://datatracker.ietf.org/doc/html/rfc9460#section-7.2)
721    ///
722    /// ```text
723    ///    7.2.  "port"
724    ///
725    ///   The "port" SvcParamKey defines the TCP or UDP port that should be
726    ///   used to reach this alternative endpoint.  If this key is not present,
727    ///   clients SHALL use the authority endpoint's port number.
728    ///
729    ///   The presentation value of the SvcParamValue is a single decimal
730    ///   integer between 0 and 65535 in ASCII.  Any other value (e.g. an
731    ///   empty value) is a syntax error.  To enable simpler parsing, this
732    ///   SvcParam MUST NOT contain escape sequences.
733    ///
734    ///   The wire format of the SvcParamValue is the corresponding 2 octet
735    ///   numeric value in network byte order.
736    ///
737    ///   If a port-restricting firewall is in place between some client and
738    ///   the service endpoint, changing the port number might cause that
739    ///   client to lose access to the service, so operators should exercise
740    ///   caution when using this SvcParamKey to specify a non-default port.
741    /// ```
742    #[cfg_attr(feature = "serde", serde(rename = "port"))]
743    Port(u16),
744    ///  [RFC 9460 SVCB and HTTPS Resource Records, Nov 2023](https://datatracker.ietf.org/doc/html/rfc9460#section-7.2)
745    ///
746    ///   The "ipv4hint" and "ipv6hint" keys convey IP addresses that clients
747    ///   MAY use to reach the service.  If A and AAAA records for TargetName
748    ///   are locally available, the client SHOULD ignore these hints.
749    ///   Otherwise, clients SHOULD perform A and/or AAAA queries for
750    ///   TargetName as in Section 3, and clients SHOULD use the IP address in
751    ///   those responses for future connections.  Clients MAY opt to terminate
752    ///   any connections using the addresses in hints and instead switch to
753    ///   the addresses in response to the TargetName query.  Failure to use A
754    ///   and/or AAAA response addresses could negatively impact load balancing
755    ///   or other geo-aware features and thereby degrade client performance.
756    ///
757    /// see `IpHint`
758    #[cfg_attr(feature = "serde", serde(rename = "ipv4hint"))]
759    Ipv4Hint(IpHint<A>),
760    /// [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)
761    ///
762    /// ```text
763    /// 2.  "SvcParam for ECH configuration"
764    ///
765    ///   The "ech" SvcParamKey is defined for conveying the ECH configuration
766    ///   of an alternative endpoint. It is applicable to all TLS-based protocols
767    ///   (including DTLS [RFC9147] and QUIC version 1 [RFC9001]) unless otherwise
768    ///   specified.
769    /// ```
770    #[cfg_attr(feature = "serde", serde(rename = "ech"))]
771    EchConfigList(EchConfigList),
772    /// See `IpHint`
773    #[cfg_attr(feature = "serde", serde(rename = "ipv6hint"))]
774    Ipv6Hint(IpHint<AAAA>),
775    /// Unparsed network data. Refer to documents on the associated key value
776    ///
777    /// This will be left as is when read off the wire, and encoded in bas64
778    ///    for presentation.
779    Unknown(Unknown),
780}
781
782impl SvcParamValue {
783    // a 2 octet field containing the length of the SvcParamValue as an
784    //      integer between 0 and 65535 in network byte order (but constrained
785    //      by the RDATA and DNS message sizes).
786    fn read(key: SvcParamKey, decoder: &mut BinDecoder<'_>) -> Result<Self, DecodeError> {
787        let len: usize = decoder
788            .read_u16()?
789            .verify_unwrap(|len| *len as usize <= decoder.len())
790            .map(|len| len as usize)
791            .map_err(|u| DecodeError::IncorrectRDataLengthRead {
792                read: decoder.len(),
793                len: u as usize,
794            })?;
795
796        let param_data = decoder.read_slice(len)?.unverified(/*verification to be done by individual param types*/);
797        let mut decoder = BinDecoder::new(param_data);
798
799        let value = match key {
800            SvcParamKey::Mandatory => Self::Mandatory(Mandatory::read(&mut decoder)?),
801            SvcParamKey::Alpn => Self::Alpn(Alpn::read(&mut decoder)?),
802            // should always be empty
803            SvcParamKey::NoDefaultAlpn => {
804                if len > 0 {
805                    return Err(DecodeError::IncorrectRDataLengthRead { read: len, len: 0 });
806                }
807
808                Self::NoDefaultAlpn
809            }
810            // The wire format of the SvcParamValue is the corresponding 2 octet
811            // numeric value in network byte order.
812            SvcParamKey::Port => {
813                let port = decoder.read_u16()?.unverified(/*all values are legal ports*/);
814                Self::Port(port)
815            }
816            SvcParamKey::Ipv4Hint => Self::Ipv4Hint(IpHint::<A>::read(&mut decoder)?),
817            SvcParamKey::EchConfigList => Self::EchConfigList(EchConfigList::read(&mut decoder)?),
818            SvcParamKey::Ipv6Hint => Self::Ipv6Hint(IpHint::<AAAA>::read(&mut decoder)?),
819            SvcParamKey::Key(_) | SvcParamKey::Key65535 | SvcParamKey::Unknown(_) => {
820                Self::Unknown(Unknown::read(&mut decoder)?)
821            }
822        };
823
824        Ok(value)
825    }
826}
827
828impl BinEncodable for SvcParamValue {
829    // a 2 octet field containing the length of the SvcParamValue as an
830    //      integer between 0 and 65535 in network byte order (but constrained
831    //      by the RDATA and DNS message sizes).
832    fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
833        // set the place for the length...
834        let place = encoder.place::<u16>()?;
835
836        match self {
837            Self::Mandatory(mandatory) => mandatory.emit(encoder)?,
838            Self::Alpn(alpn) => alpn.emit(encoder)?,
839            Self::NoDefaultAlpn => (),
840            Self::Port(port) => encoder.emit_u16(*port)?,
841            Self::Ipv4Hint(ip_hint) => ip_hint.emit(encoder)?,
842            Self::EchConfigList(ech_config) => ech_config.emit(encoder)?,
843            Self::Ipv6Hint(ip_hint) => ip_hint.emit(encoder)?,
844            Self::Unknown(unknown) => unknown.emit(encoder)?,
845        }
846
847        // go back and set the length
848        let len = u16::try_from(encoder.len_since_place(&place))
849            .map_err(|_| ProtoError::from("Total length of SvcParamValue exceeds u16::MAX"))?;
850        place.replace(encoder, len)?;
851
852        Ok(())
853    }
854}
855
856impl fmt::Display for SvcParamValue {
857    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
858        match self {
859            Self::Mandatory(mandatory) => write!(f, "{mandatory}")?,
860            Self::Alpn(alpn) => write!(f, "{alpn}")?,
861            Self::NoDefaultAlpn => (),
862            Self::Port(port) => write!(f, "{port}")?,
863            Self::Ipv4Hint(ip_hint) => write!(f, "{ip_hint}")?,
864            Self::EchConfigList(ech_config) => write!(f, "{ech_config}")?,
865            Self::Ipv6Hint(ip_hint) => write!(f, "{ip_hint}")?,
866            Self::Unknown(unknown) => write!(f, "{unknown}")?,
867        }
868
869        Ok(())
870    }
871}
872
873///  [RFC 9460 SVCB and HTTPS Resource Records, Nov 2023](https://datatracker.ietf.org/doc/html/rfc9460#section-8)
874///
875/// ```text
876/// 8.  ServiceMode RR compatibility and mandatory keys
877///
878///    In a ServiceMode RR, a SvcParamKey is considered "mandatory" if the
879///    RR will not function correctly for clients that ignore this
880///    SvcParamKey.  Each SVCB protocol mapping SHOULD specify a set of keys
881///    that are "automatically mandatory", i.e. mandatory if they are
882///    present in an RR.  The SvcParamKey "mandatory" is used to indicate
883///    any mandatory keys for this RR, in addition to any automatically
884///    mandatory keys that are present.
885///
886///    A ServiceMode RR is considered "compatible" with a client if the
887///    client recognizes all the mandatory keys, and their values indicate
888///    that successful connection establishment is possible. Incompatible RRs
889///    are ignored (see step 5 of the procedure defined in Section 3)
890///
891///    The presentation value SHALL be a comma-separated list
892///    (Appendix A.1) of one or more valid SvcParamKeys, either by their
893///    registered name or in the unknown-key format (Section 2.1).  Keys MAY
894///    appear in any order, but MUST NOT appear more than once.  For self-
895///    consistency (Section 2.4.3), listed keys MUST also appear in the
896///    SvcParams.
897///
898///    To enable simpler parsing, this SvcParamValue MUST NOT contain escape
899///    sequences.
900///
901///    For example, the following is a valid list of SvcParams:
902///
903///    ipv6hint=... key65333=ex1 key65444=ex2 mandatory=key65444,ipv6hint
904///
905///    In wire format, the keys are represented by their numeric values in
906///    network byte order, concatenated in strictly increasing numeric order.
907///
908///    This SvcParamKey is always automatically mandatory, and MUST NOT
909///    appear in its own value-list.  Other automatically mandatory keys
910///    SHOULD NOT appear in the list either.  (Including them wastes space
911///    and otherwise has no effect.)
912/// ```
913#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
914#[derive(Debug, PartialEq, Eq, Hash, Clone)]
915#[repr(transparent)]
916pub struct Mandatory(pub Vec<SvcParamKey>);
917
918impl<'r> BinDecodable<'r> for Mandatory {
919    /// This expects the decoder to be limited to only this field, i.e. the end of input for the decoder
920    ///   is the end of input for the fields
921    ///
922    /// ```text
923    ///    In wire format, the keys are represented by their numeric values in
924    ///    network byte order, concatenated in strictly increasing numeric order.
925    /// ```
926    fn read(decoder: &mut BinDecoder<'r>) -> Result<Self, DecodeError> {
927        let mut keys = Vec::with_capacity(1);
928
929        while decoder.peek().is_some() {
930            keys.push(SvcParamKey::read(decoder)?);
931        }
932
933        if keys.is_empty() {
934            return Err(DecodeError::SvcParamMissingValue);
935        }
936
937        Ok(Self(keys))
938    }
939}
940
941impl BinEncodable for Mandatory {
942    /// This expects the decoder to be limited to only this field, i.e. the end of input for the decoder
943    ///   is the end of input for the fields
944    ///
945    /// ```text
946    ///    In wire format, the keys are represented by their numeric values in
947    ///    network byte order, concatenated in strictly increasing numeric order.
948    /// ```
949    fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
950        if self.0.is_empty() {
951            return Err(ProtoError::from("Alpn expects at least one value"));
952        }
953
954        // TODO: order by key value
955        for key in self.0.iter() {
956            key.emit(encoder)?
957        }
958
959        Ok(())
960    }
961}
962
963impl fmt::Display for Mandatory {
964    ///  [RFC 9460 SVCB and HTTPS Resource Records, Nov 2023](https://datatracker.ietf.org/doc/html/rfc9460#section-8)
965    ///
966    ///    The presentation value SHALL be a comma-separated list
967    ///    (Appendix A.1) of one or more valid SvcParamKeys, either by their
968    ///    registered name or in the unknown-key format (Section 2.1).  Keys MAY
969    ///    appear in any order, but MUST NOT appear more than once.  For self-
970    ///    consistency (Section 2.4.3), listed keys MUST also appear in the
971    ///    SvcParams.
972    ///
973    ///    To enable simpler parsing, this SvcParamValue MUST NOT contain escape
974    ///    sequences.
975    ///
976    ///    For example, the following is a valid list of SvcParams:
977    ///
978    ///    ipv6hint=... key65333=ex1 key65444=ex2 mandatory=key65444,ipv6hint
979    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
980        for key in self.0.iter() {
981            // TODO: confirm in the RFC that trailing commas are ok
982            write!(f, "{key},")?;
983        }
984
985        Ok(())
986    }
987}
988
989///  [RFC 9460 SVCB and HTTPS Resource Records, Nov 2023](https://datatracker.ietf.org/doc/html/rfc9460#section-7.1)
990///
991/// ```text
992/// 7.1.  "alpn" and "no-default-alpn"
993///
994///   The "alpn" and "no-default-alpn" SvcParamKeys together indicate the
995///   set of Application-Layer Protocol Negotiation (ALPN) protocol
996///   identifiers [ALPN] and associated transport protocols supported by
997///   this service endpoint (the "SVCB ALPN set").
998///
999///   As with Alt-Svc [AltSvc], each ALPN protocol identifier is used to
1000///   identify the application protocol and associated suite of protocols
1001///   supported by the endpoint (the "protocol suite").  The presence of an
1002///   ALPN protocol identifier in the SVCB ALPN set indicates that this
1003///   service endpoint, described by TargetName and the other parameters
1004///   (e.g., "port"), offers service with the protocol suite associated
1005///   with this ALPN identifier.
1006///
1007///   Clients filter the set of ALPN identifiers to match the protocol
1008///   suites they support, and this informs the underlying transport
1009///   protocol used (such as QUIC over UDP or TLS over TCP).  ALPN protocol
1010///   identifiers that do not uniquely identify a protocol suite (e.g., an
1011///   Identification Sequence that can be used with both TLS and DTLS) are
1012///   not compatible with this SvcParamKey and MUST NOT be included in the
1013///   SVCB ALPN set.
1014///
1015/// 7.1.1.  Representation
1016///
1017///   ALPNs are identified by their registered "Identification Sequence"
1018///   (alpn-id), which is a sequence of 1-255 octets.
1019///
1020///   alpn-id = 1*255OCTET
1021///
1022///   For "alpn", the presentation value SHALL be a comma-separated list
1023///   (Appendix A.1) of one or more alpn-ids.  Zone-file implementations
1024///   MAY disallow the "," and "\" characters in ALPN IDs instead of
1025///   implementing the value-list escaping procedure, relying on the opaque
1026///   key format (e.g., key1=\002h2) in the event that these characters are
1027///   needed.
1028///
1029///   The wire-format value for "alpn" consists of at least one alpn-id
1030///   prefixed by its length as a single octet, and these length-value
1031///   pairs are concatenated to form the SvcParamValue.  These pairs MUST
1032///   exactly fill the SvcParamValue; otherwise, the SvcParamValue is
1033///   malformed.
1034///
1035///   For "no-default-alpn", the presentation and wire-format values MUST
1036///   be empty.  When "no-default-alpn" is specified in an RR, "alpn" must
1037///   also be specified in order for the RR to be "self-consistent"
1038///   (Section 2.4.3).
1039///
1040///   Each scheme that uses this SvcParamKey defines a "default set" of
1041///   ALPN IDs that are supported by nearly all clients and servers; this
1042///   set MAY be empty.  To determine the SVCB ALPN set, the client starts
1043///   with the list of alpn-ids from the "alpn" SvcParamKey, and it adds
1044///   the default set unless the "no-default-alpn" SvcParamKey is present.
1045///
1046/// 7.1.2.  Use
1047///
1048///   To establish a connection to the endpoint, clients MUST
1049///
1050///   1.  Let SVCB-ALPN-Intersection be the set of protocols in the SVCB
1051///       ALPN set that the client supports.
1052///
1053///   2.  Let Intersection-Transports be the set of transports (e.g., TLS,
1054///       DTLS, QUIC) implied by the protocols in SVCB-ALPN-Intersection.
1055///
1056///   3.  For each transport in Intersection-Transports, construct a
1057///       ProtocolNameList containing the Identification Sequences of all
1058///       the client's supported ALPN protocols for that transport, without
1059///       regard to the SVCB ALPN set.
1060///
1061///   For example, if the SVCB ALPN set is ["http/1.1", "h3"] and the
1062///   client supports HTTP/1.1, HTTP/2, and HTTP/3, the client could
1063///   attempt to connect using TLS over TCP with a ProtocolNameList of
1064///   ["http/1.1", "h2"] and could also attempt a connection using QUIC
1065///   with a ProtocolNameList of ["h3"].
1066///
1067///   Once the client has constructed a ClientHello, protocol negotiation
1068///   in that handshake proceeds as specified in [ALPN], without regard to
1069///   the SVCB ALPN set.
1070///
1071///   Clients MAY implement a fallback procedure, using a less-preferred
1072///   transport if more-preferred transports fail to connect.  This
1073///   fallback behavior is vulnerable to manipulation by a network attacker
1074///   who blocks the more-preferred transports, but it may be necessary for
1075///   compatibility with existing networks.
1076///
1077///   With this procedure in place, an attacker who can modify DNS and
1078///   network traffic can prevent a successful transport connection but
1079///   cannot otherwise interfere with ALPN protocol selection.  This
1080///   procedure also ensures that each ProtocolNameList includes at least
1081///   one protocol from the SVCB ALPN set.
1082///
1083///   Clients SHOULD NOT attempt connection to a service endpoint whose
1084///   SVCB ALPN set does not contain any supported protocols.
1085///
1086///   To ensure consistency of behavior, clients MAY reject the entire SVCB
1087///   RRset and fall back to basic connection establishment if all of the
1088///   compatible RRs indicate "no-default-alpn", even if connection could
1089///   have succeeded using a non-default ALPN protocol.
1090///
1091///   Zone operators SHOULD ensure that at least one RR in each RRset
1092///   supports the default transports.  This enables compatibility with the
1093///   greatest number of clients.
1094/// ```
1095#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
1096#[derive(Debug, PartialEq, Eq, Hash, Clone)]
1097#[repr(transparent)]
1098pub struct Alpn(pub Vec<String>);
1099
1100impl<'r> BinDecodable<'r> for Alpn {
1101    /// This expects the decoder to be limited to only this field, i.e. the end of input for the decoder
1102    ///   is the end of input for the fields
1103    ///
1104    /// ```text
1105    ///   The wire format value for "alpn" consists of at least one alpn-id
1106    ///   prefixed by its length as a single octet, and these length-value
1107    ///   pairs are concatenated to form the SvcParamValue.  These pairs MUST
1108    ///   exactly fill the SvcParamValue; otherwise, the SvcParamValue is
1109    ///   malformed.
1110    /// ```
1111    fn read(decoder: &mut BinDecoder<'r>) -> Result<Self, DecodeError> {
1112        let mut alpns = Vec::with_capacity(1);
1113
1114        while decoder.peek().is_some() {
1115            let alpn = decoder.read_character_data()?.unverified(/*will rely on string parser*/);
1116            let alpn = String::from_utf8(alpn.to_vec())?;
1117            alpns.push(alpn);
1118        }
1119
1120        if alpns.is_empty() {
1121            return Err(DecodeError::SvcParamMissingValue);
1122        }
1123
1124        Ok(Self(alpns))
1125    }
1126}
1127
1128impl BinEncodable for Alpn {
1129    ///   The wire format value for "alpn" consists of at least one alpn-id
1130    ///   prefixed by its length as a single octet, and these length-value
1131    ///   pairs are concatenated to form the SvcParamValue.  These pairs MUST
1132    ///   exactly fill the SvcParamValue; otherwise, the SvcParamValue is
1133    ///   malformed.
1134    fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
1135        if self.0.is_empty() {
1136            return Err(ProtoError::from("Alpn expects at least one value"));
1137        }
1138
1139        for alpn in self.0.iter() {
1140            encoder.emit_character_data(alpn)?
1141        }
1142
1143        Ok(())
1144    }
1145}
1146
1147impl fmt::Display for Alpn {
1148    ///   The presentation value SHALL be a comma-separated list
1149    ///   (Appendix A.1) of one or more "alpn-id"s.
1150    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1151        for alpn in self.0.iter() {
1152            // TODO: confirm in the RFC that trailing commas are ok
1153            write!(f, "{alpn},")?;
1154        }
1155
1156        Ok(())
1157    }
1158}
1159
1160/// [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)
1161///
1162/// ```text
1163/// 2.  "SvcParam for ECH configuration"
1164///
1165///   The "ech" SvcParamKey is defined for conveying the ECH configuration
1166///   of an alternative endpoint. It is applicable to all TLS-based protocols
1167///   (including DTLS [RFC9147] and QUIC version 1 [RFC9001]) unless
1168///   otherwise specified.
1169///
1170///   In wire format, the value of the parameter is an ECHConfigList (Section 4 of draft-ietf-tls-esni-18),
1171///   including the redundant length prefix. In presentation format, the value is the ECHConfigList
1172///   in Base 64 Encoding (Section 4 of [RFC4648]). Base 64 is used here to simplify integration
1173///   with TLS server software. To enable simpler parsing, this SvcParam MUST NOT contain escape
1174///   sequences.
1175/// ```
1176#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
1177#[derive(PartialEq, Eq, Hash, Clone)]
1178#[repr(transparent)]
1179pub struct EchConfigList(pub Vec<u8>);
1180
1181impl<'r> BinDecodable<'r> for EchConfigList {
1182    /// In wire format, the value of the parameter is an ECHConfigList (Section 4 of draft-ietf-tls-esni-18),
1183    /// including the redundant length prefix. In presentation format, the value is the
1184    /// ECHConfigList in Base 64 Encoding (Section 4 of RFC4648).
1185    /// Base 64 is used here to simplify integration with TLS server software.
1186    /// To enable simpler parsing, this SvcParam MUST NOT contain escape sequences.
1187    fn read(decoder: &mut BinDecoder<'r>) -> Result<Self, DecodeError> {
1188        let data =
1189            decoder.read_vec(decoder.len())?.unverified(/*up to consumer to validate this data*/);
1190
1191        Ok(Self(data))
1192    }
1193}
1194
1195impl BinEncodable for EchConfigList {
1196    /// In wire format, the value of the parameter is an ECHConfigList (Section 4 of draft-ietf-tls-esni-18),
1197    /// including the redundant length prefix. In presentation format, the value is the
1198    /// ECHConfigList in Base 64 Encoding (Section 4 of RFC4648).
1199    /// Base 64 is used here to simplify integration with TLS server software.
1200    /// To enable simpler parsing, this SvcParam MUST NOT contain escape sequences.
1201    fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
1202        encoder.emit_vec(&self.0)?;
1203
1204        Ok(())
1205    }
1206}
1207
1208impl fmt::Display for EchConfigList {
1209    /// As the documentation states, the presentation format (what this function outputs) must be a BASE64 encoded string.
1210    ///   hickory-dns will encode to BASE64 during formatting of the internal data, and output the BASE64 value.
1211    ///
1212    /// [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)
1213    /// ```text
1214    ///  In presentation format, the value is the ECHConfigList in Base 64 Encoding
1215    ///  (Section 4 of [RFC4648]). Base 64 is used here to simplify integration with
1216    ///  TLS server software. To enable simpler parsing, this SvcParam MUST NOT
1217    ///  contain escape sequences.
1218    /// ```
1219    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1220        write!(f, "\"{}\"", data_encoding::BASE64.encode(&self.0))
1221    }
1222}
1223
1224impl fmt::Debug for EchConfigList {
1225    /// The debug format for EchConfig will output the value in BASE64 like Display, but will the addition of the type-name.
1226    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1227        write!(
1228            f,
1229            "\"EchConfig ({})\"",
1230            data_encoding::BASE64.encode(&self.0)
1231        )
1232    }
1233}
1234
1235///  [RFC 9460 SVCB and HTTPS Resource Records, Nov 2023](https://datatracker.ietf.org/doc/html/rfc9460#section-7.3)
1236///
1237/// ```text
1238///    7.3.  "ipv4hint" and "ipv6hint"
1239///
1240///   The "ipv4hint" and "ipv6hint" keys convey IP addresses that clients
1241///   MAY use to reach the service.  If A and AAAA records for TargetName
1242///   are locally available, the client SHOULD ignore these hints.
1243///   Otherwise, clients SHOULD perform A and/or AAAA queries for
1244///   TargetName per Section 3, and clients SHOULD use the IP address in
1245///   those responses for future connections.  Clients MAY opt to terminate
1246///   any connections using the addresses in hints and instead switch to
1247///   the addresses in response to the TargetName query.  Failure to use A
1248///   and/or AAAA response addresses could negatively impact load balancing
1249///   or other geo-aware features and thereby degrade client performance.
1250///
1251///   The presentation value SHALL be a comma-separated list (Appendix A.1)
1252///   of one or more IP addresses of the appropriate family in standard
1253///   textual format [RFC5952] [RFC4001].  To enable simpler parsing, this
1254///   SvcParamValue MUST NOT contain escape sequences.
1255///
1256///   The wire format for each parameter is a sequence of IP addresses in
1257///   network byte order (for the respective address family).  Like an A or
1258///   AAAA RRset, the list of addresses represents an unordered collection,
1259///   and clients SHOULD pick addresses to use in a random order.  An empty
1260///   list of addresses is invalid.
1261///
1262///   When selecting between IPv4 and IPv6 addresses to use, clients may
1263///   use an approach such as Happy Eyeballs [HappyEyeballsV2].  When only
1264///   "ipv4hint" is present, NAT64 clients may synthesize IPv6 addresses as
1265///   specified in [RFC7050] or ignore the "ipv4hint" key and wait for AAAA
1266///   resolution (Section 3).  For best performance, server operators
1267///   SHOULD include an "ipv6hint" parameter whenever they include an
1268///   "ipv4hint" parameter.
1269///
1270///   These parameters are intended to minimize additional connection
1271///   latency when a recursive resolver is not compliant with the
1272///   requirements in Section 4 and SHOULD NOT be included if most clients
1273///   are using compliant recursive resolvers.  When TargetName is the
1274///   service name or the owner name (which can be written as "."), server
1275///   operators SHOULD NOT include these hints, because they are unlikely
1276///   to convey any performance benefit.
1277/// ```
1278#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
1279#[derive(Debug, PartialEq, Eq, Hash, Clone)]
1280#[repr(transparent)]
1281pub struct IpHint<T>(pub Vec<T>);
1282
1283impl<'r, T> BinDecodable<'r> for IpHint<T>
1284where
1285    T: BinDecodable<'r>,
1286{
1287    ///   The wire format for each parameter is a sequence of IP addresses in
1288    ///   network byte order (for the respective address family). Like an A or
1289    ///   AAAA RRSet, the list of addresses represents an unordered collection,
1290    ///   and clients SHOULD pick addresses to use in a random order.  An empty
1291    ///   list of addresses is invalid.
1292    fn read(decoder: &mut BinDecoder<'r>) -> Result<Self, DecodeError> {
1293        let mut ips = Vec::new();
1294
1295        while decoder.peek().is_some() {
1296            ips.push(T::read(decoder)?)
1297        }
1298
1299        Ok(Self(ips))
1300    }
1301}
1302
1303impl<T> BinEncodable for IpHint<T>
1304where
1305    T: BinEncodable,
1306{
1307    ///   The wire format for each parameter is a sequence of IP addresses in
1308    ///   network byte order (for the respective address family). Like an A or
1309    ///   AAAA RRSet, the list of addresses represents an unordered collection,
1310    ///   and clients SHOULD pick addresses to use in a random order.  An empty
1311    ///   list of addresses is invalid.
1312    fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
1313        for ip in self.0.iter() {
1314            ip.emit(encoder)?;
1315        }
1316
1317        Ok(())
1318    }
1319}
1320
1321impl<T> fmt::Display for IpHint<T>
1322where
1323    T: fmt::Display,
1324{
1325    ///   The presentation value SHALL be a comma-separated list
1326    ///   (Appendix A.1) of one or more IP addresses of the appropriate family
1327    ///   in standard textual format [RFC 5952](https://tools.ietf.org/html/rfc5952).  To enable simpler parsing,
1328    ///   this SvcParamValue MUST NOT contain escape sequences.
1329    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1330        for ip in self.0.iter() {
1331            write!(f, "{ip},")?;
1332        }
1333
1334        Ok(())
1335    }
1336}
1337
1338///  [RFC 9460 SVCB and HTTPS Resource Records, Nov 2023](https://datatracker.ietf.org/doc/html/rfc9460#section-2.1)
1339///
1340/// ```text
1341///   Arbitrary keys can be represented using the unknown-key presentation
1342///   format "keyNNNNN" where NNNNN is the numeric value of the key type
1343///   without leading zeros. A SvcParam in this form SHALL be parsed as specified
1344///   above, and the decoded value SHALL be used as its wire-format encoding.
1345///
1346///   For some SvcParamKeys, the value corresponds to a list or set of
1347///   items.  Presentation formats for such keys SHOULD use a comma-
1348///   separated list (Appendix A.1).
1349///
1350///   SvcParams in presentation format MAY appear in any order, but keys
1351///   MUST NOT be repeated.
1352/// ```
1353#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
1354#[derive(Debug, PartialEq, Eq, Hash, Clone)]
1355#[repr(transparent)]
1356pub struct Unknown(pub Vec<u8>);
1357
1358impl<'r> BinDecodable<'r> for Unknown {
1359    fn read(decoder: &mut BinDecoder<'r>) -> Result<Self, DecodeError> {
1360        // The passed slice is already length delimited, and we cannot
1361        // assume it's a collection of anything.
1362        let len = decoder.len();
1363
1364        let data = decoder.read_vec(len)?;
1365        let unknowns = data.unverified(/*any data is valid here*/).to_vec();
1366
1367        Ok(Self(unknowns))
1368    }
1369}
1370
1371impl BinEncodable for Unknown {
1372    fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
1373        encoder.emit_vec(&self.0)?;
1374
1375        Ok(())
1376    }
1377}
1378
1379impl fmt::Display for Unknown {
1380    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1381        // TODO: this needs to be properly encoded
1382        write!(f, "\"{}\",", String::from_utf8_lossy(&self.0))?;
1383
1384        Ok(())
1385    }
1386}
1387
1388impl BinEncodable for SVCB {
1389    fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
1390        let mut encoder = encoder.with_rdata_behavior(RDataEncoding::Other);
1391
1392        self.svc_priority.emit(&mut encoder)?;
1393        self.target_name.emit(&mut encoder)?;
1394
1395        let mut last_key: Option<SvcParamKey> = None;
1396        for (key, param) in self.svc_params.iter() {
1397            if let Some(last_key) = last_key {
1398                if key <= &last_key {
1399                    return Err(ProtoError::from("SvcParams out of order"));
1400                }
1401            }
1402
1403            key.emit(&mut encoder)?;
1404            param.emit(&mut encoder)?;
1405
1406            last_key = Some(*key);
1407        }
1408
1409        Ok(())
1410    }
1411}
1412
1413impl RecordDataDecodable<'_> for SVCB {
1414    /// Reads the SVCB record from the decoder.
1415    ///
1416    /// [RFC 9460 SVCB and HTTPS Resource Records, Nov 2023](https://datatracker.ietf.org/doc/html/rfc9460#section-2.2)
1417    ///
1418    /// ```text
1419    ///   Clients MUST consider an RR malformed if:
1420    ///
1421    ///   *  the end of the RDATA occurs within a SvcParam.
1422    ///   *  SvcParamKeys are not in strictly increasing numeric order.
1423    ///   *  the SvcParamValue for an SvcParamKey does not have the expected
1424    ///      format.
1425    ///
1426    ///   Note that the second condition implies that there are no duplicate
1427    ///   SvcParamKeys.
1428    ///
1429    ///   If any RRs are malformed, the client MUST reject the entire RRSet and
1430    ///   fall back to non-SVCB connection establishment.
1431    /// ```
1432    fn read_data(
1433        decoder: &mut BinDecoder<'_>,
1434        rdata_length: Restrict<u16>,
1435    ) -> Result<Self, DecodeError> {
1436        let start_index = decoder.index();
1437
1438        let svc_priority = decoder.read_u16()?.unverified(/*any u16 is valid*/);
1439        let target_name = Name::read(decoder)?;
1440
1441        let mut remainder_len = rdata_length
1442            .map(|len| len as usize)
1443            .checked_sub(decoder.index() - start_index)
1444            .map_err(|len| DecodeError::IncorrectRDataLengthRead {
1445                read: decoder.index() - start_index,
1446                len,
1447            })?
1448            .unverified(); // valid len
1449        let mut svc_params: Vec<(SvcParamKey, SvcParamValue)> = Vec::new();
1450
1451        // must have at least 4 bytes left for the key and the length
1452        while remainder_len >= 4 {
1453            // a 2 octet field containing the SvcParamKey as an integer in
1454            //      network byte order.  (See Section 14.3.2 for the defined values.)
1455            let key = SvcParamKey::read(decoder)?;
1456
1457            // a 2 octet field containing the length of the SvcParamValue as an
1458            //      integer between 0 and 65535 in network byte order (but constrained
1459            //      by the RDATA and DNS message sizes).
1460            let value = SvcParamValue::read(key, decoder)?;
1461
1462            if let Some(last_key) = svc_params.last().map(|(key, _)| key) {
1463                if last_key >= &key {
1464                    return Err(DecodeError::SvcParamsOutOfOrder);
1465                }
1466            }
1467
1468            svc_params.push((key, value));
1469            remainder_len = rdata_length
1470                .map(|len| len as usize)
1471                .checked_sub(decoder.index() - start_index)
1472                .map_err(|len| DecodeError::IncorrectRDataLengthRead {
1473                    read: decoder.index() - start_index,
1474                    len,
1475                })?
1476                .unverified(); // valid len
1477        }
1478
1479        Ok(Self {
1480            svc_priority,
1481            target_name,
1482            svc_params,
1483        })
1484    }
1485}
1486
1487impl RecordData for SVCB {
1488    fn try_borrow(data: &RData) -> Option<&Self> {
1489        match data {
1490            RData::SVCB(data) => Some(data),
1491            _ => None,
1492        }
1493    }
1494
1495    fn record_type(&self) -> RecordType {
1496        RecordType::SVCB
1497    }
1498
1499    fn into_rdata(self) -> RData {
1500        RData::SVCB(self)
1501    }
1502}
1503
1504/// [RFC 9460 SVCB and HTTPS Resource Records, Nov 2023](https://datatracker.ietf.org/doc/html/rfc9460#section-10.4)
1505///
1506/// ```text
1507/// simple.example. 7200 IN HTTPS 1 . alpn=h3
1508/// pool  7200 IN HTTPS 1 h3pool alpn=h2,h3 ech="123..."
1509///               HTTPS 2 .      alpn=h2 ech="abc..."
1510/// @     7200 IN HTTPS 0 www
1511/// _8765._baz.api.example.com. 7200 IN SVCB 0 svc4-baz.example.net.
1512/// ```
1513impl fmt::Display for SVCB {
1514    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1515        write!(
1516            f,
1517            "{svc_priority} {target_name}",
1518            svc_priority = self.svc_priority,
1519            target_name = self.target_name,
1520        )?;
1521
1522        for (key, param) in self.svc_params.iter() {
1523            write!(f, " {key}={param}")?
1524        }
1525
1526        Ok(())
1527    }
1528}
1529
1530#[cfg(test)]
1531mod tests {
1532    use alloc::{borrow::ToOwned, string::ToString};
1533
1534    use crate::{rr::rdata::HTTPS, serialize::txt::Parser};
1535
1536    use super::*;
1537
1538    #[test]
1539    fn read_svcb_key() {
1540        assert_eq!(SvcParamKey::Mandatory, 0.into());
1541        assert_eq!(SvcParamKey::Alpn, 1.into());
1542        assert_eq!(SvcParamKey::NoDefaultAlpn, 2.into());
1543        assert_eq!(SvcParamKey::Port, 3.into());
1544        assert_eq!(SvcParamKey::Ipv4Hint, 4.into());
1545        assert_eq!(SvcParamKey::EchConfigList, 5.into());
1546        assert_eq!(SvcParamKey::Ipv6Hint, 6.into());
1547        assert_eq!(SvcParamKey::Key(65280), 65280.into());
1548        assert_eq!(SvcParamKey::Key(65534), 65534.into());
1549        assert_eq!(SvcParamKey::Key65535, 65535.into());
1550        assert_eq!(SvcParamKey::Unknown(65279), 65279.into());
1551    }
1552
1553    #[test]
1554    fn read_svcb_key_to_u16() {
1555        assert_eq!(u16::from(SvcParamKey::Mandatory), 0);
1556        assert_eq!(u16::from(SvcParamKey::Alpn), 1);
1557        assert_eq!(u16::from(SvcParamKey::NoDefaultAlpn), 2);
1558        assert_eq!(u16::from(SvcParamKey::Port), 3);
1559        assert_eq!(u16::from(SvcParamKey::Ipv4Hint), 4);
1560        assert_eq!(u16::from(SvcParamKey::EchConfigList), 5);
1561        assert_eq!(u16::from(SvcParamKey::Ipv6Hint), 6);
1562        assert_eq!(u16::from(SvcParamKey::Key(65280)), 65280);
1563        assert_eq!(u16::from(SvcParamKey::Key(65534)), 65534);
1564        assert_eq!(u16::from(SvcParamKey::Key65535), 65535);
1565        assert_eq!(u16::from(SvcParamKey::Unknown(65279)), 65279);
1566    }
1567
1568    #[track_caller]
1569    fn test_encode_decode(rdata: SVCB) {
1570        let mut bytes = Vec::new();
1571        let mut encoder = BinEncoder::new(&mut bytes);
1572        rdata.emit(&mut encoder).expect("failed to emit SVCB");
1573        let bytes = encoder.into_bytes();
1574
1575        let mut decoder = BinDecoder::new(bytes);
1576        let read_rdata = SVCB::read_data(&mut decoder, Restrict::new(bytes.len() as u16))
1577            .expect("failed to read back");
1578        assert_eq!(rdata, read_rdata);
1579    }
1580
1581    #[test]
1582    fn test_encode_decode_svcb() {
1583        test_encode_decode(SVCB::new(
1584            0,
1585            Name::from_utf8("www.example.com.").unwrap(),
1586            vec![],
1587        ));
1588        test_encode_decode(SVCB::new(
1589            0,
1590            Name::from_utf8(".").unwrap(),
1591            vec![(
1592                SvcParamKey::Alpn,
1593                SvcParamValue::Alpn(Alpn(vec!["h2".to_string()])),
1594            )],
1595        ));
1596        test_encode_decode(SVCB::new(
1597            0,
1598            Name::from_utf8("example.com.").unwrap(),
1599            vec![
1600                (
1601                    SvcParamKey::Mandatory,
1602                    SvcParamValue::Mandatory(Mandatory(vec![SvcParamKey::Alpn])),
1603                ),
1604                (
1605                    SvcParamKey::Alpn,
1606                    SvcParamValue::Alpn(Alpn(vec!["h2".to_string()])),
1607                ),
1608            ],
1609        ));
1610    }
1611
1612    #[test]
1613    #[should_panic]
1614    fn test_encode_decode_svcb_bad_order() {
1615        test_encode_decode(SVCB::new(
1616            0,
1617            Name::from_utf8(".").unwrap(),
1618            vec![
1619                (
1620                    SvcParamKey::Alpn,
1621                    SvcParamValue::Alpn(Alpn(vec!["h2".to_string()])),
1622                ),
1623                (
1624                    SvcParamKey::Mandatory,
1625                    SvcParamValue::Mandatory(Mandatory(vec![SvcParamKey::Alpn])),
1626                ),
1627            ],
1628        ));
1629    }
1630
1631    #[test]
1632    fn test_no_panic() {
1633        const BUF: &[u8] = &[
1634            255, 121, 0, 0, 0, 0, 40, 255, 255, 160, 160, 0, 0, 0, 64, 0, 1, 255, 158, 0, 0, 0, 8,
1635            0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0,
1636        ];
1637        assert!(crate::op::Message::from_vec(BUF).is_err());
1638    }
1639
1640    #[test]
1641    fn test_unrestricted_output_size() {
1642        let svcb = SVCB::new(
1643            8224,
1644            Name::from_utf8(".").unwrap(),
1645            vec![(
1646                SvcParamKey::Unknown(8224),
1647                SvcParamValue::Unknown(Unknown(vec![32; 257])),
1648            )],
1649        );
1650
1651        let mut buf = Vec::new();
1652        let mut encoder = BinEncoder::new(&mut buf);
1653        svcb.emit(&mut encoder).unwrap();
1654    }
1655
1656    #[test]
1657    fn test_unknown_value_round_trip() {
1658        let svcb = SVCB::new(
1659            8224,
1660            Name::from_utf8(".").unwrap(),
1661            vec![(
1662                SvcParamKey::Unknown(8224),
1663                SvcParamValue::Unknown(Unknown(vec![32; 10])),
1664            )],
1665        );
1666
1667        let mut buf = Vec::new();
1668        let mut encoder = BinEncoder::new(&mut buf);
1669        svcb.emit(&mut encoder).unwrap();
1670
1671        let mut decoder = BinDecoder::new(&buf);
1672        let decoded = SVCB::read_data(
1673            &mut decoder,
1674            Restrict::new(u16::try_from(buf.len()).unwrap()),
1675        )
1676        .unwrap();
1677
1678        assert_eq!(svcb, decoded);
1679    }
1680
1681    // this assumes that only a single record is parsed
1682    // TODO: make Parser return an iterator over all records in a stream.
1683    fn parse_record<D: RecordData>(txt: &str) -> D {
1684        let records = Parser::new(txt, None, Some(Name::root()))
1685            .parse()
1686            .expect("failed to parse record")
1687            .1;
1688        let record_set = records.into_iter().next().expect("no record found").1;
1689        D::try_borrow(&record_set.into_iter().next().unwrap().data)
1690            .expect("Not the correct record")
1691            .clone()
1692    }
1693
1694    #[test]
1695    fn test_parsing() {
1696        let svcb: HTTPS = parse_record(CF_HTTPS_RECORD);
1697
1698        assert_eq!(svcb.svc_priority, 1);
1699        assert_eq!(svcb.target_name, Name::root());
1700
1701        let mut params = svcb.svc_params.iter();
1702
1703        // alpn
1704        let param = params.next().expect("not alpn");
1705        assert_eq!(param.0, SvcParamKey::Alpn);
1706        let SvcParamValue::Alpn(value) = &param.1 else {
1707            panic!("expected alpn");
1708        };
1709        assert_eq!(value.0, &["http/1.1", "h2"]);
1710
1711        // ipv4 hint
1712        let param = params.next().expect("ipv4hint");
1713        assert_eq!(SvcParamKey::Ipv4Hint, param.0);
1714        let SvcParamValue::Ipv4Hint(value) = &param.1 else {
1715            panic!("expected ipv4hint");
1716        };
1717        assert_eq!(
1718            value.0,
1719            &[A::new(162, 159, 137, 85), A::new(162, 159, 138, 85)]
1720        );
1721
1722        // echconfig
1723        let param = params.next().expect("echconfig");
1724        assert_eq!(SvcParamKey::EchConfigList, param.0);
1725        let SvcParamValue::EchConfigList(value) = &param.1 else {
1726            panic!("expected echconfig");
1727        };
1728        assert_eq!(
1729            value.0,
1730            data_encoding::BASE64.decode(b"AEX+DQBBtgAgACBMmGJQR02doup+5VPMjYpe5HQQ/bpntFCxDa8LT2PLAgAEAAEAAQASY2xvdWRmbGFyZS1lY2guY29tAAA=").unwrap()
1731        );
1732
1733        // ipv6 hint
1734        let param = params.next().expect("ipv6hint");
1735        assert_eq!(SvcParamKey::Ipv6Hint, param.0);
1736        let SvcParamValue::Ipv6Hint(value) = &param.1 else {
1737            panic!("expected ipv6hint");
1738        };
1739        assert_eq!(
1740            value.0,
1741            &[
1742                AAAA::new(0x2606, 0x4700, 0x7, 0, 0, 0, 0xa29f, 0x8955),
1743                AAAA::new(0x2606, 0x4700, 0x7, 0, 0, 0, 0xa29f, 0x8a5)
1744            ]
1745        );
1746    }
1747
1748    #[test]
1749    fn test_parse_display() {
1750        let svcb: SVCB = parse_record(CF_SVCB_RECORD);
1751
1752        let svcb_display = svcb.to_string();
1753
1754        // add back the name, etc...
1755        let svcb_display = format!("crypto.cloudflare.com. 299 IN SVCB {svcb_display}");
1756        let svcb_display = parse_record(&svcb_display);
1757
1758        assert_eq!(svcb, svcb_display);
1759    }
1760
1761    /// sanity check for https
1762    #[test]
1763    fn test_parsing_https() {
1764        let records = [GOOGLE_HTTPS_RECORD, CF_HTTPS_RECORD];
1765        for record in records.iter() {
1766            let svcb: HTTPS = parse_record(record);
1767
1768            assert_eq!(svcb.svc_priority, 1);
1769            assert_eq!(svcb.target_name, Name::root());
1770        }
1771    }
1772
1773    #[test]
1774    fn invalid_alpn_token() {
1775        let _ = RData::try_from_str(RecordType::SVCB, "1 . alpn=\"h")
1776            .expect_err("invalid alpn token should fail");
1777    }
1778
1779    #[test]
1780    fn single_quote_value() {
1781        let _ = RData::try_from_str(RecordType::SVCB, "1 . alpn=\"")
1782            .expect_err("single quoted value should fail");
1783    }
1784
1785    /// Test with RFC 9460 Appendix D test vectors
1786    /// <https://datatracker.ietf.org/doc/html/rfc9460#appendix-D>
1787    // TODO(XXX): Consider adding the negative "Failure Cases" from D.3.
1788    #[test]
1789    fn test_rfc9460_vectors() {
1790        #[derive(Debug)]
1791        struct TestVector {
1792            record: &'static str,
1793            record_type: RecordType,
1794            target_name: Name,
1795            priority: u16,
1796            params: Vec<(SvcParamKey, SvcParamValue)>,
1797        }
1798
1799        #[derive(Debug)]
1800        enum RecordType {
1801            SVCB,
1802            HTTPS,
1803        }
1804
1805        // NOTE: In each case the test vector from the RFC was augmented with a TTL (42 in each
1806        //       case). The parser requires this but the test vectors do not include it.
1807        let vectors: [TestVector; 9] = [
1808            // https://datatracker.ietf.org/doc/html/rfc9460#appendix-D.1
1809            // Figure 2: AliasMode
1810            TestVector {
1811                record: "example.com. 42  HTTPS   0 foo.example.com.",
1812                record_type: RecordType::HTTPS,
1813                target_name: Name::from_str("foo.example.com.").unwrap(),
1814                priority: 0,
1815                params: Vec::new(),
1816            },
1817            // https://datatracker.ietf.org/doc/html/rfc9460#appendix-D.2
1818            // Figure 3: TargetName Is "."
1819            TestVector {
1820                record: "example.com. 42  SVCB   1 .",
1821                record_type: RecordType::SVCB,
1822                target_name: Name::from_str(".").unwrap(),
1823                priority: 1,
1824                params: Vec::new(),
1825            },
1826            // Figure 4: Specifies a Port
1827            TestVector {
1828                record: "example.com. 42  SVCB   16 foo.example.com. port=53",
1829                record_type: RecordType::SVCB,
1830                target_name: Name::from_str("foo.example.com.").unwrap(),
1831                priority: 16,
1832                params: vec![(SvcParamKey::Port, SvcParamValue::Port(53))],
1833            },
1834            // Figure 5: A Generic Key and Unquoted Value
1835            TestVector {
1836                record: "example.com. 42  SVCB   1 foo.example.com. key667=hello",
1837                record_type: RecordType::SVCB,
1838                target_name: Name::from_str("foo.example.com.").unwrap(),
1839                priority: 1,
1840                params: vec![(
1841                    SvcParamKey::Key(667),
1842                    SvcParamValue::Unknown(Unknown(b"hello".into())),
1843                )],
1844            },
1845            // Figure 6: A Generic Key and Quoted Value with a Decimal Escape
1846            TestVector {
1847                record: r#"example.com. 42  SVCB   1 foo.example.com. key667="hello\210qoo""#,
1848                record_type: RecordType::SVCB,
1849                target_name: Name::from_str("foo.example.com.").unwrap(),
1850                priority: 1,
1851                params: vec![(
1852                    SvcParamKey::Key(667),
1853                    SvcParamValue::Unknown(Unknown(b"hello\\210qoo".into())),
1854                )],
1855            },
1856            // Figure 7: Two Quoted IPv6 Hints
1857            TestVector {
1858                record: r#"example.com. 42  SVCB   1 foo.example.com. (ipv6hint="2001:db8::1,2001:db8::53:1")"#,
1859                record_type: RecordType::SVCB,
1860                target_name: Name::from_str("foo.example.com.").unwrap(),
1861                priority: 1,
1862                params: vec![(
1863                    SvcParamKey::Ipv6Hint,
1864                    SvcParamValue::Ipv6Hint(IpHint(vec![
1865                        AAAA::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1),
1866                        AAAA::new(0x2001, 0xdb8, 0, 0, 0, 0, 0x53, 1),
1867                    ])),
1868                )],
1869            },
1870            // Figure 8: An IPv6 Hint Using the Embedded IPv4 Syntax
1871            TestVector {
1872                record: r#"example.com.  42 SVCB   1 example.com. (ipv6hint="2001:db8:122:344::192.0.2.33")"#,
1873                record_type: RecordType::SVCB,
1874                target_name: Name::from_str("example.com.").unwrap(),
1875                priority: 1,
1876                params: vec![(
1877                    SvcParamKey::Ipv6Hint,
1878                    SvcParamValue::Ipv6Hint(IpHint(vec![AAAA::new(
1879                        0x2001, 0xdb8, 0x122, 0x344, 0, 0, 0xc000, 0x221,
1880                    )])),
1881                )],
1882            },
1883            // Figure 9: SvcParamKey Ordering Is Arbitrary in Presentation Format but Sorted in Wire Format
1884            TestVector {
1885                record: r#"example.com. 42  SVCB   16 foo.example.org. (alpn=h2,h3-19 mandatory=ipv4hint,alpn ipv4hint=192.0.2.1)"#,
1886                record_type: RecordType::SVCB,
1887                target_name: Name::from_str("foo.example.org.").unwrap(),
1888                priority: 16,
1889                params: vec![
1890                    (
1891                        SvcParamKey::Alpn,
1892                        SvcParamValue::Alpn(Alpn(vec!["h2".to_owned(), "h3-19".to_owned()])),
1893                    ),
1894                    (
1895                        SvcParamKey::Mandatory,
1896                        SvcParamValue::Mandatory(Mandatory(vec![
1897                            SvcParamKey::Ipv4Hint,
1898                            SvcParamKey::Alpn,
1899                        ])),
1900                    ),
1901                    (
1902                        SvcParamKey::Ipv4Hint,
1903                        SvcParamValue::Ipv4Hint(IpHint(vec![A::new(192, 0, 2, 1)])),
1904                    ),
1905                ],
1906            },
1907            // Figure 10: An "alpn" Value with an Escaped Comma and an Escaped Backslash in Two Presentation Formats
1908            TestVector {
1909                record: r#"example.com.  42  SVCB   16 foo.example.org. alpn="f\\\\oo\,bar,h2""#,
1910                record_type: RecordType::SVCB,
1911                target_name: Name::from_str("foo.example.org.").unwrap(),
1912                priority: 16,
1913                params: vec![(
1914                    SvcParamKey::Alpn,
1915                    SvcParamValue::Alpn(Alpn(vec![r#"f\\oo,bar"#.to_owned(), "h2".to_owned()])),
1916                )],
1917            },
1918            /*
1919             * TODO(XXX): Parser does not replace escaped characters, does not see "\092," as
1920             *            an escaped delim.
1921            TestVector {
1922                record: r#"example.com.  42  SVCB   116 foo.example.org. alpn=f\\\092oo\092,bar,h2""#,
1923                record_type: RecordType::SVCB,
1924                target_name: Name::from_str("foo.example.org.").unwrap(),
1925                priority: 16,
1926                params: vec![(
1927                    SvcParamKey::Alpn,
1928                    SvcParamValue::Alpn(Alpn(vec![r#"f\\oo,bar"#.to_owned(), "h2".to_owned()])),
1929                )],
1930            },
1931            */
1932        ];
1933
1934        for record in vectors {
1935            let expected_scvb = SVCB::new(record.priority, record.target_name, record.params);
1936            match record.record_type {
1937                RecordType::SVCB => {
1938                    let parsed: SVCB = parse_record(record.record);
1939                    assert_eq!(parsed, expected_scvb);
1940                }
1941                RecordType::HTTPS => {
1942                    let parsed: HTTPS = parse_record(record.record);
1943                    assert_eq!(parsed, HTTPS(expected_scvb));
1944                }
1945            };
1946        }
1947    }
1948
1949    const CF_SVCB_RECORD: &str = "crypto.cloudflare.com. 1664 IN SVCB 1 . alpn=\"http/1.1,h2\" ipv4hint=162.159.137.85,162.159.138.85 ech=AEX+DQBBtgAgACBMmGJQR02doup+5VPMjYpe5HQQ/bpntFCxDa8LT2PLAgAEAAEAAQASY2xvdWRmbGFyZS1lY2guY29tAAA= ipv6hint=2606:4700:7::a29f:8955,2606:4700:7::a29f:8a5";
1950    const CF_HTTPS_RECORD: &str = "crypto.cloudflare.com. 1664 IN HTTPS 1 . alpn=\"http/1.1,h2\" ipv4hint=162.159.137.85,162.159.138.85 ech=AEX+DQBBtgAgACBMmGJQR02doup+5VPMjYpe5HQQ/bpntFCxDa8LT2PLAgAEAAEAAQASY2xvdWRmbGFyZS1lY2guY29tAAA= ipv6hint=2606:4700:7::a29f:8955,2606:4700:7::a29f:8a5";
1951    const GOOGLE_HTTPS_RECORD: &str = "google.com 21132 IN HTTPS 1 . alpn=\"h2,h3\"";
1952}