1use std::fmt;
11
12use crate::{
13 error::*,
14 rr::{
15 rdata::{
16 opt::{EdnsCode, EdnsOption},
17 OPT,
18 },
19 DNSClass, Name, RData, Record, RecordType,
20 },
21 serialize::binary::{BinEncodable, BinEncoder},
22};
23
24#[derive(Debug, PartialEq, Eq, Clone)]
27pub struct Edns {
28 rcode_high: u8,
31 version: u8,
33 dnssec_ok: bool,
35 max_payload: u16,
37
38 options: OPT,
39}
40
41impl Default for Edns {
42 fn default() -> Self {
43 Self {
44 rcode_high: 0,
45 version: 0,
46 dnssec_ok: false,
47 max_payload: 512,
48 options: OPT::default(),
49 }
50 }
51}
52
53impl Edns {
54 pub fn new() -> Self {
56 Self::default()
57 }
58
59 pub fn rcode_high(&self) -> u8 {
61 self.rcode_high
62 }
63
64 pub fn version(&self) -> u8 {
66 self.version
67 }
68
69 pub fn dnssec_ok(&self) -> bool {
71 self.dnssec_ok
72 }
73
74 pub fn max_payload(&self) -> u16 {
76 self.max_payload
77 }
78
79 pub fn option(&self, code: EdnsCode) -> Option<&EdnsOption> {
81 self.options.get(code)
82 }
83
84 pub fn options(&self) -> &OPT {
86 &self.options
87 }
88
89 pub fn options_mut(&mut self) -> &mut OPT {
91 &mut self.options
92 }
93
94 pub fn set_rcode_high(&mut self, rcode_high: u8) -> &mut Self {
96 self.rcode_high = rcode_high;
97 self
98 }
99
100 pub fn set_version(&mut self, version: u8) -> &mut Self {
102 self.version = version;
103 self
104 }
105
106 pub fn set_dnssec_ok(&mut self, dnssec_ok: bool) -> &mut Self {
108 self.dnssec_ok = dnssec_ok;
109 self
110 }
111
112 pub fn set_max_payload(&mut self, max_payload: u16) -> &mut Self {
115 self.max_payload = max_payload.max(512);
116 self
117 }
118
119 #[deprecated(note = "Please use options_mut().insert() to modify")]
121 pub fn set_option(&mut self, option: EdnsOption) {
122 self.options.insert(option);
123 }
124}
125
126impl<'a> From<&'a Record> for Edns {
128 fn from(value: &'a Record) -> Self {
129 assert!(value.record_type() == RecordType::OPT);
130
131 let rcode_high: u8 = ((value.ttl() & 0xFF00_0000u32) >> 24) as u8;
132 let version: u8 = ((value.ttl() & 0x00FF_0000u32) >> 16) as u8;
133 let dnssec_ok: bool = value.ttl() & 0x0000_8000 == 0x0000_8000;
134 let max_payload: u16 = u16::from(value.dns_class());
135
136 let options: OPT = match value.data() {
137 Some(RData::NULL(..)) | None => {
138 OPT::default()
140 }
141 Some(RData::OPT(ref option_data)) => {
142 option_data.clone() }
144 _ => {
145 panic!("rr_type doesn't match the RData: {:?}", value.data()) }
148 };
149
150 Self {
151 rcode_high,
152 version,
153 dnssec_ok,
154 max_payload,
155 options,
156 }
157 }
158}
159
160impl<'a> From<&'a Edns> for Record {
161 fn from(value: &'a Edns) -> Self {
164 let mut record = Self::new();
165
166 record.set_name(Name::root());
167 record.set_rr_type(RecordType::OPT);
168 record.set_dns_class(DNSClass::for_opt(value.max_payload()));
169
170 let mut ttl: u32 = u32::from(value.rcode_high()) << 24;
172 ttl |= u32::from(value.version()) << 16;
173
174 if value.dnssec_ok() {
175 ttl |= 0x0000_8000;
176 }
177 record.set_ttl(ttl);
178
179 record.set_data(Some(RData::OPT(value.options().clone())));
184
185 record
186 }
187}
188
189impl BinEncodable for Edns {
190 fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
191 encoder.emit(0)?; RecordType::OPT.emit(encoder)?; DNSClass::for_opt(self.max_payload()).emit(encoder)?; let mut ttl: u32 = u32::from(self.rcode_high()) << 24;
197 ttl |= u32::from(self.version()) << 16;
198
199 if self.dnssec_ok() {
200 ttl |= 0x0000_8000;
201 }
202
203 encoder.emit_u32(ttl)?;
204
205 let place = encoder.place::<u16>()?;
207 self.options.emit(encoder)?;
208 let len = encoder.len_since_place(&place);
209 assert!(len <= u16::MAX as usize);
210
211 place.replace(encoder, len as u16)?;
212 Ok(())
213 }
214}
215
216impl fmt::Display for Edns {
217 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
218 let version = self.version;
219 let dnssec_ok = self.dnssec_ok;
220 let max_payload = self.max_payload;
221
222 write!(
223 f,
224 "version: {version} dnssec_ok: {dnssec_ok} max_payload: {max_payload} opts: {opts_len}",
225 version = version,
226 dnssec_ok = dnssec_ok,
227 max_payload = max_payload,
228 opts_len = self.options().as_ref().len()
229 )
230 }
231}
232
233#[cfg(feature = "dnssec")]
234#[test]
235fn test_encode_decode() {
236 use crate::rr::dnssec::SupportedAlgorithms;
237
238 let mut edns: Edns = Edns::new();
239
240 edns.set_dnssec_ok(true);
241 edns.set_max_payload(0x8008);
242 edns.set_version(0x40);
243 edns.set_rcode_high(0x01);
244 edns.options_mut()
245 .insert(EdnsOption::DAU(SupportedAlgorithms::all()));
246
247 let record: Record = (&edns).into();
248 let edns_decode: Edns = (&record).into();
249
250 assert_eq!(edns.dnssec_ok(), edns_decode.dnssec_ok());
251 assert_eq!(edns.max_payload(), edns_decode.max_payload());
252 assert_eq!(edns.version(), edns_decode.version());
253 assert_eq!(edns.rcode_high(), edns_decode.rcode_high());
254 assert_eq!(edns.options(), edns_decode.options());
255
256 edns.options_mut()
258 .insert(EdnsOption::DAU(SupportedAlgorithms::all()));
259 edns.options_mut().remove(EdnsCode::DAU);
260 assert!(edns.option(EdnsCode::DAU).is_none());
261}