use crate::{
Error, Result,
bytes::{Cursor, Reader},
constants::HEADER_LENGTH,
message::{
Header, Question, RecordsSection,
reader::{
NameRef, QuestionRef, RecordHeader, RecordHeaderRef, RecordMarker, RecordOffset,
SectionTracker,
},
},
names::DName,
records::{Class, Opt, Type, data::RData},
};
#[derive(Debug)]
pub struct MessageReader<'a> {
cursor: Cursor<'a>,
section_tracker: SectionTracker,
done: bool,
}
impl<'s, 'a: 's> MessageReader<'a> {
#[inline]
pub fn new(msg: &'a [u8]) -> Result<MessageReader<'a>> {
if msg.len() > u16::MAX as usize {
return Err(Error::MessageTooLong(msg.len()));
}
Ok(MessageReader {
cursor: Cursor::new(msg),
section_tracker: Default::default(),
done: false,
})
}
#[inline]
pub fn header(&mut self) -> Result<Header> {
let res = self.header_impl();
if res.is_err() {
self.done = true;
}
res
}
#[inline(always)]
fn header_impl(&mut self) -> Result<Header> {
let header = self.cursor.read()?;
self.section_tracker.set(&header);
Ok(header)
}
pub fn seek(&mut self, section: RecordsSection) -> Result<()> {
if self.done {
return Err(Error::ReaderDone);
}
if let Some(offset) = self.section_tracker.section_offset(section) {
self.cursor.set_pos(offset);
self.section_tracker.seek(section);
return Ok(());
}
if self.cursor.pos() != HEADER_LENGTH {
return Err(Error::RecordsSectionOffsetUnknown(section));
}
let res = self.seek_impl(section);
if res.is_err() {
self.done = true;
}
res
}
#[inline(always)]
fn seek_impl(&mut self, section: RecordsSection) -> Result<()> {
self.skip_questions_impl()?;
match section {
RecordsSection::Answer => Ok(()),
RecordsSection::Authority => self.skip_section_impl(RecordsSection::Answer),
RecordsSection::Additional => {
self.skip_section_impl(RecordsSection::Answer)?;
self.skip_section_impl(RecordsSection::Authority)
}
}
}
#[inline(always)]
fn skip_section_impl(&mut self, section: RecordsSection) -> Result<()> {
while self.section_tracker.records_left_in(section) > 0 {
let marker = self.marker_impl()?;
self.skip_record_data_impl(&marker)?;
}
Ok(())
}
#[inline]
pub fn has_questions(&self) -> bool {
self.questions_count() > 0
}
#[inline]
pub fn questions_count(&self) -> usize {
if !self.done {
self.section_tracker.questions_left()
} else {
0
}
}
#[inline]
pub fn question(&mut self) -> Result<Question> {
question!(self, question_check_no_questions)
}
#[inline]
pub fn question_ref(&'s mut self) -> Result<QuestionRef<'a>> {
question!(self, question_check_no_questions)
}
#[inline]
pub fn the_question(&mut self) -> Result<Question> {
question!(self, question_check_single_question)
}
#[inline]
pub fn the_question_ref(&'s mut self) -> Result<QuestionRef<'a>> {
question!(self, question_check_single_question)
}
pub fn skip_questions(&mut self) -> Result<()> {
if self.done {
return Err(Error::ReaderDone);
}
let res = self.skip_questions_impl();
if res.is_err() {
self.done = true;
}
res
}
#[inline(always)]
fn skip_questions_impl(&mut self) -> Result<()> {
while self.section_tracker.questions_left() > 0 {
self.cursor.skip_question()?;
self.section_tracker.question_read(self.cursor.pos());
}
Ok(())
}
#[inline]
pub fn has_records(&self) -> bool {
self.records_count() > 0
}
#[inline]
pub fn has_records_in(&self, section: RecordsSection) -> bool {
self.records_count_in(section) > 0
}
#[inline]
pub fn records_count(&self) -> usize {
if !self.done {
self.section_tracker.records_left()
} else {
0
}
}
#[inline]
pub fn records_count_in(&self, section: RecordsSection) -> usize {
if !self.done {
self.section_tracker.records_left_in(section)
} else {
0
}
}
#[inline]
pub fn record_marker(&mut self) -> Result<RecordMarker> {
if self.done {
return Err(Error::ReaderDone);
}
let res = self.marker_impl();
if res.is_err() {
self.done = true;
}
res
}
#[inline(always)]
fn marker_impl(&mut self) -> Result<RecordMarker> {
let pos = self.cursor.pos();
let section = self.calc_section()?;
self.cursor.skip_domain_name()?;
self.raw_marker_impl(pos, section)
}
#[inline(always)]
fn raw_marker_impl(&mut self, pos: usize, section: RecordsSection) -> Result<RecordMarker> {
let offset = RecordOffset {
offset: pos,
type_offset: self.cursor.pos(),
};
let rtype = Type::from(self.cursor.u16_be()?);
let rclass = Class::from(self.cursor.u16_be()?);
let ttl = self.cursor.u32_be()?;
let rdlen = self.cursor.u16_be()?;
Ok(RecordMarker {
offset,
rtype,
rclass,
ttl,
rdlen,
section,
})
}
#[inline]
pub fn record_header_ref(&'s mut self) -> Result<RecordHeaderRef<'a>> {
if self.done {
return Err(Error::ReaderDone);
}
let res = self.record_header_ref_impl();
if res.is_err() {
self.done = true;
}
res
}
#[inline(always)]
fn record_header_ref_impl(&'s mut self) -> Result<RecordHeaderRef<'a>> {
let pos = self.cursor.pos();
let section = self.calc_section()?;
let name_ref = NameRef::new(self.cursor.clone());
self.cursor.skip_domain_name()?;
let marker = self.raw_marker_impl(pos, section)?;
Ok(RecordHeaderRef { name_ref, marker })
}
#[inline]
pub fn record_header<N: DName>(&mut self) -> Result<RecordHeader<N>> {
if self.done {
return Err(Error::ReaderDone);
}
let res = self.record_header_impl::<N>();
if res.is_err() {
self.done = true;
}
res
}
#[inline(always)]
fn record_header_impl<N: DName>(&mut self) -> Result<RecordHeader<N>> {
let pos = self.cursor.pos();
let section = self.calc_section()?;
let name = N::from_cursor(&mut self.cursor)?;
let marker = self.raw_marker_impl(pos, section)?;
Ok(RecordHeader { name, marker })
}
#[inline]
pub fn skip_record_data(&mut self, marker: &RecordMarker) -> Result<()> {
debug_assert!(self.cursor.pos() == marker.rdata_pos());
if self.done {
return Err(Error::ReaderDone);
}
self.skip_record_data_impl(marker)
}
#[inline(always)]
fn skip_record_data_impl(&mut self, marker: &RecordMarker) -> Result<()> {
let res = self.cursor.skip(marker.rdlen as usize);
if res.is_ok() {
self.section_tracker
.section_read(marker.section, self.cursor.pos());
} else {
self.done = true;
}
res
}
#[inline]
pub fn record_data_bytes(&'s mut self, marker: &RecordMarker) -> Result<&'a [u8]> {
debug_assert!(self.cursor.pos() == marker.rdata_pos());
if self.done {
return Err(Error::ReaderDone);
}
let res = self.cursor.slice(marker.rdlen as usize);
if res.is_ok() {
self.section_tracker
.section_read(marker.section, self.cursor.pos());
} else {
self.done = true;
}
res
}
#[inline]
pub fn record_data<D: RData>(&mut self, marker: &RecordMarker) -> Result<D> {
debug_assert!(self.cursor.pos() == marker.rdata_pos());
if self.done {
return Err(Error::ReaderDone);
}
let res = D::from_cursor(&mut self.cursor, marker.rdlen as usize);
if res.is_ok() {
self.section_tracker
.section_read(marker.section, self.cursor.pos());
} else {
self.done = true;
}
res
}
#[inline]
pub fn opt_record(&mut self, marker: &RecordMarker) -> Result<Opt> {
if self.done {
return Err(Error::ReaderDone);
}
debug_assert!(self.cursor.pos() == marker.rdata_pos());
debug_assert!(marker.rtype == Type::OPT);
let res = self.opt_record_impl(marker);
if res.is_ok() {
self.section_tracker
.section_read(marker.section, self.cursor.pos());
} else {
self.done = true;
}
res
}
#[inline(always)]
fn opt_record_impl(&mut self, marker: &RecordMarker) -> Result<Opt> {
self.cursor.skip(marker.rdlen as usize)?;
Ok(Opt::from_msg(marker.rclass.value(), marker.ttl))
}
#[inline]
pub fn record_data_bytes_at(&'s self, marker: &RecordMarker) -> Result<&'a [u8]> {
let mut cursor = self.cursor.clone_with_pos(marker.rdata_pos());
cursor.slice(marker.rdlen as usize)
}
#[inline]
pub fn record_data_at<D: RData>(&self, marker: &RecordMarker) -> Result<D> {
let mut cursor = self.cursor.clone_with_pos(marker.rdata_pos());
D::from_cursor(&mut cursor, marker.rdlen as usize)
}
#[inline]
pub fn name_ref_at(&'s self, marker: &RecordMarker) -> NameRef<'a> {
NameRef::new(self.cursor.clone_with_pos(marker.rdata_pos()))
}
#[inline(always)]
fn calc_section(&mut self) -> Result<RecordsSection> {
self.section_tracker
.next_section(self.cursor.pos())
.ok_or(Error::ReaderDone)
}
}