sawp_dns/
edns.rs

1//! Extended DNS
2//! [RFC6891](https://tools.ietf.org/html/rfc6891)
3//!
4//! EDNS adds information to DNS messages in the form of pseudo-Resource Records
5//! ("pseudo-RR"s) included in the "additional data" section of a DNS message.
6//! This takes the form of an OPT RR which contains a UDP payload size the server supports,
7//! an EDNS version number, flags, an extended RCode, and possibly a variable number of KVP options.
8//! Most notably this allows servers to advertise that they can process UDP messages of size > 512
9//! bytes (the default maximum for DNS over UDP).
10
11use nom::bytes::streaming::take;
12use nom::number::streaming::be_u16;
13
14use num_enum::TryFromPrimitive;
15
16use sawp_flags::{Flag, Flags};
17
18use std::convert::TryFrom;
19
20use crate::{custom_many0, ErrorFlags, IResult};
21#[cfg(feature = "ffi")]
22use sawp_ffi::GenerateFFI;
23
24#[derive(Clone, Copy, Debug, PartialEq, Eq, TryFromPrimitive)]
25#[repr(u16)]
26pub enum OptionCode {
27    /// Long-Lived Queries
28    LLQ = 1,
29    /// Update Leases
30    UL = 2,
31    /// Name Server Identifier
32    NSID = 3,
33    /// DNSSEC Algorithm Understood
34    DAU = 5,
35    /// DS Hash Understood
36    DHU = 6,
37    /// NSEC3 Hash Understood
38    N3U = 7,
39    /// EDNS0 option to allow Recursive Resolvers, if they are willing, to forward details about the origin network from which a query is coming when talking to other nameservers
40    EDNSCLIENTSUBNET = 8,
41    /// See https://tools.ietf.org/html/rfc7314
42    EDNSEXPIRE = 9,
43    /// See https://tools.ietf.org/html/rfc7873#section-4
44    COOKIE = 10,
45    /// Signals a variable idle timeout
46    EDNSTCPKEEPALIVE = 11,
47    /// Allows DNS clients and servers to pad requests and responses by a variable number of octets
48    PADDING = 12,
49    /// Allows a security-aware validating resolver to send a single query requesting a complete validation path along with the regular answer
50    CHAIN = 13,
51    /// Provides origin authentication using digital signatures
52    EDNSKEYTAG = 14,
53    /// Returning additional information about the cause of DNS errors
54    EDNSERROR = 15,
55    /// Draft, usage is being determined. See https://www.ietf.org/archive/id/draft-bellis-dnsop-edns-tags-01.txt
56    EDNSCLIENTTAG = 16,
57    /// Draft, usage is being determined. See https://www.ietf.org/archive/id/draft-bellis-dnsop-edns-tags-01.txt
58    EDNSSERVERTAG = 17,
59    /// A way of identifying a device via DNS in the OPT RDATA
60    DEVICEID = 26946,
61    UNKNOWN,
62}
63
64impl OptionCode {
65    pub fn from_raw(val: u16) -> Self {
66        OptionCode::try_from(val).unwrap_or(OptionCode::UNKNOWN)
67    }
68}
69
70#[cfg_attr(feature = "ffi", derive(GenerateFFI))]
71#[cfg_attr(feature = "ffi", sawp_ffi(prefix = "sawp_dns"))]
72#[derive(Debug, PartialEq, Eq)]
73pub struct EdnsOption {
74    #[cfg_attr(feature = "ffi", sawp_ffi(copy))]
75    pub code: OptionCode,
76    pub data: Vec<u8>,
77}
78
79impl EdnsOption {
80    pub fn parse(input: &[u8]) -> IResult<(EdnsOption, Flags<ErrorFlags>)> {
81        let (input, (code, inner_error_flags)) = EdnsOption::parse_option_code(input)?;
82        let (input, option_length) = be_u16(input)?;
83        let (input, data) = take(option_length)(input)?;
84
85        Ok((
86            input,
87            (
88                EdnsOption {
89                    code,
90                    data: data.to_vec(),
91                },
92                inner_error_flags,
93            ),
94        ))
95    }
96
97    fn parse_option_code(input: &[u8]) -> IResult<(OptionCode, Flags<ErrorFlags>)> {
98        let mut error_flags = ErrorFlags::none();
99
100        let (input, raw_option_code) = be_u16(input)?;
101        let code = OptionCode::from_raw(raw_option_code);
102        if code == OptionCode::UNKNOWN {
103            error_flags |= ErrorFlags::EdnsParseFail;
104        }
105        Ok((input, (code, error_flags)))
106    }
107
108    pub fn parse_options(
109        input: &[u8],
110        data_len: u16,
111    ) -> IResult<(Vec<EdnsOption>, Flags<ErrorFlags>)> {
112        let mut error_flags = ErrorFlags::none();
113        if data_len < 4 {
114            return Ok((input, (vec![], error_flags)));
115        }
116
117        let (input, options) = custom_many0(|input| {
118            let (input, (option, inner_error_flags)) = EdnsOption::parse(input)?;
119            error_flags |= inner_error_flags;
120            Ok((input, option))
121        })(input)?;
122
123        Ok((input, (options, error_flags)))
124    }
125}