Bring back the message builder.

This commit is contained in:
Martin Hoffmann
2017-11-19 09:32:51 +01:00
parent 8e9e6a26d4
commit 1bebfbbe00
18 changed files with 769 additions and 251 deletions
+133 -16
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
*/
+2 -2
View File
@@ -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
View File
@@ -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)]
+10
View File
@@ -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 {
+12
View File
@@ -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
+7
View File
@@ -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
}
+7
View File
@@ -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
}
+6
View File
@@ -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
+6
View File
@@ -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 }
+7
View File
@@ -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
View File
@@ -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
View File
@@ -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))
}
}
}
+15
View File
@@ -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
View File
@@ -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;
+25
View File
@@ -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 {