sawp-dns 0.11.1

SAWP Protocol Parser for DNS
Documentation
//! Extended DNS
//! [RFC6891](https://tools.ietf.org/html/rfc6891)
//!
//! EDNS adds information to DNS messages in the form of pseudo-Resource Records
//! ("pseudo-RR"s) included in the "additional data" section of a DNS message.
//! This takes the form of an OPT RR which contains a UDP payload size the server supports,
//! an EDNS version number, flags, an extended RCode, and possibly a variable number of KVP options.
//! Most notably this allows servers to advertise that they can process UDP messages of size > 512
//! bytes (the default maximum for DNS over UDP).

#![allow(clippy::upper_case_acronyms)]

use nom::bytes::streaming::take;
use nom::number::streaming::be_u16;

use num_enum::TryFromPrimitive;

use sawp_flags::{Flag, Flags};

use std::convert::TryFrom;

use crate::{custom_many0, ErrorFlags, IResult};
#[cfg(feature = "ffi")]
use sawp_ffi::GenerateFFI;

#[derive(Clone, Copy, Debug, PartialEq, TryFromPrimitive)]
#[repr(u16)]
pub enum OptionCode {
    /// Long-Lived Queries
    LLQ = 1,
    /// Update Leases
    UL = 2,
    /// Name Server Identifier
    NSID = 3,
    /// DNSSEC Algorithm Understood
    DAU = 5,
    /// DS Hash Understood
    DHU = 6,
    /// NSEC3 Hash Understood
    N3U = 7,
    /// 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
    EDNSCLIENTSUBNET = 8,
    /// See https://tools.ietf.org/html/rfc7314
    EDNSEXPIRE = 9,
    /// See https://tools.ietf.org/html/rfc7873#section-4
    COOKIE = 10,
    /// Signals a variable idle timeout
    EDNSTCPKEEPALIVE = 11,
    /// Allows DNS clients and servers to pad requests and responses by a variable number of octets
    PADDING = 12,
    /// Allows a security-aware validating resolver to send a single query requesting a complete validation path along with the regular answer
    CHAIN = 13,
    /// Provides origin authentication using digital signatures
    EDNSKEYTAG = 14,
    /// Returning additional information about the cause of DNS errors
    EDNSERROR = 15,
    /// Draft, usage is being determined. See https://www.ietf.org/archive/id/draft-bellis-dnsop-edns-tags-01.txt
    EDNSCLIENTTAG = 16,
    /// Draft, usage is being determined. See https://www.ietf.org/archive/id/draft-bellis-dnsop-edns-tags-01.txt
    EDNSSERVERTAG = 17,
    /// A way of identifying a device via DNS in the OPT RDATA
    DEVICEID = 26946,
    UNKNOWN,
}

impl OptionCode {
    pub fn from_raw(val: u16) -> Self {
        OptionCode::try_from(val).unwrap_or(OptionCode::UNKNOWN)
    }
}

#[cfg_attr(feature = "ffi", derive(GenerateFFI))]
#[cfg_attr(feature = "ffi", sawp_ffi(prefix = "sawp_dns"))]
#[derive(Debug, PartialEq)]
pub struct EdnsOption {
    #[cfg_attr(feature = "ffi", sawp_ffi(copy))]
    pub code: OptionCode,
    pub data: Vec<u8>,
}

impl EdnsOption {
    pub fn parse(input: &[u8]) -> IResult<(EdnsOption, Flags<ErrorFlags>)> {
        let (input, (code, inner_error_flags)) = EdnsOption::parse_option_code(input)?;
        let (input, option_length) = be_u16(input)?;
        let (input, data) = take(option_length)(input)?;

        Ok((
            input,
            (
                EdnsOption {
                    code,
                    data: data.to_vec(),
                },
                inner_error_flags,
            ),
        ))
    }

    fn parse_option_code(input: &[u8]) -> IResult<(OptionCode, Flags<ErrorFlags>)> {
        let mut error_flags = ErrorFlags::none();

        let (input, raw_option_code) = be_u16(input)?;
        let code = OptionCode::from_raw(raw_option_code);
        if code == OptionCode::UNKNOWN {
            error_flags |= ErrorFlags::EdnsParseFail;
        }
        Ok((input, (code, error_flags)))
    }

    pub fn parse_options(
        input: &[u8],
        data_len: u16,
    ) -> IResult<(Vec<EdnsOption>, Flags<ErrorFlags>)> {
        let mut error_flags = ErrorFlags::none();
        if data_len < 4 {
            return Ok((input, (vec![], error_flags)));
        }

        let (input, options) = custom_many0(|input| {
            let (input, (option, inner_error_flags)) = EdnsOption::parse(input)?;
            error_flags |= inner_error_flags;
            Ok((input, option))
        })(input)?;

        Ok((input, (options, error_flags)))
    }
}