simple_dns/dns/rdata/
opt.rs

1use crate::{
2    bytes_buffer::BytesBuffer,
3    dns::{header::Header, WireFormat},
4    RCODE,
5};
6use std::borrow::Cow;
7
8use super::RR;
9
10pub mod masks {
11    pub const RCODE_MASK: u32 = 0b0000_0000_0000_0000_0000_0000_1111_1111;
12    pub const VERSION_MASK: u32 = 0b0000_0000_0000_0000_1111_1111_0000_0000;
13}
14
15/// OPT is a pseudo-rr used to carry control information  
16/// If an OPT record is present in a received request, responders MUST include an OPT record in their respective responses.  
17/// OPT RRs MUST NOT be cached, forwarded, or stored in or loaded from master files.  
18///
19/// There must be only one OPT record in the message.
20/// If a query message with more than one OPT RR is received, a FORMERR (RCODE=1) MUST be returned.
21#[derive(Debug, PartialEq, Eq, Hash, Clone)]
22pub struct OPT<'a> {
23    /// The variable part of this OPT RR
24    pub opt_codes: Vec<OPTCode<'a>>,
25    /// UDP packet size supported by the responder
26    pub udp_packet_size: u16,
27
28    /// EDNS version supported by the responder
29    pub version: u8,
30}
31
32impl RR for OPT<'_> {
33    const TYPE_CODE: u16 = 41;
34}
35
36impl<'a> WireFormat<'a> for OPT<'a> {
37    const MINIMUM_LEN: usize = 10;
38
39    fn parse(data: &mut BytesBuffer<'a>) -> crate::Result<Self>
40    where
41        Self: Sized,
42    {
43        // first 2 bytes where already skiped in the RData parse
44
45        // udp packet size comes from CLASS
46        let udp_packet_size = data.get_u16()?;
47        // version comes from ttl
48        let ttl = data.get_u32()?;
49        let version = ((ttl & masks::VERSION_MASK) >> masks::VERSION_MASK.trailing_zeros()) as u8;
50
51        data.advance(2)?;
52
53        let mut opt_codes = Vec::new();
54        while data.has_remaining() {
55            let code = data.get_u16()?;
56            let length = data.get_u16()? as usize; // length is the length of the data field in bytes
57
58            let inner_data = Cow::Borrowed(data.get_slice(length)?);
59            opt_codes.push(OPTCode {
60                code,
61                data: inner_data,
62            });
63        }
64
65        Ok(Self {
66            opt_codes,
67            udp_packet_size,
68            version,
69        })
70    }
71
72    fn write_to<T: std::io::Write>(&self, out: &mut T) -> crate::Result<()> {
73        for code in self.opt_codes.iter() {
74            out.write_all(&code.code.to_be_bytes())?;
75            out.write_all(&(code.data.len() as u16).to_be_bytes())?;
76            out.write_all(&code.data)?;
77        }
78
79        Ok(())
80    }
81
82    fn len(&self) -> usize {
83        self.opt_codes.iter().map(|o| o.data.len() + 4).sum()
84    }
85}
86
87impl OPT<'_> {
88    pub(crate) fn extract_rcode_from_ttl(ttl: u32, header: &Header) -> RCODE {
89        let mut rcode = (ttl & masks::RCODE_MASK) << 4;
90        rcode |= header.response_code as u32;
91        RCODE::from(rcode as u16)
92    }
93
94    pub(crate) fn encode_ttl(&self, header: &Header) -> u32 {
95        let mut ttl: u32 = (header.response_code as u32 & masks::RCODE_MASK) >> 4;
96        ttl |= (self.version as u32) << masks::VERSION_MASK.trailing_zeros();
97        ttl
98    }
99    /// Transforms the inner data into its owned type
100    pub fn into_owned<'b>(self) -> OPT<'b> {
101        OPT {
102            // length: self.length,
103            udp_packet_size: self.udp_packet_size,
104            version: self.version,
105            opt_codes: self.opt_codes.into_iter().map(|o| o.into_owned()).collect(),
106        }
107    }
108}
109
110/// Represents the variable part of an OPT rr
111#[derive(Debug, PartialEq, Eq, Hash, Clone)]
112pub struct OPTCode<'a> {
113    // TODO: include an OPT_CODE enum???
114    /// Assigned by the Expert Review process as defined by the DNSEXT working group and the IESG.
115    pub code: u16,
116    /// Varies per OPTION-CODE.  MUST be treated as a bit field.
117    pub data: Cow<'a, [u8]>,
118}
119
120impl OPTCode<'_> {
121    /// Transforms the inner data into its owned type
122    pub fn into_owned<'b>(self) -> OPTCode<'b> {
123        OPTCode {
124            code: self.code,
125            data: self.data.into_owned().into(),
126        }
127    }
128}
129
130#[cfg(test)]
131mod tests {
132    use crate::{rdata::RData, Name, ResourceRecord};
133
134    use super::*;
135
136    #[test]
137    fn parse_and_write_opt_empty() {
138        let header = Header::new_reply(1, crate::OPCODE::StandardQuery);
139
140        let opt = OPT {
141            udp_packet_size: 500,
142            version: 2,
143            opt_codes: Vec::new(),
144        };
145        let opt_rr = ResourceRecord {
146            ttl: opt.encode_ttl(&header),
147            name: Name::new_unchecked("."),
148            class: crate::CLASS::IN,
149            cache_flush: false,
150            rdata: RData::OPT(opt),
151        };
152
153        let mut data = Vec::new();
154        assert!(opt_rr.write_to(&mut data).is_ok());
155
156        let opt = match ResourceRecord::parse(&mut data[..].into())
157            .expect("failed to parse")
158            .rdata
159        {
160            RData::OPT(rdata) => rdata,
161            _ => unreachable!(),
162        };
163
164        assert_eq!(data.len(), opt_rr.len());
165        assert_eq!(500, opt.udp_packet_size);
166        assert_eq!(2, opt.version);
167        assert!(opt.opt_codes.is_empty());
168    }
169
170    #[test]
171    fn parse_and_write_opt() {
172        let header = Header::new_reply(1, crate::OPCODE::StandardQuery);
173
174        let opt = OPT {
175            udp_packet_size: 500,
176            version: 2,
177            opt_codes: vec![
178                OPTCode {
179                    code: 1,
180                    data: Cow::Owned(vec![255, 255]),
181                },
182                OPTCode {
183                    code: 2,
184                    data: Cow::Owned(vec![255, 255, 255]),
185                },
186            ],
187        };
188
189        let opt_rr = ResourceRecord {
190            ttl: opt.encode_ttl(&header),
191            name: Name::new_unchecked("."),
192            class: crate::CLASS::IN,
193            cache_flush: false,
194            rdata: RData::OPT(opt),
195        };
196
197        let mut data = Vec::new();
198        assert!(opt_rr.write_to(&mut data).is_ok());
199
200        let mut opt = match ResourceRecord::parse(&mut data[..].into())
201            .expect("failed to parse")
202            .rdata
203        {
204            RData::OPT(rdata) => rdata,
205            _ => unreachable!(),
206        };
207
208        assert_eq!(data.len(), opt_rr.len());
209        assert_eq!(500, opt.udp_packet_size);
210        assert_eq!(2, opt.version);
211        assert_eq!(2, opt.opt_codes.len());
212
213        let opt_code = opt.opt_codes.pop().unwrap();
214        assert_eq!(2, opt_code.code);
215        assert_eq!(vec![255, 255, 255], *opt_code.data);
216
217        let opt_code = opt.opt_codes.pop().unwrap();
218        assert_eq!(1, opt_code.code);
219        assert_eq!(vec![255, 255], *opt_code.data);
220    }
221}