xref: /webrtc/mdns/src/message/resource/mod.rs (revision 630c46fe)
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