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