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 {
from(value: (EdnsCode, &'a [u8])) -> EdnsOption439 fn from(value: (EdnsCode, &'a [u8])) -> EdnsOption {
440 match value.0 {
441 #[cfg(feature = "dnssec")]
442 EdnsCode::DAU => EdnsOption::DAU(value.1.into()),
443 #[cfg(feature = "dnssec")]
444 EdnsCode::DHU => EdnsOption::DHU(value.1.into()),
445 #[cfg(feature = "dnssec")]
446 EdnsCode::N3U => EdnsOption::N3U(value.1.into()),
447 _ => EdnsOption::Unknown(value.0.into(), value.1.to_vec()),
448 }
449 }
450 }
451
452 impl<'a> From<&'a EdnsOption> for Vec<u8> {
from(value: &'a EdnsOption) -> Vec<u8>453 fn from(value: &'a EdnsOption) -> Vec<u8> {
454 match *value {
455 #[cfg(feature = "dnssec")]
456 EdnsOption::DAU(ref algorithms)
457 | EdnsOption::DHU(ref algorithms)
458 | EdnsOption::N3U(ref algorithms) => algorithms.into(),
459 EdnsOption::Unknown(_, ref data) => data.clone(), // gah, clone needed or make a crazy api.
460 }
461 }
462 }
463
464 impl<'a> From<&'a EdnsOption> for EdnsCode {
from(value: &'a EdnsOption) -> EdnsCode465 fn from(value: &'a EdnsOption) -> EdnsCode {
466 match *value {
467 #[cfg(feature = "dnssec")]
468 EdnsOption::DAU(..) => EdnsCode::DAU,
469 #[cfg(feature = "dnssec")]
470 EdnsOption::DHU(..) => EdnsCode::DHU,
471 #[cfg(feature = "dnssec")]
472 EdnsOption::N3U(..) => EdnsCode::N3U,
473 EdnsOption::Unknown(code, _) => code.into(),
474 }
475 }
476 }
477
478 #[cfg(test)]
479 mod tests {
480 #![allow(clippy::dbg_macro, clippy::print_stdout)]
481
482 use super::*;
483
484 #[test]
485 #[cfg(feature = "dnssec")]
test()486 pub fn test() {
487 let mut rdata = OPT::default();
488 rdata.insert(EdnsOption::DAU(SupportedAlgorithms::all()));
489
490 let mut bytes = Vec::new();
491 let mut encoder: BinEncoder = BinEncoder::new(&mut bytes);
492 assert!(emit(&mut encoder, &rdata).is_ok());
493 let bytes = encoder.into_bytes();
494
495 println!("bytes: {:?}", bytes);
496
497 let mut decoder: BinDecoder = BinDecoder::new(bytes);
498 let restrict = Restrict::new(bytes.len() as u16);
499 let read_rdata = read(&mut decoder, restrict).expect("Decoding error");
500 assert_eq!(rdata, read_rdata);
501 }
502 }
503