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