webrtc_mdns/message/resource/
mod.rs

1pub mod a;
2pub mod aaaa;
3pub mod cname;
4pub mod mx;
5pub mod ns;
6pub mod opt;
7pub mod ptr;
8pub mod soa;
9pub mod srv;
10pub mod txt;
11
12use std::collections::HashMap;
13use std::fmt;
14
15use a::*;
16use aaaa::*;
17use cname::*;
18use mx::*;
19use ns::*;
20use opt::*;
21use ptr::*;
22use soa::*;
23use srv::*;
24use txt::*;
25
26use super::name::*;
27use super::packer::*;
28use super::*;
29use crate::error::*;
30
31// EDNS(0) wire constants.
32
33const EDNS0_VERSION: u32 = 0;
34const EDNS0_DNSSEC_OK: u32 = 0x00008000;
35const EDNS_VERSION_MASK: u32 = 0x00ff0000;
36const EDNS0_DNSSEC_OK_MASK: u32 = 0x00ff8000;
37
38// A Resource is a DNS resource record.
39#[derive(Default, Debug)]
40pub struct Resource {
41    pub header: ResourceHeader,
42    pub body: Option<Box<dyn ResourceBody>>,
43}
44
45impl fmt::Display for Resource {
46    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
60impl Resource {
61    // pack appends the wire format of the Resource to msg.
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
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
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)]
107pub 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
131impl fmt::Display for ResourceHeader {
132    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
141impl 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.
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
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.
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 extended RCode.
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.
216    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.
223    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.
234pub 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.
237    fn real_type(&self) -> DnsType;
238
239    // pack packs a Resource except for its header.
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
247    fn unpack(&mut self, msg: &[u8], off: usize, length: usize) -> Result<usize>;
248}
249
250pub 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}