trust_dns_proto/rr/rdata/opt.rs
1/*
2 * Copyright (C) 2015 Benjamin Fry <benjaminfry@me.com>
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//! option record for passing protocol options between the client and server
18#![allow(clippy::use_self)]
19
20use std::collections::HashMap;
21
22#[cfg(feature = "serde-config")]
23use serde::{Deserialize, Serialize};
24
25use tracing::warn;
26
27use crate::error::*;
28use crate::serialize::binary::*;
29
30#[cfg(feature = "dnssec")]
31use crate::rr::dnssec::SupportedAlgorithms;
32
33/// The OPT record type is used for ExtendedDNS records.
34///
35/// These allow for additional information to be associated with the DNS request that otherwise
36/// would require changes to the DNS protocol.
37///
38/// [RFC 6891, EDNS(0) Extensions, April 2013](https://tools.ietf.org/html/rfc6891#section-6)
39///
40/// ```text
41/// 6.1. OPT Record Definition
42///
43/// 6.1.1. Basic Elements
44///
45/// An OPT pseudo-RR (sometimes called a meta-RR) MAY be added to the
46/// additional data section of a request.
47///
48/// The OPT RR has RR type 41.
49///
50/// If an OPT record is present in a received request, compliant
51/// responders MUST include an OPT record in their respective responses.
52///
53/// An OPT record does not carry any DNS data. It is used only to
54/// contain control information pertaining to the question-and-answer
55/// sequence of a specific transaction. OPT RRs MUST NOT be cached,
56/// forwarded, or stored in or loaded from Zone Files.
57///
58/// The OPT RR MAY be placed anywhere within the additional data section.
59/// When an OPT RR is included within any DNS message, it MUST be the
60/// only OPT RR in that message. If a query message with more than one
61/// OPT RR is received, a FORMERR (RCODE=1) MUST be returned. The
62/// placement flexibility for the OPT RR does not override the need for
63/// the TSIG or SIG(0) RRs to be the last in the additional section
64/// whenever they are present.
65///
66/// 6.1.2. Wire Format
67///
68/// An OPT RR has a fixed part and a variable set of options expressed as
69/// {attribute, value} pairs. The fixed part holds some DNS metadata,
70/// and also a small collection of basic extension elements that we
71/// expect to be so popular that it would be a waste of wire space to
72/// encode them as {attribute, value} pairs.
73///
74/// The fixed part of an OPT RR is structured as follows:
75///
76/// +------------+--------------+------------------------------+
77/// | Field Name | Field Type | Description |
78/// +------------+--------------+------------------------------+
79/// | NAME | domain name | MUST be 0 (root domain) |
80/// | TYPE | u_int16_t | OPT (41) |
81/// | CLASS | u_int16_t | requestor's UDP payload size |
82/// | TTL | u_int32_t | extended RCODE and flags |
83/// | RDLEN | u_int16_t | length of all RDATA |
84/// | RDATA | octet stream | {attribute,value} pairs |
85/// +------------+--------------+------------------------------+
86///
87/// OPT RR Format
88///
89/// The variable part of an OPT RR may contain zero or more options in
90/// the RDATA. Each option MUST be treated as a bit field. Each option
91/// is encoded as:
92///
93/// +0 (MSB) +1 (LSB)
94/// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
95/// 0: | OPTION-CODE |
96/// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
97/// 2: | OPTION-LENGTH |
98/// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
99/// 4: | |
100/// / OPTION-DATA /
101/// / /
102/// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
103///
104/// OPTION-CODE
105/// Assigned by the Expert Review process as defined by the DNSEXT
106/// working group and the IESG.
107///
108/// OPTION-LENGTH
109/// Size (in octets) of OPTION-DATA.
110///
111/// OPTION-DATA
112/// Varies per OPTION-CODE. MUST be treated as a bit field.
113///
114/// The order of appearance of option tuples is not defined. If one
115/// option modifies the behaviour of another or multiple options are
116/// related to one another in some way, they have the same effect
117/// regardless of ordering in the RDATA wire encoding.
118///
119/// Any OPTION-CODE values not understood by a responder or requestor
120/// MUST be ignored. Specifications of such options might wish to
121/// include some kind of signaled acknowledgement. For example, an
122/// option specification might say that if a responder sees and supports
123/// option XYZ, it MUST include option XYZ in its response.
124///
125/// 6.1.3. OPT Record TTL Field Use
126///
127/// The extended RCODE and flags, which OPT stores in the RR Time to Live
128/// (TTL) field, are structured as follows:
129///
130/// +0 (MSB) +1 (LSB)
131/// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
132/// 0: | EXTENDED-RCODE | VERSION |
133/// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
134/// 2: | DO| Z |
135/// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
136///
137/// EXTENDED-RCODE
138/// Forms the upper 8 bits of extended 12-bit RCODE (together with the
139/// 4 bits defined in [RFC1035]. Note that EXTENDED-RCODE value 0
140/// indicates that an unextended RCODE is in use (values 0 through
141/// 15).
142///
143/// VERSION
144/// Indicates the implementation level of the setter. Full
145/// conformance with this specification is indicated by version '0'.
146/// Requestors are encouraged to set this to the lowest implemented
147/// level capable of expressing a transaction, to minimise the
148/// responder and network load of discovering the greatest common
149/// implementation level between requestor and responder. A
150/// requestor's version numbering strategy MAY ideally be a run-time
151/// configuration option.
152/// If a responder does not implement the VERSION level of the
153/// request, then it MUST respond with RCODE=BADVERS. All responses
154/// MUST be limited in format to the VERSION level of the request, but
155/// the VERSION of each response SHOULD be the highest implementation
156/// level of the responder. In this way, a requestor will learn the
157/// implementation level of a responder as a side effect of every
158/// response, including error responses and including RCODE=BADVERS.
159///
160/// 6.1.4. Flags
161///
162/// DO
163/// DNSSEC OK bit as defined by [RFC3225].
164///
165/// Z
166/// Set to zero by senders and ignored by receivers, unless modified
167/// in a subsequent specification.
168/// ```
169#[cfg_attr(feature = "serde-config", derive(Deserialize, Serialize))]
170#[derive(Default, Debug, PartialEq, Eq, Clone)]
171pub struct OPT {
172 options: HashMap<EdnsCode, EdnsOption>,
173}
174
175impl OPT {
176 /// Creates a new OPT record data.
177 ///
178 /// # Arguments
179 ///
180 /// * `options` - A map of the codes and record types
181 ///
182 /// # Return value
183 ///
184 /// The newly created OPT data
185 pub fn new(options: HashMap<EdnsCode, EdnsOption>) -> Self {
186 Self { options }
187 }
188
189 #[deprecated(note = "Please use as_ref() or as_mut() for shared/mutable references")]
190 /// The entire map of options
191 pub fn options(&self) -> &HashMap<EdnsCode, EdnsOption> {
192 &self.options
193 }
194
195 /// Get a single option based on the code
196 pub fn get(&self, code: EdnsCode) -> Option<&EdnsOption> {
197 self.options.get(&code)
198 }
199
200 /// Insert a new option, the key is derived from the `EdnsOption`
201 pub fn insert(&mut self, option: EdnsOption) {
202 self.options.insert((&option).into(), option);
203 }
204
205 /// Remove an option, the key is derived from the `EdnsOption`
206 pub fn remove(&mut self, option: EdnsCode) {
207 self.options.remove(&option);
208 }
209}
210
211impl AsMut<HashMap<EdnsCode, EdnsOption>> for OPT {
212 fn as_mut(&mut self) -> &mut HashMap<EdnsCode, EdnsOption> {
213 &mut self.options
214 }
215}
216
217impl AsRef<HashMap<EdnsCode, EdnsOption>> for OPT {
218 fn as_ref(&self) -> &HashMap<EdnsCode, EdnsOption> {
219 &self.options
220 }
221}
222
223/// Read the RData from the given Decoder
224pub fn read(decoder: &mut BinDecoder<'_>, rdata_length: Restrict<u16>) -> ProtoResult<OPT> {
225 let mut state: OptReadState = OptReadState::ReadCode;
226 let mut options: HashMap<EdnsCode, EdnsOption> = HashMap::new();
227 let start_idx = decoder.index();
228
229 // There is no unsafe direct use of the rdata length after this point
230 let rdata_length =
231 rdata_length.map(|u| u as usize).unverified(/*rdata length usage is bounded*/);
232 while rdata_length > decoder.index() - start_idx {
233 match state {
234 OptReadState::ReadCode => {
235 state = OptReadState::Code {
236 code: EdnsCode::from(
237 decoder.read_u16()?.unverified(/*EdnsCode is verified as safe*/),
238 ),
239 };
240 }
241 OptReadState::Code { code } => {
242 let length = decoder
243 .read_u16()?
244 .map(|u| u as usize)
245 .verify_unwrap(|u| *u <= rdata_length)
246 .map_err(|_| ProtoError::from("OPT value length exceeds rdata length"))?;
247 // If we know that the length is 0, we can avoid the `OptReadState::Data` state
248 // and directly add the option to the map.
249 // The data state does not process 0-length correctly, since it always reads at
250 // least 1 byte, thus making the length check fail.
251 state = if length == 0 {
252 options.insert(code, (code, &[] as &[u8]).into());
253 OptReadState::ReadCode
254 } else {
255 OptReadState::Data {
256 code,
257 length,
258 // TODO: this cean be replaced with decoder.read_vec(), right?
259 // the current version allows for malformed opt to be skipped...
260 collected: Vec::<u8>::with_capacity(length),
261 }
262 };
263 }
264 OptReadState::Data {
265 code,
266 length,
267 mut collected,
268 } => {
269 // TODO: can this be replaced by read_slice()?
270 collected.push(decoder.pop()?.unverified(/*byte array is safe*/));
271 if length == collected.len() {
272 options.insert(code, (code, &collected as &[u8]).into());
273 state = OptReadState::ReadCode;
274 } else {
275 state = OptReadState::Data {
276 code,
277 length,
278 collected,
279 };
280 }
281 }
282 }
283 }
284
285 if state != OptReadState::ReadCode {
286 // there was some problem parsing the data for the options, ignoring them
287 // TODO: should we ignore all of the EDNS data in this case?
288 warn!("incomplete or poorly formatted EDNS options: {:?}", state);
289 options.clear();
290 }
291
292 // the record data is stored as unstructured data, the expectation is that this will be processed after initial parsing.
293 Ok(OPT::new(options))
294}
295
296/// Write the RData from the given Decoder
297pub fn emit(encoder: &mut BinEncoder<'_>, opt: &OPT) -> ProtoResult<()> {
298 for (edns_code, edns_option) in opt.as_ref().iter() {
299 encoder.emit_u16(u16::from(*edns_code))?;
300 encoder.emit_u16(edns_option.len())?;
301 edns_option.emit(encoder)?
302 }
303 Ok(())
304}
305
306#[derive(Debug, PartialEq, Eq)]
307enum OptReadState {
308 ReadCode,
309 Code {
310 code: EdnsCode,
311 }, // expect LSB for the opt code, store the high byte
312 Data {
313 code: EdnsCode,
314 length: usize,
315 collected: Vec<u8>,
316 }, // expect the data for the option
317}
318
319/// The code of the EDNS data option
320#[cfg_attr(feature = "serde-config", derive(Deserialize, Serialize))]
321#[derive(Hash, Debug, Copy, Clone, PartialEq, Eq)]
322#[non_exhaustive]
323pub enum EdnsCode {
324 /// [RFC 6891, Reserved](https://tools.ietf.org/html/rfc6891)
325 Zero,
326
327 /// [RFC 8764l, Apple's Long-Lived Queries, Optional](https://tools.ietf.org/html/rfc8764)
328 LLQ,
329
330 /// [UL On-hold](http://files.dns-sd.org/draft-sekar-dns-ul.txt)
331 UL,
332
333 /// [RFC 5001, NSID](https://tools.ietf.org/html/rfc5001)
334 NSID,
335 // 4 Reserved [draft-cheshire-edns0-owner-option] -EXPIRED-
336 /// [RFC 6975, DNSSEC Algorithm Understood](https://tools.ietf.org/html/rfc6975)
337 DAU,
338
339 /// [RFC 6975, DS Hash Understood](https://tools.ietf.org/html/rfc6975)
340 DHU,
341
342 /// [RFC 6975, NSEC3 Hash Understood](https://tools.ietf.org/html/rfc6975)
343 N3U,
344
345 /// [RFC 7871, Client Subnet, Optional](https://tools.ietf.org/html/rfc7871)
346 Subnet,
347
348 /// [RFC 7314, EDNS EXPIRE, Optional](https://tools.ietf.org/html/rfc7314)
349 Expire,
350
351 /// [RFC 7873, DNS Cookies](https://tools.ietf.org/html/rfc7873)
352 Cookie,
353
354 /// [RFC 7828, edns-tcp-keepalive](https://tools.ietf.org/html/rfc7828)
355 Keepalive,
356
357 /// [RFC 7830, The EDNS(0) Padding](https://tools.ietf.org/html/rfc7830)
358 Padding,
359
360 /// [RFC 7901, CHAIN Query Requests in DNS, Optional](https://tools.ietf.org/html/rfc7901)
361 Chain,
362
363 /// Unknown, used to deal with unknown or unsupported codes
364 Unknown(u16),
365}
366
367// TODO: implement a macro to perform these inversions
368impl From<u16> for EdnsCode {
369 fn from(value: u16) -> Self {
370 match value {
371 0 => Self::Zero,
372 1 => Self::LLQ,
373 2 => Self::UL,
374 3 => Self::NSID,
375 // 4 Reserved [draft-cheshire-edns0-owner-option] -EXPIRED-
376 5 => Self::DAU,
377 6 => Self::DHU,
378 7 => Self::N3U,
379 8 => Self::Subnet,
380 9 => Self::Expire,
381 10 => Self::Cookie,
382 11 => Self::Keepalive,
383 12 => Self::Padding,
384 13 => Self::Chain,
385 _ => Self::Unknown(value),
386 }
387 }
388}
389
390impl From<EdnsCode> for u16 {
391 fn from(value: EdnsCode) -> Self {
392 match value {
393 EdnsCode::Zero => 0,
394 EdnsCode::LLQ => 1,
395 EdnsCode::UL => 2,
396 EdnsCode::NSID => 3,
397 // 4 Reserved [draft-cheshire-edns0-owner-option] -EXPIRED-
398 EdnsCode::DAU => 5,
399 EdnsCode::DHU => 6,
400 EdnsCode::N3U => 7,
401 EdnsCode::Subnet => 8,
402 EdnsCode::Expire => 9,
403 EdnsCode::Cookie => 10,
404 EdnsCode::Keepalive => 11,
405 EdnsCode::Padding => 12,
406 EdnsCode::Chain => 13,
407 EdnsCode::Unknown(value) => value,
408 }
409 }
410}
411
412/// options used to pass information about capabilities between client and server
413///
414/// `note: Not all EdnsOptions are supported at this time.`
415///
416/// <http://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-13>
417#[cfg_attr(feature = "serde-config", derive(Deserialize, Serialize))]
418#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Hash)]
419#[non_exhaustive]
420pub enum EdnsOption {
421 /// [RFC 6975, DNSSEC Algorithm Understood](https://tools.ietf.org/html/rfc6975)
422 #[cfg(feature = "dnssec")]
423 #[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))]
424 DAU(SupportedAlgorithms),
425
426 /// [RFC 6975, DS Hash Understood](https://tools.ietf.org/html/rfc6975)
427 #[cfg(feature = "dnssec")]
428 #[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))]
429 DHU(SupportedAlgorithms),
430
431 /// [RFC 6975, NSEC3 Hash Understood](https://tools.ietf.org/html/rfc6975)
432 #[cfg(feature = "dnssec")]
433 #[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))]
434 N3U(SupportedAlgorithms),
435
436 /// Unknown, used to deal with unknown or unsupported codes
437 Unknown(u16, Vec<u8>),
438}
439
440impl EdnsOption {
441 /// Returns the length in bytes of the EdnsOption
442 pub fn len(&self) -> u16 {
443 match *self {
444 #[cfg(feature = "dnssec")]
445 EdnsOption::DAU(ref algorithms)
446 | EdnsOption::DHU(ref algorithms)
447 | EdnsOption::N3U(ref algorithms) => algorithms.len(),
448 EdnsOption::Unknown(_, ref data) => data.len() as u16, // TODO: should we verify?
449 }
450 }
451
452 /// Returns `true` if the length in bytes of the EdnsOption is 0
453 pub fn is_empty(&self) -> bool {
454 match *self {
455 #[cfg(feature = "dnssec")]
456 EdnsOption::DAU(ref algorithms)
457 | EdnsOption::DHU(ref algorithms)
458 | EdnsOption::N3U(ref algorithms) => algorithms.is_empty(),
459 EdnsOption::Unknown(_, ref data) => data.is_empty(),
460 }
461 }
462}
463
464impl BinEncodable for EdnsOption {
465 fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
466 match *self {
467 #[cfg(feature = "dnssec")]
468 EdnsOption::DAU(ref algorithms)
469 | EdnsOption::DHU(ref algorithms)
470 | EdnsOption::N3U(ref algorithms) => algorithms.emit(encoder),
471 EdnsOption::Unknown(_, ref data) => encoder.emit_vec(data), // gah, clone needed or make a crazy api.
472 }
473 }
474}
475
476/// only the supported extensions are listed right now.
477impl<'a> From<(EdnsCode, &'a [u8])> for EdnsOption {
478 #[allow(clippy::match_single_binding)]
479 fn from(value: (EdnsCode, &'a [u8])) -> Self {
480 match value.0 {
481 #[cfg(feature = "dnssec")]
482 EdnsCode::DAU => Self::DAU(value.1.into()),
483 #[cfg(feature = "dnssec")]
484 EdnsCode::DHU => Self::DHU(value.1.into()),
485 #[cfg(feature = "dnssec")]
486 EdnsCode::N3U => Self::N3U(value.1.into()),
487 _ => Self::Unknown(value.0.into(), value.1.to_vec()),
488 }
489 }
490}
491
492impl<'a> From<&'a EdnsOption> for Vec<u8> {
493 fn from(value: &'a EdnsOption) -> Self {
494 match *value {
495 #[cfg(feature = "dnssec")]
496 EdnsOption::DAU(ref algorithms)
497 | EdnsOption::DHU(ref algorithms)
498 | EdnsOption::N3U(ref algorithms) => algorithms.into(),
499 EdnsOption::Unknown(_, ref data) => data.clone(), // gah, clone needed or make a crazy api.
500 }
501 }
502}
503
504impl<'a> From<&'a EdnsOption> for EdnsCode {
505 fn from(value: &'a EdnsOption) -> Self {
506 match *value {
507 #[cfg(feature = "dnssec")]
508 EdnsOption::DAU(..) => Self::DAU,
509 #[cfg(feature = "dnssec")]
510 EdnsOption::DHU(..) => Self::DHU,
511 #[cfg(feature = "dnssec")]
512 EdnsOption::N3U(..) => Self::N3U,
513 EdnsOption::Unknown(code, _) => code.into(),
514 }
515 }
516}
517
518#[cfg(test)]
519mod tests {
520 #![allow(clippy::dbg_macro, clippy::print_stdout)]
521
522 use super::*;
523
524 #[test]
525 #[cfg(feature = "dnssec")]
526 fn test() {
527 let mut rdata = OPT::default();
528 rdata.insert(EdnsOption::DAU(SupportedAlgorithms::all()));
529
530 let mut bytes = Vec::new();
531 let mut encoder: BinEncoder<'_> = BinEncoder::new(&mut bytes);
532 assert!(emit(&mut encoder, &rdata).is_ok());
533 let bytes = encoder.into_bytes();
534
535 println!("bytes: {:?}", bytes);
536
537 let mut decoder: BinDecoder<'_> = BinDecoder::new(bytes);
538 let restrict = Restrict::new(bytes.len() as u16);
539 let read_rdata = read(&mut decoder, restrict).expect("Decoding error");
540 assert_eq!(rdata, read_rdata);
541 }
542
543 #[test]
544 fn test_read_empty_option_at_end_of_opt() {
545 let bytes: Vec<u8> = vec![
546 0x00, 0x0a, 0x00, 0x08, 0x0b, 0x64, 0xb4, 0xdc, 0xd7, 0xb0, 0xcc, 0x8f, 0x00, 0x08,
547 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00,
548 ];
549
550 let mut decoder: BinDecoder<'_> = BinDecoder::new(&bytes);
551 let read_rdata = read(&mut decoder, Restrict::new(bytes.len() as u16));
552 assert!(
553 read_rdata.is_ok(),
554 "error decoding: {:?}",
555 read_rdata.unwrap_err()
556 );
557
558 let opt = read_rdata.unwrap();
559 let mut options = HashMap::default();
560 options.insert(EdnsCode::Subnet, EdnsOption::Unknown(8, vec![0, 1, 0, 0]));
561 options.insert(
562 EdnsCode::Cookie,
563 EdnsOption::Unknown(10, vec![0x0b, 0x64, 0xb4, 0xdc, 0xd7, 0xb0, 0xcc, 0x8f]),
564 );
565 options.insert(EdnsCode::Keepalive, EdnsOption::Unknown(11, vec![]));
566 let options = OPT::new(options);
567 assert_eq!(opt, options);
568 }
569}