mirror of
https://github.com/NLnetLabs/domain.git
synced 2026-09-26 11:44:54 +02:00
Bring back the message builder.
This commit is contained in:
+133
-16
@@ -16,7 +16,7 @@
|
||||
|
||||
use std::ops;
|
||||
use std::collections::HashMap;
|
||||
use bytes::{BigEndian, BufMut, BytesMut};
|
||||
use bytes::{BigEndian, BufMut, Bytes, BytesMut};
|
||||
use super::error::ShortBuf;
|
||||
use super::name::{Dname, Label, ToDname};
|
||||
|
||||
@@ -108,6 +108,16 @@ impl Composable for u32 {
|
||||
}
|
||||
}
|
||||
|
||||
impl Composable for [u8] {
|
||||
fn compose_len(&self) -> usize {
|
||||
self.len()
|
||||
}
|
||||
|
||||
fn compose<B: BufMut>(&self, buf: &mut B) {
|
||||
buf.put_slice(self)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ Compressable --------------------------------------------------
|
||||
|
||||
@@ -123,19 +133,104 @@ pub trait Compressable {
|
||||
/// This type is used for name compression through the
|
||||
/// `Composable::compose_compressed` method.
|
||||
///
|
||||
/// Note: The implementation is rather naive right now and could do with a
|
||||
/// smarter approach.
|
||||
/// Note: Name compression is currently implemented in a rather naive way
|
||||
/// and could do with a smarter approach.
|
||||
#[derive(Clone, Debug, )]
|
||||
pub struct Compressor {
|
||||
/// The bytes buffer we work on.
|
||||
buf: BytesMut,
|
||||
|
||||
/// Index of where in `buf` the message starts.
|
||||
start: usize,
|
||||
map: HashMap<Dname, u16>,
|
||||
|
||||
/// The maximum size of `buf` in bytes.
|
||||
limit: usize,
|
||||
|
||||
/// The number of bytes to grow each time we run out of space.
|
||||
///
|
||||
/// If this is 0, we grow exactly once to the size given by `limit`.
|
||||
page_size: usize,
|
||||
|
||||
/// The optional compression map.
|
||||
///
|
||||
/// This keeps the position relative to the start of the message for each
|
||||
/// name we’ve ever written.
|
||||
map: Option<HashMap<Dname, u16>>,
|
||||
}
|
||||
|
||||
impl Compressor {
|
||||
/// Creates a new empty compressor.
|
||||
pub fn new(buf: BytesMut) -> Self {
|
||||
Compressor { start: buf.remaining_mut(), buf, map: HashMap::new() }
|
||||
/// Creates a compressor from the given bytes buffer.
|
||||
///
|
||||
/// The compressor will have a default limit equal to the buffer’s current
|
||||
/// capacity and a page size of 0.
|
||||
pub fn from_buf(buf: BytesMut) -> Self {
|
||||
Compressor {
|
||||
start: buf.remaining_mut(),
|
||||
limit: buf.capacity(),
|
||||
page_size: 0,
|
||||
buf,
|
||||
map: None }
|
||||
}
|
||||
|
||||
/// Creates a new compressor with the given capacity.
|
||||
///
|
||||
/// The compressor will have a default limit equal to the given capacity
|
||||
/// and a page size of 0.
|
||||
pub fn with_capacity(capacity: usize) -> Self {
|
||||
Self::from_buf(BytesMut::with_capacity(capacity))
|
||||
}
|
||||
|
||||
pub fn enable_compression(&mut self) {
|
||||
if self.map.is_none() {
|
||||
self.map = Some(HashMap::new())
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the size limit for the compressor.
|
||||
///
|
||||
/// This limit only regards the part of the underlying buffer that is
|
||||
/// being built by the compressor. That is, if the compressor was created
|
||||
/// on top of a buffer that already contained data, the buffer will never
|
||||
/// exceed that amount of data plus `limit`.
|
||||
///
|
||||
/// If you try to set the limit to a value smaller than what’s already
|
||||
/// there, it will silently be increased to the current size.
|
||||
///
|
||||
/// A new compressor starts out with a size limit equal to the capacity
|
||||
/// of the buffer it is being created with.
|
||||
pub fn set_limit(&mut self, limit: usize) {
|
||||
let limit = limit + self.start;
|
||||
self.limit = ::std::cmp::max(limit, self.buf.len())
|
||||
}
|
||||
|
||||
/// Sets the number of bytes by which the buffer should be grown.
|
||||
///
|
||||
/// Each time the buffer runs out of capacity and is still below its
|
||||
/// size limit, it will be grown by `page_size` bytes. This may result in
|
||||
/// a buffer with more capacity than the limit.
|
||||
///
|
||||
/// If `page_size` is set to 0, the buffer will be expanded only once to
|
||||
/// match the size limit.
|
||||
///
|
||||
/// A new compressor starts out with a page size of 0.
|
||||
pub fn set_page_size(&mut self, page_size: usize) {
|
||||
self.page_size = page_size
|
||||
}
|
||||
|
||||
pub fn unwrap(self) -> BytesMut {
|
||||
self.buf
|
||||
}
|
||||
|
||||
pub fn freeze(self) -> Bytes {
|
||||
self.buf.freeze()
|
||||
}
|
||||
|
||||
pub fn slice(&self) -> &[u8] {
|
||||
&self.buf.as_ref()[self.start..]
|
||||
}
|
||||
|
||||
pub fn slice_mut(&mut self) -> &mut [u8] {
|
||||
&mut self.buf.as_mut()[self.start..]
|
||||
}
|
||||
|
||||
/// Composes a the given name compressed into the buffer.
|
||||
@@ -148,7 +243,7 @@ impl Compressor {
|
||||
}
|
||||
let pos = {
|
||||
let first = name.first();
|
||||
let pos = self.start - self.buf.remaining_mut();
|
||||
let pos = self.buf.len() - self.start;
|
||||
self.compose(first)?;
|
||||
pos
|
||||
};
|
||||
@@ -169,24 +264,29 @@ impl Compressor {
|
||||
|
||||
pub fn compose<C>(&mut self, what: &C) -> Result<(), ShortBuf>
|
||||
where C: Composable + ?Sized {
|
||||
if self.buf.remaining_mut() < what.compose_len() {
|
||||
if self.remaining_mut() < what.compose_len() {
|
||||
return Err(ShortBuf)
|
||||
}
|
||||
what.compose(&mut self.buf);
|
||||
what.compose(self);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn add_name(&mut self, name: &Dname, pos: usize) {
|
||||
if pos > 0x3FFF {
|
||||
// Position exceeds encodable position. Don’t add the name, then.
|
||||
return
|
||||
if let Some(ref mut map) = self.map {
|
||||
if pos > 0x3FFF {
|
||||
// Position exceeds encodable position. Don’t add.
|
||||
return
|
||||
}
|
||||
map.insert(name.clone(), pos as u16);
|
||||
}
|
||||
self.map.insert(name.clone(), pos as u16);
|
||||
}
|
||||
|
||||
/// Returns the index of a name if it is known.
|
||||
fn get_pos(&self, name: &Dname) -> Option<u16> {
|
||||
self.map.get(name).map(|v| *v)
|
||||
match self.map {
|
||||
Some(ref map) => map.get(name).map(|v| *v),
|
||||
None => None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_slice(&self) -> &[u8] {
|
||||
@@ -196,6 +296,16 @@ impl Compressor {
|
||||
pub fn as_slice_mut(&mut self) -> &mut [u8] {
|
||||
self.buf.as_mut()
|
||||
}
|
||||
|
||||
fn grow(&mut self) {
|
||||
if self.page_size == 0 {
|
||||
let additional = self.limit - self.buf.capacity();
|
||||
self.buf.reserve(additional)
|
||||
}
|
||||
else {
|
||||
self.buf.reserve(self.page_size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -247,14 +357,21 @@ impl ops::DerefMut for Compressor {
|
||||
|
||||
impl BufMut for Compressor {
|
||||
fn remaining_mut(&self) -> usize {
|
||||
self.buf.remaining_mut()
|
||||
self.limit - self.buf.len()
|
||||
}
|
||||
|
||||
unsafe fn advance_mut(&mut self, cnt: usize) {
|
||||
assert!(cnt <= self.remaining_mut());
|
||||
while cnt > self.buf.remaining_mut() {
|
||||
self.grow();
|
||||
}
|
||||
self.buf.advance_mut(cnt)
|
||||
}
|
||||
|
||||
unsafe fn bytes_mut(&mut self) -> &mut [u8] {
|
||||
if self.buf.remaining_mut() == 0 && self.remaining_mut() > 0 {
|
||||
self.grow()
|
||||
}
|
||||
self.buf.bytes_mut()
|
||||
}
|
||||
}
|
||||
|
||||
+85
-4
@@ -19,8 +19,11 @@
|
||||
//! [RFC 1035]: https://tools.ietf.org/html/rfc1035
|
||||
|
||||
use std::mem;
|
||||
use bytes::{BigEndian, ByteOrder};
|
||||
use bytes::{BigEndian, BufMut, ByteOrder};
|
||||
use ::iana::{Opcode, Rcode};
|
||||
use super::compose::Composable;
|
||||
use super::error::ShortBuf;
|
||||
use super::parse::{Parseable, Parser};
|
||||
|
||||
|
||||
//------------ Header --------------------------------------------------
|
||||
@@ -51,7 +54,7 @@ use ::iana::{Opcode, Rcode};
|
||||
/// [Field Access]: #field-access
|
||||
/// [RFC 1035]: https://tools.ietf.org/html/rfc1035
|
||||
/// [RFC 4035]: https://tools.ietf.org/html/rfc4035
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
||||
pub struct Header {
|
||||
/// The actual header in its wire format representation.
|
||||
///
|
||||
@@ -278,7 +281,7 @@ impl Header {
|
||||
///
|
||||
/// [RFC 1035]: https://tools.ietf.org/html/rfc1035
|
||||
/// [RFC 2136]: https://tools.ietf.org/html/rfc2136
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
||||
pub struct HeaderCounts {
|
||||
/// The actual headers in their wire-format representation.
|
||||
///
|
||||
@@ -324,6 +327,16 @@ impl HeaderCounts {
|
||||
pub fn as_slice(&self) -> &[u8] {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying byte slice.
|
||||
pub fn as_slice_mut(&mut self) -> &mut [u8] {
|
||||
&mut self.inner
|
||||
}
|
||||
|
||||
/// Sets the counts to those from `counts`.
|
||||
pub fn set(&mut self, counts: &HeaderCounts) {
|
||||
self.as_slice_mut().copy_from_slice(counts.as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -345,6 +358,17 @@ impl HeaderCounts {
|
||||
self.set_u16(0, value)
|
||||
}
|
||||
|
||||
/// Increases the value of the QDCOUNT field by own.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This method panics if the count is already at its maximum.
|
||||
pub fn inc_qdcount(&mut self) {
|
||||
let count = self.qdcount();
|
||||
assert!(count < ::std::u16::MAX);
|
||||
self.set_qdcount(count + 1);
|
||||
}
|
||||
|
||||
/// Returns the value of the ANCOUNT field.
|
||||
///
|
||||
/// This field contains the number of resource records in the second
|
||||
@@ -358,6 +382,17 @@ impl HeaderCounts {
|
||||
self.set_u16(2, value)
|
||||
}
|
||||
|
||||
/// Increases the value of the ANCOUNT field by own.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This method panics if the count is already at its maximum.
|
||||
pub fn inc_ancount(&mut self) {
|
||||
let count = self.ancount();
|
||||
assert!(count < ::std::u16::MAX);
|
||||
self.set_ancount(count + 1);
|
||||
}
|
||||
|
||||
/// Returns the value of the NSCOUNT field.
|
||||
///
|
||||
/// This field contains the number of resource records in the third
|
||||
@@ -371,6 +406,17 @@ impl HeaderCounts {
|
||||
self.set_u16(4, value)
|
||||
}
|
||||
|
||||
/// Increases the value of the NSCOUNT field by own.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This method panics if the count is already at its maximum.
|
||||
pub fn inc_nscount(&mut self) {
|
||||
let count = self.nscount();
|
||||
assert!(count < ::std::u16::MAX);
|
||||
self.set_nscount(count + 1);
|
||||
}
|
||||
|
||||
/// Returns the value of the ARCOUNT field.
|
||||
///
|
||||
/// This field contains the number of resource records in the fourth
|
||||
@@ -384,6 +430,17 @@ impl HeaderCounts {
|
||||
self.set_u16(6, value)
|
||||
}
|
||||
|
||||
/// Increases the value of the ARCOUNT field by own.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This method panics if the count is already at its maximum.
|
||||
pub fn inc_arcount(&mut self) {
|
||||
let count = self.arcount();
|
||||
assert!(count < ::std::u16::MAX);
|
||||
self.set_arcount(count + 1);
|
||||
}
|
||||
|
||||
|
||||
//--- Count fields in UPDATE messages
|
||||
|
||||
@@ -459,7 +516,7 @@ impl HeaderCounts {
|
||||
/// The complete header section of a DNS message.
|
||||
///
|
||||
/// Consists of a `Header` and a `HeaderCounts`.
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
||||
pub struct HeaderSection {
|
||||
inner: [u8; 12]
|
||||
}
|
||||
@@ -526,6 +583,30 @@ impl HeaderSection {
|
||||
}
|
||||
|
||||
|
||||
//--- Parseable and Composable
|
||||
|
||||
impl Parseable for HeaderSection {
|
||||
type Err = ShortBuf;
|
||||
|
||||
fn parse(parser: &mut Parser) -> Result<Self, Self::Err> {
|
||||
let slice = parser.peek(12)?;
|
||||
let mut res = Self::default();
|
||||
res.inner.copy_from_slice(slice);
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl Composable for HeaderSection {
|
||||
fn compose_len(&self) -> usize {
|
||||
12
|
||||
}
|
||||
|
||||
fn compose<B: BufMut>(&self, buf: &mut B) {
|
||||
buf.put_slice(&self.inner)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============ Testing ======================================================
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
+291
-215
@@ -83,13 +83,18 @@
|
||||
//! [`new()`]: struct.MessageBuilder.html#method.new
|
||||
//! [`from_vec()`]: struct.MessageBuilder.html#method.from_vec
|
||||
|
||||
use std::mem;
|
||||
use ::iana::{Class, OptRcode, Rtype};
|
||||
use super::{Composer, ComposeError, ComposeMode, ComposeResult,
|
||||
ComposeSnapshot, DName, DNameSlice, HeaderSection, Header,
|
||||
HeaderCounts, Message, Question, Record, RecordData};
|
||||
use super::record::RecordBuilder;
|
||||
use super::opt::OptData;
|
||||
use std::{mem, ops};
|
||||
use std::marker::PhantomData;
|
||||
use bytes::{BigEndian, BufMut, ByteOrder, Bytes, BytesMut};
|
||||
use iana::opt::OptionCode;
|
||||
use super::compose::{Composable, Compressable, Compressor};
|
||||
use super::error::ShortBuf;
|
||||
use super::header::{Header, HeaderCounts, HeaderSection};
|
||||
use super::name::ToDname;
|
||||
use super::opt::{OptData, OptHeader};
|
||||
use super::question::Question;
|
||||
use super::rdata::RecordData;
|
||||
use super::record::Record;
|
||||
|
||||
|
||||
//------------ MessageBuilder -----------------------------------------------
|
||||
@@ -106,44 +111,35 @@ pub struct MessageBuilder {
|
||||
}
|
||||
|
||||
|
||||
/// # Creation
|
||||
/// # Creation and Preparation
|
||||
///
|
||||
impl MessageBuilder {
|
||||
/// Creates a new empty DNS message.
|
||||
///
|
||||
/// The `mode` argument decides whether the message will have a size
|
||||
/// limit and whether it should include the length prefix for use with
|
||||
/// stream transports. If `compress` is `true`, name compression will
|
||||
/// be enabled for the message.
|
||||
///
|
||||
/// This function may fail if the size limit in `mode` is too small to
|
||||
/// even add the header section.
|
||||
pub fn new(mode: ComposeMode, compress: bool) -> ComposeResult<Self> {
|
||||
Self::from_composer(Composer::new(mode, compress))
|
||||
pub fn from_buf(buf: BytesMut) -> Self {
|
||||
MessageBuilder { target: MessageTarget::from_buf(buf) }
|
||||
}
|
||||
|
||||
/// Creates a new DNS message appended to the content of a bytes vector.
|
||||
///
|
||||
/// The `mode` argument decides whether the message will have a size
|
||||
/// limit and whether it should include the length prefix for use with
|
||||
/// stream transports. If `compress` is `true`, name compression will
|
||||
/// be enabled for the message.
|
||||
///
|
||||
/// This function may fail if the size limit in `mode` is too small to
|
||||
/// even add the header section.
|
||||
pub fn from_vec(vec: Vec<u8>, mode: ComposeMode, compress: bool)
|
||||
-> ComposeResult<Self> {
|
||||
Self::from_composer(Composer::from_vec(vec, mode, compress))
|
||||
pub fn with_capacity(capacity: usize) -> Self {
|
||||
Self::from_buf(BytesMut::with_capacity(capacity))
|
||||
}
|
||||
|
||||
/// Creates a new DNS message atop an existing composer.
|
||||
///
|
||||
/// This doesn’t reset the composer but starts off after whatever is in
|
||||
/// there already. As this may result in invalid message, user discretion
|
||||
/// is advised.
|
||||
pub fn from_composer(mut composer: Composer) -> ComposeResult<Self> {
|
||||
try!(composer.compose_empty(mem::size_of::<HeaderSection>()));
|
||||
Ok(MessageBuilder{target: MessageTarget::new(composer)})
|
||||
pub fn with_params(initial: usize, limit: usize, page_size: usize)
|
||||
-> Self {
|
||||
let mut res = Self::with_capacity(initial);
|
||||
res.set_limit(limit);
|
||||
res.set_page_size(page_size);
|
||||
res
|
||||
}
|
||||
|
||||
pub fn enable_compression(&mut self) {
|
||||
self.target.buf.enable_compression()
|
||||
}
|
||||
|
||||
pub fn set_limit(&mut self, limit: usize) {
|
||||
self.target.buf.set_limit(limit)
|
||||
}
|
||||
|
||||
pub fn set_page_size(&mut self, page_size: usize) {
|
||||
self.target.buf.set_page_size(page_size)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,6 +157,14 @@ impl MessageBuilder {
|
||||
self.target.header_mut()
|
||||
}
|
||||
|
||||
pub fn snapshot(&self) -> Snapshot<Self> {
|
||||
self.target.snapshot()
|
||||
}
|
||||
|
||||
pub fn rewind(&mut self, snapshot: Snapshot<Self>) {
|
||||
self.target.rewind(snapshot)
|
||||
}
|
||||
|
||||
/// Appends a new question to the message.
|
||||
///
|
||||
/// This function is generic over anything that can be converted into a
|
||||
@@ -170,22 +174,15 @@ impl MessageBuilder {
|
||||
/// the latter case.
|
||||
///
|
||||
/// [`Question`]: ../question/struct.Question.html
|
||||
pub fn push<N: DName, Q: Into<Question<N>>>(&mut self, question: Q)
|
||||
-> ComposeResult<()> {
|
||||
self.target.push(|target| question.into().compose(target),
|
||||
|counts| counts.inc_qdcount(1))
|
||||
}
|
||||
|
||||
/// Rewinds to the beginning of the question section.
|
||||
///
|
||||
/// This drops all previously assembled questions.
|
||||
pub fn rewind(&mut self) {
|
||||
self.target.rewind(|counts| counts.set_qdcount(0));
|
||||
pub fn push<N: ToDname>(&mut self, question: &Question<N>)
|
||||
-> Result<(), ShortBuf> {
|
||||
self.target.push(|target| question.compress(target),
|
||||
|counts| counts.inc_qdcount())
|
||||
}
|
||||
|
||||
/// Proceeds to building the answer section.
|
||||
pub fn answer(self) -> AnswerBuilder {
|
||||
AnswerBuilder::new(self.target.commit())
|
||||
AnswerBuilder::new(self.target)
|
||||
}
|
||||
|
||||
/// Proceeds to building the authority section, skipping the answer.
|
||||
@@ -200,6 +197,11 @@ impl MessageBuilder {
|
||||
self.answer().authority().additional()
|
||||
}
|
||||
|
||||
/// Proceeds to building the OPT record.
|
||||
pub fn opt(self) -> Result<OptBuilder, ShortBuf> {
|
||||
self.additional().opt()
|
||||
}
|
||||
|
||||
/// Returns a reference to the message assembled so far.
|
||||
///
|
||||
/// This method requires a `&mut self` since it may need to update some
|
||||
@@ -211,11 +213,15 @@ impl MessageBuilder {
|
||||
self.target.preview()
|
||||
}
|
||||
|
||||
/// Finishes the message and returns the underlying target.
|
||||
/// Finishes the message and returns the underlying bytes buffer.
|
||||
///
|
||||
/// This will result in a message with all three record sections empty.
|
||||
pub fn finish(self) -> Vec<u8> {
|
||||
self.target.finish()
|
||||
pub fn finish(self) -> BytesMut {
|
||||
self.target.unwrap()
|
||||
}
|
||||
|
||||
pub fn freeze(self) -> Bytes {
|
||||
self.target.freeze()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,10 +244,12 @@ pub struct AnswerBuilder {
|
||||
|
||||
impl AnswerBuilder {
|
||||
/// Creates a new answer builder from a compser.
|
||||
fn new(composer: Composer) -> Self {
|
||||
AnswerBuilder {
|
||||
target: MessageTarget::new(composer)
|
||||
}
|
||||
fn new(target: MessageTarget) -> Self {
|
||||
AnswerBuilder { target }
|
||||
}
|
||||
|
||||
pub fn set_limit(&mut self, limit: usize) {
|
||||
self.target.buf.set_limit(limit)
|
||||
}
|
||||
|
||||
/// Returns a reference to the messages header.
|
||||
@@ -254,6 +262,14 @@ impl AnswerBuilder {
|
||||
self.target.header_mut()
|
||||
}
|
||||
|
||||
pub fn snapshot(&self) -> Snapshot<Self> {
|
||||
self.target.snapshot()
|
||||
}
|
||||
|
||||
pub fn rewind(&mut self, snapshot: Snapshot<Self>) {
|
||||
self.target.rewind(snapshot)
|
||||
}
|
||||
|
||||
/// Appends a new resource record to the answer section.
|
||||
///
|
||||
/// This method is generic over anything that can be converted into a
|
||||
@@ -262,24 +278,17 @@ impl AnswerBuilder {
|
||||
/// the class which will then be assumed to be `Class::In`.
|
||||
///
|
||||
/// [`Record`]: ../record/struct.Record.html
|
||||
pub fn push<N, D, R>(&mut self, record: R) -> ComposeResult<()>
|
||||
where N: DName,
|
||||
D: RecordData,
|
||||
R: Into<Record<N, D>> {
|
||||
self.target.push(|target| record.into().compose(target),
|
||||
|counts| counts.inc_ancount(1))
|
||||
}
|
||||
|
||||
/// Rewinds to the beginning of the answer section.
|
||||
///
|
||||
/// This drops all previously assembled answer records.
|
||||
pub fn rewind(&mut self) {
|
||||
self.target.rewind(|counts| counts.set_ancount(0))
|
||||
pub fn push<N, D>(&mut self, record: &Record<N, D>)
|
||||
-> Result<(), ShortBuf>
|
||||
where N: ToDname,
|
||||
D: RecordData {
|
||||
self.target.push(|target| record.compress(target),
|
||||
|counts| counts.inc_ancount())
|
||||
}
|
||||
|
||||
/// Proceeds to building the authority section.
|
||||
pub fn authority(self) -> AuthorityBuilder {
|
||||
AuthorityBuilder::new(self.target.commit())
|
||||
AuthorityBuilder::new(self.target)
|
||||
}
|
||||
|
||||
/// Proceeds to building the additional section, skipping authority.
|
||||
@@ -287,6 +296,11 @@ impl AnswerBuilder {
|
||||
self.authority().additional()
|
||||
}
|
||||
|
||||
/// Proceeds to building the OPT record.
|
||||
pub fn opt(self) -> Result<OptBuilder, ShortBuf> {
|
||||
self.additional().opt()
|
||||
}
|
||||
|
||||
/// Returns a reference to the message assembled so far.
|
||||
///
|
||||
/// This method requires a `&mut self` since it may need to update some
|
||||
@@ -298,12 +312,15 @@ impl AnswerBuilder {
|
||||
self.target.preview()
|
||||
}
|
||||
|
||||
/// Finishes the message.
|
||||
/// Finishes the message and returns the underlying bytes buffer.
|
||||
///
|
||||
/// The resulting message will have empty authority and additional
|
||||
/// sections.
|
||||
pub fn finish(self) -> Vec<u8> {
|
||||
self.target.finish()
|
||||
/// This will result in a message with all three record sections empty.
|
||||
pub fn finish(self) -> BytesMut {
|
||||
self.target.unwrap()
|
||||
}
|
||||
|
||||
pub fn freeze(self) -> Bytes {
|
||||
self.target.freeze()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -327,10 +344,12 @@ pub struct AuthorityBuilder {
|
||||
|
||||
impl AuthorityBuilder {
|
||||
/// Creates a new authority builder from a compser.
|
||||
fn new(composer: Composer) -> Self {
|
||||
AuthorityBuilder {
|
||||
target: MessageTarget::new(composer)
|
||||
}
|
||||
fn new(target: MessageTarget) -> Self {
|
||||
AuthorityBuilder { target }
|
||||
}
|
||||
|
||||
pub fn set_limit(&mut self, limit: usize) {
|
||||
self.target.buf.set_limit(limit)
|
||||
}
|
||||
|
||||
/// Returns a reference to the messages header.
|
||||
@@ -343,6 +362,14 @@ impl AuthorityBuilder {
|
||||
self.target.header_mut()
|
||||
}
|
||||
|
||||
pub fn snapshot(&self) -> Snapshot<Self> {
|
||||
self.target.snapshot()
|
||||
}
|
||||
|
||||
pub fn rewind(&mut self, snapshot: Snapshot<Self>) {
|
||||
self.target.rewind(snapshot)
|
||||
}
|
||||
|
||||
/// Appends a new resource record to the authority section.
|
||||
///
|
||||
/// This method is generic over anything that can be converted into a
|
||||
@@ -351,24 +378,22 @@ impl AuthorityBuilder {
|
||||
/// the class which will then be assumed to be `Class::In`.
|
||||
///
|
||||
/// [`Record`]: ../record/struct.Record.html
|
||||
pub fn push<N, D, R>(&mut self, record: R) -> ComposeResult<()>
|
||||
where N: DName,
|
||||
D: RecordData,
|
||||
R: Into<Record<N, D>> {
|
||||
self.target.push(|target| record.into().compose(target),
|
||||
|counts| counts.inc_nscount(1))
|
||||
}
|
||||
|
||||
/// Rewinds to the beginning of the authority section.
|
||||
///
|
||||
/// This drops all previously assembled authority records.
|
||||
pub fn rewind(&mut self) {
|
||||
self.target.rewind(|counts| counts.set_nscount(0))
|
||||
pub fn push<N, D>(&mut self, record: &Record<N, D>)
|
||||
-> Result<(), ShortBuf>
|
||||
where N: ToDname,
|
||||
D: RecordData {
|
||||
self.target.push(|target| record.compress(target),
|
||||
|counts| counts.inc_nscount())
|
||||
}
|
||||
|
||||
/// Proceeds to building the additional section.
|
||||
pub fn additional(self) -> AdditionalBuilder {
|
||||
AdditionalBuilder::new(self.target.commit())
|
||||
AdditionalBuilder::new(self.target)
|
||||
}
|
||||
|
||||
/// Proceeds to building the OPT record.
|
||||
pub fn opt(self) -> Result<OptBuilder, ShortBuf> {
|
||||
self.additional().opt()
|
||||
}
|
||||
|
||||
/// Returns a reference to the message assembled so far.
|
||||
@@ -382,11 +407,15 @@ impl AuthorityBuilder {
|
||||
self.target.preview()
|
||||
}
|
||||
|
||||
/// Finishes the message.
|
||||
/// Finishes the message and returns the underlying bytes buffer.
|
||||
///
|
||||
/// The resulting message will have an empty additional section.
|
||||
pub fn finish(self) -> Vec<u8> {
|
||||
self.target.finish()
|
||||
/// This will result in a message with all three record sections empty.
|
||||
pub fn finish(self) -> BytesMut {
|
||||
self.target.unwrap()
|
||||
}
|
||||
|
||||
pub fn freeze(self) -> Bytes {
|
||||
self.target.freeze()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -411,10 +440,8 @@ pub struct AdditionalBuilder {
|
||||
|
||||
impl AdditionalBuilder {
|
||||
/// Creates a new additional builder from a compser.
|
||||
fn new(composer: Composer) -> Self {
|
||||
AdditionalBuilder {
|
||||
target: MessageTarget::new(composer)
|
||||
}
|
||||
fn new(target: MessageTarget) -> Self {
|
||||
AdditionalBuilder { target }
|
||||
}
|
||||
|
||||
/// Returns a reference to the messages header.
|
||||
@@ -422,11 +449,23 @@ impl AdditionalBuilder {
|
||||
self.target.header()
|
||||
}
|
||||
|
||||
pub fn set_limit(&mut self, limit: usize) {
|
||||
self.target.buf.set_limit(limit)
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the messages header.
|
||||
pub fn header_mut(&mut self) -> &mut Header {
|
||||
self.target.header_mut()
|
||||
}
|
||||
|
||||
pub fn snapshot(&self) -> Snapshot<Self> {
|
||||
self.target.snapshot()
|
||||
}
|
||||
|
||||
pub fn rewind(&mut self, snapshot: Snapshot<Self>) {
|
||||
self.target.rewind(snapshot)
|
||||
}
|
||||
|
||||
/// Appends a new resource record to the additional section.
|
||||
///
|
||||
/// This method is generic over anything that can be converted into a
|
||||
@@ -435,44 +474,17 @@ impl AdditionalBuilder {
|
||||
/// the class which will then be assumed to be `Class::In`.
|
||||
///
|
||||
/// [`Record`]: ../record/struct.Record.html
|
||||
pub fn push<N, D, R>(&mut self, record: R) -> ComposeResult<()>
|
||||
where N: DName,
|
||||
D: RecordData,
|
||||
R: Into<Record<N, D>> {
|
||||
self.target.push(|target| record.into().compose(target),
|
||||
|counts| counts.inc_arcount(1))
|
||||
pub fn push<N, D>(&mut self, record: &Record<N, D>)
|
||||
-> Result<(), ShortBuf>
|
||||
where N: ToDname,
|
||||
D: RecordData {
|
||||
self.target.push(|target| record.compress(target),
|
||||
|counts| counts.inc_arcount())
|
||||
}
|
||||
|
||||
/// Starts appending an OPT record to the section.
|
||||
///
|
||||
/// The method expects the values of the OPT record that are encoded in
|
||||
/// various fields of the record header.
|
||||
///
|
||||
/// The *payload_size* field contains
|
||||
/// the maximum size of UDP payload a requestor can assemble and process.
|
||||
///
|
||||
/// The `rcode` argument should contain the Rcode used for a response
|
||||
/// or `OptRcode::NoError` for a message. Only the upper eight bits are
|
||||
/// used here, the lower for bits go into the message header’s rcode
|
||||
/// field.
|
||||
///
|
||||
/// The `dnssec_ok` flag indicates whether a sender is prepared to
|
||||
/// receive and process DNSSEC-related resource records in a response.
|
||||
/// In a response it must be equal to its value in a request.
|
||||
///
|
||||
/// This method trades in the additional section builder for an OPT
|
||||
/// record builder. Once the record is finished, it can be traded back
|
||||
/// to continue building the additional section.
|
||||
pub fn build_opt(self, payload_size: u16, rcode: OptRcode,
|
||||
dnssec_ok: bool) -> ComposeResult<OptBuilder> {
|
||||
OptBuilder::new(self, payload_size, rcode, dnssec_ok)
|
||||
}
|
||||
|
||||
/// Rewinds to the beginning of the additional section.
|
||||
///
|
||||
/// This drops all previously assembled additonal records.
|
||||
pub fn rewind(&mut self) {
|
||||
self.target.rewind(|counts| counts.set_arcount(0))
|
||||
/// Proceeds to building the OPT record.
|
||||
pub fn opt(self) -> Result<OptBuilder, ShortBuf> {
|
||||
OptBuilder::new(self.target)
|
||||
}
|
||||
|
||||
/// Returns a reference to the message assembled so far.
|
||||
@@ -486,63 +498,94 @@ impl AdditionalBuilder {
|
||||
self.target.preview()
|
||||
}
|
||||
|
||||
/// Finishes the message.
|
||||
pub fn finish(self) -> Vec<u8> {
|
||||
self.target.finish()
|
||||
/// Finishes the message and returns the underlying bytes buffer.
|
||||
///
|
||||
/// This will result in a message with all three record sections empty.
|
||||
pub fn finish(self) -> BytesMut {
|
||||
self.target.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Message> for AdditionalBuilder {
|
||||
fn as_ref(&self) -> &Message {
|
||||
self.target.as_ref()
|
||||
pub fn freeze(self) -> Bytes {
|
||||
self.target.freeze()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ OptBuilder ----------------------------------------------------
|
||||
|
||||
/// A type for building an OPT record on the fly.
|
||||
///
|
||||
/// The OPT record is part of the additional section. You can therefore get
|
||||
/// hold of a value of this type through the `AdditionalBuilder::build_opt()`
|
||||
/// method.
|
||||
///
|
||||
/// You use this value to add options to the record via the `push()` method.
|
||||
/// Once you are done, call `complete()` to finish up the record and get the
|
||||
/// additional builder back.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct OptBuilder {
|
||||
builder: RecordBuilder<ComposeSnapshot>,
|
||||
target: MessageTarget,
|
||||
pos: usize,
|
||||
}
|
||||
|
||||
impl OptBuilder {
|
||||
/// Creates a new OPT builder from an additional builder
|
||||
fn new(builder: AdditionalBuilder, payload_size: u16, rcode: OptRcode,
|
||||
dnssec_ok: bool) -> ComposeResult<Self> {
|
||||
let mut ttl = (rcode.ext() as u32) << 24;
|
||||
if dnssec_ok {
|
||||
ttl |= 0x8000
|
||||
}
|
||||
let builder = RecordBuilder::new(builder.target.composer,
|
||||
&DNameSlice::root(),
|
||||
Class::Int(payload_size),
|
||||
Rtype::Opt, ttl)?;
|
||||
Ok(OptBuilder { builder })
|
||||
fn new(mut target: MessageTarget) -> Result<Self, ShortBuf> {
|
||||
let pos = target.len();
|
||||
target.compose(&OptHeader::default())?;
|
||||
target.compose(&0u16)?;
|
||||
Ok(OptBuilder { pos, target })
|
||||
}
|
||||
|
||||
/// Pushes an option to the OPT record.
|
||||
pub fn push<O: OptData>(&mut self, option: O) -> ComposeResult<()> {
|
||||
option.compose(&mut self.builder)
|
||||
pub fn push<O: OptData>(&mut self, option: &O) -> Result<(), ShortBuf> {
|
||||
self.target.compose(&option.code())?;
|
||||
let len = option.compose_len();
|
||||
assert!(len <= ::std::u16::MAX as usize);
|
||||
self.target.compose(&(len as u16))?;
|
||||
self.target.compose(option)
|
||||
}
|
||||
|
||||
pub(super) fn build<F>(&mut self, code: OptionCode, len: u16, op: F)
|
||||
-> Result<(), ShortBuf>
|
||||
where F: FnOnce(&mut Compressor)
|
||||
-> Result<(), ShortBuf> {
|
||||
self.target.compose(&code)?;
|
||||
self.target.compose(&len)?;
|
||||
op(&mut self.target.buf)
|
||||
}
|
||||
|
||||
/// Completes the OPT record and returns the additional section builder.
|
||||
pub fn complete(self) -> ComposeResult<AdditionalBuilder> {
|
||||
let mut target = MessageTarget { composer: self.builder.finish()? };
|
||||
target.counts_mut().inc_arcount(1)?;
|
||||
Ok(AdditionalBuilder { target })
|
||||
pub fn additional(self) -> AdditionalBuilder {
|
||||
AdditionalBuilder::new(self.complete())
|
||||
}
|
||||
|
||||
/// Finishes the message and returns the underlying bytes buffer.
|
||||
///
|
||||
/// This will result in a message with all three record sections empty.
|
||||
pub fn finish(self) -> BytesMut {
|
||||
self.complete().unwrap()
|
||||
}
|
||||
|
||||
pub fn freeze(self) -> Bytes {
|
||||
self.complete().freeze()
|
||||
}
|
||||
|
||||
fn complete(mut self) -> MessageTarget {
|
||||
let len = self.target.len()
|
||||
- (self.pos + mem::size_of::<OptHeader>() + 2);
|
||||
assert!(len <= ::std::u16::MAX as usize);
|
||||
BigEndian::write_u16(&mut self.target.as_slice_mut()[self.pos..],
|
||||
len as u16);
|
||||
self.target.counts_mut().inc_arcount();
|
||||
self.target
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Deref for OptBuilder {
|
||||
type Target = OptHeader;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
OptHeader::for_record_slice(&self.target.as_slice()[self.pos..])
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::DerefMut for OptBuilder {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
OptHeader::for_record_slice_mut(&mut self.target.as_slice_mut()
|
||||
[self.pos..])
|
||||
}
|
||||
}
|
||||
|
||||
//------------ MessageTarget -------------------------------------------------
|
||||
|
||||
@@ -551,81 +594,114 @@ impl OptBuilder {
|
||||
/// This private type does all the heavy lifting for constructing messages.
|
||||
#[derive(Clone, Debug)]
|
||||
struct MessageTarget {
|
||||
composer: ComposeSnapshot,
|
||||
buf: Compressor,
|
||||
start: usize,
|
||||
}
|
||||
|
||||
|
||||
impl MessageTarget {
|
||||
/// Creates a new message target atop a given composer.
|
||||
fn new(composer: Composer) -> Self {
|
||||
MessageTarget{composer: composer.snapshot()}
|
||||
/// Creates a new message target atop a given buffer.
|
||||
fn from_buf(mut buf: BytesMut) -> Self {
|
||||
let start = buf.len();
|
||||
if buf.remaining_mut() < 2 + mem::size_of::<HeaderSection>() {
|
||||
let additional = 2 + mem::size_of::<HeaderSection>()
|
||||
- buf.remaining_mut();
|
||||
buf.reserve(additional)
|
||||
}
|
||||
0u16.compose(&mut buf);
|
||||
let mut buf = Compressor::from_buf(buf);
|
||||
HeaderSection::default().compose(&mut buf);
|
||||
MessageTarget { buf, start }
|
||||
}
|
||||
|
||||
/// Returns a reference to the message’s header.
|
||||
fn header(&self) -> &Header {
|
||||
Header::from_message(self.composer.so_far())
|
||||
Header::for_message_slice(self.buf.slice())
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the message’s header.
|
||||
fn header_mut(&mut self) -> &mut Header {
|
||||
Header::from_message_mut(self.composer.so_far_mut())
|
||||
Header::for_message_slice_mut(self.buf.slice_mut())
|
||||
}
|
||||
|
||||
fn counts(&self) -> &HeaderCounts {
|
||||
HeaderCounts::for_message_slice(self.buf.slice())
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the message’s header counts.
|
||||
fn counts_mut(&mut self) -> &mut HeaderCounts {
|
||||
HeaderCounts::from_message_mut(self.composer.so_far_mut())
|
||||
HeaderCounts::for_message_slice_mut(self.buf.slice_mut())
|
||||
}
|
||||
|
||||
/// Pushes something to the end of the message.
|
||||
///
|
||||
/// There’s two closures here. The first one, `composeop` actually
|
||||
/// writes the data. The second, `incop` increments the counter in the
|
||||
/// messages header to reflect the new element.
|
||||
fn push<O, I>(&mut self, composeop: O, incop: I) -> ComposeResult<()>
|
||||
where O: FnOnce(&mut Composer) -> ComposeResult<()>,
|
||||
I: FnOnce(&mut HeaderCounts) -> ComposeResult<()> {
|
||||
if !self.composer.is_truncated() {
|
||||
self.composer.mark_checkpoint();
|
||||
match composeop(&mut self.composer) {
|
||||
Ok(()) => {
|
||||
try!(incop(self.counts_mut()));
|
||||
Ok(())
|
||||
}
|
||||
Err(ComposeError::SizeExceeded) => Ok(()),
|
||||
Err(error) => Err(error)
|
||||
}
|
||||
/// messages header to reflect the new element. The latter is assumed to
|
||||
/// never fail. This means you need to check before you push whether
|
||||
/// there is still space in whatever counter you plan to increase.
|
||||
/// `HeaderCount`’s `inc_*` methods, which are supposed to be used here,
|
||||
/// have assertions for your own safety.
|
||||
fn push<O, I, E>(&mut self, composeop: O, incop: I) -> Result<(), E>
|
||||
where O: FnOnce(&mut Compressor) -> Result<(), E>,
|
||||
I: FnOnce(&mut HeaderCounts) {
|
||||
composeop(&mut self.buf).map(|()| incop(self.counts_mut()))
|
||||
}
|
||||
|
||||
fn snapshot<T>(&self) -> Snapshot<T> {
|
||||
Snapshot {
|
||||
pos: self.buf.len(),
|
||||
counts: self.counts().clone(),
|
||||
marker: PhantomData,
|
||||
}
|
||||
else { Ok(()) }
|
||||
}
|
||||
|
||||
/// Returns a reference to the message assembled so far.
|
||||
fn rewind<T>(&mut self, snapshot: Snapshot<T>) {
|
||||
self.buf.truncate(snapshot.pos);
|
||||
self.counts_mut().set(&snapshot.counts);
|
||||
}
|
||||
|
||||
fn update_shim(&mut self) {
|
||||
let len = (self.buf.len() - self.start) as u16;
|
||||
BigEndian::write_u16(&mut self.buf.as_slice_mut()[self.start..], len);
|
||||
}
|
||||
|
||||
fn preview(&mut self) -> &[u8] {
|
||||
self.composer.preview()
|
||||
self.update_shim();
|
||||
self.buf.as_slice()
|
||||
}
|
||||
|
||||
/// Finishes the message building and extracts the underlying vector.
|
||||
fn finish(mut self) -> Vec<u8> {
|
||||
let tc = self.composer.is_truncated();
|
||||
self.header_mut().set_tc(tc);
|
||||
self.composer.commit().finish()
|
||||
fn unwrap(mut self) -> BytesMut {
|
||||
self.update_shim();
|
||||
self.buf.unwrap()
|
||||
}
|
||||
|
||||
/// Rewinds the compose snapshots and allows updating the header counts.
|
||||
fn rewind<F>(&mut self, op: F)
|
||||
where F: FnOnce(&mut HeaderCounts) {
|
||||
op(self.counts_mut());
|
||||
self.composer.rewind()
|
||||
fn freeze(mut self) -> Bytes {
|
||||
self.update_shim();
|
||||
self.unwrap().freeze()
|
||||
}
|
||||
}
|
||||
|
||||
/// Commit the compose snapshot.
|
||||
fn commit(self) -> Composer {
|
||||
self.composer.commit()
|
||||
impl ops::Deref for MessageTarget {
|
||||
type Target = Compressor;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.buf
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::DerefMut for MessageTarget {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.buf
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl AsRef<Message> for MessageTarget {
|
||||
fn as_ref(&self) -> &Message {
|
||||
unsafe { Message::from_bytes_unsafe(self.composer.so_far()) }
|
||||
}
|
||||
//------------ Snapshot ------------------------------------------------------
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub struct Snapshot<T> {
|
||||
pos: usize,
|
||||
counts: HeaderCounts,
|
||||
marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
+1
-7
@@ -1,7 +1,3 @@
|
||||
// TODO:
|
||||
//
|
||||
// o Rename ShortParser into something more generic.
|
||||
//
|
||||
//! DNS data.
|
||||
//!
|
||||
//! This module provides types and traits for working with DNS data as well
|
||||
@@ -103,6 +99,7 @@ pub mod compose;
|
||||
pub mod error;
|
||||
pub mod header;
|
||||
pub mod message;
|
||||
pub mod message_builder;
|
||||
pub mod name;
|
||||
pub mod opt;
|
||||
pub mod parse;
|
||||
@@ -110,6 +107,3 @@ pub mod question;
|
||||
pub mod rdata;
|
||||
pub mod record;
|
||||
|
||||
/*
|
||||
pub mod message_builder;
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Domain name-related traits.
|
||||
|
||||
use ::bits::compose::Composable;
|
||||
use ::bits::compose::{Composable, Compressable};
|
||||
use bytes::BytesMut;
|
||||
use super::dname::Dname;
|
||||
use super::label::Label;
|
||||
@@ -105,7 +105,7 @@ pub trait ToRelativeDname: Composable + for<'a> ToLabelIter<'a> {
|
||||
/// [`Chain<L, R>`]: struct.Chain.html
|
||||
/// [`Dname`]: struct.Dname.html
|
||||
/// [`ParsedDname`]: struct.ParsedDname.html
|
||||
pub trait ToDname: Composable + for<'a> ToLabelIter<'a> {
|
||||
pub trait ToDname: Composable + Compressable + for<'a> ToLabelIter<'a> {
|
||||
/// Creates an uncompressed value of the domain name.
|
||||
///
|
||||
/// The method has a default implementation that composes the name into
|
||||
|
||||
+92
-2
@@ -6,11 +6,13 @@
|
||||
//! `rdata` module and the types defined for operating on them differ from
|
||||
//! how other record types are handled.
|
||||
|
||||
use bytes::{BufMut, Bytes};
|
||||
use std::mem;
|
||||
use std::marker::PhantomData;
|
||||
use ::iana::{OptionCode, Rtype};
|
||||
use bytes::{BigEndian, BufMut, ByteOrder, Bytes};
|
||||
use ::iana::{OptionCode, OptRcode, Rtype};
|
||||
use super::compose::{Composable, Compressable, Compressor};
|
||||
use super::error::ShortBuf;
|
||||
use super::header::Header;
|
||||
use super::parse::Parser;
|
||||
use super::rdata::RecordData;
|
||||
|
||||
@@ -82,6 +84,94 @@ impl Compressable for Opt {
|
||||
}
|
||||
|
||||
|
||||
//------------ OptHeader -----------------------------------------------------
|
||||
|
||||
/// The header of an OPT record.
|
||||
///
|
||||
/// The OPT record reappropriates the record header for encoding some
|
||||
/// basic information. This type provides access to this information. It
|
||||
/// consists of the record header accept for its `rdlen` field.
|
||||
///
|
||||
/// This is so that `OptBuilder` can safely deref to this type.
|
||||
///
|
||||
// +------------+--------------+------------------------------+
|
||||
// | Field Name | Field Type | Description |
|
||||
// +------------+--------------+------------------------------+
|
||||
// | NAME | domain name | MUST be 0 (root domain) |
|
||||
// | TYPE | u_int16_t | OPT (41) |
|
||||
// | CLASS | u_int16_t | requestor's UDP payload size |
|
||||
// | TTL | u_int32_t | extended RCODE and flags |
|
||||
// | RDLEN | u_int16_t | length of all RDATA |
|
||||
// | RDATA | octet stream | {attribute,value} pairs |
|
||||
// +------------+--------------+------------------------------+
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct OptHeader {
|
||||
/// The bytes of the header.
|
||||
inner: [u8; 9],
|
||||
}
|
||||
|
||||
impl OptHeader {
|
||||
pub fn for_record_slice(slice: &[u8]) -> &OptHeader {
|
||||
assert!(slice.len() >= mem::size_of::<Self>());
|
||||
unsafe { &*(slice.as_ptr() as *const OptHeader) }
|
||||
}
|
||||
|
||||
pub fn for_record_slice_mut(slice: &mut [u8]) -> &mut OptHeader {
|
||||
assert!(slice.len() >= mem::size_of::<Self>());
|
||||
unsafe { &mut *(slice.as_ptr() as *mut OptHeader) }
|
||||
}
|
||||
|
||||
pub fn udp_payload_size(&self) -> u16 {
|
||||
BigEndian::read_u16(&self.inner[3..])
|
||||
}
|
||||
|
||||
pub fn set_udp_payload_size(&mut self, value: u16) {
|
||||
BigEndian::write_u16(&mut self.inner[3..], value)
|
||||
}
|
||||
|
||||
pub fn rcode(&self, header: &Header) -> OptRcode {
|
||||
OptRcode::from_parts(header.rcode(), self.inner[5])
|
||||
}
|
||||
|
||||
pub fn set_rcode(&mut self, rcode: OptRcode) {
|
||||
self.inner[5] = rcode.ext()
|
||||
}
|
||||
|
||||
pub fn version(&self) -> u8 {
|
||||
self.inner[6]
|
||||
}
|
||||
|
||||
pub fn dnssec_ok(&self) -> bool {
|
||||
self.inner[7] & 0x80 != 0
|
||||
}
|
||||
|
||||
pub fn set_dnssec_ok(&mut self, value: bool) {
|
||||
if value {
|
||||
self.inner[7] |= 0x80
|
||||
}
|
||||
else {
|
||||
self.inner[7] &= 0x7F
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for OptHeader {
|
||||
fn default() -> Self {
|
||||
OptHeader { inner: [0, 41, 0, 0, 0, 0, 0, 0, 0] }
|
||||
}
|
||||
}
|
||||
|
||||
impl Composable for OptHeader {
|
||||
fn compose_len(&self) -> usize {
|
||||
9
|
||||
}
|
||||
|
||||
fn compose<B: BufMut>(&self, buf: &mut B) {
|
||||
buf.put_slice(&self.inner)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ OptIter -------------------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
||||
@@ -4,6 +4,7 @@ use std::fmt;
|
||||
use bytes::{BufMut, Bytes};
|
||||
use ::bits::compose::Composable;
|
||||
use ::bits::error::ShortBuf;
|
||||
use ::bits::message_builder::OptBuilder;
|
||||
use ::bits::parse::Parser;
|
||||
use ::iana::OptionCode;
|
||||
use super::OptData;
|
||||
@@ -23,6 +24,15 @@ impl Nsid {
|
||||
pub fn new(bytes: Bytes) -> Self {
|
||||
Nsid { bytes }
|
||||
}
|
||||
|
||||
pub fn push<T: AsRef<[u8]>>(builder: &mut OptBuilder, data: &T)
|
||||
-> Result<(), ShortBuf> {
|
||||
let data = data.as_ref();
|
||||
assert!(data.len() <= ::std::u16::MAX as usize);
|
||||
builder.build(OptionCode::Nsid, data.len() as u16, |buf| {
|
||||
buf.compose(data)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl OptData for Nsid {
|
||||
|
||||
@@ -4,6 +4,7 @@ use std::slice;
|
||||
use bytes::{BufMut, Bytes};
|
||||
use ::bits::compose::Composable;
|
||||
use ::bits::error::ShortBuf;
|
||||
use ::bits::message_builder::OptBuilder;
|
||||
use ::bits::parse::Parser;
|
||||
use ::iana::{OptionCode, SecAlg};
|
||||
use super::OptData;
|
||||
@@ -26,6 +27,17 @@ macro_rules! option_type {
|
||||
pub fn iter(&self) -> SecAlgsIter {
|
||||
SecAlgsIter::new(self.bytes.as_ref())
|
||||
}
|
||||
|
||||
pub fn push(builder: &mut OptBuilder, algs: &[SecAlg])
|
||||
-> Result<(), ShortBuf> {
|
||||
assert!(algs.len() <= ::std::u16::MAX as usize);
|
||||
builder.build(OptionCode::$name, algs.len() as u16, |buf| {
|
||||
for alg in algs {
|
||||
buf.compose(&alg.to_int())?
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//--- Composable and OptData
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
use bytes::BufMut;
|
||||
use ::bits::compose::Composable;
|
||||
use ::bits::error::ShortBuf;
|
||||
use ::bits::message_builder::OptBuilder;
|
||||
use ::bits::parse::Parser;
|
||||
use ::iana::OptionCode;
|
||||
use super::{OptData, OptionParseError};
|
||||
@@ -17,6 +19,11 @@ impl Expire {
|
||||
Expire(expire)
|
||||
}
|
||||
|
||||
pub fn push(builder: &mut OptBuilder, expire: Option<u32>)
|
||||
-> Result<(), ShortBuf> {
|
||||
builder.push(&Self::new(expire))
|
||||
}
|
||||
|
||||
pub fn expire(&self) -> Option<u32> {
|
||||
self.0
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
use bytes::BufMut;
|
||||
use ::bits::compose::Composable;
|
||||
use ::bits::error::ShortBuf;
|
||||
use ::bits::message_builder::OptBuilder;
|
||||
use ::bits::parse::Parser;
|
||||
use ::iana::OptionCode;
|
||||
use super::{OptData, OptionParseError};
|
||||
@@ -17,6 +19,11 @@ impl TcpKeepalive {
|
||||
TcpKeepalive(timeout)
|
||||
}
|
||||
|
||||
pub fn push(builder: &mut OptBuilder, timeout: u16)
|
||||
-> Result<(), ShortBuf> {
|
||||
builder.push(&Self::new(timeout))
|
||||
}
|
||||
|
||||
pub fn timeout(&self) -> u16 {
|
||||
self.0
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ use bytes::BufMut;
|
||||
use rand::random;
|
||||
use ::bits::compose::Composable;
|
||||
use ::bits::error::ShortBuf;
|
||||
use ::bits::message_builder::OptBuilder;
|
||||
use ::bits::parse::Parser;
|
||||
use ::iana::OptionCode;
|
||||
use super::OptData;
|
||||
@@ -31,6 +32,11 @@ impl Padding {
|
||||
pub fn new(len: u16, mode: PaddingMode) -> Self {
|
||||
Padding { len, mode }
|
||||
}
|
||||
|
||||
pub fn push(builder: &mut OptBuilder, len: u16, mode: PaddingMode)
|
||||
-> Result<(), ShortBuf> {
|
||||
builder.push(&Self::new(len, mode))
|
||||
}
|
||||
|
||||
pub fn len(&self) -> u16 {
|
||||
self.len
|
||||
|
||||
@@ -5,6 +5,7 @@ use std::net::IpAddr;
|
||||
use bytes::BufMut;
|
||||
use ::bits::compose::Composable;
|
||||
use ::bits::error::ShortBuf;
|
||||
use ::bits::message_builder::OptBuilder;
|
||||
use ::bits::parse::Parser;
|
||||
use ::iana::OptionCode;
|
||||
use super::OptData;
|
||||
@@ -26,6 +27,11 @@ impl ClientSubnet {
|
||||
ClientSubnet { source_prefix_len, scope_prefix_len, addr }
|
||||
}
|
||||
|
||||
pub fn push(builder: &mut OptBuilder, source_prefix_len: u8,
|
||||
scope_prefix_len: u8, addr: IpAddr) -> Result<(), ShortBuf> {
|
||||
builder.push(&Self::new(source_prefix_len, scope_prefix_len, addr))
|
||||
}
|
||||
|
||||
pub fn source_prefix_len(&self) -> u8 { self.source_prefix_len }
|
||||
pub fn scope_prefix_len(&self) -> u8 { self.scope_prefix_len }
|
||||
pub fn addr(&self) -> IpAddr { self.addr }
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
use std::mem;
|
||||
use bytes::BufMut;
|
||||
use ::bits::compose::Composable;
|
||||
use ::bits::error::ShortBuf;
|
||||
use ::bits::message_builder::OptBuilder;
|
||||
use ::bits::parse::Parser;
|
||||
use ::iana::OptionCode;
|
||||
use super::{OptData, OptionParseError};
|
||||
@@ -18,6 +20,11 @@ impl Cookie {
|
||||
Cookie(cookie)
|
||||
}
|
||||
|
||||
pub fn push(builder: &mut OptBuilder, cookie: [u8; 8])
|
||||
-> Result<(), ShortBuf> {
|
||||
builder.push(&Self::new(cookie))
|
||||
}
|
||||
|
||||
pub fn cookie(&self) -> &[u8; 8] {
|
||||
&self.0
|
||||
}
|
||||
|
||||
+11
-1
@@ -3,7 +3,8 @@
|
||||
use bytes::BufMut;
|
||||
use ::bits::compose::Composable;
|
||||
use ::bits::error::ShortBuf;
|
||||
use ::bits::name::{Dname, DnameError};
|
||||
use ::bits::message_builder::OptBuilder;
|
||||
use ::bits::name::{Dname, DnameError, ToDname};
|
||||
use ::bits::parse::Parser;
|
||||
use ::iana::OptionCode;
|
||||
use super::OptData;
|
||||
@@ -21,6 +22,15 @@ impl Chain {
|
||||
Chain { start }
|
||||
}
|
||||
|
||||
pub fn push<N: ToDname>(builder: &mut OptBuilder, start: &N)
|
||||
-> Result<(), ShortBuf> {
|
||||
let len = start.compose_len();
|
||||
assert!(len <= ::std::u16::MAX as usize);
|
||||
builder.build(OptionCode::Chain, len as u16, |buf| {
|
||||
buf.compose(start)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn start(&self) -> &Dname {
|
||||
&self.start
|
||||
}
|
||||
|
||||
+50
-1
@@ -1,7 +1,9 @@
|
||||
//! EDNS Options from RFC 8145.
|
||||
|
||||
use bytes::{BufMut, Bytes};
|
||||
use bytes::{BigEndian, BufMut, ByteOrder, Bytes};
|
||||
use ::bits::compose::Composable;
|
||||
use ::bits::error::ShortBuf;
|
||||
use ::bits::message_builder::OptBuilder;
|
||||
use ::bits::parse::Parser;
|
||||
use ::iana::OptionCode;
|
||||
use super::{OptData, OptionParseError};
|
||||
@@ -18,6 +20,22 @@ impl KeyTag {
|
||||
pub fn new(bytes: Bytes) -> Self {
|
||||
KeyTag { bytes }
|
||||
}
|
||||
|
||||
pub fn push(builder: &mut OptBuilder, tags: &[u16])
|
||||
-> Result<(), ShortBuf> {
|
||||
let len = tags.len() * 2;
|
||||
assert!(len <= ::std::u16::MAX as usize);
|
||||
builder.build(OptionCode::EdnsKeyTag, len as u16, |buf| {
|
||||
for tag in tags {
|
||||
buf.compose(&tag)?
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> KeyTagIter {
|
||||
KeyTagIter(self.bytes.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl Composable for KeyTag {
|
||||
@@ -48,3 +66,34 @@ impl OptData for KeyTag {
|
||||
Ok(Some(Self::new(parser.parse_bytes(len)?)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a KeyTag {
|
||||
type Item = u16;
|
||||
type IntoIter = KeyTagIter<'a>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ KeyTagIter ----------------------------------------------------
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct KeyTagIter<'a>(&'a [u8]);
|
||||
|
||||
impl<'a> Iterator for KeyTagIter<'a> {
|
||||
type Item = u16;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.0.len() < 2 {
|
||||
None
|
||||
}
|
||||
else {
|
||||
let (item, tail) = self.0.split_at(2);
|
||||
self.0 = tail;
|
||||
Some(BigEndian::read_u16(item))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,21 @@ impl<N> Question<N> {
|
||||
}
|
||||
|
||||
|
||||
//--- From
|
||||
|
||||
impl<N> From<(N, Rtype, Class)> for Question<N> {
|
||||
fn from((name, rtype, class): (N, Rtype, Class)) -> Self {
|
||||
Question::new(name, rtype, class)
|
||||
}
|
||||
}
|
||||
|
||||
impl<N> From<(N, Rtype)> for Question<N> {
|
||||
fn from((name, rtype): (N, Rtype)) -> Self {
|
||||
Question::new(name, rtype, Class::In)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//--- Parseable, Composable, and Compressable
|
||||
|
||||
impl<N: Parseable> Parseable for Question<N> {
|
||||
|
||||
+9
-3
@@ -27,7 +27,7 @@
|
||||
use std::fmt;
|
||||
use bytes::{BufMut, Bytes};
|
||||
use ::iana::Rtype;
|
||||
use super::compose::Composable;
|
||||
use super::compose::{Composable, Compressable, Compressor};
|
||||
use super::error::ShortBuf;
|
||||
use super::parse::Parser;
|
||||
|
||||
@@ -35,7 +35,7 @@ use super::parse::Parser;
|
||||
//----------- RecordData -----------------------------------------------------
|
||||
|
||||
/// A trait for types representing record data.
|
||||
pub trait RecordData: Composable + Sized {
|
||||
pub trait RecordData: Composable + Compressable + Sized {
|
||||
/// The type of an error returned when parsing fails.
|
||||
type ParseErr: Clone;
|
||||
|
||||
@@ -100,7 +100,7 @@ impl UnknownRecordData {
|
||||
}
|
||||
|
||||
|
||||
//--- Composable and RecordData
|
||||
//--- Composable, Compressable, and RecordData
|
||||
|
||||
impl Composable for UnknownRecordData {
|
||||
fn compose_len(&self) -> usize {
|
||||
@@ -112,6 +112,12 @@ impl Composable for UnknownRecordData {
|
||||
}
|
||||
}
|
||||
|
||||
impl Compressable for UnknownRecordData {
|
||||
fn compress(&self, buf: &mut Compressor) -> Result<(), ShortBuf> {
|
||||
buf.compose(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl RecordData for UnknownRecordData {
|
||||
type ParseErr = ShortBuf;
|
||||
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
use std::cmp;
|
||||
use std::fmt;
|
||||
use std::hash;
|
||||
use bytes::BufMut;
|
||||
use ::bits::compose::Composable;
|
||||
use ::bits::error::ShortBuf;
|
||||
use ::bits::parse::{Parseable, Parser};
|
||||
|
||||
|
||||
//------------ OptionCode ---------------------------------------------------
|
||||
@@ -81,6 +85,27 @@ impl OptionCode {
|
||||
}
|
||||
|
||||
|
||||
//--- Parseable and Composable
|
||||
|
||||
impl Parseable for OptionCode {
|
||||
type Err = ShortBuf;
|
||||
|
||||
fn parse(parser: &mut Parser) -> Result<Self, Self::Err> {
|
||||
u16::parse(parser).map(OptionCode::from_int)
|
||||
}
|
||||
}
|
||||
|
||||
impl Composable for OptionCode {
|
||||
fn compose_len(&self) -> usize {
|
||||
2
|
||||
}
|
||||
|
||||
fn compose<B: BufMut>(&self, buf: &mut B) {
|
||||
self.to_int().compose(buf)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//--- From
|
||||
|
||||
impl From<u16> for OptionCode {
|
||||
|
||||
Reference in New Issue
Block a user