use crate::{
proto::{
ProtoError,
op::{Edns, EmitAndCount, Header, LowerQuery, Message, Metadata, emit_message_parts},
rr::{Record, rdata::TSIG},
serialize::binary::{
BinDecodable, BinDecoder, BinEncodable, BinEncoder, DecodeError, NameEncoding,
},
},
zone_handler::LookupError,
};
#[derive(Debug, PartialEq)]
pub struct MessageRequest {
pub metadata: Metadata,
pub queries: Queries,
pub answers: Vec<Record>,
pub authorities: Vec<Record>,
pub additionals: Vec<Record>,
pub signature: Option<Box<Record<TSIG>>>,
pub edns: Option<Edns>,
}
impl MessageRequest {
pub(crate) fn read(decoder: &mut BinDecoder<'_>, header: Header) -> Result<Self, DecodeError> {
let Header {
mut metadata,
counts,
} = header;
let queries = Queries::read(decoder, counts.queries as usize)?;
let (answers, _, _) = Message::read_records(decoder, counts.answers as usize, false)?;
let (authorities, _, _) =
Message::read_records(decoder, counts.authorities as usize, false)?;
let (additionals, edns, signature) =
Message::read_records(decoder, counts.additionals as usize, true)?;
if let Some(edns) = &edns {
let high_response_code = edns.rcode_high();
metadata.merge_response_code(high_response_code);
}
Ok(Self {
metadata,
queries,
answers,
authorities,
additionals,
signature,
edns,
})
}
#[cfg(any(test, feature = "testing"))]
pub fn mock(metadata: Metadata, query: impl Into<LowerQuery>) -> Self {
Self {
metadata,
queries: Queries::new(vec![query.into()]),
answers: Vec::new(),
authorities: Vec::new(),
additionals: Vec::new(),
signature: None,
edns: None,
}
}
pub fn max_payload(&self) -> u16 {
let max_size = self.edns.as_ref().map_or(512, Edns::max_payload);
if max_size < 512 { 512 } else { max_size }
}
pub fn version(&self) -> u8 {
self.edns.as_ref().map_or(0, Edns::version)
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct Queries {
queries: Vec<LowerQuery>,
original: Box<[u8]>,
}
impl Queries {
#[cfg(any(test, feature = "testing"))]
pub fn new(query: Vec<LowerQuery>) -> Self {
let mut encoded = Vec::new();
let mut encoder = BinEncoder::new(&mut encoded);
for q in query.iter() {
q.emit(&mut encoder).unwrap();
}
Self {
queries: query,
original: encoded.into_boxed_slice(),
}
}
pub fn read(decoder: &mut BinDecoder<'_>, num_queries: usize) -> Result<Self, DecodeError> {
let queries_start = decoder.index();
let mut queries = Vec::with_capacity(num_queries);
for _ in 0..num_queries {
queries.push(LowerQuery::read(decoder)?);
}
let original = decoder
.slice_from(queries_start)?
.to_vec()
.into_boxed_slice();
Ok(Self { queries, original })
}
pub fn len(&self) -> usize {
self.queries.len()
}
pub fn is_empty(&self) -> bool {
self.queries.is_empty()
}
pub fn queries(&self) -> &[LowerQuery] {
&self.queries
}
pub fn as_bytes(&self) -> &[u8] {
self.original.as_ref()
}
pub(crate) fn as_emit_and_count(&self) -> QueriesEmitAndCount<'_> {
QueriesEmitAndCount {
length: self.queries.len(),
first_query: self.queries.first(),
cached_serialized: self.original.as_ref(),
}
}
pub(crate) fn try_as_query(&self) -> Result<&LowerQuery, LookupError> {
let count = self.queries.len();
if count != 1 {
return Err(LookupError::BadQueryCount(count));
}
Ok(&self.queries[0])
}
pub(crate) fn empty() -> Self {
Self {
queries: Vec::new(),
original: (*b"").into(),
}
}
}
pub(crate) struct QueriesEmitAndCount<'q> {
length: usize,
first_query: Option<&'q LowerQuery>,
cached_serialized: &'q [u8],
}
impl EmitAndCount for QueriesEmitAndCount<'_> {
fn emit(&mut self, encoder: &mut BinEncoder<'_>) -> Result<usize, ProtoError> {
let original_offset = encoder.offset();
encoder.emit_vec(self.cached_serialized)?;
if matches!(encoder.name_encoding(), NameEncoding::Compressed) && self.first_query.is_some()
{
encoder.store_label_pointer(
original_offset,
original_offset + self.cached_serialized.len(),
)
}
Ok(self.length)
}
}
impl BinEncodable for MessageRequest {
fn emit(&self, encoder: &mut BinEncoder<'_>) -> Result<(), ProtoError> {
emit_message_parts(
&self.metadata,
&mut self.queries.queries.iter(),
&mut self.answers.iter(),
&mut self.authorities.iter(),
&mut self.additionals.iter(),
self.edns.as_ref(),
self.signature.as_deref(),
encoder,
)?;
Ok(())
}
}
pub trait UpdateRequest {
fn id(&self) -> u16;
fn zone(&self) -> Result<&LowerQuery, LookupError>;
fn prerequisites(&self) -> &[Record];
fn updates(&self) -> &[Record];
fn additionals(&self) -> &[Record];
fn signature(&self) -> Option<&Record<TSIG>>;
}
impl UpdateRequest for MessageRequest {
fn id(&self) -> u16 {
self.metadata.id
}
fn zone(&self) -> Result<&LowerQuery, LookupError> {
self.queries.try_as_query()
}
fn prerequisites(&self) -> &[Record] {
&self.answers
}
fn updates(&self) -> &[Record] {
&self.authorities
}
fn additionals(&self) -> &[Record] {
&self.additionals
}
fn signature(&self) -> Option<&Record<TSIG>> {
self.signature.as_deref()
}
}