simple_dns/dns/rdata/
opt.rs

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