1 pub mod a;
2 pub mod aaaa;
3 pub mod cname;
4 pub mod mx;
5 pub mod ns;
6 pub mod opt;
7 pub mod ptr;
8 pub mod soa;
9 pub mod srv;
10 pub mod txt;
11
12 use super::name::*;
13 use super::packer::*;
14 use super::*;
15 use crate::error::*;
16
17 use a::*;
18 use aaaa::*;
19 use cname::*;
20 use mx::*;
21 use ns::*;
22 use opt::*;
23 use ptr::*;
24 use soa::*;
25 use srv::*;
26 use txt::*;
27
28 use std::collections::HashMap;
29 use std::fmt;
30
31 // EDNS(0) wire constants.
32
33 const EDNS0_VERSION: u32 = 0;
34 const EDNS0_DNSSEC_OK: u32 = 0x00008000;
35 const EDNS_VERSION_MASK: u32 = 0x00ff0000;
36 const EDNS0_DNSSEC_OK_MASK: u32 = 0x00ff8000;
37
38 // A Resource is a DNS resource record.
39 #[derive(Default, Debug)]
40 pub struct Resource {
41 pub header: ResourceHeader,
42 pub body: Option<Box<dyn ResourceBody>>,
43 }
44
45 impl fmt::Display for Resource {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result46 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47 write!(
48 f,
49 "dnsmessage.Resource{{Header: {}, Body: {}}}",
50 self.header,
51 if let Some(body) = &self.body {
52 body.to_string()
53 } else {
54 "None".to_owned()
55 }
56 )
57 }
58 }
59
60 impl Resource {
61 // pack appends the wire format of the Resource to msg.
pack( &mut self, msg: Vec<u8>, compression: &mut Option<HashMap<String, usize>>, compression_off: usize, ) -> Result<Vec<u8>>62 pub fn pack(
63 &mut self,
64 msg: Vec<u8>,
65 compression: &mut Option<HashMap<String, usize>>,
66 compression_off: usize,
67 ) -> Result<Vec<u8>> {
68 if let Some(body) = &self.body {
69 self.header.typ = body.real_type();
70 } else {
71 return Err(Error::ErrNilResourceBody);
72 }
73 let (mut msg, len_off) = self.header.pack(msg, compression, compression_off)?;
74 let pre_len = msg.len();
75 if let Some(body) = &self.body {
76 msg = body.pack(msg, compression, compression_off)?;
77 self.header.fix_len(&mut msg, len_off, pre_len)?;
78 }
79 Ok(msg)
80 }
81
unpack(&mut self, msg: &[u8], mut off: usize) -> Result<usize>82 pub fn unpack(&mut self, msg: &[u8], mut off: usize) -> Result<usize> {
83 off = self.header.unpack(msg, off, 0)?;
84 let (rb, off) =
85 unpack_resource_body(self.header.typ, msg, off, self.header.length as usize)?;
86 self.body = Some(rb);
87 Ok(off)
88 }
89
skip(msg: &[u8], off: usize) -> Result<usize>90 pub(crate) fn skip(msg: &[u8], off: usize) -> Result<usize> {
91 let mut new_off = Name::skip(msg, off)?;
92 new_off = DnsType::skip(msg, new_off)?;
93 new_off = DnsClass::skip(msg, new_off)?;
94 new_off = skip_uint32(msg, new_off)?;
95 let (length, mut new_off) = unpack_uint16(msg, new_off)?;
96 new_off += length as usize;
97 if new_off > msg.len() {
98 return Err(Error::ErrResourceLen);
99 }
100 Ok(new_off)
101 }
102 }
103
104 // A ResourceHeader is the header of a DNS resource record. There are
105 // many types of DNS resource records, but they all share the same header.
106 #[derive(Clone, Default, PartialEq, Eq, Debug)]
107 pub struct ResourceHeader {
108 // Name is the domain name for which this resource record pertains.
109 pub name: Name,
110
111 // Type is the type of DNS resource record.
112 //
113 // This field will be set automatically during packing.
114 pub typ: DnsType,
115
116 // Class is the class of network to which this DNS resource record
117 // pertains.
118 pub class: DnsClass,
119
120 // TTL is the length of time (measured in seconds) which this resource
121 // record is valid for (time to live). All Resources in a set should
122 // have the same TTL (RFC 2181 Section 5.2).
123 pub ttl: u32,
124
125 // Length is the length of data in the resource record after the header.
126 //
127 // This field will be set automatically during packing.
128 pub length: u16,
129 }
130
131 impl fmt::Display for ResourceHeader {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result132 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133 write!(
134 f,
135 "dnsmessage.ResourceHeader{{Name: {}, Type: {}, Class: {}, TTL: {}, Length: {}}}",
136 self.name, self.typ, self.class, self.ttl, self.length,
137 )
138 }
139 }
140
141 impl ResourceHeader {
142 // pack appends the wire format of the ResourceHeader to oldMsg.
143 //
144 // lenOff is the offset in msg where the Length field was packed.
pack( &self, mut msg: Vec<u8>, compression: &mut Option<HashMap<String, usize>>, compression_off: usize, ) -> Result<(Vec<u8>, usize)>145 pub fn pack(
146 &self,
147 mut msg: Vec<u8>,
148 compression: &mut Option<HashMap<String, usize>>,
149 compression_off: usize,
150 ) -> Result<(Vec<u8>, usize)> {
151 msg = self.name.pack(msg, compression, compression_off)?;
152 msg = self.typ.pack(msg);
153 msg = self.class.pack(msg);
154 msg = pack_uint32(msg, self.ttl);
155 let len_off = msg.len();
156 msg = pack_uint16(msg, self.length);
157 Ok((msg, len_off))
158 }
159
unpack(&mut self, msg: &[u8], off: usize, _length: usize) -> Result<usize>160 pub fn unpack(&mut self, msg: &[u8], off: usize, _length: usize) -> Result<usize> {
161 let mut new_off = off;
162 new_off = self.name.unpack(msg, new_off)?;
163 new_off = self.typ.unpack(msg, new_off)?;
164 new_off = self.class.unpack(msg, new_off)?;
165 let (ttl, new_off) = unpack_uint32(msg, new_off)?;
166 self.ttl = ttl;
167 let (l, new_off) = unpack_uint16(msg, new_off)?;
168 self.length = l;
169
170 Ok(new_off)
171 }
172
173 // fixLen updates a packed ResourceHeader to include the length of the
174 // ResourceBody.
175 //
176 // lenOff is the offset of the ResourceHeader.Length field in msg.
177 //
178 // preLen is the length that msg was before the ResourceBody was packed.
fix_len(&mut self, msg: &mut [u8], len_off: usize, pre_len: usize) -> Result<()>179 pub fn fix_len(&mut self, msg: &mut [u8], len_off: usize, pre_len: usize) -> Result<()> {
180 if msg.len() < pre_len || msg.len() > pre_len + u16::MAX as usize {
181 return Err(Error::ErrResTooLong);
182 }
183
184 let con_len = msg.len() - pre_len;
185
186 // Fill in the length now that we know how long the content is.
187 msg[len_off] = ((con_len >> 8) & 0xFF) as u8;
188 msg[len_off + 1] = (con_len & 0xFF) as u8;
189 self.length = con_len as u16;
190
191 Ok(())
192 }
193
194 // set_edns0 configures h for EDNS(0).
195 //
196 // The provided ext_rcode must be an extedned RCode.
set_edns0( &mut self, udp_payload_len: u16, ext_rcode: u32, dnssec_ok: bool, ) -> Result<()>197 pub fn set_edns0(
198 &mut self,
199 udp_payload_len: u16,
200 ext_rcode: u32,
201 dnssec_ok: bool,
202 ) -> Result<()> {
203 self.name = Name {
204 data: ".".to_owned(),
205 }; // RFC 6891 section 6.1.2
206 self.typ = DnsType::Opt;
207 self.class = DnsClass(udp_payload_len);
208 self.ttl = (ext_rcode >> 4) << 24;
209 if dnssec_ok {
210 self.ttl |= EDNS0_DNSSEC_OK;
211 }
212 Ok(())
213 }
214
215 // dnssec_allowed reports whether the DNSSEC OK bit is set.
dnssec_allowed(&self) -> bool216 pub fn dnssec_allowed(&self) -> bool {
217 self.ttl & EDNS0_DNSSEC_OK_MASK == EDNS0_DNSSEC_OK // RFC 6891 section 6.1.3
218 }
219
220 // extended_rcode returns an extended RCode.
221 //
222 // The provided rcode must be the RCode in DNS message header.
extended_rcode(&self, rcode: RCode) -> RCode223 pub fn extended_rcode(&self, rcode: RCode) -> RCode {
224 if self.ttl & EDNS_VERSION_MASK == EDNS0_VERSION {
225 // RFC 6891 section 6.1.3
226 let ttl = ((self.ttl >> 24) << 4) as u8 | rcode as u8;
227 return RCode::from(ttl);
228 }
229 rcode
230 }
231 }
232
233 // A ResourceBody is a DNS resource record minus the header.
234 pub trait ResourceBody: fmt::Display + fmt::Debug {
235 // real_type returns the actual type of the Resource. This is used to
236 // fill in the header Type field.
real_type(&self) -> DnsType237 fn real_type(&self) -> DnsType;
238
239 // pack packs a Resource except for its header.
pack( &self, msg: Vec<u8>, compression: &mut Option<HashMap<String, usize>>, compression_off: usize, ) -> Result<Vec<u8>>240 fn pack(
241 &self,
242 msg: Vec<u8>,
243 compression: &mut Option<HashMap<String, usize>>,
244 compression_off: usize,
245 ) -> Result<Vec<u8>>;
246
unpack(&mut self, msg: &[u8], off: usize, length: usize) -> Result<usize>247 fn unpack(&mut self, msg: &[u8], off: usize, length: usize) -> Result<usize>;
248 }
249
unpack_resource_body( typ: DnsType, msg: &[u8], mut off: usize, length: usize, ) -> Result<(Box<dyn ResourceBody>, usize)>250 pub fn unpack_resource_body(
251 typ: DnsType,
252 msg: &[u8],
253 mut off: usize,
254 length: usize,
255 ) -> Result<(Box<dyn ResourceBody>, usize)> {
256 let mut rb: Box<dyn ResourceBody> = match typ {
257 DnsType::A => Box::<AResource>::default(),
258 DnsType::Ns => Box::<NsResource>::default(),
259 DnsType::Cname => Box::<CnameResource>::default(),
260 DnsType::Soa => Box::<SoaResource>::default(),
261 DnsType::Ptr => Box::<PtrResource>::default(),
262 DnsType::Mx => Box::<MxResource>::default(),
263 DnsType::Txt => Box::<TxtResource>::default(),
264 DnsType::Aaaa => Box::<AaaaResource>::default(),
265 DnsType::Srv => Box::<SrvResource>::default(),
266 DnsType::Opt => Box::<OptResource>::default(),
267 _ => return Err(Error::ErrNilResourceBody),
268 };
269
270 off = rb.unpack(msg, off, length)?;
271
272 Ok((rb, off))
273 }
274