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);
#[derive(Debug, displaydoc::Display)]
#[prefix_enum_doc_attributes]
pub enum ResponseError {
Sink(SinkError),
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)
}
}
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;
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,
});
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,
};
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> {
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);
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);
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));
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())
});
}
}