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