mirror of
https://github.com/NLnetLabs/domain.git
synced 2026-10-01 14:15:00 +02:00
Rewrite everything. Again.
This commit is contained in:
@@ -0,0 +1,287 @@
|
||||
//! Building of wire-format DNS data.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
use super::error::{ComposeError, ComposeResult};
|
||||
use super::name::{DName, DNameSlice, Label, OwnedDName};
|
||||
|
||||
|
||||
//------------ ComposeBytes ---------------------------------------------------
|
||||
|
||||
/// A trait for composing a DNS wire format message.
|
||||
///
|
||||
/// Messages are created by pushing data to the end of the message.
|
||||
/// However, in order to avoid having to preassemble length-value parts of
|
||||
/// the message such as record data, there is an option to update previously
|
||||
/// written data.
|
||||
pub trait ComposeBytes: Sized + fmt::Debug {
|
||||
type Pos: Copy + fmt::Debug;
|
||||
|
||||
//--- Appending of basic types
|
||||
|
||||
/// Pushes a bytes slice to the end of the builder.
|
||||
fn push_bytes(&mut self, data: &[u8]) -> ComposeResult<()>;
|
||||
|
||||
/// Pushes placeholder bytes to the end of the target.
|
||||
fn push_empty(&mut self, len: usize) -> ComposeResult<()>;
|
||||
|
||||
/// Pushes a single octet to the end of the builder.
|
||||
fn push_u8(&mut self, data: u8) -> ComposeResult<()> {
|
||||
let bytes: [u8; 1] = unsafe { mem::transmute(data) };
|
||||
self.push_bytes(&bytes)
|
||||
}
|
||||
|
||||
/// Pushes an unsigned 16-bit word to the end of the builder.
|
||||
///
|
||||
/// The word is converted to network byte order before writing if
|
||||
/// necessary.
|
||||
fn push_u16(&mut self, data: u16) -> ComposeResult<()> {
|
||||
let data = data.to_be();
|
||||
let bytes: [u8; 2] = unsafe { mem::transmute(data) };
|
||||
self.push_bytes(&bytes)
|
||||
}
|
||||
|
||||
/// Pushes a unsigned 32-bit word to the end of the builder.
|
||||
///
|
||||
/// The word is converted to network byte order before writing if
|
||||
/// necessary.
|
||||
fn push_u32(&mut self, data: u32) -> ComposeResult<()> {
|
||||
let data = data.to_be();
|
||||
let bytes: [u8; 4] = unsafe { mem::transmute(data) };
|
||||
self.push_bytes(&bytes)
|
||||
}
|
||||
|
||||
//--- Appending of domain names.
|
||||
|
||||
/// Pushes a domain name to the end of the builder.
|
||||
fn push_dname<D: DName>(&mut self, name: &D) -> ComposeResult<()>;
|
||||
|
||||
/// Pushes a domain name to the end of the builder using compression.
|
||||
fn push_dname_compressed<D: DName>(&mut self, name: &D)
|
||||
-> ComposeResult<()>;
|
||||
|
||||
|
||||
//--- Checkpoint and rollback.
|
||||
|
||||
/// Mark the current position as a point for truncation.
|
||||
fn truncation_point(&mut self);
|
||||
|
||||
/// Returns whether the target has been truncated.
|
||||
fn truncated(&self) -> bool;
|
||||
|
||||
|
||||
//--- Updating of earlier data.
|
||||
|
||||
/// Returns the current write position of the builder.
|
||||
fn pos(&self) -> Self::Pos;
|
||||
|
||||
/// Returns the length of data added since the given position.
|
||||
fn delta(&self, pos: Self::Pos) -> usize;
|
||||
|
||||
/// Updates the builder starting at the given position with a bytes slice.
|
||||
///
|
||||
/// This method panics if the bytes slice is longer than the data
|
||||
/// assembled since the given write position.
|
||||
fn update_bytes(&mut self, pos: Self::Pos, data: &[u8])
|
||||
-> ComposeResult<()>;
|
||||
|
||||
/// Updates the octet at the given write position.
|
||||
///
|
||||
/// This method panics if there is no data at the given write position.
|
||||
fn update_u8(&mut self, pos: Self::Pos, data: u8)
|
||||
-> ComposeResult<()> {
|
||||
let bytes: [u8; 1] = unsafe { mem::transmute(data) };
|
||||
self.update_bytes(pos, &bytes)
|
||||
}
|
||||
|
||||
/// Updates an unsigned 16-bit word starting at the given write position.
|
||||
///
|
||||
/// This method panics if there aren’t two octets following the write
|
||||
/// position.
|
||||
fn update_u16(&mut self, pos: Self::Pos, data: u16)
|
||||
-> ComposeResult<()> {
|
||||
let data = data.to_be();
|
||||
let bytes: [u8; 2] = unsafe { mem::transmute(data) };
|
||||
self.update_bytes(pos, &bytes)
|
||||
}
|
||||
|
||||
/// Updates an unsigned 32-bit word starting at the given write position.
|
||||
///
|
||||
/// This method panics if there aren’t four octets following the write
|
||||
/// position.
|
||||
fn update_u32(&mut self, pos: Self::Pos, data: u32)
|
||||
-> ComposeResult<()> {
|
||||
let data = data.to_be();
|
||||
let bytes: [u8; 4] = unsafe { mem::transmute(data) };
|
||||
self.update_bytes(pos, &bytes)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ ComposeVec -----------------------------------------------------
|
||||
|
||||
/// A compose target based on a simple vector.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ComposeVec {
|
||||
vec: Vec<u8>,
|
||||
start: usize,
|
||||
maxlen: Option<usize>,
|
||||
checkpoint: Option<usize>,
|
||||
truncated: bool,
|
||||
compress: Option<HashMap<OwnedDName, u16>>,
|
||||
}
|
||||
|
||||
|
||||
impl ComposeVec {
|
||||
pub fn new(maxlen: Option<usize>, compress: bool) -> ComposeVec {
|
||||
ComposeVec::with_vec(Vec::new(), maxlen, compress)
|
||||
}
|
||||
|
||||
pub fn with_vec(vec: Vec<u8>, maxlen: Option<usize>, compress: bool)
|
||||
-> ComposeVec {
|
||||
let start = vec.len();
|
||||
ComposeVec {
|
||||
vec: Vec::new(),
|
||||
start: start,
|
||||
maxlen: maxlen,
|
||||
checkpoint: None,
|
||||
truncated: false,
|
||||
compress: if compress { Some(HashMap::new()) }
|
||||
else { None }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn finish(self) -> Vec<u8> {
|
||||
self.vec
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl ComposeVec {
|
||||
fn keep_pushing(&mut self, len: usize) -> ComposeResult<()> {
|
||||
if self.truncated { return Err(ComposeError::SizeExceeded) }
|
||||
else if let Some(maxlen) = self.maxlen {
|
||||
if maxlen < self.vec.len() + len {
|
||||
self.checkpoint.map(|len| self.vec.truncate(len));
|
||||
self.truncated = true;
|
||||
return Err(ComposeError::SizeExceeded)
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn push_dname_simple<D: DName>(&mut self, name: &D) -> ComposeResult<()> {
|
||||
for label in try!(name.to_cow()).iter() {
|
||||
try!(label.compose(self))
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn compress_pos(&self) -> usize {
|
||||
self.vec.len() - self.start
|
||||
}
|
||||
|
||||
fn add_compress_target(&mut self, name: OwnedDName, pos: usize) {
|
||||
if let Some(ref mut compress) = self.compress {
|
||||
if pos <= ::std::u16::MAX as usize {
|
||||
let _ = compress.insert(name, pos as u16);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_compress_target(&self, name: &DNameSlice) -> Option<u16> {
|
||||
if let Some(ref compress) = self.compress {
|
||||
compress.get(name).map(|v| *v)
|
||||
}
|
||||
else { None }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl ComposeBytes for ComposeVec {
|
||||
type Pos = usize;
|
||||
|
||||
fn push_bytes(&mut self, data: &[u8]) -> ComposeResult<()> {
|
||||
try!(self.keep_pushing(data.len()));
|
||||
self.vec.extend(data);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn push_empty(&mut self, len: usize) -> ComposeResult<()> {
|
||||
try!(self.keep_pushing(len));
|
||||
let len = self.vec.len() + len;
|
||||
self.vec.resize(len, 0);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn push_dname<D: DName>(&mut self, name: &D) -> ComposeResult<()> {
|
||||
if self.compress.is_some() {
|
||||
let name = try!(name.to_owned());
|
||||
let pos = self.compress_pos();
|
||||
try!(self.push_dname_simple(&name));
|
||||
self.add_compress_target(name, pos);
|
||||
Ok(())
|
||||
}
|
||||
else {
|
||||
self.push_dname_simple(name)
|
||||
}
|
||||
}
|
||||
|
||||
fn push_dname_compressed<D: DName>(&mut self, name: &D)
|
||||
-> ComposeResult<()> {
|
||||
if self.compress.is_some() {
|
||||
let name = try!(name.to_cow());
|
||||
let mut iter = name.iter();
|
||||
loop {
|
||||
let name = iter.as_name();
|
||||
if let Some(pos) = self.get_compress_target(name) {
|
||||
try!(Label::compose_compressed(self, pos));
|
||||
break;
|
||||
}
|
||||
let label = match iter.next() {
|
||||
Some(x) => x, None => break
|
||||
};
|
||||
let pos = self.compress_pos();
|
||||
try!(label.compose(self));
|
||||
self.add_compress_target(name.to_owned(), pos);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
else {
|
||||
self.push_dname_simple(name)
|
||||
}
|
||||
}
|
||||
|
||||
fn truncation_point(&mut self) {
|
||||
self.checkpoint = Some(self.vec.len())
|
||||
}
|
||||
|
||||
fn truncated(&self) -> bool {
|
||||
self.truncated
|
||||
}
|
||||
|
||||
fn pos(&self) -> Self::Pos {
|
||||
self.vec.len()
|
||||
}
|
||||
|
||||
fn delta(&self, pos: Self::Pos) -> usize {
|
||||
self.vec.len().checked_sub(pos).unwrap()
|
||||
}
|
||||
|
||||
fn update_bytes(&mut self, pos: Self::Pos, data: &[u8])
|
||||
-> ComposeResult<()> {
|
||||
if pos + data.len() > self.vec.len() {
|
||||
panic!("composer update overrun")
|
||||
}
|
||||
unsafe {
|
||||
ptr::copy_nonoverlapping(data.as_ptr(),
|
||||
self.vec[pos..pos + data.len()].as_mut_ptr(),
|
||||
data.len())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
//! Errors and results
|
||||
//!
|
||||
//! There are three different pairs of errors and results related to the
|
||||
//! three different operations that can fail:
|
||||
//!
|
||||
//! `ParseError` and `ParseResult` are used when parsing DNS data from its
|
||||
//! wire-format.
|
||||
//!
|
||||
//! `ComposeError` and `ComposeResult` are used when composing DNS wire-format
|
||||
//! data.
|
||||
//!
|
||||
//! `FromStrError` and `FromStrResult` are used when converting data from
|
||||
//! strings.
|
||||
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
|
||||
|
||||
//------------ ComposeError and ComposeResult -------------------------------
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum ComposeError {
|
||||
SizeExceeded,
|
||||
Overflow,
|
||||
ParseError(ParseError),
|
||||
}
|
||||
|
||||
impl Error for ComposeError {
|
||||
fn description(&self) -> &str {
|
||||
use self::ComposeError::*;
|
||||
|
||||
match *self {
|
||||
SizeExceeded => "message size has been exceeded",
|
||||
Overflow => "a counter has overflowed",
|
||||
ParseError(ref error) => error.description(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ParseError> for ComposeError {
|
||||
fn from(error: ParseError) -> ComposeError {
|
||||
ComposeError::ParseError(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ComposeError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.description().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub type ComposeResult<T> = Result<T, ComposeError>;
|
||||
|
||||
|
||||
//------------ ParseError and ParseResult -----------------------------------
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum ParseError {
|
||||
UnexpectedEnd,
|
||||
UnknownLabel,
|
||||
UnknownType,
|
||||
CompressedLabel,
|
||||
}
|
||||
|
||||
impl Error for ParseError {
|
||||
fn description(&self) -> &str {
|
||||
use self::ParseError::*;
|
||||
|
||||
match *self {
|
||||
UnexpectedEnd => "unexpected end of data",
|
||||
UnknownLabel => "unknown label type in domain name",
|
||||
UnknownType => "unknown type",
|
||||
CompressedLabel => "a compressed label was encountered",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ParseError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.description().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
pub type ParseResult<T> = Result<T, ParseError>;
|
||||
|
||||
|
||||
//------------ FromStrError and FromStrResult -------------------------------
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum FromStrError {
|
||||
UnexpectedEnd,
|
||||
LongLabel,
|
||||
IllegalEscape,
|
||||
IllegalCharacter,
|
||||
UnknownType,
|
||||
UnknownClass,
|
||||
}
|
||||
|
||||
impl Error for FromStrError {
|
||||
fn description(&self) -> &str {
|
||||
use self::FromStrError::*;
|
||||
|
||||
match *self {
|
||||
UnexpectedEnd => "unexpected end of input",
|
||||
LongLabel => "domain name label with more than 63 octets",
|
||||
IllegalEscape => "illegal escape sequence",
|
||||
IllegalCharacter => "illegal character",
|
||||
UnknownType => "unknown type",
|
||||
UnknownClass => "unknown class",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for FromStrError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.description().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
pub type FromStrResult<T> = Result<T, FromStrError>;
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
//! The flavors of DNS data.
|
||||
//!
|
||||
//! Because of the various types of domain names, all DNS data comes
|
||||
//! in one of three flavors: it can be `Owned`, that is, entirely
|
||||
//! self-contained; `Ref` when its data references an underlying bytes
|
||||
//! slice; `Lazy` when its data references an underlying DNS message and
|
||||
//! domain names can be compressed.
|
||||
//!
|
||||
//! This module defines the `Flavor` trait for referencing these three
|
||||
//! flavors as well as the actual flavors as types.
|
||||
|
||||
use std::marker::PhantomData;
|
||||
use super::name;
|
||||
use super::nest;
|
||||
|
||||
|
||||
/// The trait for the three flavors of DNS data.
|
||||
///
|
||||
/// This trait doesn’t actually define any methods but rather only collects
|
||||
/// the associated types for each flavor.
|
||||
pub trait Flavor<'a>: Sized {
|
||||
type DName: name::DName;
|
||||
type Nest: nest::Nest<'a, Self>;
|
||||
}
|
||||
|
||||
|
||||
/// The flavor for owned DNS data.
|
||||
pub struct Owned;
|
||||
|
||||
impl<'a> Flavor<'a> for Owned {
|
||||
type DName = name::OwnedDName;
|
||||
type Nest = nest::OwnedNest;
|
||||
}
|
||||
|
||||
/// The flavor for DNS data referencing an underlying bytes slice.
|
||||
pub struct Ref<'a> {
|
||||
marker: PhantomData<&'a u8>
|
||||
}
|
||||
|
||||
impl<'a> Flavor<'a> for Ref<'a> {
|
||||
type DName = name::DNameRef<'a>;
|
||||
type Nest = nest::NestRef<'a>;
|
||||
}
|
||||
|
||||
|
||||
/// The flavor for DNS data referencing an underlying DNS message.
|
||||
pub struct Lazy<'a> {
|
||||
marker: PhantomData<&'a u8>
|
||||
}
|
||||
|
||||
impl<'a> Flavor<'a> for Lazy<'a> {
|
||||
type DName = name::LazyDName<'a>;
|
||||
type Nest = nest::LazyNest<'a>;
|
||||
}
|
||||
@@ -13,7 +13,7 @@
|
||||
//!
|
||||
|
||||
use std::mem;
|
||||
use super::bytes::{BytesSlice, Error, Result};
|
||||
use super::error::{ComposeError, ComposeResult};
|
||||
use super::iana::{Opcode, Rcode};
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ use super::iana::{Opcode, Rcode};
|
||||
|
||||
/// The first part of the header of a DNS message.
|
||||
///
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Header {
|
||||
inner: [u8; 4]
|
||||
}
|
||||
@@ -39,16 +39,6 @@ impl Header {
|
||||
Header { inner: [0; 4] }
|
||||
}
|
||||
|
||||
/// Creates a header reference from the given slice.
|
||||
///
|
||||
/// Returns the header reference and the remainder of the slice. Or
|
||||
/// an error if the slice is to short.
|
||||
///
|
||||
pub fn split_from(slice: &[u8]) -> Result<(&Header, &[u8])> {
|
||||
let (left, right) = try!(slice.split_bytes(mem::size_of::<Self>()));
|
||||
Ok((unsafe { Self::from_message(left) }, right))
|
||||
}
|
||||
|
||||
/// Creates a header reference from a bytes slice of a message.
|
||||
///
|
||||
/// This function is unsafe as it assumes the bytes slice to have the
|
||||
@@ -183,7 +173,7 @@ impl Header {
|
||||
|
||||
/// The section count part of the header of a DNS message.
|
||||
///
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct HeaderCounts {
|
||||
inner: [u8; 8]
|
||||
}
|
||||
@@ -196,14 +186,6 @@ impl HeaderCounts {
|
||||
HeaderCounts { inner: [0; 8] }
|
||||
}
|
||||
|
||||
/// Creates a reference from the given slice.
|
||||
///
|
||||
/// Returns the reference and the remainder of the slice.
|
||||
pub fn split_from(slice: &[u8]) -> Result<(&HeaderCounts, &[u8])> {
|
||||
let (left, right) = try!(slice.split_bytes(mem::size_of::<Self>()));
|
||||
Ok((unsafe { mem::transmute(left.as_ptr()) }, right))
|
||||
}
|
||||
|
||||
/// Creates a reference from the bytes slice of a message.
|
||||
///
|
||||
/// This function is unsafe as it assumes the bytes slice to have the
|
||||
@@ -243,7 +225,7 @@ impl HeaderCounts {
|
||||
}
|
||||
|
||||
/// Increase the QDCOUNT field.
|
||||
pub fn inc_qdcount(&mut self, inc: u16) -> Result<()> {
|
||||
pub fn inc_qdcount(&mut self, inc: u16) -> ComposeResult<()> {
|
||||
self.inc_u16(0, inc)
|
||||
}
|
||||
|
||||
@@ -258,7 +240,7 @@ impl HeaderCounts {
|
||||
}
|
||||
|
||||
/// Increases the ANCOUNT field.
|
||||
pub fn inc_ancount(&mut self, inc: u16) -> Result<()> {
|
||||
pub fn inc_ancount(&mut self, inc: u16) -> ComposeResult<()> {
|
||||
self.inc_u16(2, inc)
|
||||
}
|
||||
|
||||
@@ -273,7 +255,7 @@ impl HeaderCounts {
|
||||
}
|
||||
|
||||
/// Increases the NSCOUNT field.
|
||||
pub fn inc_nscount(&mut self, inc: u16) -> Result<()> {
|
||||
pub fn inc_nscount(&mut self, inc: u16) -> ComposeResult<()> {
|
||||
self.inc_u16(4, inc)
|
||||
}
|
||||
|
||||
@@ -288,7 +270,7 @@ impl HeaderCounts {
|
||||
}
|
||||
|
||||
/// Increases the ARCOUNT field.
|
||||
pub fn inc_arcount(&mut self, inc: u16) -> Result<()> {
|
||||
pub fn inc_arcount(&mut self, inc: u16) -> ComposeResult<()> {
|
||||
self.inc_u16(6, inc)
|
||||
}
|
||||
|
||||
@@ -331,10 +313,10 @@ impl HeaderCounts {
|
||||
self.inner[offset + 1] = value as u8;
|
||||
}
|
||||
|
||||
fn inc_u16(&mut self, offset: usize, inc: u16) -> Result<()> {
|
||||
fn inc_u16(&mut self, offset: usize, inc: u16) -> ComposeResult<()> {
|
||||
let value = match self.get_u16(offset).checked_add(inc) {
|
||||
Some(value) => value,
|
||||
None => return Err(Error::Overflow),
|
||||
None => return Err(ComposeError::Overflow),
|
||||
};
|
||||
self.set_u16(offset, value);
|
||||
Ok(())
|
||||
@@ -346,9 +328,65 @@ impl HeaderCounts {
|
||||
|
||||
/// The complete header of a DNS message.
|
||||
///
|
||||
/// Currently, this type is only used to conveniently get the size of the
|
||||
/// entire header.
|
||||
pub type FullHeader = [u8; 12];
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct FullHeader {
|
||||
inner: [u8; 12]
|
||||
}
|
||||
|
||||
/// # Creation and Conversion
|
||||
///
|
||||
impl FullHeader {
|
||||
/// Creates a new empty header.
|
||||
pub fn new() -> FullHeader {
|
||||
FullHeader { inner: [0; 12] }
|
||||
}
|
||||
|
||||
/// Creates a reference from the bytes slice of a message.
|
||||
///
|
||||
/// This function is unsafe as it assumes the bytes slice to have the
|
||||
/// correct length.
|
||||
pub unsafe fn from_message(s: &[u8]) -> &FullHeader {
|
||||
mem::transmute(s.as_ptr())
|
||||
}
|
||||
|
||||
/// Creates a mutable reference from the bytes slice of a message.
|
||||
///
|
||||
/// This function is unsafe as it assumes the bytes slice to have the
|
||||
/// correct length.
|
||||
pub unsafe fn from_message_mut(s: &mut [u8]) -> &mut FullHeader {
|
||||
mem::transmute(s.as_ptr())
|
||||
}
|
||||
|
||||
/// Returns the underlying bytes slice.
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// # Access to Header and Dounts
|
||||
///
|
||||
impl FullHeader {
|
||||
/// Returns a reference to the header.
|
||||
pub fn header(&self) -> &Header {
|
||||
unsafe { Header::from_message(&self.inner) }
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the header.
|
||||
pub fn header_mut(&mut self) -> &mut Header {
|
||||
unsafe { Header::from_message_mut(&mut self. inner) }
|
||||
}
|
||||
|
||||
/// Returns a reference to the header counts.
|
||||
pub fn counts(&self) -> &HeaderCounts {
|
||||
unsafe { HeaderCounts::from_message(&self.inner) }
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the header counts.
|
||||
pub fn counts_mut(&mut self) -> &mut HeaderCounts {
|
||||
unsafe { HeaderCounts::from_message_mut(&mut self.inner) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============ Testing ======================================================
|
||||
@@ -1,12 +1,9 @@
|
||||
//! DNS CLASSes.
|
||||
|
||||
use std::convert;
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
use std::num;
|
||||
use std::result;
|
||||
use std::str;
|
||||
use super::super::bytes::BytesBuf;
|
||||
use super::super::error::{FromStrError, FromStrResult};
|
||||
|
||||
|
||||
/// DNS CLASSes.
|
||||
@@ -62,10 +59,6 @@ impl Class {
|
||||
Int(value) => value
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push_buf<B: BytesBuf>(self, buf: &mut B) {
|
||||
buf.push_u16(self.to_int());
|
||||
}
|
||||
}
|
||||
|
||||
impl convert::From<u16> for Class {
|
||||
@@ -74,10 +67,16 @@ impl convert::From<u16> for Class {
|
||||
}
|
||||
}
|
||||
|
||||
impl str::FromStr for Class {
|
||||
type Err = ParseError;
|
||||
impl convert::From<Class> for u16 {
|
||||
fn from(value: Class) -> u16 {
|
||||
value.to_int()
|
||||
}
|
||||
}
|
||||
|
||||
fn from_str(s: &str) -> ParseResult<Self> {
|
||||
impl str::FromStr for Class {
|
||||
type Err = FromStrError;
|
||||
|
||||
fn from_str(s: &str) -> FromStrResult<Self> {
|
||||
use std::ascii::AsciiExt;
|
||||
use self::Class::*;
|
||||
|
||||
@@ -90,14 +89,18 @@ impl str::FromStr for Class {
|
||||
if let Some((n, _)) = s.char_indices().nth(5) {
|
||||
let (l, r) = s.split_at(n);
|
||||
if l.eq_ignore_ascii_case("CLASS") {
|
||||
Ok(Int(try!(u16::from_str_radix(r, 10))))
|
||||
let value = match u16::from_str_radix(r, 10) {
|
||||
Ok(x) => x,
|
||||
Err(..) => return Err(FromStrError::UnknownClass)
|
||||
};
|
||||
Ok(Int(value))
|
||||
}
|
||||
else {
|
||||
Err(ParseError::UnknownClass)
|
||||
Err(FromStrError::UnknownClass)
|
||||
}
|
||||
}
|
||||
else {
|
||||
Err(ParseError::UnknownClass)
|
||||
Err(FromStrError::UnknownClass)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -140,35 +143,3 @@ impl PartialEq<Class> for u16 {
|
||||
|
||||
impl Eq for Class { }
|
||||
|
||||
|
||||
//------------ ParseError and ParseResult -----------------------------------
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum ParseError {
|
||||
UnknownClass,
|
||||
}
|
||||
|
||||
impl error::Error for ParseError {
|
||||
fn description(&self) -> &str {
|
||||
match *self {
|
||||
ParseError::UnknownClass => "unknown class",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl convert::From<num::ParseIntError> for ParseError {
|
||||
fn from(_: num::ParseIntError) -> Self {
|
||||
ParseError::UnknownClass
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ParseError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
use std::error::Error;
|
||||
|
||||
self.description().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
pub type ParseResult<T> = result::Result<T, ParseError>;
|
||||
|
||||
@@ -2,12 +2,9 @@
|
||||
//!
|
||||
|
||||
use std::convert;
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
use std::num;
|
||||
use std::result;
|
||||
use std::str;
|
||||
use super::super::bytes::BytesBuf;
|
||||
use super::super::error::{FromStrError, FromStrResult};
|
||||
|
||||
|
||||
/// Resource Record Types.
|
||||
@@ -586,10 +583,6 @@ impl RRType {
|
||||
Int(value) => value
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push_buf<B: BytesBuf>(self, buf: &mut B) {
|
||||
buf.push_u16(self.to_int())
|
||||
}
|
||||
}
|
||||
|
||||
impl convert::From<u16> for RRType {
|
||||
@@ -598,11 +591,16 @@ impl convert::From<u16> for RRType {
|
||||
}
|
||||
}
|
||||
|
||||
impl convert::From<RRType> for u16 {
|
||||
fn from(value: RRType) -> u16 {
|
||||
value.to_int()
|
||||
}
|
||||
}
|
||||
|
||||
impl str::FromStr for RRType {
|
||||
type Err = ParseError;
|
||||
type Err = FromStrError;
|
||||
|
||||
fn from_str(s: &str) -> ParseResult<Self> {
|
||||
fn from_str(s: &str) -> FromStrResult<Self> {
|
||||
use std::ascii::AsciiExt;
|
||||
use self::RRType::*;
|
||||
|
||||
@@ -694,14 +692,18 @@ impl str::FromStr for RRType {
|
||||
if let Some((n, _)) = s.char_indices().nth(4) {
|
||||
let (l, r) = s.split_at(n);
|
||||
if l.eq_ignore_ascii_case("TYPE") {
|
||||
Ok(Int(try!(u16::from_str_radix(r, 10))))
|
||||
let value = match u16::from_str_radix(r, 10) {
|
||||
Ok(x) => x,
|
||||
Err(..) => return Err(FromStrError::UnknownType)
|
||||
};
|
||||
Ok(Int(value))
|
||||
}
|
||||
else {
|
||||
Err(ParseError::UnknownType)
|
||||
Err(FromStrError::UnknownType)
|
||||
}
|
||||
}
|
||||
else {
|
||||
Err(ParseError::UnknownType)
|
||||
Err(FromStrError::UnknownType)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -823,35 +825,3 @@ impl PartialEq<RRType> for u16 {
|
||||
|
||||
impl Eq for RRType { }
|
||||
|
||||
|
||||
//------------ ParseError and ParseResult -----------------------------------
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum ParseError {
|
||||
UnknownType,
|
||||
}
|
||||
|
||||
impl error::Error for ParseError {
|
||||
fn description(&self) -> &str {
|
||||
match *self {
|
||||
ParseError::UnknownType => "unknown type",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl convert::From<num::ParseIntError> for ParseError {
|
||||
fn from(_: num::ParseIntError) -> Self {
|
||||
ParseError::UnknownType
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ParseError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
use std::error::Error;
|
||||
|
||||
self.description().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
pub type ParseResult<T> = result::Result<T, ParseError>;
|
||||
|
||||
@@ -0,0 +1,470 @@
|
||||
//! DNS messages.
|
||||
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use super::compose::ComposeBytes;
|
||||
use super::error::{ComposeError, ComposeResult, ParseResult};
|
||||
use super::flavor::{self, Flavor};
|
||||
use super::header::{Header, HeaderCounts, FullHeader};
|
||||
use super::nest::{self, Nest};
|
||||
use super::parse::ParseBytes;
|
||||
use super::question::{ComposeQuestion, Question};
|
||||
use super::rdata::{GenericRecordData, RecordData};
|
||||
use super::record::{ComposeRecord, Record};
|
||||
|
||||
|
||||
//============ Disecting Existing Messages ==================================
|
||||
|
||||
//============ Message ======================================================
|
||||
|
||||
/// A DNS message.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Message<'a, F: Flavor<'a>> {
|
||||
nest: F::Nest
|
||||
}
|
||||
|
||||
pub type OwnedMessage<'a> = Message<'a, flavor::Owned>;
|
||||
pub type MessageRef<'a> = Message<'a, flavor::Ref<'a>>;
|
||||
pub type LazyMessage<'a> = Message<'a, flavor::Lazy<'a>>;
|
||||
|
||||
/// # Creation and Conversion
|
||||
///
|
||||
impl<'a, F: Flavor<'a>> Message<'a, F> {
|
||||
/// Creates a message from a nest.
|
||||
pub fn from_nest(nest: F::Nest) -> Self {
|
||||
Message { nest: nest }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Message<'a, flavor::Ref<'a>> {
|
||||
pub fn from_bytes(bytes: &'a [u8]) -> Self {
|
||||
Message::from_nest(nest::NestRef::from_bytes(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Message<'a, flavor::Lazy<'a>> {
|
||||
pub fn from_bytes(bytes: &'a [u8]) -> Self {
|
||||
Message::from_nest(nest::LazyNest::new(bytes, bytes))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// # Header Access
|
||||
//
|
||||
impl<'a, F: Flavor<'a>> Message<'a, F> {
|
||||
/// Returns a reference to the message header.
|
||||
pub fn header<'b: 'a>(&'b self) -> &'a Header {
|
||||
unsafe { Header::from_message(self.nest.as_slice()) }
|
||||
}
|
||||
|
||||
/// Returns a reference to the header counts of the message.
|
||||
pub fn counts<'b: 'a>(&'b self) -> &'a HeaderCounts {
|
||||
unsafe { HeaderCounts::from_message(self.nest.as_slice()) }
|
||||
}
|
||||
|
||||
/// Returns an iterator over the question section
|
||||
pub fn question<'b: 'a>(&'b self) -> QuestionSection<'a, F> {
|
||||
let mut parser = self.nest.parser();
|
||||
parser.skip(mem::size_of::<FullHeader>()).unwrap(); // XXX Hmm.
|
||||
QuestionSection::new(parser, (*self.counts()).clone())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ QuestionSection ----------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct QuestionSection<'a, F: Flavor<'a>> {
|
||||
parser: <F::Nest as Nest<'a, F>>::Parser,
|
||||
counts: HeaderCounts,
|
||||
count: u16
|
||||
}
|
||||
|
||||
impl<'a, F: Flavor<'a>> QuestionSection<'a, F> {
|
||||
fn new(parser: <F::Nest as Nest<'a, F>>::Parser, counts: HeaderCounts)
|
||||
-> Self {
|
||||
let count = counts.qdcount();
|
||||
QuestionSection { parser: parser, counts: counts, count: count }
|
||||
}
|
||||
|
||||
pub fn iter(&mut self) -> &mut Self {
|
||||
self
|
||||
}
|
||||
|
||||
/// Continues to the answer section.
|
||||
pub fn answer(mut self) -> ParseResult<AnswerSection<'a, F>> {
|
||||
for question in self.iter() {
|
||||
if let Err(e) = question {
|
||||
return Err(e)
|
||||
}
|
||||
}
|
||||
Ok(AnswerSection::new(self.parser, self.counts))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, F: Flavor<'a>> Iterator for QuestionSection<'a, F> {
|
||||
type Item = ParseResult<Question<'a, F>>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.count == 0 { return None }
|
||||
Some(Question::parse(&mut self.parser)
|
||||
.map(|res| { self.count -= 1; res }))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ AnswerSection ------------------------------------------------
|
||||
|
||||
/// The answer section of a message.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AnswerSection<'a, F: Flavor<'a>> {
|
||||
parser: <F::Nest as Nest<'a, F>>::Parser,
|
||||
counts: HeaderCounts,
|
||||
}
|
||||
|
||||
impl<'a, F: Flavor<'a>> AnswerSection<'a, F> {
|
||||
fn new(parser: <F::Nest as Nest<'a, F>>::Parser, counts: HeaderCounts)
|
||||
-> Self {
|
||||
AnswerSection { parser: parser, counts: counts }
|
||||
}
|
||||
|
||||
pub fn iter<D: RecordData<'a, F>>(&self) -> RecordIter<'a, F, D> {
|
||||
RecordIter::new(self.parser.clone(), self.counts.ancount())
|
||||
}
|
||||
|
||||
pub fn authority(self) -> ParseResult<AuthoritySection<'a, F>> {
|
||||
let mut iter = self.iter::<GenericRecordData<'a, F>>();
|
||||
try!(iter.exhaust());
|
||||
Ok(AuthoritySection::new(iter.parser, self.counts))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ AuthoritySection ---------------------------------------------
|
||||
|
||||
/// The authority section of a message.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AuthoritySection<'a, F: Flavor<'a>> {
|
||||
parser: <F::Nest as Nest<'a, F>>::Parser,
|
||||
counts: HeaderCounts,
|
||||
}
|
||||
|
||||
impl<'a, F: Flavor<'a>> AuthoritySection<'a, F> {
|
||||
fn new(parser: <F::Nest as Nest<'a, F>>::Parser, counts: HeaderCounts)
|
||||
-> Self {
|
||||
AuthoritySection { parser: parser, counts: counts }
|
||||
}
|
||||
|
||||
pub fn iter<D: RecordData<'a, F>>(&self) -> RecordIter<'a, F, D> {
|
||||
RecordIter::new(self.parser.clone(), self.counts.nscount())
|
||||
}
|
||||
|
||||
pub fn additional(self) -> ParseResult<AdditionalSection<'a, F>> {
|
||||
let mut iter = self.iter::<GenericRecordData<'a, F>>();
|
||||
try!(iter.exhaust());
|
||||
Ok(AdditionalSection::new(iter.parser, self.counts))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ AdditionalSection --------------------------------------------
|
||||
|
||||
/// The additional section of a message.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AdditionalSection<'a, F: Flavor<'a>> {
|
||||
parser: <F::Nest as Nest<'a, F>>::Parser,
|
||||
counts: HeaderCounts,
|
||||
}
|
||||
|
||||
impl<'a, F: Flavor<'a>> AdditionalSection<'a, F> {
|
||||
fn new(parser: <F::Nest as Nest<'a, F>>::Parser, counts: HeaderCounts)
|
||||
-> Self {
|
||||
AdditionalSection { parser: parser, counts: counts }
|
||||
}
|
||||
|
||||
pub fn iter<D: RecordData<'a, F>>(&self) -> RecordIter<'a, F, D> {
|
||||
RecordIter::new(self.parser.clone(), self.counts.ancount())
|
||||
}
|
||||
}
|
||||
|
||||
//------------ RecordIter ---------------------------------------------------
|
||||
|
||||
/// An iterator over the records in one of a record section.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RecordIter<'a, F: Flavor<'a>, D: RecordData<'a, F>> {
|
||||
parser: <F::Nest as Nest<'a, F>>::Parser,
|
||||
count: u16,
|
||||
marker: PhantomData<D>
|
||||
}
|
||||
|
||||
impl<'a, F: Flavor<'a>, D: RecordData<'a, F>> RecordIter<'a, F, D> {
|
||||
fn new(parser: <F::Nest as Nest<'a, F>>::Parser, count: u16) -> Self {
|
||||
RecordIter { parser: parser, count: count, marker: PhantomData }
|
||||
}
|
||||
|
||||
pub fn iter(&mut self) -> &mut Self { self }
|
||||
|
||||
fn exhaust(&mut self) -> ParseResult<()> {
|
||||
for record in self.iter() {
|
||||
if let Err(e) = record {
|
||||
return Err(e)
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn step(&mut self) -> ParseResult<Option<Record<'a, F, D>>> {
|
||||
Record::parse(&mut self.parser).map(|res| { self.count -= 1; res })
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, F, D> Iterator for RecordIter<'a, F, D>
|
||||
where F: Flavor<'a>, D: RecordData<'a, F> {
|
||||
type Item = ParseResult<Record<'a, F, D>>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.count == 0 { return None }
|
||||
loop {
|
||||
match self.step() {
|
||||
Ok(Some(record)) => return Some(Ok(record)),
|
||||
Ok(None) => (),
|
||||
Err(e) => return Some(Err(e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============ Building New Message =========================================
|
||||
|
||||
//------------ MessageBuilder -----------------------------------------------
|
||||
|
||||
/// A builder for constructing a DNS message.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MessageBuilder<C: ComposeBytes> {
|
||||
target: MessageTarget<C>
|
||||
}
|
||||
|
||||
impl<C: ComposeBytes> MessageBuilder<C> {
|
||||
/// Creates a new message builder.
|
||||
pub fn new(target: C) -> ComposeResult<Self> {
|
||||
MessageTarget::new(target)
|
||||
.map(|target| MessageBuilder { target: target })
|
||||
}
|
||||
|
||||
pub fn header(&self) -> &Header {
|
||||
self.target.header()
|
||||
}
|
||||
|
||||
pub fn header_mut(&mut self) -> &mut Header {
|
||||
self.target.header_mut()
|
||||
}
|
||||
|
||||
pub fn finish(self) -> ComposeResult<C> {
|
||||
self.target.finish()
|
||||
}
|
||||
|
||||
pub fn question(self) -> QuestionBuilder<C> {
|
||||
QuestionBuilder::new(self.target)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ QuestionBuilder ----------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct QuestionBuilder<C: ComposeBytes> {
|
||||
target: MessageTarget<C>
|
||||
}
|
||||
|
||||
impl<C: ComposeBytes> QuestionBuilder<C> {
|
||||
fn new(target: MessageTarget<C>) -> Self {
|
||||
QuestionBuilder { target: target }
|
||||
}
|
||||
|
||||
pub fn header(&self) -> &Header {
|
||||
self.target.header()
|
||||
}
|
||||
|
||||
pub fn header_mut(&mut self) -> &mut Header {
|
||||
self.target.header_mut()
|
||||
}
|
||||
|
||||
pub fn push<Q: ComposeQuestion>(&mut self, question: &Q)
|
||||
-> ComposeResult<()> {
|
||||
self.target.push(|target| question.compose(target),
|
||||
|counts| counts.inc_qdcount(1))
|
||||
}
|
||||
|
||||
pub fn answer(self) -> AnswerBuilder<C> {
|
||||
AnswerBuilder::new(self.target)
|
||||
}
|
||||
|
||||
pub fn finish(self) -> ComposeResult<C> {
|
||||
self.target.finish()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ AnswerBuilder ------------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AnswerBuilder<C: ComposeBytes> {
|
||||
target: MessageTarget<C>
|
||||
}
|
||||
|
||||
impl<C: ComposeBytes> AnswerBuilder<C> {
|
||||
fn new(target: MessageTarget<C>) -> Self {
|
||||
AnswerBuilder { target: target }
|
||||
}
|
||||
|
||||
pub fn header(&self) -> &Header {
|
||||
self.target.header()
|
||||
}
|
||||
|
||||
pub fn header_mut(&mut self) -> &mut Header {
|
||||
self.target.header_mut()
|
||||
}
|
||||
|
||||
pub fn push<R: ComposeRecord>(&mut self, record: &R) -> ComposeResult<()> {
|
||||
self.target.push(|target| record.compose(target),
|
||||
|counts| counts.inc_ancount(1))
|
||||
}
|
||||
|
||||
pub fn authority(self) -> AuthorityBuilder<C> {
|
||||
AuthorityBuilder::new(self.target)
|
||||
}
|
||||
|
||||
pub fn finish(self) -> ComposeResult<C> {
|
||||
self.target.finish()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ AuthorityBuilder ---------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AuthorityBuilder<C: ComposeBytes> {
|
||||
target: MessageTarget<C>
|
||||
}
|
||||
|
||||
impl<C: ComposeBytes> AuthorityBuilder<C> {
|
||||
fn new(target: MessageTarget<C>) -> Self {
|
||||
AuthorityBuilder { target: target }
|
||||
}
|
||||
|
||||
pub fn header(&self) -> &Header {
|
||||
self.target.header()
|
||||
}
|
||||
|
||||
pub fn header_mut(&mut self) -> &mut Header {
|
||||
self.target.header_mut()
|
||||
}
|
||||
|
||||
pub fn push<R: ComposeRecord>(&mut self, record: &R) -> ComposeResult<()> {
|
||||
self.target.push(|target| record.compose(target),
|
||||
|counts| counts.inc_nscount(1))
|
||||
}
|
||||
|
||||
pub fn additional(self) -> AdditionalBuilder<C> {
|
||||
AdditionalBuilder::new(self.target)
|
||||
}
|
||||
|
||||
pub fn finish(self) -> ComposeResult<C> {
|
||||
self.target.finish()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ AuthorityBuilder ---------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AdditionalBuilder<C: ComposeBytes> {
|
||||
target: MessageTarget<C>
|
||||
}
|
||||
|
||||
impl<C: ComposeBytes> AdditionalBuilder<C> {
|
||||
fn new(target: MessageTarget<C>) -> Self {
|
||||
AdditionalBuilder { target: target }
|
||||
}
|
||||
|
||||
pub fn header(&self) -> &Header {
|
||||
self.target.header()
|
||||
}
|
||||
|
||||
pub fn header_mut(&mut self) -> &mut Header {
|
||||
self.target.header_mut()
|
||||
}
|
||||
|
||||
pub fn push<R: ComposeRecord>(&mut self, record: &R) -> ComposeResult<()> {
|
||||
self.target.push(|target| record.compose(target),
|
||||
|counts| counts.inc_nscount(1))
|
||||
}
|
||||
|
||||
pub fn finish(self) -> ComposeResult<C> {
|
||||
self.target.finish()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ MessageTarget ------------------------------------------------
|
||||
|
||||
/// Underlying data for constructing a DNS message.
|
||||
///
|
||||
/// This private type does all the heavy lifting for constructing messages.
|
||||
#[derive(Clone, Debug)]
|
||||
struct MessageTarget<C: ComposeBytes> {
|
||||
/// The underlying bytes target.
|
||||
target: C,
|
||||
|
||||
/// The message’s header.
|
||||
header: FullHeader,
|
||||
|
||||
/// Position in build where the message starts.
|
||||
start: C::Pos,
|
||||
}
|
||||
|
||||
|
||||
impl<C: ComposeBytes> MessageTarget<C> {
|
||||
fn new(mut target: C) -> ComposeResult<Self> {
|
||||
let start = target.pos();
|
||||
try!(target.push_empty(mem::size_of::<FullHeader>()));
|
||||
Ok(MessageTarget {
|
||||
target: target,
|
||||
header: FullHeader::new(),
|
||||
start: start
|
||||
})
|
||||
}
|
||||
|
||||
fn header(&self) -> &Header {
|
||||
self.header.header()
|
||||
}
|
||||
|
||||
fn header_mut(&mut self) -> &mut Header {
|
||||
self.header.header_mut()
|
||||
}
|
||||
|
||||
fn push<O, I>(&mut self, composeop: O, incop: I) -> ComposeResult<()>
|
||||
where O: FnOnce(&mut C) -> ComposeResult<()>,
|
||||
I: FnOnce(&mut HeaderCounts) -> ComposeResult<()> {
|
||||
if !self.target.truncated() {
|
||||
self.target.truncation_point();
|
||||
match composeop(&mut self.target) {
|
||||
Ok(()) => {
|
||||
try!(incop(self.header.counts_mut()));
|
||||
Ok(())
|
||||
}
|
||||
Err(ComposeError::SizeExceeded) => Ok(()),
|
||||
Err(error) => Err(error)
|
||||
}
|
||||
}
|
||||
else { Ok(()) }
|
||||
}
|
||||
|
||||
fn finish(mut self) -> ComposeResult<C> {
|
||||
self.header.header_mut().set_tc(self.target.truncated());
|
||||
try!(self.target.update_bytes(self.start, self.header.as_bytes()));
|
||||
Ok(self.target)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
//! DNS data.
|
||||
|
||||
pub mod compose;
|
||||
pub mod error;
|
||||
pub mod flavor;
|
||||
pub mod header;
|
||||
pub mod iana;
|
||||
pub mod message;
|
||||
pub mod name;
|
||||
pub mod nest;
|
||||
pub mod question;
|
||||
pub mod parse;
|
||||
pub mod rdata;
|
||||
pub mod record;
|
||||
pub mod u8;
|
||||
+706
-545
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,247 @@
|
||||
//! Arbitrary sequences of embedded DNS data.
|
||||
|
||||
use std::borrow::Borrow;
|
||||
use std::fmt::Debug;
|
||||
use std::ops::Deref;
|
||||
use super::compose::ComposeBytes;
|
||||
use super::parse::{ParseBytes, ParseFlavor, ParseLazy, SliceParser,
|
||||
ContextParser};
|
||||
use super::error::{ComposeResult, ParseResult};
|
||||
use super::flavor::{Flavor, Owned, Ref, Lazy};
|
||||
use super::rdata::RecordData;
|
||||
|
||||
|
||||
//------------ Nest --------------------------------------------------------
|
||||
|
||||
/// A trait common to all nest types.
|
||||
pub trait Nest<'a, F: Flavor<'a>>: Sized + Clone + Debug {
|
||||
type Parser: ParseFlavor<'a, F> + Clone + Debug;
|
||||
|
||||
fn as_slice<'b: 'a>(&'b self) -> &'a [u8];
|
||||
fn len(&self) -> usize();
|
||||
fn compose<C: ComposeBytes>(&self, target: &mut C) -> ComposeResult<()>;
|
||||
fn parser<'b: 'a>(&'b self) -> Self::Parser;
|
||||
}
|
||||
|
||||
|
||||
//------------ NestRef -----------------------------------------------------
|
||||
|
||||
/// A reference to a nest.
|
||||
///
|
||||
/// This is a thin wrapper around an actual `&[u8]` and even derefs to it.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct NestRef<'a> {
|
||||
inner: &'a [u8]
|
||||
}
|
||||
|
||||
impl<'a> NestRef<'a> {
|
||||
pub fn from_bytes(bytes: &'a [u8]) -> Self {
|
||||
NestRef { inner: bytes }
|
||||
}
|
||||
|
||||
pub fn as_slice(&self) -> &'a [u8] {
|
||||
self.inner
|
||||
}
|
||||
|
||||
pub fn to_owned(&self) -> OwnedNest {
|
||||
OwnedNest::from_bytes(self.inner)
|
||||
}
|
||||
|
||||
pub fn parse<P: ParseBytes<'a>>(parser: &mut P, len: usize)
|
||||
-> ParseResult<Self> {
|
||||
Ok(NestRef { inner: try!(parser.parse_bytes(len)) })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//--- Nest
|
||||
|
||||
impl<'a> Nest<'a, Ref<'a>> for NestRef<'a> {
|
||||
type Parser = SliceParser<'a>;
|
||||
|
||||
fn as_slice<'b: 'a>(&'b self) -> &'a [u8] {
|
||||
self.inner
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.inner.len()
|
||||
}
|
||||
|
||||
fn compose<C: ComposeBytes>(&self, target: &mut C) -> ComposeResult<()> {
|
||||
target.push_bytes(self.inner)
|
||||
}
|
||||
|
||||
fn parser<'b: 'a>(&'b self) -> SliceParser<'a> {
|
||||
SliceParser::new(&self.inner)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//--- From
|
||||
|
||||
impl<'a> From<&'a [u8]> for NestRef<'a> {
|
||||
fn from(bytes: &'a[u8]) -> NestRef<'a> {
|
||||
NestRef::from_bytes(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//--- Deref, Borrow, AsRef
|
||||
|
||||
impl<'a> Deref for NestRef<'a> {
|
||||
type Target = [u8];
|
||||
|
||||
fn deref(&self) -> &[u8] {
|
||||
self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Borrow<[u8]> for NestRef<'a> {
|
||||
fn borrow(&self) -> &[u8] {
|
||||
self.deref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AsRef<[u8]> for NestRef<'a> {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.deref()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ OwnedNest ---------------------------------------------------
|
||||
|
||||
/// An owned nest.
|
||||
///
|
||||
/// This type derefs to `Vec<u8>` for all bytes slice methods.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct OwnedNest {
|
||||
inner: Vec<u8>
|
||||
}
|
||||
|
||||
impl OwnedNest {
|
||||
pub fn new() -> Self {
|
||||
OwnedNest { inner: Vec::new() }
|
||||
}
|
||||
|
||||
pub fn from_bytes(slice: &[u8]) -> Self {
|
||||
OwnedNest { inner: Vec::from(slice) }
|
||||
}
|
||||
|
||||
pub fn parse<'a, P>(p: &mut P, len: usize) -> ParseResult<Self>
|
||||
where P: ParseBytes<'a> {
|
||||
Ok(try!(NestRef::parse(p, len)).to_owned())
|
||||
}
|
||||
|
||||
pub fn as_slice(&self) -> &[u8] {
|
||||
self
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//--- Nest
|
||||
|
||||
impl<'a> Nest<'a, Owned> for OwnedNest {
|
||||
type Parser = SliceParser<'a>;
|
||||
|
||||
fn as_slice<'b: 'a>(&'b self) -> &'a [u8] {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.inner.len()
|
||||
}
|
||||
|
||||
fn compose<C: ComposeBytes>(&self, target: &mut C) -> ComposeResult<()> {
|
||||
target.push_bytes(&self.inner)
|
||||
}
|
||||
|
||||
fn parser<'b: 'a>(&'b self) -> SliceParser<'a> {
|
||||
SliceParser::new(&self.inner)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//--- From
|
||||
|
||||
impl<'a> From<&'a [u8]> for OwnedNest {
|
||||
fn from(bytes: &'a [u8]) -> Self {
|
||||
OwnedNest::from_bytes(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//--- Deref
|
||||
|
||||
impl Deref for OwnedNest {
|
||||
type Target = Vec<u8>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ LazyNest ----------------------------------------------------
|
||||
|
||||
/// A bytes sequence possibly containing compressed domain names.
|
||||
///
|
||||
/// For most purposes, this is identical to `NestRef` except that it
|
||||
/// carries the context for decompressing domain names around.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct LazyNest<'a> {
|
||||
bytes: &'a [u8],
|
||||
context: &'a [u8]
|
||||
}
|
||||
|
||||
impl<'a> LazyNest<'a> {
|
||||
pub fn new(bytes: &'a[u8], context: &'a[u8]) -> Self {
|
||||
LazyNest { bytes: bytes, context: context }
|
||||
}
|
||||
|
||||
pub fn parse<P: ParseLazy<'a>>(parser: &mut P, len: usize)
|
||||
-> ParseResult<Self> {
|
||||
Ok(LazyNest::new(try!(parser.parse_bytes(len)),
|
||||
parser.context()))
|
||||
}
|
||||
|
||||
pub fn to_owned(&self) -> OwnedNest {
|
||||
OwnedNest::from_bytes(self.bytes)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//--- Nest
|
||||
|
||||
impl<'a> Nest<'a, Lazy<'a>> for LazyNest<'a> {
|
||||
type Parser = ContextParser<'a>;
|
||||
|
||||
fn as_slice<'b: 'a>(&'b self) -> &'a [u8] {
|
||||
self.bytes
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.bytes.len()
|
||||
}
|
||||
|
||||
fn compose<C: ComposeBytes>(&self, target: &mut C) -> ComposeResult<()> {
|
||||
target.push_bytes(self.bytes)
|
||||
}
|
||||
|
||||
fn parser<'b: 'a>(&'b self) -> ContextParser<'a> {
|
||||
ContextParser::new(self.bytes, self.context)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//--- Deref
|
||||
|
||||
impl<'a> Deref for LazyNest<'a> {
|
||||
type Target = [u8];
|
||||
|
||||
fn deref(&self) -> &[u8] {
|
||||
&self.bytes
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
//! Parsing of wire-format DNS data.
|
||||
|
||||
use std::mem;
|
||||
use super::error::{ParseResult, ParseError};
|
||||
use super::flavor::{Flavor, Owned, Ref, Lazy};
|
||||
use super::name::{OwnedDName, DNameRef, LazyDName};
|
||||
use super::nest::{OwnedNest, NestRef, LazyNest};
|
||||
|
||||
|
||||
//------------ Traits -------------------------------------------------------
|
||||
|
||||
/// A trait for parsing simple wire-format DNS data.
|
||||
pub trait ParseBytes<'a>: Sized {
|
||||
/// Parses a bytes slice of a given length.
|
||||
fn parse_bytes(&mut self, len: usize) -> ParseResult<&'a [u8]>;
|
||||
|
||||
/// Skip the next `len` bytes.
|
||||
fn skip(&mut self, len: usize) -> ParseResult<()>;
|
||||
|
||||
/// Parses a single octet.
|
||||
fn parse_u8(&mut self) -> ParseResult<u8> {
|
||||
self.parse_bytes(1).map(|res| res[0])
|
||||
}
|
||||
|
||||
/// Parses an unsigned 16-bit word.
|
||||
fn parse_u16(&mut self) -> ParseResult<u16> {
|
||||
self.parse_bytes(2).map(|res| {
|
||||
let res: &[u8; 2] = unsafe { mem::transmute(res.as_ptr()) };
|
||||
let res = unsafe { mem::transmute(*res) };
|
||||
u16::from_be(res)
|
||||
})
|
||||
}
|
||||
|
||||
/// Parses an unsigned 32-bit word.
|
||||
fn parse_u32(&mut self) -> ParseResult<u32> {
|
||||
self.parse_bytes(4).map(|res| {
|
||||
let res: &[u8; 4] = unsafe { mem::transmute(res.as_ptr()) };
|
||||
let res = unsafe { mem::transmute(*res) };
|
||||
u32::from_be(res)
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates a sup-parser starting a the current position.
|
||||
fn sub(&self) -> Self;
|
||||
|
||||
/// Creates a sub-parser limited to `len` bytes and advance position.
|
||||
fn parse_sub(&mut self, len: usize) -> ParseResult<Self>;
|
||||
|
||||
/// Returns the length of the data we have seen already.
|
||||
fn seen(&self) -> usize;
|
||||
|
||||
/// Returns the length of the data left.
|
||||
fn left(&self) -> usize;
|
||||
}
|
||||
|
||||
pub trait ParseLazy<'a>: ParseBytes<'a> {
|
||||
fn context(&self) -> &'a[u8];
|
||||
}
|
||||
|
||||
|
||||
/// A trait for parsing wire-format DNS data.
|
||||
///
|
||||
/// While the basic types are implemented for every parser through the
|
||||
/// `ParseBytesSimple` trait, not every parser can parse every kind of
|
||||
/// domain name. Because of this, parsers may implement the
|
||||
/// `ParseBytes` trait only for specific flavors.
|
||||
pub trait ParseFlavor<'a, F: Flavor<'a>>: ParseBytes<'a> {
|
||||
fn parse_name(&mut self) -> ParseResult<F::DName>;
|
||||
fn parse_nest(&mut self, len: usize) -> ParseResult<F::Nest>;
|
||||
}
|
||||
|
||||
impl<'a, P: ParseBytes<'a>> ParseFlavor<'a, Owned> for P {
|
||||
fn parse_name(&mut self) -> ParseResult<OwnedDName> {
|
||||
OwnedDName::parse_complete(self)
|
||||
}
|
||||
|
||||
fn parse_nest(&mut self, len: usize) -> ParseResult<OwnedNest> {
|
||||
OwnedNest::parse(self, len)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, P: ParseBytes<'a>> ParseFlavor<'a, Ref<'a>> for P {
|
||||
fn parse_name(&mut self) -> ParseResult<DNameRef<'a>> {
|
||||
DNameRef::parse(self)
|
||||
}
|
||||
|
||||
fn parse_nest(&mut self, len: usize) -> ParseResult<NestRef<'a>> {
|
||||
NestRef::parse(self, len)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ParseFlavor<'a, Lazy<'a>> for ContextParser<'a> {
|
||||
fn parse_name(&mut self) -> ParseResult<LazyDName<'a>> {
|
||||
LazyDName::parse(self)
|
||||
}
|
||||
|
||||
fn parse_nest(&mut self, len: usize) -> ParseResult<LazyNest<'a>> {
|
||||
LazyNest::parse(self, len)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ SliceParser --------------------------------------------------
|
||||
|
||||
/// A parser that operates on an arbitrary bytes slice.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SliceParser<'a> {
|
||||
slice: &'a [u8],
|
||||
seen: usize,
|
||||
}
|
||||
|
||||
impl<'a> SliceParser<'a> {
|
||||
pub fn new(slice: &'a [u8]) -> Self {
|
||||
SliceParser { slice: slice, seen: 0 }
|
||||
}
|
||||
|
||||
fn check_len(&self, len: usize) -> ParseResult<()> {
|
||||
if len > self.slice.len() {
|
||||
Err(ParseError::UnexpectedEnd)
|
||||
}
|
||||
else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ParseBytes<'a> for SliceParser<'a> {
|
||||
fn parse_bytes(&mut self, len: usize) -> ParseResult<&'a [u8]> {
|
||||
try!(self.check_len(len));
|
||||
let (l, r) = self.slice.split_at(len);
|
||||
self.slice = r;
|
||||
self.seen += len;
|
||||
Ok(l)
|
||||
}
|
||||
|
||||
fn skip(&mut self, len: usize) -> ParseResult<()> {
|
||||
try!(self.check_len(len));
|
||||
self.slice = &self.slice[len..];
|
||||
self.seen += len;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn sub(&self) -> Self {
|
||||
SliceParser { slice: self.slice, seen: 0 }
|
||||
}
|
||||
|
||||
fn parse_sub(&mut self, len: usize) -> ParseResult<Self> {
|
||||
Ok(SliceParser { slice: try!(self.parse_bytes(len)), seen: 0 })
|
||||
}
|
||||
|
||||
fn seen(&self) -> usize {
|
||||
self.seen
|
||||
}
|
||||
|
||||
fn left(&self) -> usize {
|
||||
self.slice.len()
|
||||
}
|
||||
}
|
||||
|
||||
//------------ ContextParser ------------------------------------------------
|
||||
|
||||
/// A parser that operates on an entire DNS message.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ContextParser<'a> {
|
||||
parser: SliceParser<'a>,
|
||||
context: &'a [u8]
|
||||
}
|
||||
|
||||
impl<'a> ContextParser<'a> {
|
||||
pub fn new(slice: &'a[u8], context: &'a[u8]) -> Self {
|
||||
ContextParser {
|
||||
parser: SliceParser::new(slice),
|
||||
context: context
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_message(message: &'a [u8]) -> Self {
|
||||
ContextParser {
|
||||
parser: SliceParser::new(message),
|
||||
context: message
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ParseBytes<'a> for ContextParser<'a> {
|
||||
fn parse_bytes(&mut self, len: usize) -> ParseResult<&'a [u8]> {
|
||||
self.parser.parse_bytes(len)
|
||||
}
|
||||
|
||||
fn skip(&mut self, len: usize) -> ParseResult<()> {
|
||||
self.parser.skip(len)
|
||||
}
|
||||
|
||||
fn sub(&self) -> Self {
|
||||
ContextParser {
|
||||
parser: self.parser.sub(),
|
||||
context: self.context
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_sub(&mut self, len: usize) -> ParseResult<Self> {
|
||||
Ok(ContextParser {
|
||||
parser: try!(self.parser.parse_sub(len)),
|
||||
context: self.context
|
||||
})
|
||||
}
|
||||
|
||||
fn seen(&self) -> usize {
|
||||
self.parser.seen()
|
||||
}
|
||||
|
||||
fn left(&self) -> usize {
|
||||
self.parser.left()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ParseLazy<'a> for ContextParser<'a> {
|
||||
fn context(&self) -> &'a [u8] {
|
||||
self.context
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
//! A single question of a DNS message.
|
||||
|
||||
use super::compose::ComposeBytes;
|
||||
use super::error::{ComposeResult, ParseResult};
|
||||
use super::flavor::{self, Flavor};
|
||||
use super::iana::{Class, RRType};
|
||||
use super::name::DName;
|
||||
use super::parse::ParseFlavor;
|
||||
|
||||
|
||||
//------------ Question -----------------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Question<'a, F: Flavor<'a>> {
|
||||
qname: F::DName,
|
||||
qtype: RRType,
|
||||
qclass: Class,
|
||||
}
|
||||
|
||||
pub type OwnedQuestion<'a> = Question<'a, flavor::Owned>;
|
||||
pub type QuestionRef<'a> = Question<'a, flavor::Ref<'a>>;
|
||||
pub type LazyQuestion<'a> = Question<'a, flavor::Lazy<'a>>;
|
||||
|
||||
|
||||
/// # Creation and Conversion
|
||||
///
|
||||
impl<'a, F: Flavor<'a>> Question<'a, F> {
|
||||
pub fn new(qname: F::DName, qtype: RRType, qclass: Class) -> Self {
|
||||
Question { qname: qname, qtype: qtype, qclass: qclass }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// # Element Access
|
||||
///
|
||||
impl<'a, F: Flavor<'a>> Question<'a, F> {
|
||||
/// Returns the requested domain name.
|
||||
pub fn qname(&self) -> &F::DName {
|
||||
&self.qname
|
||||
}
|
||||
|
||||
/// Returns the requested record type.
|
||||
pub fn qtype(&self) -> RRType {
|
||||
self.qtype
|
||||
}
|
||||
|
||||
/// Returns the requested class.
|
||||
pub fn qclass(&self) -> Class {
|
||||
self.qclass
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Parsing and Composing
|
||||
///
|
||||
impl<'a, F: Flavor<'a>> Question<'a, F> {
|
||||
pub fn parse<P>(parser: &mut P) -> ParseResult<Self>
|
||||
where P: ParseFlavor<'a, F> {
|
||||
Ok(Question::new(try!(parser.parse_name()),
|
||||
try!(parser.parse_u16()).into(),
|
||||
try!(parser.parse_u16()).into()))
|
||||
}
|
||||
|
||||
pub fn compose<C: ComposeBytes>(&self, target: &mut C)
|
||||
-> ComposeResult<()> {
|
||||
try!(target.push_dname_compressed(&self.qname));
|
||||
try!(target.push_u16(self.qtype.into()));
|
||||
target.push_u16(self.qclass.into())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ ComposeQuestion ----------------------------------------------
|
||||
|
||||
/// Helper trait to allow composing questions from tuples.
|
||||
pub trait ComposeQuestion {
|
||||
fn compose<C: ComposeBytes>(&self, target: &mut C) -> ComposeResult<()>;
|
||||
}
|
||||
|
||||
impl<'a, F: Flavor<'a>> ComposeQuestion for Question<'a, F> {
|
||||
fn compose<C: ComposeBytes>(&self, target: &mut C) -> ComposeResult<()> {
|
||||
self.compose(target)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DName> ComposeQuestion for (D, RRType, Class) {
|
||||
fn compose<C: ComposeBytes>(&self, target: &mut C) -> ComposeResult<()> {
|
||||
try!(target.push_dname_compressed(&self.0));
|
||||
try!(target.push_u16(self.1.into()));
|
||||
target.push_u16(self.2.into())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
//! Generic Record Data Types
|
||||
|
||||
use std::fmt;
|
||||
use super::super::compose::ComposeBytes;
|
||||
use super::super::flavor::Flavor;
|
||||
use super::super::error::{ComposeResult, ParseResult};
|
||||
use super::super::iana::RRType;
|
||||
use super::super::nest::Nest;
|
||||
use super::super::parse::ParseFlavor;
|
||||
use super::traits::RecordData;
|
||||
|
||||
|
||||
pub struct GenericRecordData<'a, F: Flavor<'a>> {
|
||||
rtype: RRType,
|
||||
data: F::Nest,
|
||||
}
|
||||
|
||||
impl<'a, F: Flavor<'a>> GenericRecordData<'a, F> {
|
||||
pub fn new(rtype: RRType, data: F::Nest) -> Self {
|
||||
GenericRecordData { rtype: rtype, data: data }
|
||||
}
|
||||
|
||||
pub fn rtype(&self) -> RRType { self.rtype }
|
||||
pub fn data(&self) -> &F::Nest { &self.data }
|
||||
|
||||
/*
|
||||
pub fn fmt<R: RecordData<'a, F>>(&'a self, f: &mut fmt::Formatter)
|
||||
-> fmt::Result {
|
||||
let mut parser = self.data.parser();
|
||||
match R::parse(self.rtype, &mut parser) {
|
||||
Err(..) => Ok(()),
|
||||
Ok(None) => Ok(()),
|
||||
Ok(Some(data)) => data.fmt(f)
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
impl<'a, F: Flavor<'a>> RecordData<'a, F> for GenericRecordData<'a, F> {
|
||||
|
||||
fn rtype(&self) -> RRType {
|
||||
self.rtype
|
||||
}
|
||||
|
||||
fn compose<C: ComposeBytes>(&self, target: &mut C) -> ComposeResult<()> {
|
||||
self.data.compose(target)
|
||||
}
|
||||
|
||||
fn parse<P>(rtype: RRType, parser: &mut P) -> ParseResult<Option<Self>>
|
||||
where P: ParseFlavor<'a, F> {
|
||||
let len = parser.left();
|
||||
let nest = try!(parser.parse_nest(len));
|
||||
Ok(Some(GenericRecordData::new(rtype, nest)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<'a, F: Flavor<'a>> fmt::Display for GenericRecordData<'a, F> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
/*
|
||||
use super::rfc1035::*;
|
||||
|
||||
match self.rtype {
|
||||
RRType::A => self.fmt::<A>(f),
|
||||
RRType::NS => self.fmt::<NS<F>>(f),
|
||||
_ => "...".fmt(f)
|
||||
}
|
||||
*/
|
||||
"...".fmt(f)
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
//! Resource data handling.
|
||||
|
||||
pub use self::traits::RecordData;
|
||||
pub use self::generic::GenericRecordData;
|
||||
|
||||
pub mod traits;
|
||||
pub mod generic;
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
use std::fmt;
|
||||
use std::net;
|
||||
use super::super::compose::ComposeBytes;
|
||||
use super::super::error::{ComposeResult, ParseResult};
|
||||
use super::super::flavor::Flavor;
|
||||
use super::super::iana::RRType;
|
||||
use super::super::parse::ParseFlavor;
|
||||
use super::traits::RecordData;
|
||||
|
||||
|
||||
|
||||
|
||||
//------------ A ------------------------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct A {
|
||||
addr: net::Ipv4Addr,
|
||||
}
|
||||
|
||||
impl A {
|
||||
pub fn new(addr: net::Ipv4Addr) -> A {
|
||||
A { addr: addr }
|
||||
}
|
||||
|
||||
pub fn addr(&self) -> &net::Ipv4Addr { &self.addr }
|
||||
pub fn addr_mut(&mut self) -> &mut net::Ipv4Addr { &mut self.addr }
|
||||
}
|
||||
|
||||
impl<'a, F: Flavor<'a>> RecordData<'a, F> for A {
|
||||
fn rtype(&self) -> RRType { RRType::A }
|
||||
|
||||
fn compose<C: ComposeBytes>(&self, target: &mut C) -> ComposeResult<()> {
|
||||
for i in self.addr.octets().iter() {
|
||||
try!(target.push_u8(*i))
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse<P>(rtype: RRType, parser: &mut P) -> ParseResult<Option<Self>>
|
||||
where P: ParseFlavor<'a, F> {
|
||||
if rtype != RRType::A { return Ok(None) }
|
||||
Ok(Some(A::new(net::Ipv4Addr::new(try!(parser.parse_u8()),
|
||||
try!(parser.parse_u8()),
|
||||
try!(parser.parse_u8()),
|
||||
try!(parser.parse_u8())))))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for A {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.addr.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ NS -----------------------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct NS<'a, F: Flavor<'a>> {
|
||||
nsdname: F::DName
|
||||
}
|
||||
|
||||
impl<'a, F: Flavor<'a>> NS<'a, F> {
|
||||
pub fn new(nsdname: F::DName) -> Self {
|
||||
NS { nsdname: nsdname }
|
||||
}
|
||||
|
||||
pub fn nsdname(&self) -> &F::DName {
|
||||
&self.nsdname
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, F: Flavor<'a>> RecordData<'a, F> for NS<'a, F> {
|
||||
fn rtype(&self) -> RRType { RRType::NS }
|
||||
|
||||
fn compose<C: ComposeBytes>(&self, target: &mut C) -> ComposeResult<()> {
|
||||
target.push_dname_compressed(&self.nsdname)
|
||||
}
|
||||
|
||||
fn parse<P>(rtype: RRType, parser: &mut P) -> ParseResult<Option<Self>>
|
||||
where P: ParseFlavor<'a, F> {
|
||||
if rtype != RRType::NS { return Ok(None) }
|
||||
Ok(Some(NS::new(try!(parser.parse_name()))))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<'a, F: Flavor<'a>> fmt::Display for NS<'a, F> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.nsdname.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
//! Traits for record data.
|
||||
|
||||
use std::fmt;
|
||||
use super::super::compose::ComposeBytes;
|
||||
use super::super::error::{ComposeResult, ParseResult};
|
||||
use super::super::flavor::Flavor;
|
||||
use super::super::iana::RRType;
|
||||
use super::super::parse::ParseFlavor;
|
||||
|
||||
|
||||
/// A trait for creating record data.
|
||||
pub trait RecordData<'a, F: Flavor<'a>>: fmt::Display + Sized {
|
||||
/// Returns the record type for this record data instance.
|
||||
fn rtype(&self) -> RRType;
|
||||
|
||||
/// Appends the record data to the end of a buffer.
|
||||
fn compose<C: ComposeBytes>(&self, target: &mut C) -> ComposeResult<()>;
|
||||
|
||||
/// Parse the record data from a cursor if the type is right.
|
||||
///
|
||||
/// If this record data type does not feel responsible for records of
|
||||
/// type `rtype`, it should return `Ok(None)`. Otherwise it should
|
||||
/// return something or an error if parsing fails.
|
||||
fn parse<P>(rtype: RRType, parser: &mut P) -> ParseResult<Option<Self>>
|
||||
where P: ParseFlavor<'a, F>;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
//! DNS Records
|
||||
|
||||
use std::fmt;
|
||||
use super::compose::ComposeBytes;
|
||||
use super::error::{ComposeError, ComposeResult, ParseResult};
|
||||
use super::flavor::Flavor;
|
||||
use super::iana::{Class, RRType};
|
||||
use super::parse::ParseFlavor;
|
||||
use super::rdata::RecordData;
|
||||
|
||||
//------------ Record -------------------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Record<'a, F: Flavor<'a>, D: RecordData<'a, F>> {
|
||||
name: F::DName,
|
||||
class: Class,
|
||||
ttl: u32,
|
||||
rdata: D
|
||||
}
|
||||
|
||||
|
||||
/// # Creation and Conversion
|
||||
///
|
||||
impl<'a, F: Flavor<'a>, D: RecordData<'a, F>> Record<'a, F, D> {
|
||||
/// Creates a new record from its parts.
|
||||
pub fn new(name: F::DName, class: Class, ttl: u32, rdata: D) -> Self {
|
||||
Record { name: name, class: class, ttl: ttl, rdata: rdata }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// # Element Access
|
||||
///
|
||||
impl<'a, F: Flavor<'a>, D: RecordData<'a, F>> Record<'a, F, D> {
|
||||
/// Returns a reference to the domain name.
|
||||
pub fn name(&self) -> &F::DName {
|
||||
&self.name
|
||||
}
|
||||
|
||||
/// Returns the record type.
|
||||
pub fn rtype(&self) -> RRType {
|
||||
self.rdata.rtype()
|
||||
}
|
||||
|
||||
/// Returns the record class.
|
||||
pub fn class(&self) -> Class {
|
||||
self.class
|
||||
}
|
||||
|
||||
/// Returns the record’s time to live.
|
||||
pub fn ttl(&self) -> u32 {
|
||||
self.ttl
|
||||
}
|
||||
|
||||
/// Return a reference to the record data.
|
||||
pub fn rdata(&self) -> &D {
|
||||
&self.rdata
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Parsing and Composing
|
||||
///
|
||||
impl<'a, F: Flavor<'a>, D: RecordData<'a, F>> Record<'a, F, D> {
|
||||
pub fn parse<P>(parser: &mut P) -> ParseResult<Option<Self>>
|
||||
where P: ParseFlavor<'a, F> {
|
||||
let name = try!(parser.parse_name());
|
||||
let rtype = try!(parser.parse_u16()).into();
|
||||
let class = try!(parser.parse_u16()).into();
|
||||
let ttl = try!(parser.parse_u32());
|
||||
let rdlen = try!(parser.parse_u16()) as usize;
|
||||
let mut rdata_sub = try!(parser.parse_sub(rdlen));
|
||||
Ok(try!(D::parse(rtype, &mut rdata_sub))
|
||||
.map(|rdata| Record::new(name, class, ttl, rdata)))
|
||||
}
|
||||
|
||||
pub fn compose<C: ComposeBytes>(&self, target: &mut C)
|
||||
-> ComposeResult<()> {
|
||||
try!(target.push_dname_compressed(&self.name));
|
||||
try!(target.push_u16(self.rdata.rtype().into()));
|
||||
try!(target.push_u16(self.class.into()));
|
||||
try!(target.push_u32(self.ttl));
|
||||
let pos = target.pos();
|
||||
try!(target.push_u16(0));
|
||||
try!(self.rdata.compose(target));
|
||||
let delta = target.delta(pos);
|
||||
if delta > (::std::u16::MAX as usize) {
|
||||
return Err(ComposeError::Overflow)
|
||||
}
|
||||
target.update_u16(pos, delta as u16)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//--- Display
|
||||
|
||||
impl<'a, F, D> fmt::Display for Record<'a, F, D>
|
||||
where F: Flavor<'a>, D: RecordData<'a, F> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}\t{}\t{}\t{}\t{}",
|
||||
self.name, self.ttl, self.class, self.rdata.rtype(),
|
||||
self.rdata)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ ComposeRecord ------------------------------------------------
|
||||
|
||||
/// Helper trait to allow composing records from tuples.
|
||||
pub trait ComposeRecord {
|
||||
fn compose<C: ComposeBytes>(&self, target: &mut C) -> ComposeResult<()>;
|
||||
}
|
||||
|
||||
impl<'a, F, D> ComposeRecord for Record<'a, F, D>
|
||||
where F: Flavor<'a>, D: RecordData<'a, F> {
|
||||
fn compose<C: ComposeBytes>(&self, target: &mut C) -> ComposeResult<()> {
|
||||
self.compose(target)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
//! Dealing with bytes slices
|
||||
//!
|
||||
//! XXX This is an obsolete module and will be removed.
|
||||
|
||||
use std::mem;
|
||||
use super::error::{ParseError, ParseResult};
|
||||
|
||||
|
||||
//------------ BytesExt -----------------------------------------------------
|
||||
|
||||
/// A trait extending a bytes slice for reading of DNS data.
|
||||
///
|
||||
pub trait BytesExt {
|
||||
fn split_u8(&self) -> ParseResult<(u8, &Self)>;
|
||||
fn split_u16(&self) -> ParseResult<(u16, &Self)>;
|
||||
fn split_u32(&self) -> ParseResult<(u32, &Self)>;
|
||||
fn split_bytes(&self, at: usize) -> ParseResult<(&[u8], &Self)>;
|
||||
fn tail(&self, start: usize) -> ParseResult<&Self>;
|
||||
fn check_len(&self, len: usize) -> ParseResult<()>;
|
||||
}
|
||||
|
||||
impl BytesExt for [u8] {
|
||||
fn split_u8(&self) -> ParseResult<(u8, &[u8])> {
|
||||
self.split_first().map(|(l,r)| (*l, r)).ok_or(ParseError::UnexpectedEnd)
|
||||
}
|
||||
|
||||
fn split_u16(&self) -> ParseResult<(u16, &[u8])> {
|
||||
try!(self.check_len(2));
|
||||
let (l, r) = self.split_at(2);
|
||||
let l: &[u8; 2] = unsafe { mem::transmute(l.as_ptr()) };
|
||||
let l = unsafe { mem::transmute(*l) };
|
||||
Ok((u16::from_be(l), r))
|
||||
}
|
||||
|
||||
fn split_u32(&self) -> ParseResult<(u32, &[u8])> {
|
||||
try!(self.check_len(4));
|
||||
if self.len() < 4 { return Err(ParseError::UnexpectedEnd) }
|
||||
let (l, r) = self.split_at(4);
|
||||
let l: &[u8; 4] = unsafe { mem::transmute(l.as_ptr()) };
|
||||
let l = unsafe { mem::transmute(*l) };
|
||||
Ok((u32::from_be(l), r))
|
||||
}
|
||||
|
||||
fn split_bytes(&self, at: usize) -> ParseResult<(&[u8], &[u8])> {
|
||||
try!(self.check_len(at));
|
||||
Ok(self.split_at(at))
|
||||
}
|
||||
|
||||
fn tail(&self, start: usize) -> ParseResult<&[u8]> {
|
||||
try!(self.check_len(start));
|
||||
if self.len() < start { return Err(ParseError::UnexpectedEnd) }
|
||||
Ok(&self[start..])
|
||||
}
|
||||
|
||||
fn check_len(&self, len: usize) -> ParseResult<()> {
|
||||
if len > self.len() { Err(ParseError::UnexpectedEnd) }
|
||||
else { Ok(()) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ BytesVecExt --------------------------------------------------
|
||||
|
||||
/// A trait extending a bytes vec for pushing DNS data to its end.
|
||||
pub trait BytesVecExt {
|
||||
fn push_bytes(&mut self, data: &[u8]);
|
||||
fn push_u8(&mut self, data: u8);
|
||||
fn push_u16(&mut self, data: u16);
|
||||
fn push_u32(&mut self, data: u32);
|
||||
}
|
||||
|
||||
impl BytesVecExt for Vec<u8> {
|
||||
fn push_bytes(&mut self, data: &[u8]) {
|
||||
self.extend(data)
|
||||
}
|
||||
|
||||
fn push_u8(&mut self, data: u8) {
|
||||
let bytes: [u8; 1] = unsafe { mem::transmute(data) };
|
||||
self.push_bytes(&bytes);
|
||||
}
|
||||
|
||||
fn push_u16(&mut self, data: u16) {
|
||||
let data = data.to_be();
|
||||
let bytes: [u8; 2] = unsafe { mem::transmute(data) };
|
||||
self.push_bytes(&bytes);
|
||||
}
|
||||
|
||||
fn push_u32(&mut self, data: u32) {
|
||||
let data = data.to_be();
|
||||
let bytes: [u8; 4] = unsafe { mem::transmute(data) };
|
||||
self.push_bytes(&bytes);
|
||||
}
|
||||
}
|
||||
|
||||
-189
@@ -1,189 +0,0 @@
|
||||
//! Low-level access to wire format DNS.
|
||||
//!
|
||||
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
use std::result;
|
||||
use super::name::DomainNameSlice;
|
||||
|
||||
|
||||
//------------ BytesBuf -----------------------------------------------------
|
||||
|
||||
/// A trait for writing binary DNS data.
|
||||
///
|
||||
pub trait BytesBuf {
|
||||
type Pos: Copy;
|
||||
|
||||
//--- Appending basic types
|
||||
fn push_bytes(&mut self, data: &[u8]);
|
||||
|
||||
fn push_u8(&mut self, data: u8) {
|
||||
let bytes: [u8; 1] = unsafe { mem::transmute(data) };
|
||||
self.push_bytes(&bytes);
|
||||
}
|
||||
|
||||
fn push_u16(&mut self, data: u16) {
|
||||
let data = data.to_be();
|
||||
let bytes: [u8; 2] = unsafe { mem::transmute(data) };
|
||||
self.push_bytes(&bytes);
|
||||
}
|
||||
|
||||
fn push_u32(&mut self, data: u32) {
|
||||
let data = data.to_be();
|
||||
let bytes: [u8; 4] = unsafe { mem::transmute(data) };
|
||||
self.push_bytes(&bytes);
|
||||
}
|
||||
|
||||
//--- Updating of earlier data.
|
||||
fn pos(&self) -> Self::Pos;
|
||||
fn delta(&self, pos: Self::Pos) -> usize;
|
||||
fn update_bytes(&mut self, pos: Self::Pos, data: &[u8]);
|
||||
|
||||
fn update_u8(&mut self, pos: Self::Pos, data: u8) {
|
||||
let bytes: [u8; 1] = unsafe { mem::transmute(data) };
|
||||
self.update_bytes(pos, &bytes);
|
||||
}
|
||||
|
||||
fn update_u16(&mut self, pos: Self::Pos, data: u16) {
|
||||
let data = data.to_be();
|
||||
let bytes: [u8; 2] = unsafe { mem::transmute(data) };
|
||||
self.update_bytes(pos, &bytes);
|
||||
}
|
||||
|
||||
fn update_u32(&mut self, pos: Self::Pos, data: u32) {
|
||||
let data = data.to_be();
|
||||
let bytes: [u8; 4] = unsafe { mem::transmute(data) };
|
||||
self.update_bytes(pos, &bytes);
|
||||
}
|
||||
|
||||
//--- Name compression support
|
||||
//
|
||||
// This is disabled by default
|
||||
|
||||
/// Returns whether `self` can compress names.
|
||||
fn can_compress(&self) -> bool { false }
|
||||
|
||||
/// Adds a reference to a domain name at the current position.
|
||||
fn add_name_pos<N: AsRef<DomainNameSlice>>(&mut self, name: N) {
|
||||
let _ = name;
|
||||
}
|
||||
|
||||
/// Retrieves the position for `name`, if available.
|
||||
fn get_name_pos<N: AsRef<DomainNameSlice>>(&self, name: N) -> Option<u16> {
|
||||
let _ = name; None
|
||||
}
|
||||
}
|
||||
|
||||
impl BytesBuf for Vec<u8> {
|
||||
type Pos = usize;
|
||||
|
||||
fn push_bytes(&mut self, data: &[u8]) {
|
||||
self.extend(data)
|
||||
}
|
||||
|
||||
fn pos(&self) -> Self::Pos {
|
||||
self.len()
|
||||
}
|
||||
|
||||
fn delta(&self, pos: Self::Pos) -> usize {
|
||||
assert!(pos < self.len());
|
||||
self.len() - pos
|
||||
}
|
||||
|
||||
fn update_bytes(&mut self, pos: Self::Pos, data: &[u8]) {
|
||||
assert!(pos + data.len() < self.len());
|
||||
unsafe {
|
||||
ptr::copy_nonoverlapping(data.as_ptr(),
|
||||
self[pos..pos + data.len()].as_mut_ptr(),
|
||||
data.len())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ BytesSlice ---------------------------------------------------
|
||||
|
||||
/// A trait extending a bytes slice for reading of DNS data.
|
||||
///
|
||||
pub trait BytesSlice {
|
||||
fn split_u8(&self) -> Result<(u8, &Self)>;
|
||||
fn split_u16(&self) -> Result<(u16, &Self)>;
|
||||
fn split_u32(&self) -> Result<(u32, &Self)>;
|
||||
fn split_bytes(&self, at: usize) -> Result<(&[u8], &Self)>;
|
||||
fn tail(&self, start: usize) -> Result<&Self>;
|
||||
fn check_len(&self, len: usize) -> Result<()>;
|
||||
}
|
||||
|
||||
impl BytesSlice for [u8] {
|
||||
fn split_u8(&self) -> Result<(u8, &[u8])> {
|
||||
self.split_first().map(|(l,r)| (*l, r)).ok_or(Error::PrematureEnd)
|
||||
}
|
||||
|
||||
fn split_u16(&self) -> Result<(u16, &[u8])> {
|
||||
try!(self.check_len(2));
|
||||
let (l, r) = self.split_at(2);
|
||||
let l: &[u8; 2] = unsafe { mem::transmute(l.as_ptr()) };
|
||||
let l = unsafe { mem::transmute(*l) };
|
||||
Ok((u16::from_be(l), r))
|
||||
}
|
||||
|
||||
fn split_u32(&self) -> Result<(u32, &[u8])> {
|
||||
try!(self.check_len(4));
|
||||
if self.len() < 4 { return Err(Error::PrematureEnd) }
|
||||
let (l, r) = self.split_at(4);
|
||||
let l: &[u8; 4] = unsafe { mem::transmute(l.as_ptr()) };
|
||||
let l = unsafe { mem::transmute(*l) };
|
||||
Ok((u32::from_be(l), r))
|
||||
}
|
||||
|
||||
fn split_bytes(&self, at: usize) -> Result<(&[u8], &[u8])> {
|
||||
try!(self.check_len(at));
|
||||
Ok(self.split_at(at))
|
||||
}
|
||||
|
||||
fn tail(&self, start: usize) -> Result<&[u8]> {
|
||||
try!(self.check_len(start));
|
||||
if self.len() < start { return Err(Error::PrematureEnd) }
|
||||
Ok(&self[start..])
|
||||
}
|
||||
|
||||
fn check_len(&self, len: usize) -> Result<()> {
|
||||
if len > self.len() { Err(Error::PrematureEnd) }
|
||||
else { Ok(()) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ Error and Result ---------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Error {
|
||||
PrematureEnd,
|
||||
SizeExceeded,
|
||||
Overflow,
|
||||
}
|
||||
|
||||
impl error::Error for Error {
|
||||
fn description(&self) -> &str {
|
||||
match *self {
|
||||
Error::PrematureEnd => "premature end of domain name",
|
||||
Error::SizeExceeded => "the message size has been exceeded",
|
||||
Error::Overflow => "a counter has overflowed",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
use std::error::Error;
|
||||
|
||||
self.description().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
|
||||
|
||||
|
||||
+40
-40
@@ -8,11 +8,14 @@ use std::io;
|
||||
use std::net::{SocketAddr, UdpSocket};
|
||||
use std::result;
|
||||
use std::str::FromStr;
|
||||
use domain::iana::{rrtype, Class, RRType};
|
||||
use domain::message::{self, MessageBuf, MessageBuilder, RecordSection};
|
||||
use domain::rdata::generic::CompactGenericRecordData;
|
||||
use domain::bits::compose::ComposeVec;
|
||||
use domain::bits::error::{ComposeError, FromStrError, ParseError};
|
||||
use domain::bits::flavor::Lazy;
|
||||
use domain::bits::iana::{Class, RRType};
|
||||
use domain::bits::message::{LazyMessage, MessageBuilder, RecordIter};
|
||||
use domain::bits::name::OwnedDName;
|
||||
use domain::bits::rdata::generic::GenericRecordData;
|
||||
use domain::resolver::conf::ResolvConf;
|
||||
use domain::name::{self, DomainNameBuf};
|
||||
|
||||
|
||||
//------------ Options ------------------------------------------------------
|
||||
@@ -75,13 +78,13 @@ impl Options {
|
||||
}
|
||||
|
||||
impl Options {
|
||||
fn name(&self) -> Result<DomainNameBuf> {
|
||||
fn name(&self) -> Result<OwnedDName> {
|
||||
if self.name.is_empty() {
|
||||
Ok(DomainNameBuf::root())
|
||||
Ok(OwnedDName::root())
|
||||
}
|
||||
else {
|
||||
let mut res = try!(DomainNameBuf::from_str(&self.name));
|
||||
res.append(DomainNameBuf::root());
|
||||
let mut res = try!(OwnedDName::from_str(&self.name));
|
||||
res.append(OwnedDName::root());
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
@@ -118,20 +121,20 @@ impl error::Error for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl convert::From<name::ParseError> for Error {
|
||||
fn from(error: name::ParseError) -> Error {
|
||||
impl convert::From<ComposeError> for Error {
|
||||
fn from(error: ComposeError) -> Error {
|
||||
Error { inner: Box::new(error) }
|
||||
}
|
||||
}
|
||||
|
||||
impl convert::From<rrtype::ParseError> for Error {
|
||||
fn from(error: rrtype::ParseError) -> Error {
|
||||
impl convert::From<FromStrError> for Error {
|
||||
fn from(error: FromStrError) -> Error {
|
||||
Error { inner: Box::new(error) }
|
||||
}
|
||||
}
|
||||
|
||||
impl convert::From<message::Error> for Error {
|
||||
fn from(error: message::Error) -> Error {
|
||||
impl convert::From<ParseError> for Error {
|
||||
fn from(error: ParseError) -> Error {
|
||||
Error { inner: Box::new(error) }
|
||||
}
|
||||
}
|
||||
@@ -152,42 +155,42 @@ type Result<T> = result::Result<T, Error>;
|
||||
|
||||
//------------ Processing Steps ---------------------------------------------
|
||||
|
||||
fn create_query(options: &Options) -> Result<MessageBuf> {
|
||||
let mut msg = MessageBuilder::new(::std::u16::MAX as usize, 2, true);
|
||||
fn create_query(options: &Options) -> Result<Vec<u8>> {
|
||||
let mut msg = try!(MessageBuilder::new(ComposeVec::new(Some(512), true)));
|
||||
|
||||
// XXX make a header
|
||||
msg.header_mut().set_id(17);
|
||||
|
||||
let mut question = msg.question();
|
||||
try!(question.push_question(try!(options.name()), try!(options.qtype()),
|
||||
try!(options.qclass())));
|
||||
try!(question.push(&(try!(options.name()), try!(options.qtype()),
|
||||
try!(options.qclass()))));
|
||||
|
||||
// XXX set prefix
|
||||
Ok(question.finish())
|
||||
Ok(try!(question.finish()).finish())
|
||||
}
|
||||
|
||||
fn send_query(options: &Options, query: &MessageBuf)
|
||||
-> Result<(MessageBuf, SocketAddr)> {
|
||||
fn send_query(options: &Options, query: &Vec<u8>)
|
||||
-> Result<(Vec<u8>, SocketAddr)> {
|
||||
send_query_udp(options, query)
|
||||
}
|
||||
|
||||
fn send_query_udp(options: &Options, query: &MessageBuf)
|
||||
-> Result<(MessageBuf, SocketAddr)> {
|
||||
fn send_query_udp(options: &Options, query: &Vec<u8>)
|
||||
-> Result<(Vec<u8>, SocketAddr)> {
|
||||
let sock = try!(UdpSocket::bind("0.0.0.0:0"));
|
||||
try!(sock.set_read_timeout(Some(options.conf().timeout)));
|
||||
for server in options.conf().servers.iter() {
|
||||
try!(sock.send_to(query.message_bytes(), server));
|
||||
try!(sock.send_to(&query, server));
|
||||
let mut buf = Vec::new();
|
||||
buf.resize(2000, 0);
|
||||
let (len, from) = try!(sock.recv_from(&mut buf));
|
||||
buf.truncate(len);
|
||||
let msg = try!(MessageBuf::from_vec(buf, 0));
|
||||
return Ok((msg, from));
|
||||
return Ok((buf, from));
|
||||
}
|
||||
Err(io::Error::new(io::ErrorKind::Other, "No more servers").into())
|
||||
}
|
||||
|
||||
fn print_result(response: MessageBuf) {
|
||||
fn print_result(response: Vec<u8>) {
|
||||
let response = LazyMessage::from_bytes(&response);
|
||||
println!(";; Got answer:");
|
||||
println!(";; ->>HEADER<<- opcode: {}, status: {}, id: {}",
|
||||
response.header().opcode(), response.header().rcode(),
|
||||
@@ -216,44 +219,41 @@ fn print_result(response: MessageBuf) {
|
||||
println!("");
|
||||
}
|
||||
|
||||
let mut answer = question.answer().unwrap();
|
||||
let answer = question.answer().unwrap();
|
||||
if response.counts().ancount() > 0 {
|
||||
println!(";; ANSWER SECTION");
|
||||
print_section(&mut answer);
|
||||
print_records(answer.iter());
|
||||
println!("");
|
||||
}
|
||||
|
||||
let mut authority = answer.authority().unwrap();
|
||||
let authority = answer.authority().unwrap();
|
||||
if response.counts().nscount() > 0 {
|
||||
println!(";; AUTHORITY SECTION");
|
||||
print_section(&mut authority);
|
||||
print_records(authority.iter());
|
||||
println!("");
|
||||
}
|
||||
|
||||
let mut additional = authority.additional().unwrap();
|
||||
let additional = authority.additional().unwrap();
|
||||
if response.counts().arcount() > 0 {
|
||||
println!(";; ADDITIONAL SECTION");
|
||||
print_section(&mut additional);
|
||||
print_records(additional.iter());
|
||||
println!("");
|
||||
}
|
||||
}
|
||||
|
||||
fn print_section<'a>(section: &mut RecordSection<'a, CompactGenericRecordData<'a>>) {
|
||||
for record in section.iter() {
|
||||
let record = record.unwrap();
|
||||
println!("{}\t{}\t{}\t{}\t{}", record.name(), record.ttl(),
|
||||
record.rclass(), record.rtype(), record.rdata())
|
||||
fn print_records<'a>(iter: RecordIter<'a, Lazy<'a>, GenericRecordData<'a, Lazy<'a>>>) {
|
||||
for record in iter {
|
||||
println!("{}", record.unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ Main Function ------------------------------------------------
|
||||
|
||||
fn main() {
|
||||
let options = Options::from_args();
|
||||
let query = create_query(&options).unwrap();
|
||||
let (response, server) = send_query(&options, &query).unwrap();
|
||||
let len = response.message_bytes().len();
|
||||
let len = response.len();
|
||||
print_result(response);
|
||||
println!(";; Query time: not yet available.");
|
||||
println!(";; SERVER: {}", server);
|
||||
|
||||
+4
-8
@@ -1,12 +1,8 @@
|
||||
/*
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
*/
|
||||
|
||||
pub mod bytes;
|
||||
pub mod header;
|
||||
pub mod iana;
|
||||
pub mod message;
|
||||
pub mod name;
|
||||
pub mod question;
|
||||
pub mod rdata;
|
||||
pub mod bits;
|
||||
pub mod resolver;
|
||||
pub mod record;
|
||||
|
||||
|
||||
-912
@@ -1,912 +0,0 @@
|
||||
//! DNS messages
|
||||
//!
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::convert;
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::result;
|
||||
use super::header::{Header, HeaderCounts, FullHeader};
|
||||
use super::iana::{Class, RRType};
|
||||
use super::name::{self, DomainName, DomainNameBuf, DomainNameSlice,
|
||||
CompactDomainName};
|
||||
use super::bytes::{self, BytesBuf};
|
||||
use super::question::{self, Question};
|
||||
use super::record::Record;
|
||||
use super::rdata::traits::{RecordData, CompactRecordData};
|
||||
|
||||
|
||||
//============ Message Slice ================================================
|
||||
|
||||
//------------ Message ------------------------------------------------------
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Message {
|
||||
slice: [u8]
|
||||
}
|
||||
|
||||
|
||||
/// # Creation and Conversion
|
||||
///
|
||||
impl Message {
|
||||
/// Creates a message from a bytes slice.
|
||||
pub fn from_bytes(s: &[u8]) -> Result<&Message> {
|
||||
if s.len() < mem::size_of::<FullHeader>() {
|
||||
return Err(Error::OctetError(bytes::Error::PrematureEnd))
|
||||
}
|
||||
Ok(unsafe { mem::transmute(s) })
|
||||
}
|
||||
|
||||
/// Creates a message from any byte slice.
|
||||
unsafe fn from_bytes_unsafe(s: &[u8]) -> &Message {
|
||||
mem::transmute(s)
|
||||
}
|
||||
|
||||
/// Converts `self` into an owned message.
|
||||
pub fn to_owned(&self) -> MessageBuf {
|
||||
MessageBuf::from(self)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// # Header Access
|
||||
///
|
||||
impl Message {
|
||||
pub fn header(&self) -> &Header {
|
||||
unsafe { Header::from_message(&self.slice) }
|
||||
}
|
||||
|
||||
pub fn counts(&self) -> &HeaderCounts {
|
||||
unsafe { HeaderCounts::from_message(&self.slice) }
|
||||
}
|
||||
|
||||
pub fn question(&self) -> QuestionSection {
|
||||
QuestionSection::new(self,
|
||||
&self.slice[mem::size_of::<FullHeader>()..])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ QuestionSection ----------------------------------------------
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct QuestionSection<'a> {
|
||||
message: &'a Message,
|
||||
slice: &'a [u8],
|
||||
count: u16,
|
||||
}
|
||||
|
||||
impl<'a> QuestionSection<'a> {
|
||||
fn new(message: &'a Message, slice: &'a[u8]) -> Self {
|
||||
QuestionSection {
|
||||
message: message,
|
||||
slice: slice,
|
||||
count: message.counts().qdcount(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter(&mut self) -> &mut Self {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn answer<D: CompactRecordData<'a>>(self)
|
||||
-> Option<AnswerSection<'a, D>> {
|
||||
if self.count == 0 {
|
||||
Some(AnswerSection::new(self.message, self.slice))
|
||||
}
|
||||
else { None }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for QuestionSection<'a> {
|
||||
type Item = Result<Question<CompactDomainName<'a>>>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.count == 0 { return None }
|
||||
match Question::split_from(self.slice, &self.message.slice) {
|
||||
Ok((res, slice)) => {
|
||||
self.count -= 1;
|
||||
self.slice = slice;
|
||||
Some(Ok(res))
|
||||
}
|
||||
Err(e) => Some(Err(Error::from(e)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ RecordSection ------------------------------------------------
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RecordSection<'a, D: CompactRecordData<'a>> {
|
||||
message: &'a Message,
|
||||
slice: &'a [u8],
|
||||
count: u16,
|
||||
phantom: PhantomData<D>,
|
||||
}
|
||||
|
||||
impl<'a, D: CompactRecordData<'a>> RecordSection<'a, D> {
|
||||
fn new(message: &'a Message, slice: &'a[u8], count: u16) -> Self {
|
||||
RecordSection { message: message, slice: slice, count: count,
|
||||
phantom: PhantomData }
|
||||
}
|
||||
|
||||
pub fn iter(&mut self) -> &mut Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, D: CompactRecordData<'a>> Iterator for RecordSection<'a, D> {
|
||||
type Item = Result<Record<CompactDomainName<'a>, D>>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
loop {
|
||||
if self.count == 0 { return None }
|
||||
match Record::split_from(self.slice, &self.message.slice) {
|
||||
Ok((inner, slice)) => {
|
||||
self.count -= 1;
|
||||
self.slice = slice;
|
||||
match inner {
|
||||
Some(record) => return Some(Ok(record)),
|
||||
None => { }
|
||||
}
|
||||
}
|
||||
Err(e) => return Some(Err(Error::from(e)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ AnswerSection ------------------------------------------------
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AnswerSection<'a, D: CompactRecordData<'a>> {
|
||||
inner: RecordSection<'a, D>,
|
||||
}
|
||||
|
||||
impl<'a, D: CompactRecordData<'a>> AnswerSection<'a, D> {
|
||||
fn new(message: &'a Message, slice: &'a[u8]) -> Self {
|
||||
AnswerSection {
|
||||
inner: RecordSection::new(message, slice,
|
||||
message.counts().ancount())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn authority(self) -> Option<AuthoritySection<'a, D>> {
|
||||
if self.inner.count == 0 {
|
||||
Some(AuthoritySection::new(self.inner.message, self.inner.slice))
|
||||
}
|
||||
else { None }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, D: CompactRecordData<'a>> Deref for AnswerSection<'a, D> {
|
||||
type Target = RecordSection<'a, D>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, D: CompactRecordData<'a>> DerefMut for AnswerSection<'a, D> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ AuthoritySection ---------------------------------------------
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AuthoritySection<'a, D: CompactRecordData<'a>> {
|
||||
inner: RecordSection<'a, D>
|
||||
}
|
||||
|
||||
impl<'a, D: CompactRecordData<'a>> AuthoritySection<'a, D> {
|
||||
fn new(message: &'a Message, slice: &'a[u8]) -> Self {
|
||||
AuthoritySection {
|
||||
inner: RecordSection::new(message, slice,
|
||||
message.counts().nscount())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn additional(self) -> Option<AdditionalSection<'a, D>> {
|
||||
if self.inner.count == 0 {
|
||||
Some(AdditionalSection::new(self.inner.message,
|
||||
self.inner.slice))
|
||||
}
|
||||
else { None }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, D: CompactRecordData<'a>> Deref for AuthoritySection<'a, D> {
|
||||
type Target = RecordSection<'a, D>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, D: CompactRecordData<'a>> DerefMut for AuthoritySection<'a, D> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ AdditionalSection --------------------------------------------
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AdditionalSection<'a, D: CompactRecordData<'a>> {
|
||||
inner: RecordSection<'a, D>
|
||||
}
|
||||
|
||||
impl<'a, D: CompactRecordData<'a>> AdditionalSection<'a, D> {
|
||||
fn new(message: &'a Message, slice: &'a[u8]) -> Self {
|
||||
AdditionalSection {
|
||||
inner: RecordSection::new(message, slice,
|
||||
message.counts().arcount())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, D: CompactRecordData<'a>> Deref for AdditionalSection<'a, D> {
|
||||
type Target = RecordSection<'a, D>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, D: CompactRecordData<'a>> DerefMut for AdditionalSection<'a, D> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============ Owned Message ================================================
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MessageBuf {
|
||||
buf: Vec<u8>,
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
|
||||
impl MessageBuf {
|
||||
/// Creates an owned message from a bytes slice.
|
||||
///
|
||||
/// This is only safe if this really is a proper message.
|
||||
unsafe fn from_bytes(s: &[u8], offset: usize) -> MessageBuf {
|
||||
MessageBuf { buf: Vec::from(s), offset: offset }
|
||||
}
|
||||
|
||||
pub fn from_vec(buf: Vec<u8>, offset: usize) -> Result<MessageBuf> {
|
||||
if buf.len() < mem::size_of::<FullHeader>() + offset {
|
||||
return Err(Error::OctetError(bytes::Error::PrematureEnd))
|
||||
}
|
||||
Ok(MessageBuf { buf: buf, offset: offset })
|
||||
}
|
||||
|
||||
/// Coerces to a message slice.
|
||||
pub fn as_slice(&self) -> &Message {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn prefix(&self) -> &[u8] {
|
||||
&self.buf[..self.offset]
|
||||
}
|
||||
|
||||
pub fn prefix_mut(&mut self) -> &mut [u8] {
|
||||
&mut self.buf[..self.offset]
|
||||
}
|
||||
|
||||
pub fn message_bytes(&self) -> &[u8] {
|
||||
&self.buf[self.offset..]
|
||||
}
|
||||
|
||||
pub fn message_bytes_mut(&mut self) -> &mut [u8] {
|
||||
&mut self.buf[self.offset..]
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Message> for MessageBuf {
|
||||
fn from(msg: &'a Message) -> MessageBuf {
|
||||
unsafe { MessageBuf::from_bytes(&msg.slice, 0) }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MessageVec> for MessageBuf {
|
||||
fn from(vec: MessageVec) -> MessageBuf {
|
||||
MessageBuf { buf: vec.vec, offset: vec.offset }
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for MessageBuf {
|
||||
type Target = Message;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { Message::from_bytes_unsafe(&self.message_bytes()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Message> for MessageBuf {
|
||||
fn as_ref(&self) -> &Message { self }
|
||||
}
|
||||
|
||||
|
||||
//============ Message Builder ==============================================
|
||||
|
||||
//------------ MessageBuilder -----------------------------------------------
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MessageBuilder {
|
||||
buf: MessageVec
|
||||
}
|
||||
|
||||
impl MessageBuilder {
|
||||
/// Creates a new messsage builder.
|
||||
///
|
||||
pub fn new(maxlen: usize, offset: usize, compress: bool)
|
||||
-> MessageBuilder {
|
||||
MessageBuilder { buf: MessageVec::new(maxlen, offset, compress) }
|
||||
}
|
||||
|
||||
/// Proceeds to building the question section.
|
||||
pub fn question(self) -> QuestionBuilder {
|
||||
QuestionBuilder::new(self.buf)
|
||||
}
|
||||
|
||||
/// Finish off the message and return the underlying vector.
|
||||
pub fn finish(self) -> Vec<u8> {
|
||||
self.buf.vec
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for MessageBuilder {
|
||||
type Target = HeaderBuilder;
|
||||
|
||||
fn deref(&self) -> &HeaderBuilder {
|
||||
HeaderBuilder::from_buf(&self.buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for MessageBuilder {
|
||||
fn deref_mut(&mut self) -> &mut HeaderBuilder {
|
||||
HeaderBuilder::from_buf_mut(&mut self.buf)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ QuestionBuilder ----------------------------------------------
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct QuestionBuilder {
|
||||
buf: MessageVec
|
||||
}
|
||||
|
||||
impl QuestionBuilder {
|
||||
fn new(buf: MessageVec) -> Self {
|
||||
QuestionBuilder { buf: buf }
|
||||
}
|
||||
|
||||
/// Appends a new question to the question section.
|
||||
pub fn push<N: DomainName>(&mut self, question: &Question<N>)
|
||||
-> Result<()> {
|
||||
self.buf.push(|buf| question.push_buf(buf).map_err(|e| e.into()),
|
||||
|counts| counts.inc_qdcount(1))
|
||||
}
|
||||
|
||||
pub fn push_question<N: DomainName>(&mut self, name: N, qtype: RRType,
|
||||
qclass: Class) -> Result<()> {
|
||||
self.push(&Question::new(name, qtype, qclass))
|
||||
}
|
||||
|
||||
/// Move on to the answer section
|
||||
pub fn answer(self) -> AnswerBuilder {
|
||||
AnswerBuilder::new(self.buf)
|
||||
}
|
||||
|
||||
/// Finish off the message and return the underlying vector.
|
||||
pub fn finish(self) -> MessageBuf {
|
||||
self.buf.into()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Deref for QuestionBuilder {
|
||||
type Target = HeaderBuilder;
|
||||
|
||||
fn deref(&self) -> &HeaderBuilder {
|
||||
HeaderBuilder::from_buf(&self.buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for QuestionBuilder {
|
||||
fn deref_mut(&mut self) -> &mut HeaderBuilder {
|
||||
HeaderBuilder::from_buf_mut(&mut self.buf)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ AnswerBuilder ------------------------------------------------
|
||||
|
||||
pub struct AnswerBuilder {
|
||||
buf: MessageVec,
|
||||
}
|
||||
|
||||
impl AnswerBuilder {
|
||||
fn new(buf: MessageVec) -> Self {
|
||||
AnswerBuilder { buf: buf }
|
||||
}
|
||||
|
||||
pub fn push<N, D>(&mut self, record: &Record<N, D>) -> Result<()>
|
||||
where N: DomainName, D: RecordData {
|
||||
self.buf.push(|buf| record.push_buf(buf).map_err(|e| e.into()),
|
||||
|counts| counts.inc_ancount(1))
|
||||
}
|
||||
|
||||
pub fn push_record<N, D>(&mut self, name: N, rclass: Class, ttl: u32,
|
||||
data: D) -> Result<()>
|
||||
where N: DomainName, D: RecordData {
|
||||
self.push(&Record::new(name, rclass, ttl, data))
|
||||
}
|
||||
|
||||
/*
|
||||
pub fn push<N: AsRef<DomainNameSlice>>(&mut self, name: N, rclass: Class,
|
||||
ttl: u32, dataop: Box<PushDataOp>)
|
||||
-> Result<()> {
|
||||
self.buf.push_record(name.as_ref(), rclass, ttl, dataop,
|
||||
|counts| counts.inc_ancount(1))
|
||||
}
|
||||
*/
|
||||
|
||||
pub fn authority(self) -> AuthorityBuilder {
|
||||
AuthorityBuilder::new(self.buf)
|
||||
}
|
||||
|
||||
/// Finish off the message and return the underlying message.
|
||||
pub fn finish(self) -> MessageBuf {
|
||||
self.buf.into()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Deref for AnswerBuilder {
|
||||
type Target = HeaderBuilder;
|
||||
|
||||
fn deref(&self) -> &HeaderBuilder {
|
||||
HeaderBuilder::from_buf(&self.buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for AnswerBuilder {
|
||||
fn deref_mut(&mut self) -> &mut HeaderBuilder {
|
||||
HeaderBuilder::from_buf_mut(&mut self.buf)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ AuthorityBuilder ---------------------------------------------
|
||||
|
||||
pub struct AuthorityBuilder {
|
||||
buf: MessageVec
|
||||
}
|
||||
|
||||
impl AuthorityBuilder {
|
||||
fn new(buf: MessageVec) -> Self {
|
||||
AuthorityBuilder { buf: buf }
|
||||
}
|
||||
|
||||
/*
|
||||
pub fn push<N, F>(&mut self, name: N, rclass: Class, ttl: u32,
|
||||
dataop: F) -> Result<()>
|
||||
where N: AsRef<DomainName>,
|
||||
F: FnOnce(&mut Vec<u8>) -> RRType {
|
||||
self.buf.push_record(name.as_ref(), rclass, ttl, dataop,
|
||||
|counts| counts.inc_ancount(1))
|
||||
}
|
||||
*/
|
||||
|
||||
pub fn additional(self) -> AdditionalBuilder {
|
||||
AdditionalBuilder::new(self.buf)
|
||||
}
|
||||
|
||||
/// Finish off the message and return the underlying vector.
|
||||
pub fn finish(self) -> MessageBuf {
|
||||
self.buf.into()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Deref for AuthorityBuilder {
|
||||
type Target = HeaderBuilder;
|
||||
|
||||
fn deref(&self) -> &HeaderBuilder {
|
||||
HeaderBuilder::from_buf(&self.buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for AuthorityBuilder {
|
||||
fn deref_mut(&mut self) -> &mut HeaderBuilder {
|
||||
HeaderBuilder::from_buf_mut(&mut self.buf)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ AdditionalBuilder --------------------------------------------
|
||||
|
||||
pub struct AdditionalBuilder {
|
||||
buf: MessageVec
|
||||
}
|
||||
|
||||
impl AdditionalBuilder {
|
||||
fn new(buf: MessageVec) -> Self {
|
||||
AdditionalBuilder { buf: buf }
|
||||
}
|
||||
|
||||
/*
|
||||
pub fn push<N, F>(&mut self, name: N, rclass: Class, ttl: u32,
|
||||
dataop: F) -> Result<()>
|
||||
where N: AsRef<DomainName>,
|
||||
F: FnOnce(&mut Vec<u8>) -> RRType {
|
||||
self.buf.push_record(name.as_ref(), rclass, ttl, dataop,
|
||||
|counts| counts.inc_ancount(1))
|
||||
}
|
||||
*/
|
||||
|
||||
/// Finish off the message and return the underlying vector.
|
||||
pub fn finish(self) -> MessageBuf {
|
||||
self.buf.into()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Deref for AdditionalBuilder {
|
||||
type Target = HeaderBuilder;
|
||||
|
||||
fn deref(&self) -> &HeaderBuilder {
|
||||
HeaderBuilder::from_buf(&self.buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for AdditionalBuilder {
|
||||
fn deref_mut(&mut self) -> &mut HeaderBuilder {
|
||||
HeaderBuilder::from_buf_mut(&mut self.buf)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ HeaderBuilder ------------------------------------------------
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct HeaderBuilder {
|
||||
buf: MessageVec
|
||||
}
|
||||
|
||||
impl HeaderBuilder {
|
||||
fn from_buf(buf: &MessageVec) -> &HeaderBuilder {
|
||||
unsafe { mem::transmute(buf) }
|
||||
}
|
||||
|
||||
fn from_buf_mut(buf: &mut MessageVec) -> &mut HeaderBuilder {
|
||||
unsafe { mem::transmute(buf) }
|
||||
}
|
||||
}
|
||||
|
||||
impl HeaderBuilder {
|
||||
/// Returns a reference to the message header.
|
||||
pub fn header(&self) -> &Header {
|
||||
self.buf.header()
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the message header.
|
||||
pub fn header_mut(&mut self) -> &mut Header {
|
||||
self.buf.header_mut()
|
||||
}
|
||||
|
||||
/// Returns a reference to the message header counts.
|
||||
pub fn counts(&self) -> &HeaderCounts {
|
||||
self.buf.counts()
|
||||
}
|
||||
|
||||
/// Returns a reference to the prefix.
|
||||
pub fn prefix(&self) -> &[u8] {
|
||||
self.buf.prefix()
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the prefix.
|
||||
pub fn prefix_mut(&mut self) -> &mut [u8] {
|
||||
self.buf.prefix_mut()
|
||||
}
|
||||
|
||||
/// Returns a reference to the message so far.
|
||||
pub fn message(&self) -> &Message {
|
||||
unsafe { Message::from_bytes_unsafe(self.buf.message_bytes()) }
|
||||
}
|
||||
|
||||
/// Returns the lenth of the message so far.
|
||||
pub fn len(&self) -> usize {
|
||||
self.buf.message_bytes().len()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ MessageVec ---------------------------------------------------
|
||||
|
||||
/// A bytes vector for assembling DNS messages.
|
||||
///
|
||||
/// This private type does all the heavy lifting for creating a message.
|
||||
///
|
||||
#[derive(Debug)]
|
||||
struct MessageVec {
|
||||
/// The underlying vector.
|
||||
vec: Vec<u8>,
|
||||
|
||||
/// When writing to a stream socket, the message actually starts with
|
||||
/// a two byte length indicator. In order to be able to assemble such
|
||||
/// messages herein, we generalize this a little and allow an
|
||||
/// arbitrarily sized prefix. This field keeps the offset where the
|
||||
/// actual message starts.
|
||||
offset: usize,
|
||||
|
||||
/// Maximum size of the message.
|
||||
///
|
||||
/// While existing wire protocols limit messages to 65535 bytes, we
|
||||
/// can build messages up to `std::usize::MAX` bytes in length.
|
||||
maxlen: usize,
|
||||
|
||||
/// Whether then message has exceeded its length and has been truncated.
|
||||
///
|
||||
/// We need to store this separatly since cutting back to the checkpoint
|
||||
/// will get us below the maximum length again.
|
||||
truncated: bool,
|
||||
|
||||
/// Position of the optional check point.
|
||||
///
|
||||
/// If this is set, the vector will be cut back to this length when
|
||||
/// it crosses its length boundary.
|
||||
checkpoint: Option<usize>,
|
||||
|
||||
/// If we do compression, we will store domain names and their offset
|
||||
/// in this here map. If we don't do compression, there is no map.
|
||||
compress: Option<HashMap<DomainNameBuf, u16>>,
|
||||
}
|
||||
|
||||
|
||||
impl MessageVec {
|
||||
fn new(maxlen: usize, offset: usize, compress: bool) -> MessageVec {
|
||||
MessageVec {
|
||||
vec: vec![0; offset + mem::size_of::<FullHeader>()],
|
||||
offset: offset,
|
||||
maxlen: maxlen,
|
||||
truncated: false,
|
||||
checkpoint: None,
|
||||
compress: if compress { Some(HashMap::new()) }
|
||||
else { None }
|
||||
}
|
||||
}
|
||||
|
||||
fn prefix(&self) -> &[u8] {
|
||||
&self.vec[..self.offset]
|
||||
}
|
||||
|
||||
fn prefix_mut(&mut self) -> &mut [u8] {
|
||||
&mut self.vec[..self.offset]
|
||||
}
|
||||
|
||||
fn message_bytes(&self) -> &[u8] {
|
||||
&self.vec[self.offset..]
|
||||
}
|
||||
|
||||
fn message_bytes_mut(&mut self) -> &mut [u8] {
|
||||
&mut self.vec[self.offset..]
|
||||
}
|
||||
|
||||
fn header(&self) -> &Header {
|
||||
unsafe { Header::from_message(self.message_bytes()) }
|
||||
}
|
||||
|
||||
fn header_mut(&mut self) -> &mut Header {
|
||||
unsafe { Header::from_message_mut(self.message_bytes_mut()) }
|
||||
}
|
||||
|
||||
fn counts(&self) -> &HeaderCounts {
|
||||
unsafe { HeaderCounts::from_message(self.message_bytes()) }
|
||||
}
|
||||
|
||||
fn counts_mut(&mut self) -> &mut HeaderCounts {
|
||||
unsafe { HeaderCounts::from_message_mut(self.message_bytes_mut()) }
|
||||
}
|
||||
|
||||
fn keep_pushing(&mut self, len: usize) -> bool {
|
||||
if self.truncated { false }
|
||||
else if self.vec.len() + len > self.maxlen {
|
||||
self.checkpoint.map(|len| self.vec.truncate(len));
|
||||
self.truncated = true;
|
||||
self.header_mut().set_tc(true);
|
||||
false
|
||||
}
|
||||
else { true }
|
||||
}
|
||||
|
||||
fn checkpoint(&mut self) {
|
||||
self.checkpoint = Some(self.vec.len())
|
||||
}
|
||||
|
||||
fn rollback(&mut self) {
|
||||
if let Some(len) = self.checkpoint {
|
||||
self.vec.truncate(len);
|
||||
self.checkpoint = None;
|
||||
}
|
||||
}
|
||||
|
||||
fn push<B, I>(&mut self, buildop: B, incop: I) -> Result<()>
|
||||
where B: FnOnce(&mut MessageVec) -> Result<()>,
|
||||
I: FnOnce(&mut HeaderCounts) -> bytes::Result<()> {
|
||||
self.checkpoint();
|
||||
try!(buildop(self));
|
||||
if self.truncated {
|
||||
self.rollback();
|
||||
Err(bytes::Error::SizeExceeded.into())
|
||||
}
|
||||
else {
|
||||
incop(self.counts_mut()).map_err(|err| {
|
||||
self.rollback();
|
||||
err.into()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
fn push_record<I>(&mut self, name: &DomainNameSlice, rclass: Class,
|
||||
ttl: u32, dataop: Box<PushDataOp>, incop: I) -> Result<()>
|
||||
where
|
||||
I: FnOnce(&mut HeaderCounts) -> bytes::Result<()> {
|
||||
let buildop = |buf: &mut MessageVec| {
|
||||
try!(name.push_buf_compressed(buf));
|
||||
let type_pos = buf.pos();
|
||||
buf.push_u16(0);
|
||||
rclass.push_buf(buf);
|
||||
buf.push_u32(ttl);
|
||||
let len_pos = buf.pos();
|
||||
buf.push_u16(0);
|
||||
let rtype = dataop(&mut buf.vec);
|
||||
let delta = buf.pos();
|
||||
if delta > (::std::u16::MAX as usize) {
|
||||
return Err(Error::OctetError(bytes::Error::Overflow));
|
||||
}
|
||||
buf.update_u16(type_pos, rtype.to_int());
|
||||
buf.update_u16(len_pos, delta as u16);
|
||||
Ok(())
|
||||
};
|
||||
self.push(buildop, incop)
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
impl BytesBuf for MessageVec {
|
||||
type Pos = <Vec<u8> as BytesBuf>::Pos;
|
||||
|
||||
fn push_bytes(&mut self, data: &[u8]) {
|
||||
if self.keep_pushing(data.len()) {
|
||||
self.vec.push_bytes(data)
|
||||
}
|
||||
}
|
||||
|
||||
fn pos(&self) -> Self::Pos { self.vec.pos() }
|
||||
fn delta(&self, pos: Self::Pos) -> usize { self.vec.delta(pos) }
|
||||
fn update_bytes(&mut self, pos: Self::Pos, data: &[u8]) {
|
||||
self.vec.update_bytes(pos, data)
|
||||
}
|
||||
|
||||
fn can_compress(&self) -> bool {
|
||||
self.compress.is_some()
|
||||
}
|
||||
|
||||
fn add_name_pos<N: AsRef<DomainNameSlice>>(&mut self, name: N) {
|
||||
if self.truncated { return }
|
||||
if let Some(ref mut map) = self.compress {
|
||||
if self.vec.len() >= 65535 { return }
|
||||
let name = name.as_ref().to_owned();
|
||||
map.insert(name, (self.vec.len() - self.offset) as u16);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_name_pos<N: AsRef<DomainNameSlice>>(&self, name: N) -> Option<u16> {
|
||||
match self.compress {
|
||||
Some(ref map) => map.get(name.as_ref()).map(|x| *x),
|
||||
None => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============ Error and Result =============================================
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Error {
|
||||
NameError(name::ErrorKind),
|
||||
OctetError(bytes::Error),
|
||||
}
|
||||
|
||||
impl error::Error for Error {
|
||||
fn description(&self) -> &str {
|
||||
match *self {
|
||||
Error::NameError(ref kind) => kind.description(),
|
||||
Error::OctetError(ref error) => {
|
||||
use std::error::Error;
|
||||
|
||||
error.description()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl convert::From<bytes::Error> for Error {
|
||||
fn from(error: bytes::Error) -> Error {
|
||||
Error::OctetError(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl convert::From<name::Error> for Error {
|
||||
fn from(error: name::Error) -> Error {
|
||||
match error {
|
||||
name::Error::NameError(kind) => Error::NameError(kind),
|
||||
name::Error::OctetError(error) => Error::OctetError(error),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl convert::From<question::Error> for Error {
|
||||
fn from(error: question::Error) -> Error {
|
||||
match error {
|
||||
question::Error::NameError(kind) => Error::NameError(kind),
|
||||
question::Error::OctetError(kind) => Error::OctetError(kind),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
use std::error::Error;
|
||||
|
||||
self.description().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
|
||||
|
||||
//============ Testing ======================================================
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::net::Ipv4Addr;
|
||||
use super::super::iana::{Class, RRType};
|
||||
use super::super::name::DomainNameBuf;
|
||||
use super::super::rdata::rfc1035::{A, NS};
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn build_message() {
|
||||
let mut msg = MessageBuilder::new(1550, 0, true).question();
|
||||
msg.push_question(DomainNameBuf::from_str("example.com.").unwrap(),
|
||||
RRType::A, Class::IN).unwrap();
|
||||
let mut msg = msg.answer();
|
||||
let data = A::new(Ipv4Addr::new(127, 0, 0, 1));
|
||||
msg.push_record(DomainNameBuf::from_str("example.com.").unwrap(),
|
||||
Class::IN, 3600, data).unwrap();
|
||||
let data = NS::new(DomainNameBuf::from_str("ns.example.com.").unwrap());
|
||||
msg.push_record(DomainNameBuf::from_str("example.com.").unwrap(),
|
||||
Class::IN, 3600, data).unwrap();
|
||||
let _ = msg.finish();
|
||||
}
|
||||
}
|
||||
|
||||
-131
@@ -1,131 +0,0 @@
|
||||
//! A single question from a DNS message
|
||||
//!
|
||||
|
||||
use std::convert;
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
use std::result;
|
||||
use super::iana::{Class, RRType};
|
||||
use super::name::{self, DomainName, DomainNameBuf, DomainNameSlice,
|
||||
CompactDomainName};
|
||||
use super::bytes::{self, BytesSlice, BytesBuf};
|
||||
|
||||
|
||||
//------------ Question -----------------------------------------------------
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Question<N: DomainName> {
|
||||
qname: N,
|
||||
qtype: RRType,
|
||||
qclass: Class,
|
||||
}
|
||||
|
||||
type QuestionSlice<'a> = Question<&'a DomainNameSlice>;
|
||||
type QuestionBuf = Question<DomainNameBuf>;
|
||||
type CompactQuestion<'a> = Question<CompactDomainName<'a>>;
|
||||
|
||||
|
||||
//--- Common functions and methods
|
||||
|
||||
impl<N: DomainName> Question<N> {
|
||||
pub fn new(qname: N, qtype: RRType, qclass: Class) -> Self {
|
||||
Question { qname: qname, qtype: qtype, qclass: qclass }
|
||||
}
|
||||
|
||||
/// Returns the requested domain name.
|
||||
pub fn qname(&self) -> &N {
|
||||
&self.qname
|
||||
}
|
||||
|
||||
/// Returns the requested record type.
|
||||
pub fn qtype(&self) -> RRType {
|
||||
self.qtype
|
||||
}
|
||||
|
||||
/// Returns the requested class.
|
||||
pub fn qclass(&self) -> Class {
|
||||
self.qclass
|
||||
}
|
||||
|
||||
pub fn push_buf<B: BytesBuf>(&self, buf: &mut B) -> Result<()> {
|
||||
try!(self.qname.push_buf_compressed(buf));
|
||||
self.qtype.push_buf(buf);
|
||||
self.qclass.push_buf(buf);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//--- Addtional functions and methods for owned questions
|
||||
|
||||
impl Default for Question<DomainNameBuf> {
|
||||
fn default() -> Self {
|
||||
Question { qname: DomainNameBuf::new(), qtype: RRType::A,
|
||||
qclass: Class::IN }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//--- Addtional functions and methods for compact questions
|
||||
|
||||
impl<'a> Question<CompactDomainName<'a>> {
|
||||
|
||||
/// Splits a questions from the front of a byte slice.
|
||||
///
|
||||
pub fn split_from(slice: &'a[u8], context: &'a[u8])
|
||||
-> Result<(Self, &'a[u8])> {
|
||||
let (qname, slice) = try!(CompactDomainName::split_from(slice,
|
||||
context));
|
||||
let (qtype, slice) = try!(slice.split_u16());
|
||||
let (qclass, slice) = try!(slice.split_u16());
|
||||
Ok((Question::new(qname, qtype.into(), qclass.into()), slice))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ Error and Result ---------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Error {
|
||||
NameError(name::ErrorKind),
|
||||
OctetError(bytes::Error),
|
||||
}
|
||||
|
||||
impl error::Error for Error {
|
||||
fn description(&self) -> &str {
|
||||
match *self {
|
||||
Error::NameError(ref kind) => kind.description(),
|
||||
Error::OctetError(ref error) => {
|
||||
use std::error::Error;
|
||||
|
||||
error.description()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl convert::From<bytes::Error> for Error {
|
||||
fn from(error: bytes::Error) -> Error {
|
||||
Error::OctetError(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl convert::From<name::Error> for Error {
|
||||
fn from(error: name::Error) -> Error {
|
||||
match error {
|
||||
name::Error::NameError(kind) => Error::NameError(kind),
|
||||
name::Error::OctetError(error) => Error::OctetError(error),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
use std::error::Error;
|
||||
|
||||
self.description().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
//! Generic Record Data Types
|
||||
|
||||
use std::fmt;
|
||||
use super::super::bytes::BytesBuf;
|
||||
use super::super::iana::RRType;
|
||||
use super::traits::{RecordData, CompactRecordData, Result};
|
||||
|
||||
/*
|
||||
//------------ GenericRecordData --------------------------------------------
|
||||
|
||||
/// A slice of generic record data.
|
||||
///
|
||||
pub struct GenericRecordData<'a> {
|
||||
rtype: RRType,
|
||||
data: &'a [u8],
|
||||
}
|
||||
|
||||
impl<'a> GenericRecordData<'a> {
|
||||
pub fn new(rtype: RRType, data: &'a[u8]) -> Self {
|
||||
GenericRecordData { rtype: rtype, data: data }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RecordData for GenericRecordData<'a> {
|
||||
fn rtype(&self) -> RRType {
|
||||
self.rtype
|
||||
}
|
||||
|
||||
fn push_buf<B: BytesBuf>(&self, buf: &mut B) -> Result<()> {
|
||||
buf.push_bytes(self.data);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
//------------ CompactGenericRecordData -------------------------------------
|
||||
|
||||
pub struct CompactGenericRecordData<'a> {
|
||||
rtype: RRType,
|
||||
data: &'a [u8],
|
||||
context: &'a [u8],
|
||||
}
|
||||
|
||||
impl<'a> CompactGenericRecordData<'a> {
|
||||
pub fn new(rtype: RRType, data: &'a[u8], context: &'a[u8]) -> Self {
|
||||
CompactGenericRecordData { rtype: rtype, data: data,
|
||||
context: context }
|
||||
}
|
||||
|
||||
pub fn rtype(&self) -> RRType { self.rtype }
|
||||
pub fn data(&self) -> &[u8] { self.data }
|
||||
pub fn context(&self) -> &[u8] { self.context }
|
||||
|
||||
pub fn fmt<C>(&self, f: &mut fmt::Formatter) -> fmt::Result
|
||||
where C: CompactRecordData<'a> + fmt::Display {
|
||||
match C::from_bytes(self.rtype, self.data, self.context) {
|
||||
Err(..) => Ok(()),
|
||||
Ok(None) => Ok(()),
|
||||
Ok(Some(data)) => data.fmt(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RecordData for CompactGenericRecordData<'a> {
|
||||
fn rtype(&self) -> RRType {
|
||||
self.rtype
|
||||
}
|
||||
|
||||
fn push_buf<B: BytesBuf>(&self, buf: &mut B) -> Result<()> {
|
||||
buf.push_bytes(self.data);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> CompactRecordData<'a> for CompactGenericRecordData<'a> {
|
||||
fn from_bytes(rtype: RRType, slice: &'a[u8], context: &'a[u8])
|
||||
-> Result<Option<Self>> {
|
||||
Ok(Some(CompactGenericRecordData::new(rtype, slice, context)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> fmt::Display for CompactGenericRecordData<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
use super::rfc1035::*;
|
||||
|
||||
match self.rtype {
|
||||
RRType::A => self.fmt::<A>(f),
|
||||
RRType::NS => self.fmt::<CompactNS<'a>>(f),
|
||||
_ => "...".fmt(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
use std::fmt;
|
||||
use std::net;
|
||||
use super::super::bytes::{BytesBuf, BytesSlice};
|
||||
use super::super::iana::RRType;
|
||||
use super::super::name::{DomainName, CompactDomainName};
|
||||
use super::super::question::Result;
|
||||
use super::traits::{ConcreteRecordData, CompactConcreteRecordData};
|
||||
|
||||
|
||||
//------------ A ------------------------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct A {
|
||||
addr: net::Ipv4Addr,
|
||||
}
|
||||
|
||||
impl A {
|
||||
pub fn new(addr: net::Ipv4Addr) -> A {
|
||||
A { addr: addr }
|
||||
}
|
||||
|
||||
pub fn addr(&self) -> &net::Ipv4Addr { &self.addr }
|
||||
pub fn addr_mut(&mut self) -> &mut net::Ipv4Addr { &mut self.addr }
|
||||
}
|
||||
|
||||
impl<'a> ConcreteRecordData<'a> for A {
|
||||
fn rtype() -> RRType { RRType::A }
|
||||
|
||||
fn push_buf<B: BytesBuf>(&self, buf: &mut B) -> Result<()> {
|
||||
for i in self.addr.octets().iter() {
|
||||
buf.push_u8(*i);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> CompactConcreteRecordData<'a> for A {
|
||||
fn parse(rdata: &'a[u8], _: &[u8]) -> Result<Self> {
|
||||
let (a, rdata) = try!(rdata.split_u8());
|
||||
let (b, rdata) = try!(rdata.split_u8());
|
||||
let (c, rdata) = try!(rdata.split_u8());
|
||||
let (d, _) = try!(rdata.split_u8());
|
||||
Ok(A::new(net::Ipv4Addr::new(a, b, c, d)))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for A {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.addr.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ NS -----------------------------------------------------------
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NS<N: DomainName> {
|
||||
nsdname: N
|
||||
}
|
||||
|
||||
pub type CompactNS<'a> = NS<CompactDomainName<'a>>;
|
||||
|
||||
impl<N: DomainName> NS<N> {
|
||||
pub fn new(nsdname: N) -> Self {
|
||||
NS { nsdname: nsdname }
|
||||
}
|
||||
|
||||
pub fn nsdname(&self) -> &N {
|
||||
&self.nsdname
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, N: DomainName> ConcreteRecordData<'a> for NS<N> {
|
||||
fn rtype() -> RRType { RRType::NS }
|
||||
|
||||
fn push_buf<B: BytesBuf>(&self, buf: &mut B) -> Result<()> {
|
||||
try!(self.nsdname.push_buf_compressed(buf));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> CompactConcreteRecordData<'a> for NS<CompactDomainName<'a>> {
|
||||
fn parse(rdata: &'a[u8], context: &'a [u8]) -> Result<Self> {
|
||||
let (name, _) = try!(CompactDomainName::split_from(rdata, context));
|
||||
Ok(NS::new(name))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<N: DomainName> fmt::Display for NS<N> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.nsdname.fmt(f)
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
//! Traits for record data.
|
||||
//!
|
||||
//! There is two classes of traits in here. The more basic two traits,
|
||||
//! `RecordData` and `CompactRecordData`, need to be implemented by all
|
||||
//! types. They represent creating and parsing record data, respectively.
|
||||
//! They are split since some record data types contain domain names and
|
||||
//! while creation is possible with all name types, parsing is only
|
||||
//! available with `CompactDomainName`.
|
||||
//!
|
||||
//! The second two traits are for types that implement exactly one type
|
||||
//! of record data. They exist to avoid having to implement certain
|
||||
//! functionality multiple times. They contain implementations for the
|
||||
//! first two traits.
|
||||
|
||||
use std::fmt;
|
||||
use super::super::bytes::{BytesBuf};
|
||||
use super::super::iana::RRType;
|
||||
pub use super::super::question::Result; // XXX Temporary
|
||||
|
||||
|
||||
//------------ Basic Traits -------------------------------------------------
|
||||
|
||||
/// A trait for creating record data.
|
||||
pub trait RecordData: fmt::Display {
|
||||
/// Returns the record type for this record data instance.
|
||||
fn rtype(&self) -> RRType;
|
||||
|
||||
/// Appends the record data to the end of a buffer.
|
||||
fn push_buf<B: BytesBuf>(&self, buf: &mut B) -> Result<()>;
|
||||
}
|
||||
|
||||
/// A trait for parsing record data.
|
||||
pub trait CompactRecordData<'a>: RecordData + Sized {
|
||||
/// Parses the record data from the slice if the type is right.
|
||||
///
|
||||
/// If this record data type does not feel responsible for records of
|
||||
/// type `rtype`, it should return `Ok(None)`. Otherwise it should
|
||||
/// return something or an error if parsing fails.
|
||||
///
|
||||
/// The `context` argument contains the slice of the entire DNS
|
||||
/// message for giving to `CompactDomainName`s.
|
||||
fn from_bytes(rtype: RRType, slice: &'a[u8], context: &'a[u8])
|
||||
-> Result<Option<Self>>;
|
||||
}
|
||||
|
||||
|
||||
//------------ Traits for Concrete Types ------------------------------------
|
||||
|
||||
/// A trait for creating concrete record data.
|
||||
///
|
||||
/// This is the companion trait to `RecordData`. The only difference is that
|
||||
/// `rtype()` is an associated function instead of a method since all records
|
||||
/// with this data have the same type.
|
||||
pub trait ConcreteRecordData<'a>: fmt::Display + Sized {
|
||||
/// Returns the record type for all records of this data type.
|
||||
fn rtype() -> RRType;
|
||||
|
||||
/// Appends the record data to the end of a buffer.
|
||||
fn push_buf<B: BytesBuf>(&self, buf: &mut B) -> Result<()>;
|
||||
}
|
||||
|
||||
/// A trait for parsing concrete record data.
|
||||
///
|
||||
/// This is the companion trait to `CompactRecordData`.
|
||||
pub trait CompactConcreteRecordData<'a>: ConcreteRecordData<'a> + Sized {
|
||||
/// Parses the record data from the slice.
|
||||
///
|
||||
/// Since the function is only ever called if the record type was
|
||||
/// right, there is no need for an `Option<Self>`.
|
||||
fn parse(rdata: &'a[u8], context: &'a [u8]) -> Result<Self>;
|
||||
}
|
||||
|
||||
impl<'a, C: ConcreteRecordData<'a>> RecordData for C {
|
||||
fn rtype(&self) -> RRType {
|
||||
Self::rtype()
|
||||
}
|
||||
|
||||
fn push_buf<B: BytesBuf>(&self, buf: &mut B) -> Result<()> {
|
||||
self.push_buf(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, C: CompactConcreteRecordData<'a>> CompactRecordData<'a> for C {
|
||||
fn from_bytes(rtype: RRType, rdata: &'a[u8], context: &'a [u8])
|
||||
-> Result<Option<Self>> {
|
||||
if rtype != Self::rtype() { Ok(None) }
|
||||
else { Ok(Some(try!(Self::parse(rdata, context)))) }
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
use std::fmt;
|
||||
use super::name::{DomainName, CompactDomainName};
|
||||
use super::bytes::{self, BytesSlice, BytesBuf};
|
||||
use super::question::Result; // XXX Temporary.
|
||||
use super::iana::{Class, RRType};
|
||||
use super::rdata::traits::{RecordData, CompactRecordData};
|
||||
|
||||
|
||||
//------------ Record -------------------------------------------------------
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Record<N: DomainName, D: RecordData> {
|
||||
name: N,
|
||||
rclass: Class,
|
||||
ttl: u32,
|
||||
rdata: D
|
||||
}
|
||||
|
||||
|
||||
//--- Common
|
||||
|
||||
impl<N: DomainName, D: RecordData> Record<N, D> {
|
||||
pub fn new(name: N, rclass: Class, ttl: u32, rdata: D) -> Self {
|
||||
Record { name: name, rclass: rclass, ttl: ttl, rdata: rdata }
|
||||
}
|
||||
|
||||
pub fn push_buf<B: BytesBuf>(&self, buf: &mut B) -> Result<()> {
|
||||
try!(self.name.push_buf(buf));
|
||||
self.rdata.rtype().push_buf(buf);
|
||||
self.rclass.push_buf(buf);
|
||||
buf.push_u32(self.ttl);
|
||||
let pos = buf.pos();
|
||||
buf.push_u16(0);
|
||||
try!(self.rdata.push_buf(buf));
|
||||
let delta = buf.delta(pos);
|
||||
if delta > (::std::u16::MAX as usize) {
|
||||
return Err(bytes::Error::Overflow.into())
|
||||
}
|
||||
buf.update_u16(pos, delta as u16);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the domain name.
|
||||
pub fn name(&self) -> &N { &self.name }
|
||||
|
||||
/// Returns the record type.
|
||||
pub fn rtype(&self) -> RRType { self.rdata.rtype() }
|
||||
|
||||
/// Returns the record class.
|
||||
pub fn rclass(&self) -> Class { self.rclass }
|
||||
|
||||
/// Returns the record’s time to live.
|
||||
pub fn ttl(&self) -> u32 { self.ttl }
|
||||
|
||||
/// Returns the raw record data.
|
||||
pub fn rdata(&self) -> &D { &self.rdata }
|
||||
}
|
||||
|
||||
|
||||
//---- Compact record
|
||||
|
||||
impl <'a, D: CompactRecordData<'a>> Record<CompactDomainName<'a>, D> {
|
||||
/// Splits a record from the front of a bytes slice.
|
||||
///
|
||||
pub fn split_from(slice: &'a[u8], context: &'a[u8])
|
||||
-> Result<(Option<Self>, &'a[u8])> {
|
||||
let (name, slice) = try!(CompactDomainName::split_from(slice,
|
||||
context));
|
||||
let (rtype, slice) = try!(slice.split_u16());
|
||||
let (rclass, slice) = try!(slice.split_u16());
|
||||
let (ttl, slice) = try!(slice.split_u32());
|
||||
let (rdlen, slice) = try!(slice.split_u16());
|
||||
let (rdata, slice) = try!(slice.split_bytes(rdlen as usize));
|
||||
match try!(D::from_bytes(rtype.into(), rdata, context)) {
|
||||
None => Ok((None, slice)),
|
||||
Some(rdata) => {
|
||||
Ok((Some(Record::new(name, rclass.into(), ttl, rdata)),
|
||||
slice))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//---- Traits
|
||||
|
||||
impl <N: DomainName, D: RecordData> fmt::Display for Record<N, D> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}\t{}\t{}\t{}\t{}",
|
||||
self.name, self.ttl, self.rclass, self.rdata.rtype(),
|
||||
self.rdata)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,8 @@ use std::path::Path;
|
||||
use std::str::{self, FromStr, SplitWhitespace};
|
||||
use std::result;
|
||||
use std::time::Duration;
|
||||
use ::name::{self, DomainNameBuf};
|
||||
use ::bits::error::FromStrError;
|
||||
use ::bits::name::OwnedDName;
|
||||
|
||||
|
||||
//------------ ResolvOptions ------------------------------------------------
|
||||
@@ -119,7 +120,7 @@ pub struct ResolvConf {
|
||||
pub servers: Vec<SocketAddr>,
|
||||
|
||||
/// Search list for host-name lookup.
|
||||
pub search: Vec<DomainNameBuf>,
|
||||
pub search: Vec<OwnedDName>,
|
||||
|
||||
/// TODO Sortlist
|
||||
/// sortlist: ??
|
||||
@@ -171,7 +172,7 @@ impl ResolvConf {
|
||||
self.servers.push(SocketAddr::new(addr, 53));
|
||||
}
|
||||
if self.search.is_empty() {
|
||||
self.search.push(DomainNameBuf::root())
|
||||
self.search.push(OwnedDName::root())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -226,7 +227,7 @@ impl ResolvConf {
|
||||
}
|
||||
|
||||
fn parse_domain(&mut self, mut words: SplitWhitespace) -> Result<()> {
|
||||
let domain = try!(DomainNameBuf::from_str(try!(next_word(&mut words))));
|
||||
let domain = try!(OwnedDName::from_str(try!(next_word(&mut words))));
|
||||
self.search = Vec::new();
|
||||
self.search.push(domain);
|
||||
no_more_words(words)
|
||||
@@ -235,7 +236,7 @@ impl ResolvConf {
|
||||
fn parse_search(&mut self, words: SplitWhitespace) -> Result<()> {
|
||||
let mut search = Vec::new();
|
||||
for word in words {
|
||||
search.push(try!(DomainNameBuf::from_str(word)))
|
||||
search.push(try!(OwnedDName::from_str(word)))
|
||||
}
|
||||
self.search = search;
|
||||
Ok(())
|
||||
@@ -352,8 +353,8 @@ impl convert::From<io::Error> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl convert::From<name::ParseError> for Error {
|
||||
fn from(_: name::ParseError) -> Error {
|
||||
impl convert::From<FromStrError> for Error {
|
||||
fn from(_: FromStrError) -> Error {
|
||||
Error::ParseError
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user