domain_core/bits/opt/
mod.rs

1// XXX TODO: Easier access to individual options.
2// XXX TODO: Documentation and tests.
3//
4//! Record data for OPT records.
5//!
6//! Since DNS message headers are relatively short, the amount of information
7//! that can be conveyed through them is very limited. In order to provide an
8//! extensible means to transmit additional information, [RFC 6891] introduces
9//! a resource record called OPT that can be added to the additional section
10//! of a message. The record data in turn consists of a sequence of options.
11//!
12//! This module contains the types for working with both the OPT record and
13//! its record data. It defines types for each of the currently defined
14//! options. As with record data types in the [rdata] module, these are
15//! arranged in sub-modules according to the RFC that defined them and then
16//! re-exported here.
17//! 
18//! [RFC 6891]: https://tools.ietf.org/html/rfc6891
19//! [rdata]: ../rdata/index.html
20
21
22//============ Sub-modules and Re-exports ====================================
23//
24// All of these are in a macro. The macro also defines `AllOptData`.
25
26#[macro_use] mod macros;
27opt_types!{
28    rfc5001::{Nsid};
29    rfc6975::{Dau, Dhu, N3u};
30    rfc7314::{Expire};
31    rfc7828::{TcpKeepalive};
32    rfc7830::{Padding};
33    rfc7871::{ClientSubnet};
34    rfc7873::{Cookie};
35    rfc7901::{Chain};
36    rfc8145::{KeyTag};
37}
38
39
40//============ Module Content ================================================
41
42use std::{fmt, mem, ops};
43use std::marker::PhantomData;
44use bytes::{BigEndian, BufMut, ByteOrder, Bytes};
45use ::iana::{OptionCode, OptRcode, Rtype};
46use super::compose::{Compose, Compress, Compressor};
47use super::header::Header;
48use super::name::ToDname;
49use super::parse::{Parse, ParseAll, Parser, ShortBuf};
50use super::rdata::RtypeRecordData;
51use super::record::Record;
52
53
54//------------ Opt -----------------------------------------------------------
55
56/// OPT record data.
57///
58/// This type wraps a bytes value containing the record data of an OPT record.
59//
60//  XXX Deriving PartialEq etc. might be wrong for some options. Have a look
61//      at this again once we have proper option handling.
62#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
63pub struct Opt {
64    bytes: Bytes,
65}
66
67impl Opt {
68    /// Creates OPT record data from the underlying bytes value.
69    ///
70    /// The function checks whether the bytes value contains a sequence of
71    /// options. It does not check whether the options itself are valid.
72    pub fn from_bytes(bytes: Bytes) -> Result<Self, ShortBuf> {
73        let mut parser = Parser::from_bytes(bytes);
74        while parser.remaining() > 0 {
75            parser.advance(2)?;
76            let len = parser.parse_u16()?;
77            parser.advance(len as usize)?;
78        }
79        Ok(Opt { bytes: parser.unwrap() })
80    }
81
82    /// Returns an iterator over options of a given type.
83    ///
84    /// The returned iterator will return only options represented by type
85    /// `D` and quietly skip over all the others.
86    pub fn iter<D: OptData>(&self) -> OptIter<D> {
87        OptIter::new(self.bytes.clone())
88    }
89}
90
91
92//--- ParseAll, Compose, Compress
93
94impl ParseAll for Opt {
95    type Err = ShortBuf;
96
97    fn parse_all(parser: &mut Parser, len: usize) -> Result<Self, Self::Err> {
98        Self::from_bytes(parser.parse_bytes(len)?)
99    }
100}
101
102impl Compose for Opt {
103    fn compose_len(&self) -> usize {
104        self.bytes.len()
105    }
106
107    fn compose<B: BufMut>(&self, buf: &mut B) {
108        buf.put_slice(self.bytes.as_ref())
109    }
110}
111
112impl Compress for Opt {
113    fn compress(&self, buf: &mut Compressor) -> Result<(), ShortBuf> {
114        buf.compose(self)
115    }
116}
117
118impl RtypeRecordData for Opt {
119    const RTYPE: Rtype = Rtype::Opt;
120}
121
122
123//--- Display
124
125impl fmt::Display for Opt {
126    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
127        // XXX TODO Print this properly.
128        f.write_str("OPT ...")
129    }
130}
131
132
133//------------ OptHeader -----------------------------------------------------
134
135/// The header of an OPT record.
136///
137/// The OPT record reappropriates the record header for encoding some
138/// basic information. This type provides access to this information. It
139/// consists of the record header accept for its `rdlen` field.
140///
141/// This is so that `OptBuilder` can safely deref to this type.
142///
143//    +------------+--------------+------------------------------+
144//    | Field Name | Field Type   | Description                  |
145//    +------------+--------------+------------------------------+
146//    | NAME       | domain name  | MUST be 0 (root domain)      |
147//    | TYPE       | u_int16_t    | OPT (41)                     |
148//    | CLASS      | u_int16_t    | requestor's UDP payload size |
149//    | TTL        | u_int32_t    | extended RCODE and flags     |
150//    | RDLEN      | u_int16_t    | length of all RDATA          |
151//    | RDATA      | octet stream | {attribute,value} pairs      |
152//    +------------+--------------+------------------------------+
153#[derive(Copy, Clone, Debug, Eq, PartialEq)]
154pub struct OptHeader {
155    /// The bytes of the header.
156    inner: [u8; 9],
157}
158
159impl OptHeader {
160    pub fn for_record_slice(slice: &[u8]) -> &OptHeader {
161        assert!(slice.len() >= mem::size_of::<Self>());
162        unsafe { &*(slice.as_ptr() as *const OptHeader) }
163    }
164
165    pub fn for_record_slice_mut(slice: &mut [u8]) -> &mut OptHeader {
166        assert!(slice.len() >= mem::size_of::<Self>());
167        unsafe { &mut *(slice.as_ptr() as *mut OptHeader) }
168    }
169
170    pub fn udp_payload_size(&self) -> u16 {
171        BigEndian::read_u16(&self.inner[3..])
172    }
173
174    pub fn set_udp_payload_size(&mut self, value: u16) {
175        BigEndian::write_u16(&mut self.inner[3..], value)
176    }
177
178    pub fn rcode(&self, header: Header) -> OptRcode {
179        OptRcode::from_parts(header.rcode(), self.inner[5])
180    }
181
182    pub fn set_rcode(&mut self, rcode: OptRcode) {
183        self.inner[5] = rcode.ext()
184    }
185
186    pub fn version(&self) -> u8 {
187        self.inner[6]
188    }
189
190    pub fn set_version(&mut self, version: u8) {
191        self.inner[6] = version
192    }
193
194    pub fn dnssec_ok(&self) -> bool {
195        self.inner[7] & 0x80 != 0
196    }
197
198    pub fn set_dnssec_ok(&mut self, value: bool) {
199        if value {
200            self.inner[7] |= 0x80
201        }
202        else {
203            self.inner[7] &= 0x7F
204        }
205    }
206}
207
208impl Default for OptHeader {
209    fn default() -> Self {
210        OptHeader { inner: [0, 0, 41, 0, 0, 0, 0, 0, 0] }
211    }
212}
213
214impl Compose for OptHeader {
215    fn compose_len(&self) -> usize {
216        9
217    }
218
219    fn compose<B: BufMut>(&self, buf: &mut B) {
220        buf.put_slice(&self.inner)
221    }
222}
223
224
225//------------ OptRecord -----------------------------------------------------
226
227/// An entire OPT record.
228#[derive(Clone, Debug)]
229pub struct OptRecord {
230    udp_payload_size: u16,
231    ext_rcode: u8,
232    version: u8,
233    flags: u16,
234    data: Opt,
235}
236
237impl OptRecord {
238    pub fn from_record<N: ToDname>(record: Record<N, Opt>) -> Self {
239        OptRecord {
240            udp_payload_size: record.class().to_int(),
241            ext_rcode: (record.ttl() >> 24) as u8,
242            version: (record.ttl() >> 16) as u8,
243            flags: record.ttl() as u16,
244            data: record.into_data()
245        }
246    }
247
248    pub fn udp_payload_size(&self) -> u16 {
249        self.udp_payload_size
250    }
251
252    pub fn rcode(&self, header: Header) -> OptRcode {
253        OptRcode::from_parts(header.rcode(), self.ext_rcode)
254    }
255
256    pub fn version(&self) -> u8 {
257        self.version
258    }
259
260    pub fn dnssec_ok(&self) -> bool {
261        self.flags & 0x8000 != 0
262    }
263
264    pub fn as_opt(&self) -> &Opt {
265        &self.data
266    }
267}
268
269
270//--- From
271
272impl<N: ToDname> From<Record<N, Opt>> for OptRecord {
273    fn from(record: Record<N, Opt>) -> Self {
274        Self::from_record(record)
275    }
276}
277
278
279//--- Deref and AsRef
280
281impl ops::Deref for OptRecord {
282    type Target = Opt;
283
284    fn deref(&self) -> &Opt {
285        &self.data
286    }
287}
288
289impl AsRef<Opt> for OptRecord {
290    fn as_ref(&self) -> &Opt {
291        &self.data
292    }
293}
294
295
296//------------ OptionHeader --------------------------------------------------
297
298#[derive(Clone, Debug)]
299pub struct OptionHeader {
300    code: u16,
301    len: u16,
302}
303
304#[allow(len_without_is_empty)]
305impl OptionHeader {
306    pub fn new(code: u16, len: u16) -> Self {
307        OptionHeader { code, len }
308    }
309
310    pub fn code(&self) -> u16 {
311        self.code
312    }
313
314    pub fn len(&self) -> u16 {
315        self.len
316    }
317}
318
319impl Parse for OptionHeader {
320    type Err = ShortBuf;
321
322    fn parse(parser: &mut Parser) -> Result<Self, Self::Err> {
323        Ok(OptionHeader::new(parser.parse_u16()?, parser.parse_u16()?))
324    }
325
326    fn skip(parser: &mut Parser) -> Result<(), Self::Err> {
327        parser.advance(4)
328    }
329}
330
331impl Compose for OptionHeader {
332    fn compose_len(&self) -> usize {
333        4
334    }
335
336    fn compose<B: BufMut>(&self, buf: &mut B) {
337        self.code.compose(buf);
338        self.len.compose(buf);
339    }
340}
341
342
343//------------ OptIter -------------------------------------------------------
344
345#[derive(Clone, Debug)]
346pub struct OptIter<D: OptData> { 
347    parser: Parser,
348    marker: PhantomData<D>
349}
350
351impl<D: OptData> OptIter<D> {
352    fn new(bytes: Bytes) -> Self {
353        OptIter { parser: Parser::from_bytes(bytes), marker: PhantomData }
354    }
355}
356
357impl<D: OptData> Iterator for OptIter<D> {
358    type Item = Result<D, D::ParseErr>;
359
360    fn next(&mut self) -> Option<Self::Item> {
361        while self.parser.remaining() > 0 {
362            match self.next_step() {
363                Ok(Some(res)) => return Some(Ok(res)),
364                Ok(None) => { }
365                Err(err) => return Some(Err(err)),
366            }
367        }
368        None
369    }
370}
371
372impl<D: OptData> OptIter<D> {
373    fn next_step(&mut self) -> Result<Option<D>, D::ParseErr> {
374        let code = self.parser.parse_u16().unwrap().into();
375        let len = self.parser.parse_u16().unwrap() as usize;
376        D::parse_option(code, &mut self.parser, len)
377    }
378}
379
380
381//------------ OptData -------------------------------------------------------
382
383pub trait OptData: Compose + Sized {
384    type ParseErr;
385
386    fn code(&self) -> OptionCode;
387
388    fn parse_option(code: OptionCode, parser: &mut Parser, len: usize)
389                    -> Result<Option<Self>, Self::ParseErr>;
390}
391
392
393//------------ CodeOptData ---------------------------------------------------
394
395pub trait CodeOptData {
396    const CODE: OptionCode;
397}
398
399impl<T: CodeOptData + ParseAll + Compose + Sized> OptData for T {
400    type ParseErr = <Self as ParseAll>::Err;
401
402    fn code(&self) -> OptionCode { Self::CODE }
403
404    fn parse_option(code: OptionCode, parser: &mut Parser, len: usize)
405                    -> Result<Option<Self>, Self::ParseErr> {
406        if code == Self::CODE {
407            Self::parse_all(parser, len).map(Some)
408        }
409        else {
410            Ok(None)
411        }
412    }
413}
414
415
416//------------ UnknownOptData ------------------------------------------------
417
418#[derive(Clone, Debug)]
419pub struct UnknownOptData {
420    code: OptionCode,
421    data: Bytes,
422}
423
424impl UnknownOptData {
425    pub fn from_bytes(code: OptionCode, data: Bytes) -> Self {
426        UnknownOptData { code, data }
427    }
428
429    pub fn code(&self) -> OptionCode {
430        self.code
431    }
432
433    pub fn data(&self) -> &Bytes {
434        &self.data
435    }
436}
437
438
439//--- Compose
440
441impl Compose for UnknownOptData {
442    fn compose_len(&self) -> usize {
443        self.data.len()
444    }
445
446    fn compose<B: BufMut>(&self, buf: &mut B) {
447        buf.put_slice(self.data.as_ref())
448    }
449}
450
451
452//--- OptData
453
454impl OptData for UnknownOptData {
455    type ParseErr = ShortBuf;
456
457    fn code(&self) -> OptionCode {
458        self.code
459    }
460
461    fn parse_option(
462        code: OptionCode,
463        parser: &mut Parser,
464        len: usize
465    ) -> Result<Option<Self>, Self::ParseErr> {
466        parser.parse_bytes(len)
467            .map(|data| Some(Self::from_bytes(code, data)))
468    }
469}
470
471
472//============ Tests =========================================================
473
474#[cfg(test)]
475mod test {
476    use super::*;
477    use super::super::record::ParsedRecord;
478
479    #[test]
480    fn opt_record_header() {
481        let mut header = OptHeader::default();
482        header.set_udp_payload_size(0x1234);
483        header.set_rcode(OptRcode::BadVers);
484        header.set_version(0xbd);
485        header.set_dnssec_ok(true);
486        let mut buf = Vec::with_capacity(11);
487        header.compose(&mut buf);
488        0u16.compose(&mut buf);
489        let mut buf = Parser::from_bytes(buf.into());
490        let record = ParsedRecord::parse(&mut buf).unwrap()
491            .into_record::<Opt>().unwrap().unwrap();
492        let record = OptRecord::from_record(record);
493        assert_eq!(record.udp_payload_size(), 0x1234);
494        assert_eq!(record.ext_rcode, OptRcode::BadVers.ext());
495        assert_eq!(record.version(), 0xbd);
496        assert!(record.dnssec_ok());
497    }
498}
499