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(¤t_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(¤t_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) = ¶m.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) = ¶m.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) = ¶m.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) = ¶m.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}