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