nonymous 0.7.0

DNS protocol and algorithm library
Documentation
use crate::{
    core::Rcode,
    emit::{
        message::{MessageBuilder, MessageError, QdSection},
        Buffer, NewBuilder, Sink, SinkError,
    },
    view::{Header, Message, View},
    MAX_SUPPORTED_EDNS_VERSION,
};

error!(ResponseError, Sink, Message);
/// failed to start emitting response
#[derive(Debug, displaydoc::Display)]
#[prefix_enum_doc_attributes]
pub enum ResponseError {
    /// failed to create sink: {0}
    Sink(SinkError),

    /// failed to emit message: {0}
    Message(MessageError),
}

impl From<SinkError> for ResponseError {
    fn from(inner: SinkError) -> Self {
        Self::Sink(inner)
    }
}

impl From<MessageError> for ResponseError {
    fn from(inner: MessageError) -> Self {
        Self::Message(inner)
    }
}

/// Starts building a new response (QR=1) for the given query (QR=0).
///
/// Returns the query view and response builder, or writes an error response to the given `buffer`
/// and returns `None`, or returns a [`MessageError`] if either of those fail.
///
/// The algorithm is as follows:
/// 1. If the query header is malformed, return an empty response with RCODE = FormErr
/// 2. Start a new response with the same ID + OPCODE + RD, defaulting to RCODE = NotImp
/// 3. If the query is well-formed and its EDNS version (if any) supported, return Ok(Some())
/// 4. If the query has any EDNS OPT RR, add EDNS OPT RR to the response
/// 5. If the query EDNS version is unimplemented, finish the response with RCODE = BADVERS
/// 6. If the query is otherwise malformed, finish the response with RCODE = FormErr
/// 7. Return Ok(None)
pub fn response<'q, 'r>(
    query: &'q [u8],
    buffer: &'r mut dyn Buffer,
    udp: bool,
) -> Result<Option<(Message<'q>, MessageBuilder<'r, Sink<'r>, QdSection>)>, ResponseError> {
    use crate::view::ExtensionError::UnimplementedVersion;
    use crate::view::MessageError;
    use crate::view::MessageErrorKind as Kind;

    // limit to 512 iff udp, until query indicates otherwise
    let limit = if udp {
        Some(512)
    } else {
        Sink::capacity_limit(buffer)?
    };
    let new = |buffer, limit| -> Result<_, ResponseError> {
        Ok(MessageBuilder::new(Sink::with_limit(buffer, limit)?)?.qr(true))
    };

    let header = match Header::view(query, ..) {
        Err(_) => {
            new(buffer, limit)?.rcode(Rcode::FormErr).finish()?.finish();
            return Ok(None);
        }
        Ok((x, _)) => x,
    };
    let new = |buffer, limit| -> Result<_, ResponseError> {
        Ok(new(buffer, limit)?
            .id(header.id())
            .opcode(header.opcode())
            .rd(header.rd())
            .rcode(Rcode::NotImp))
    };

    let query = match Message::view(query, ..) {
        Err(MessageError(edns_version, inner)) => {
            let result = new(buffer, limit)?.rcode(match inner {
                Kind::Extension(UnimplementedVersion) => Rcode::BADVERS,
                _ => Rcode::FormErr,
            });
            // RFC 6891 § 6.1.3 “All responses MUST be limited in format to the VERSION level of the
            // request, but the VERSION of each response SHOULD be the highest implementation level
            // of the responder.
            if edns_version.is_some() {
                result
                    .extension()?
                    .version(MAX_SUPPORTED_EDNS_VERSION)
                    .finish()?
                    .finish()?
                    .finish();
            } else {
                result.finish()?.finish();
            }
            return Ok(None);
        }
        Ok((x, _)) => x,
    };

    // compute limit as min(capacity limit, query udp limit)
    let limit = Sink::response_limit(buffer, udp, &query)?;

    Ok(Some((query, new(buffer, limit)?)))
}

#[cfg(test)]
mod test {
    use arrayvec::ArrayVec;

    use crate::{
        core::Rcode,
        emit::Buffer,
        view::{Message, View},
    };

    declare_any_error!(AnyError);

    fn a12() -> ArrayVec<u8, 4096> {
        ArrayVec::new()
    }

    #[test]
    fn response() -> Result<(), AnyError> {
        // empty FormErr response if header malformed
        let mut buffer = a12();
        assert!(super::response(b"", &mut buffer, false)?.is_none());
        let (result, _) = Message::view(&buffer, ..)?;
        assert_eq!(result.header().qr(), true);
        assert_eq!(result.rcode(), Rcode::FormErr);

        // matching NotImp response by default
        let mut buffer = a12();
        let (_, response) = super::response(
            b"\x13\x13\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
            &mut buffer,
            false,
        )?
        .unwrap();
        response.finish()?.finish();
        let (result, _) = Message::view(&buffer, ..)?;
        assert_eq!(result.header().id(), 0x1313);
        assert_eq!(result.header().qr(), true);
        assert_eq!(result.rcode(), Rcode::NotImp);

        // matching BADVERS response if EDNS version unimplemented
        let mut buffer = a12();
        assert!(super::response(
            b"\x13\x13\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\0\x00\x29\x10\x00\x00\x01\x00\x00\x00\x00",
            &mut buffer,
            false,
        )?.is_none());
        let (result, _) = Message::view(&buffer, ..)?;
        assert_eq!(result.header().id(), 0x1313);
        assert_eq!(result.header().qr(), true);
        assert_eq!(result.rcode(), Rcode::BADVERS);
        assert_eq!(result.opt().map(|x| x.version()), Some(0));

        // matching FormErr response if otherwise malformed
        let mut buffer = a12();
        assert!(super::response(
            b"\x13\x13\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\0\x00\x29\x10\x00\x00\x00\x00\x00\x00\x00",
            &mut buffer,
            false,
        )?.is_none());
        let (result, _) = Message::view(&buffer, ..)?;
        assert_eq!(result.header().id(), 0x1313);
        assert_eq!(result.header().qr(), true);
        assert_eq!(result.rcode(), Rcode::FormErr);
        assert_eq!(result.opt().map(|x| x.version()), Some(0));

        Ok(())
    }

    #[test]
    #[rustfmt::skip]
    fn response_limit() -> Result<(), AnyError> {
        response_limit_test(Some(4096), false, &mut a12(), b"\x13\x13\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")?;
        response_limit_test(Some(512), true, &mut a12(), b"\x13\x13\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")?;
        response_limit_test(Some(512), true, &mut a12(), b"\x13\x13\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\0\x00\x29\x01\xFF\x00\x00\x00\x00\x00\x00")?;
        response_limit_test(Some(512), true, &mut a12(), b"\x13\x13\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\0\x00\x29\x02\x00\x00\x00\x00\x00\x00\x00")?;
        response_limit_test(Some(513), true, &mut a12(), b"\x13\x13\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\0\x00\x29\x02\x01\x00\x00\x00\x00\x00\x00")?;
        response_limit_test(Some(4095), true, &mut a12(), b"\x13\x13\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\0\x00\x29\x0F\xFF\x00\x00\x00\x00\x00\x00")?;
        response_limit_test(Some(4096), true, &mut a12(), b"\x13\x13\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\0\x00\x29\x10\x00\x00\x00\x00\x00\x00\x00")?;
        response_limit_test(Some(4096), true, &mut a12(), b"\x13\x13\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\0\x00\x29\x10\x01\x00\x00\x00\x00\x00\x00")?;

        Ok(())
    }

    #[cfg(feature = "alloc")]
    #[test]
    #[rustfmt::skip]
    fn response_limit_alloc() -> Result<(), AnyError> {
        response_limit_test(None, false, &mut alloc::vec![], b"\x13\x13\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")?;
        response_limit_test(Some(512), true, &mut alloc::vec![], b"\x13\x13\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")?;
        response_limit_test(Some(512), true, &mut alloc::vec![], b"\x13\x13\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\0\x00\x29\x01\xFF\x00\x00\x00\x00\x00\x00")?;
        response_limit_test(Some(512), true, &mut alloc::vec![], b"\x13\x13\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\0\x00\x29\x02\x00\x00\x00\x00\x00\x00\x00")?;
        response_limit_test(Some(513), true, &mut alloc::vec![], b"\x13\x13\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\0\x00\x29\x02\x01\x00\x00\x00\x00\x00\x00")?;
        response_limit_test(Some(4095), true, &mut alloc::vec![], b"\x13\x13\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\0\x00\x29\x0F\xFF\x00\x00\x00\x00\x00\x00")?;
        response_limit_test(Some(4096), true, &mut alloc::vec![], b"\x13\x13\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\0\x00\x29\x10\x00\x00\x00\x00\x00\x00\x00")?;
        response_limit_test(Some(4097), true, &mut alloc::vec![], b"\x13\x13\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\0\x00\x29\x10\x01\x00\x00\x00\x00\x00\x00")?;

        Ok(())
    }

    fn response_limit_test(
        expected: Option<u16>,
        udp: bool,
        buffer: &mut dyn Buffer,
        query: &[u8],
    ) -> Result<(), AnyError> {
        let (_, response) = super::response(query, buffer, udp)?.unwrap();
        assert_eq!(response.finish()?.limit().map(u16::from), expected);

        Ok(())
    }
}

#[cfg(all(test, feature = "bench"))]
mod bench {
    extern crate test;

    use test::Bencher;

    #[bench]
    fn response(bencher: &mut Bencher) {
        let source = include_bytes!("../samples/daria.daz.cat.query.dns");
        bencher.iter(|| -> Result<usize, ()> {
            let mut buffer = alloc::vec![];
            let (query, response) = super::response(source, &mut buffer, true)
                .map_err(|_| ())?
                .expect("guaranteed by input");
            let question = query.qd().next().expect("guaranteed by input");
            response
                .into_an()
                .record_with_name(&question.qname(), question.qtype(), question.qclass())
                .map_err(|_| ())?
                .push_rdata(&[192, 0, 2, 13])
                .map_err(|_| ())?
                .finish()
                .map_err(|_| ())?
                .finish()
                .map_err(|_| ())?
                .finish();
            Ok(buffer.len())
        });
    }
}