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 //! Extended DNS options
18 
19 use crate::error::*;
20 use crate::rr::rdata::opt::{self, EdnsCode, EdnsOption};
21 use crate::rr::rdata::OPT;
22 use crate::rr::{DNSClass, Name, RData, Record, RecordType};
23 
24 use crate::serialize::binary::{BinEncodable, BinEncoder};
25 
26 /// Edns implements the higher level concepts for working with extended dns as it is used to create or be
27 /// created from OPT record data.
28 #[derive(Debug, PartialEq, Clone)]
29 pub struct Edns {
30     // high 8 bits that make up the 12 bit total field when included with the 4bit rcode from the
31     //  header (from TTL)
32     rcode_high: u8,
33     // Indicates the implementation level of the setter. (from TTL)
34     version: u8,
35     // Is DNSSec supported (from TTL)
36     dnssec_ok: bool,
37     // max payload size, minimum of 512, (from RR CLASS)
38     max_payload: u16,
39 
40     options: OPT,
41 }
42 
43 impl Default for Edns {
default() -> Self44     fn default() -> Self {
45         Edns {
46             rcode_high: 0,
47             version: 0,
48             dnssec_ok: false,
49             max_payload: 512,
50             options: OPT::default(),
51         }
52     }
53 }
54 
55 impl Edns {
56     /// Creates a new extended DNS object.
new() -> Self57     pub fn new() -> Self {
58         Default::default()
59     }
60 
61     /// The high order bytes for the response code in the DNS Message
rcode_high(&self) -> u862     pub fn rcode_high(&self) -> u8 {
63         self.rcode_high
64     }
65 
66     /// Returns the EDNS version
version(&self) -> u867     pub fn version(&self) -> u8 {
68         self.version
69     }
70 
71     /// Specifies that DNSSec is supported for this Client or Server
dnssec_ok(&self) -> bool72     pub fn dnssec_ok(&self) -> bool {
73         self.dnssec_ok
74     }
75 
76     /// Maximum supported size of the DNS payload
max_payload(&self) -> u1677     pub fn max_payload(&self) -> u16 {
78         self.max_payload
79     }
80 
81     /// Returns the Option associated with the code
option(&self, code: EdnsCode) -> Option<&EdnsOption>82     pub fn option(&self, code: EdnsCode) -> Option<&EdnsOption> {
83         self.options.get(code)
84     }
85 
86     /// Returns the options portion of EDNS
options(&self) -> &OPT87     pub fn options(&self) -> &OPT {
88         &self.options
89     }
90 
91     /// Set the high order bits for the result code.
set_rcode_high(&mut self, rcode_high: u8)92     pub fn set_rcode_high(&mut self, rcode_high: u8) {
93         self.rcode_high = rcode_high
94     }
95 
96     /// Set the EDNS version
set_version(&mut self, version: u8)97     pub fn set_version(&mut self, version: u8) {
98         self.version = version
99     }
100 
101     /// Set to true if DNSSec is supported
set_dnssec_ok(&mut self, dnssec_ok: bool)102     pub fn set_dnssec_ok(&mut self, dnssec_ok: bool) {
103         self.dnssec_ok = dnssec_ok
104     }
105 
106     /// Set the maximum payload which can be supported
107     /// From RFC 6891: `Values lower than 512 MUST be treated as equal to 512`
set_max_payload(&mut self, max_payload: u16)108     pub fn set_max_payload(&mut self, max_payload: u16) {
109         self.max_payload = max_payload.max(512);
110     }
111 
112     /// Set the specified EDNS option
set_option(&mut self, option: EdnsOption)113     pub fn set_option(&mut self, option: EdnsOption) {
114         self.options.insert(option);
115     }
116 }
117 
118 impl<'a> From<&'a Record> for Edns {
from(value: &'a Record) -> Self119     fn from(value: &'a Record) -> Self {
120         assert!(value.rr_type() == RecordType::OPT);
121 
122         let rcode_high: u8 = ((value.ttl() & 0xFF00_0000u32) >> 24) as u8;
123         let version: u8 = ((value.ttl() & 0x00FF_0000u32) >> 16) as u8;
124         let dnssec_ok: bool = value.ttl() & 0x0000_8000 == 0x0000_8000;
125         let max_payload: u16 = u16::from(value.dns_class());
126 
127         let options: OPT = match *value.rdata() {
128             RData::NULL(..) => {
129                 // NULL, there was no data in the OPT
130                 OPT::default()
131             }
132             RData::OPT(ref option_data) => {
133                 option_data.clone() // TODO: Edns should just refer to this, have the same lifetime as the Record
134             }
135             _ => {
136                 // this should be a coding error, as opposed to a parsing error.
137                 panic!("rr_type doesn't match the RData: {:?}", value.rdata()) // valid panic, never should happen
138             }
139         };
140 
141         Edns {
142             rcode_high,
143             version,
144             dnssec_ok,
145             max_payload,
146             options,
147         }
148     }
149 }
150 
151 impl<'a> From<&'a Edns> for Record {
152     /// This returns a Resource Record that is formatted for Edns(0).
153     /// Note: the rcode_high value is only part of the rcode, the rest is part of the base
from(value: &'a Edns) -> Record154     fn from(value: &'a Edns) -> Record {
155         let mut record: Record = Record::new();
156 
157         record.set_name(Name::root());
158         record.set_rr_type(RecordType::OPT);
159         record.set_dns_class(DNSClass::for_opt(value.max_payload()));
160 
161         // rebuild the TTL field
162         let mut ttl: u32 = u32::from(value.rcode_high()) << 24;
163         ttl |= u32::from(value.version()) << 16;
164 
165         if value.dnssec_ok() {
166             ttl |= 0x0000_8000;
167         }
168         record.set_ttl(ttl);
169 
170         // now for each option, write out the option array
171         //  also, since this is a hash, there is no guarantee that ordering will be preserved from
172         //  the original binary format.
173         // maybe switch to: https://crates.io/crates/linked-hash-map/
174         record.set_rdata(RData::OPT(value.options().clone()));
175 
176         record
177     }
178 }
179 
180 impl BinEncodable for Edns {
emit(&self, encoder: &mut BinEncoder) -> ProtoResult<()>181     fn emit(&self, encoder: &mut BinEncoder) -> ProtoResult<()> {
182         encoder.emit(0)?; // Name::root
183         RecordType::OPT.emit(encoder)?; //self.rr_type.emit(encoder)?;
184         DNSClass::for_opt(self.max_payload()).emit(encoder)?; // self.dns_class.emit(encoder)?;
185 
186         // rebuild the TTL field
187         let mut ttl: u32 = u32::from(self.rcode_high()) << 24;
188         ttl |= u32::from(self.version()) << 16;
189 
190         if self.dnssec_ok() {
191             ttl |= 0x0000_8000;
192         }
193 
194         encoder.emit_u32(ttl)?;
195 
196         // write the opts as rdata...
197         let place = encoder.place::<u16>()?;
198         opt::emit(encoder, &self.options)?;
199         let len = encoder.len_since_place(&place);
200         assert!(len <= u16::max_value() as usize);
201 
202         place.replace(encoder, len as u16)?;
203         Ok(())
204     }
205 }
206 
207 #[cfg(feature = "dnssec")]
208 #[test]
test_encode_decode()209 fn test_encode_decode() {
210     use crate::rr::dnssec::SupportedAlgorithms;
211 
212     let mut edns: Edns = Edns::new();
213 
214     edns.set_dnssec_ok(true);
215     edns.set_max_payload(0x8008);
216     edns.set_version(0x40);
217     edns.set_rcode_high(0x01);
218     edns.set_option(EdnsOption::DAU(SupportedAlgorithms::all()));
219 
220     let record: Record = (&edns).into();
221     let edns_decode: Edns = (&record).into();
222 
223     assert_eq!(edns.dnssec_ok(), edns_decode.dnssec_ok());
224     assert_eq!(edns.max_payload(), edns_decode.max_payload());
225     assert_eq!(edns.version(), edns_decode.version());
226     assert_eq!(edns.rcode_high(), edns_decode.rcode_high());
227     assert_eq!(edns.options(), edns_decode.options());
228 }
229