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}