This commit is contained in:
Stefan Bühler 2017-12-16 21:58:18 +01:00
commit 63cd9196f9
31 changed files with 3303 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/target/
**/*.rs.bk

217
Cargo.lock generated Normal file
View File

@ -0,0 +1,217 @@
[[package]]
name = "backtrace"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"backtrace-sys 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-demangle 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "backtrace-sys"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cc 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "byteorder"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "bytes"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"iovec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cc"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cfg-if"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "dbghelp-sys"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "dnsbox"
version = "0.1.0"
dependencies = [
"dnsbox-base 0.1.0",
]
[[package]]
name = "dnsbox-base"
version = "0.1.0"
dependencies = [
"byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"dnsbox-derive 0.1.0",
"failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "dnsbox-derive"
version = "0.1.0"
dependencies = [
"quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "failure"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"backtrace 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"failure_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "failure_derive"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
"synstructure 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "iovec"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "kernel32-sys"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "lazy_static"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "libc"
version = "0.2.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "log"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "quote"
version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rustc-demangle"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "smallvec"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "syn"
version = "0.11.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
"synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "synom"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "synstructure"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "unicode-xid"
version = "0.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi-build"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum backtrace 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "99f2ce94e22b8e664d95c57fff45b98a966c2252b60691d0b7aeeccd88d70983"
"checksum backtrace-sys 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "c63ea141ef8fdb10409d0f5daf30ac51f84ef43bff66f16627773d2a292cd189"
"checksum byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff81738b726f5d099632ceaffe7fb65b90212e8dce59d518729e7e8634032d3d"
"checksum bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d828f97b58cc5de3e40c421d0cf2132d6b2da4ee0e11b8632fa838f0f9333ad6"
"checksum cc 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7db2f146208d7e0fbee761b09cd65a7f51ccc38705d4e7262dad4d73b12a76b1"
"checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de"
"checksum dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97590ba53bcb8ac28279161ca943a924d1fd4a8fb3fa63302591647c4fc5b850"
"checksum failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "934799b6c1de475a012a02dab0ace1ace43789ee4b99bcfbf1a2e3e8ced5de82"
"checksum failure_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c7cdda555bb90c9bb67a3b670a0f42de8e73f5981524123ad8578aafec8ddb8b"
"checksum iovec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "29d062ee61fccdf25be172e70f34c9f6efc597e1fb8f6526e8437b2046ab26be"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d"
"checksum libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)" = "d1419b2939a0bc44b77feb34661583c7546b532b192feab36249ab584b86856c"
"checksum log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "880f77541efa6e5cc74e76910c9884d9859683118839d6a1dc3b11e63512565b"
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
"checksum rustc-demangle 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "aee45432acc62f7b9a108cc054142dac51f979e69e71ddce7d6fc7adf29e817e"
"checksum smallvec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ee4f357e8cd37bf8822e1b964e96fd39e2cb5a0424f8aaa284ccaccc2162411c"
"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6"
"checksum synstructure 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a761d12e6d8dcb4dcf952a7a89b475e3a9d69e4a69307e01a470977642914bd"
"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"

9
Cargo.toml Normal file
View File

@ -0,0 +1,9 @@
[package]
name = "dnsbox"
version = "0.1.0"
authors = ["Stefan Bühler <stbuehler@web.de>"]
[dependencies]
dnsbox-base = { path = "lib/dnsbox-base" }
[workspace]

View File

@ -0,0 +1,13 @@
[package]
name = "dnsbox-base"
version = "0.1.0"
authors = ["Stefan Bühler <stbuehler@web.de>"]
[dependencies]
byteorder = "1.1.0"
bytes = "0.4"
dnsbox-derive = { path = "../dnsbox-derive" }
failure = "0.1.1"
lazy_static = "1.0.0"
log = "0.3"
smallvec = "0.4.4"

View File

@ -0,0 +1,90 @@
use bytes::{Bytes, Buf};
use std::io::Cursor;
use ser::DnsPacketData;
use errors::*;
#[derive(Clone, Debug)]
pub struct HexShortBlob(Bytes);
// Similar to `ShortText`, but uses (unquoted, no spaces allowed) hex
// for text representation; "-" when empty
impl DnsPacketData for HexShortBlob {
fn deserialize(data: &mut Cursor<Bytes>) -> Result<Self> {
check_enough_data!(data, 1, "HexShortBlob length");
let label_len = data.get_u8() as usize;
check_enough_data!(data, label_len, "HexShortBlob content");
let pos = data.position() as usize;
let text = data.get_ref().slice(pos, pos + label_len);
data.advance(label_len);
Ok(HexShortBlob(text))
}
}
#[derive(Clone, Debug)]
pub struct Base32ShortBlob(Bytes);
// Similar to `ShortText`, but uses (unquoted, no spaces allowed) base32
// for text representation; "-" when empty
impl DnsPacketData for Base32ShortBlob {
fn deserialize(data: &mut Cursor<Bytes>) -> Result<Self> {
check_enough_data!(data, 1, "Base32ShortBlob length");
let label_len = data.get_u8() as usize;
check_enough_data!(data, label_len, "Base32ShortBlob content");
let pos = data.position() as usize;
let text = data.get_ref().slice(pos, pos + label_len);
data.advance(label_len);
Ok(Base32ShortBlob(text))
}
}
#[derive(Clone, Debug)]
pub struct LongText(Vec<Bytes>);
// RFC 1035 names this `One or more <character-string>`. No following
// field allowed.
impl DnsPacketData for LongText {
fn deserialize(data: &mut Cursor<Bytes>) -> Result<Self> {
let mut texts = Vec::new();
loop {
check_enough_data!(data, 1, "LongText length");
let label_len = data.get_u8() as usize;
check_enough_data!(data, label_len, "LongText content");
let pos = data.position() as usize;
texts.push(data.get_ref().slice(pos, pos + label_len));
data.advance(label_len);
if !data.has_remaining() { break; }
}
Ok(LongText(texts))
}
}
#[derive(Clone, Debug)]
pub struct RemainingText(Bytes);
// No length byte, just all data to end of record. uses base64 encoding
// for text representation
impl DnsPacketData for RemainingText {
fn deserialize(data: &mut Cursor<Bytes>) -> Result<Self> {
let pos = data.position() as usize;
let len = data.remaining();
let text = data.get_ref().slice(pos, pos + len);
data.advance(len);
Ok(RemainingText(text))
}
}
#[derive(Clone, Debug)]
pub struct HexRemainingBlob(Bytes);
// No length byte, just all data to end of record. uses hex encoding for
// text representation (spaces are ignored - last field in record).
impl DnsPacketData for HexRemainingBlob {
fn deserialize(data: &mut Cursor<Bytes>) -> Result<Self> {
let pos = data.position() as usize;
let len = data.remaining();
let text = data.get_ref().slice(pos, pos + len);
data.advance(len);
Ok(HexRemainingBlob(text))
}
}

View File

@ -0,0 +1,9 @@
pub mod name;
pub mod text;
pub mod binary;
mod rr_type;
pub use self::name::{DnsName, DnsCompressedName};
pub use self::text::{ShortText};
pub use self::binary::{HexShortBlob, Base32ShortBlob, LongText, RemainingText, HexRemainingBlob};
pub use self::rr_type::Type;

View File

@ -0,0 +1,79 @@
use std::fmt;
use super::DnsLabelRef;
/// Customize formatting of DNS labels
///
/// The default uses "." as separator and adds a trailing separator.
///
/// The `Debug` formatters just format as `Display` to a String and then
/// format that string as `Debug`.
#[derive(Clone,Copy,PartialEq,Eq,PartialOrd,Ord,Hash,Debug)]
pub struct DisplayLabelsOptions {
/// separator to insert between (escaped) labels
pub separator: &'static str,
/// whether a trailing separator is added.
///
/// without a trailing separator the root zone is represented as
/// empty string!
pub trailing: bool,
}
impl Default for DisplayLabelsOptions {
fn default() -> Self {
DisplayLabelsOptions{
separator: ".",
trailing: true,
}
}
}
/// Wrap anything representing a collection of labels (`DnsLabelRef`) to
/// format using the given `options`.
///
/// As name you can pass any cloneable `(Into)Iterator` with
/// `DnsLabelRef` items, e.g:
///
/// * `&DnsName`
/// * `DnsNameIterator`
#[derive(Clone)]
pub struct DisplayLabels<'a, I>
where
I: IntoIterator<Item=DnsLabelRef<'a>>+Clone
{
/// Label collection to iterate over
pub labels: I,
/// Options
pub options: DisplayLabelsOptions,
}
impl<'a, I> fmt::Debug for DisplayLabels<'a, I>
where
I: IntoIterator<Item=DnsLabelRef<'a>>+Clone
{
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
// just escape the display version1
format!("{}", self).fmt(w)
}
}
impl<'a, I> fmt::Display for DisplayLabels<'a, I>
where
I: IntoIterator<Item=DnsLabelRef<'a>>+Clone
{
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
let mut i = self.labels.clone().into_iter();
if let Some(first_label) = i.next() {
// first label
fmt::Display::fmt(&first_label, w)?;
// remaining labels
while let Some(label) = i.next() {
w.write_str(self.options.separator)?;
fmt::Display::fmt(&label, w)?;
}
}
if self.options.trailing {
w.write_str(self.options.separator)?;
}
Ok(())
}
}

View File

@ -0,0 +1,240 @@
use bytes::Bytes;
use errors::*;
use std::fmt;
use std::cmp::Ordering;
#[inline]
fn check_label(label: &[u8]) -> Result<()> {
if label.len() == 0 {
bail!("label must not be empty")
}
if label.len() > 63 {
bail!("label must not be longer than 63 bytes")
}
Ok(())
}
/// A DNS label (any binary string with `0 < length < 64`)
#[derive(Clone)]
pub struct DnsLabel {
pub(super) label: Bytes, // 0 < len < 64
}
impl DnsLabel {
/// Create new label from existing storage
///
/// Fails when the length doesn't match the requirement `0 < length < 64`.
pub fn new(label: Bytes) -> Result<Self> {
check_label(&label)?;
Ok(DnsLabel{label})
}
/// Convert to a representation without storage
pub fn as_ref<'a>(&'a self) -> DnsLabelRef<'a> {
DnsLabelRef{label: self.label.as_ref()}
}
/// Access as raw bytes
pub fn as_bytes(&self) -> &Bytes {
&self.label
}
/// Access as raw bytes
pub fn as_raw(&self) -> &[u8] {
&self.label
}
/// Length of label (0 < len < 64)
pub fn len(&self) -> u8 {
self.label.len() as u8
}
/// compares two labels (ASCII case insensitive)
#[inline]
pub fn compare<'a, L: Into<DnsLabelRef<'a>>>(&self, rhs: L) -> Ordering {
compare_ascii_lc(self.as_raw(), rhs.into().as_raw())
}
}
impl<'a> From<DnsLabelRef<'a>> for DnsLabel {
fn from(label_ref: DnsLabelRef<'a>) -> Self {
DnsLabel{
label: Bytes::from(label_ref.label),
}
}
}
impl PartialEq<DnsLabel> for DnsLabel {
#[inline]
fn eq(&self, rhs: &DnsLabel) -> bool {
self.as_ref().eq(&rhs.as_ref())
}
}
impl<'a> PartialEq<DnsLabelRef<'a>> for DnsLabel {
#[inline]
fn eq(&self, rhs: &DnsLabelRef<'a>) -> bool {
self.as_ref().eq(rhs)
}
}
impl Eq for DnsLabel{}
impl PartialOrd<DnsLabel> for DnsLabel {
#[inline]
fn partial_cmp(&self, rhs: &DnsLabel) -> Option<Ordering> {
Some(self.compare(rhs))
}
}
impl<'a> PartialOrd<DnsLabelRef<'a>> for DnsLabel {
#[inline]
fn partial_cmp(&self, rhs: &DnsLabelRef<'a>) -> Option<Ordering> {
Some(self.compare(*rhs))
}
}
impl Ord for DnsLabel {
#[inline]
fn cmp(&self, rhs: &Self) -> Ordering {
self.compare(rhs)
}
}
impl fmt::Debug for DnsLabel {
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
self.as_ref().fmt(w)
}
}
impl fmt::Display for DnsLabel {
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
self.as_ref().fmt(w)
}
}
/// A DNS label (any binary string with `0 < length < 64`)
///
/// Storage is provided through lifetime.
#[derive(Clone,Copy)]
pub struct DnsLabelRef<'a> {
pub(super) label: &'a [u8], // 0 < len < 64
}
impl<'a> DnsLabelRef<'a> {
/// Create new label from existing storage
///
/// Fails when the length doesn't match the requirement `0 < length < 64`.
pub fn new(label: &'a [u8]) -> Result<Self> {
check_label(label)?;
Ok(DnsLabelRef{label})
}
/// Access as raw bytes
pub fn as_raw(&self) -> &'a [u8] {
self.label
}
/// Length of label (0 < len < 64)
pub fn len(&self) -> u8 {
self.label.len() as u8
}
/// compares two labels (ASCII case insensitive)
#[inline]
pub fn compare<'b, L: Into<DnsLabelRef<'b>>>(&self, rhs: L) -> Ordering {
compare_ascii_lc(self.as_raw(), rhs.into().as_raw())
}
}
fn compare_ascii_lc(lhs: &[u8], rhs: &[u8]) -> Ordering {
use std::ascii::AsciiExt;
use std::cmp::min;
let l = min(lhs.len(), rhs.len());
for i in 0..l {
let a : u8 = AsciiExt::to_ascii_lowercase(&lhs[i]);
let b : u8 = AsciiExt::to_ascii_lowercase(&rhs[i]);
match Ord::cmp(&a, &b) {
Ordering::Equal => continue,
c => return c,
}
}
Ord::cmp(&lhs.len(), &rhs.len())
}
impl<'a> From<&'a DnsLabel> for DnsLabelRef<'a> {
fn from(label: &'a DnsLabel) -> Self {
label.as_ref()
}
}
impl<'a, 'b> PartialEq<DnsLabelRef<'a>> for DnsLabelRef<'b> {
fn eq(&self, rhs: &DnsLabelRef<'a>) -> bool {
use std::ascii::AsciiExt;
AsciiExt::eq_ignore_ascii_case(self.as_raw(), rhs.as_raw())
}
}
impl<'a> PartialEq<DnsLabel> for DnsLabelRef<'a> {
#[inline]
fn eq(&self, rhs: &DnsLabel) -> bool {
self.eq(&rhs.as_ref())
}
}
impl<'a> Eq for DnsLabelRef<'a>{}
impl<'a, 'b> PartialOrd<DnsLabelRef<'a>> for DnsLabelRef<'b> {
#[inline]
fn partial_cmp(&self, rhs: &DnsLabelRef<'a>) -> Option<Ordering> {
Some(self.compare(*rhs))
}
}
impl<'a> PartialOrd<DnsLabel> for DnsLabelRef<'a> {
#[inline]
fn partial_cmp(&self, rhs: &DnsLabel) -> Option<Ordering> {
Some(self.compare(rhs))
}
}
impl<'a> Ord for DnsLabelRef<'a> {
#[inline]
fn cmp(&self, rhs: &Self) -> Ordering {
self.compare(*rhs)
}
}
impl<'a> fmt::Debug for DnsLabelRef<'a> {
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
// just escape the display version
format!("{}", self).fmt(w)
}
}
impl<'a> fmt::Display for DnsLabelRef<'a> {
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
use std::str;
let mut done = 0;
for pos in 0..self.label.len() {
let c = self.label[pos];
if c <= 0x21 || c >= 0x7e || b'.' == c || b'\\' == c {
// flush
if done < pos {
w.write_str(unsafe {str::from_utf8_unchecked(&self.label[done..pos])})?;
}
match c {
b'.' => w.write_str(r#"\."#)?,
b'\\' => w.write_str(r#"\\"#)?,
_ => write!(w, r"\{:03o}", c)?,
}
done = pos + 1;
}
}
// final flush
if done < self.label.len() {
w.write_str(unsafe {str::from_utf8_unchecked(&self.label[done..])})?;
}
Ok(())
}
}

View File

@ -0,0 +1,509 @@
#![deny(missing_docs)]
//! Various structs to represents DNS names and labels
use bytes::Bytes;
use errors::*;
use ser::DnsPacketData;
use smallvec::SmallVec;
use std::fmt;
use std::io::Cursor;
use std::ops::{Deref, DerefMut};
pub use self::display::*;
pub use self::label::*;
mod display;
mod label;
mod name_mutations;
mod name_parser;
#[derive(Clone,Copy,PartialEq,Eq,PartialOrd,Ord,Hash,Debug)]
enum LabelOffset {
LabelStart(u8),
PacketStart(u8),
}
// the heap meta data is usually at least 2*usize big; assuming 64-bit
// platforms it should be ok to use 16 bytes in the smallvec.
#[derive(Clone,PartialEq,Eq,PartialOrd,Ord,Hash,Debug)]
enum LabelOffsets {
Uncompressed(SmallVec<[u8;16]>),
Compressed(usize, SmallVec<[LabelOffset;8]>),
}
impl LabelOffsets {
fn len(&self) -> u8 {
let l = match *self {
LabelOffsets::Uncompressed(ref offs) => offs.len(),
LabelOffsets::Compressed(_, ref offs) => offs.len(),
};
debug_assert!(l < 128);
l as u8
}
fn label_pos(&self, ndx: u8) -> usize {
debug_assert!(ndx < 127);
match *self {
LabelOffsets::Uncompressed(ref offs) => offs[ndx as usize] as usize,
LabelOffsets::Compressed(start, ref offs) => match offs[ndx as usize] {
LabelOffset::LabelStart(o) => start + (o as usize),
LabelOffset::PacketStart(o) => o as usize,
}
}
}
fn is_compressed(&self) -> bool {
match *self {
LabelOffsets::Uncompressed(_) => false,
LabelOffsets::Compressed(_, _) => true,
}
}
}
/// A DNS name
///
/// Uses the "original" raw representation for storage (i.e. can share
/// memory with a parsed packet)
#[derive(Clone,Hash)]
pub struct DnsName {
// in uncompressed form always includes terminating null octect;
// but even in uncompressed form can include unused bytes at the
// beginning
//
// may be empty for the root name (".", no labels)
data: Bytes,
// either uncompressed or compressed offsets
label_offsets: LabelOffsets,
// length of encoded form
total_len: u8,
}
impl DnsName {
/// Create new name representing the DNS root (".")
pub fn new_root() -> Self {
DnsName{
data: Bytes::new(),
label_offsets: LabelOffsets::Uncompressed(SmallVec::new()),
total_len: 1,
}
}
/// Create new name representing the DNS root (".") and pre-allocate
/// storage
pub fn with_capacity(labels: u8, total_len: u8) -> Self {
DnsName{
data: Bytes::with_capacity(total_len as usize),
label_offsets: LabelOffsets::Uncompressed(SmallVec::with_capacity(labels as usize)),
total_len: 1,
}
}
/// Returns whether name represents the DNS root (".")
pub fn is_root(&self) -> bool {
0 == self.label_count()
}
/// How many labels the name has (without the trailing empty label,
/// at most 127)
pub fn label_count(&self) -> u8 {
self.label_offsets.len()
}
/// Iterator over the labels (in the order they are stored in memory,
/// i.e. top-level name last).
pub fn labels<'a>(&'a self) -> DnsNameIterator<'a> {
DnsNameIterator{
name: &self,
front_label: 0,
back_label: self.label_offsets.len(),
}
}
/// Return label at index `ndx`
///
/// # Panics
///
/// panics if `ndx >= self.label_count()`.
pub fn label_ref<'a>(&'a self, ndx: u8) -> DnsLabelRef<'a> {
let pos = self.label_offsets.label_pos(ndx);
let label_len = self.data[pos];
debug_assert!(label_len < 64);
let end = pos + 1 + label_len as usize;
DnsLabelRef{label: &self.data[pos + 1..end]}
}
/// Return label at index `ndx`
///
/// # Panics
///
/// panics if `ndx >= self.label_count()`.
pub fn label(&self, ndx: u8) -> DnsLabel {
let pos = self.label_offsets.label_pos(ndx);
let label_len = self.data[pos];
debug_assert!(label_len < 64);
let end = pos + 1 + label_len as usize;
DnsLabel{label: self.data.slice(pos + 1, end) }
}
}
impl<'a> IntoIterator for &'a DnsName {
type Item = DnsLabelRef<'a>;
type IntoIter = DnsNameIterator<'a>;
fn into_iter(self) -> Self::IntoIter {
self.labels()
}
}
impl PartialEq<DnsName> for DnsName {
fn eq(&self, rhs: &DnsName) -> bool {
let a_labels = self.labels();
let b_labels = rhs.labels();
if a_labels.len() != b_labels.len() { return false; }
a_labels.zip(b_labels).all(|(a,b)| a == b)
}
}
impl Eq for DnsName{}
impl fmt::Debug for DnsName {
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
DisplayLabels{
labels: self,
options: Default::default(),
}.fmt(w)
}
}
impl fmt::Display for DnsName {
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
DisplayLabels{
labels: self,
options: Default::default(),
}.fmt(w)
}
}
impl DnsPacketData for DnsName {
fn deserialize(data: &mut Cursor<Bytes>) -> Result<Self> {
DnsName::parse_name(data, false)
}
}
/// Similar to `DnsName`, but allows using compressed labels in the
/// serialized form
#[derive(Clone)]
pub struct DnsCompressedName(pub DnsName);
impl Deref for DnsCompressedName {
type Target = DnsName;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for DnsCompressedName {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<'a> IntoIterator for &'a DnsCompressedName {
type Item = DnsLabelRef<'a>;
type IntoIter = DnsNameIterator<'a>;
fn into_iter(self) -> Self::IntoIter {
self.labels()
}
}
impl PartialEq<DnsCompressedName> for DnsCompressedName {
fn eq(&self, rhs: &DnsCompressedName) -> bool {
self.0 == rhs.0
}
}
impl PartialEq<DnsName> for DnsCompressedName {
fn eq(&self, rhs: &DnsName) -> bool {
&self.0 == rhs
}
}
impl PartialEq<DnsCompressedName> for DnsName {
fn eq(&self, rhs: &DnsCompressedName) -> bool {
self == &rhs.0
}
}
impl Eq for DnsCompressedName{}
impl fmt::Debug for DnsCompressedName {
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(w)
}
}
impl fmt::Display for DnsCompressedName {
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(w)
}
}
impl DnsPacketData for DnsCompressedName {
fn deserialize(data: &mut Cursor<Bytes>) -> Result<Self> {
Ok(DnsCompressedName(DnsName::parse_name(data, true)?))
}
}
/// Iterator type for [`DnsName::labels`]
///
/// [`DnsName::labels`]: struct.DnsName.html#method.labels
#[derive(Clone)]
pub struct DnsNameIterator<'a> {
name: &'a DnsName,
front_label: u8,
back_label: u8,
}
impl<'a> Iterator for DnsNameIterator<'a> {
type Item = DnsLabelRef<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.front_label >= self.back_label { return None }
let label = self.name.label_ref(self.front_label);
self.front_label += 1;
Some(label)
}
fn size_hint(&self) -> (usize, Option<usize>) {
let count = self.len();
(count, Some(count))
}
fn count(self) -> usize {
self.len()
}
}
impl<'a> ExactSizeIterator for DnsNameIterator<'a> {
fn len(&self) -> usize {
(self.back_label - self.front_label) as usize
}
}
impl<'a> DoubleEndedIterator for DnsNameIterator<'a> {
fn next_back(&mut self) -> Option<Self::Item> {
if self.front_label >= self.back_label { return None }
self.back_label -= 1;
let label = self.name.label_ref(self.back_label);
Some(label)
}
}
#[cfg(test)]
mod tests {
use ser::packet;
use super::*;
/*
fn deserialize(bytes: &'static [u8]) -> Result<DnsName> {
let result = packet::deserialize::<DnsName>(Bytes::from_static(bytes))?;
{
let check_result = packet::deserialize::<DnsName>(result.clone().encode()).unwrap();
assert_eq!(check_result, result);
}
Ok(result)
}
*/
fn de_uncompressed(bytes: &'static [u8]) -> Result<DnsName> {
let result = packet::deserialize::<DnsName>(Bytes::from_static(bytes))?;
assert_eq!(bytes, result.clone().encode());
Ok(result)
}
fn check_uncompressed_display(bytes: &'static [u8], txt: &str, label_count: u8) {
let name = de_uncompressed(bytes).unwrap();
assert_eq!(
name.labels().count(),
label_count as usize
);
assert_eq!(
format!("{}", name),
txt
);
}
fn check_uncompressed_debug(bytes: &'static [u8], txt: &str) {
let name = de_uncompressed(bytes).unwrap();
assert_eq!(
format!("{:?}", name),
txt
);
}
#[test]
fn parse_and_display_name() {
check_uncompressed_display(
b"\x07example\x03com\x00",
"example.com.",
2,
);
check_uncompressed_display(
b"\x07e!am.l\\\x03com\x00",
"e\\041am\\.l\\\\.com.",
2,
);
check_uncompressed_debug(
b"\x07e!am.l\\\x03com\x00",
r#""e\\041am\\.l\\\\.com.""#,
);
}
#[test]
fn parse_and_reverse_name() {
let name = de_uncompressed(b"\x03www\x07example\x03com\x00").unwrap();
assert_eq!(
format!(
"{}",
DisplayLabels{
labels: name.labels().rev(),
options: DisplayLabelsOptions{
separator: " ",
trailing: false,
},
}
),
"com example www"
);
}
#[test]
fn modifications() {
let mut name = de_uncompressed(b"\x07example\x03com\x00").unwrap();
name.push_front(DnsLabelRef::new(b"www").unwrap()).unwrap();
assert_eq!(
format!("{}", name),
"www.example.com."
);
name.push_back(DnsLabelRef::new(b"org").unwrap()).unwrap();
assert_eq!(
format!("{}", name),
"www.example.com.org."
);
name.pop_front();
assert_eq!(
format!("{}", name),
"example.com.org."
);
name.push_front(DnsLabelRef::new(b"mx").unwrap()).unwrap();
assert_eq!(
format!("{}", name),
"mx.example.com.org."
);
// the "mx" label should fit into the place "www" used before,
// make sure the buffer was reused and the name not moved within
assert_eq!(1, name.label_offsets.label_pos(0));
name.push_back(DnsLabelRef::new(b"com").unwrap()).unwrap();
assert_eq!(
format!("{}", name),
"mx.example.com.org.com."
);
}
fn de_compressed(bytes: &'static [u8], offset: usize) -> Result<DnsCompressedName> {
use bytes::Buf;
let mut c = Cursor::new(Bytes::from_static(bytes));
c.set_position(offset as u64);
let result = DnsPacketData::deserialize(&mut c)?;
if c.remaining() != 0 {
bail!("data remaining: {}", c.remaining())
}
Ok(result)
}
fn check_compressed_display(bytes: &'static [u8], offset: usize, txt: &str, label_count: u8) {
let name = de_compressed(bytes, offset).unwrap();
assert_eq!(
name.labels().count(),
label_count as usize
);
assert_eq!(
format!("{}", name),
txt
);
}
fn check_compressed_debug(bytes: &'static [u8], offset: usize, txt: &str) {
let name = de_compressed(bytes, offset).unwrap();
assert_eq!(
format!("{:?}", name),
txt
);
}
#[test]
fn parse_and_display_compressed_name() {
check_compressed_display(
b"\x03com\x00\x07example\xc0", 5,
"example.com.",
2,
);
check_compressed_display(
b"\x03com\x00\x07e!am.l\\\xc0", 5,
"e\\041am\\.l\\\\.com.",
2,
);
check_compressed_debug(
b"\x03com\x00\x07e!am.l\\\xc0", 5,
r#""e\\041am\\.l\\\\.com.""#,
);
}
#[test]
fn modifications_compressed() {
let mut name = de_compressed(b"\x03com\x00\x07example\xc0", 5).unwrap();
name.push_front(DnsLabelRef::new(b"www").unwrap()).unwrap();
assert_eq!(
format!("{}", name),
"www.example.com."
);
name.push_back(DnsLabelRef::new(b"org").unwrap()).unwrap();
assert_eq!(
format!("{}", name),
"www.example.com.org."
);
name.pop_front();
assert_eq!(
format!("{}", name),
"example.com.org."
);
name.push_front(DnsLabelRef::new(b"mx").unwrap()).unwrap();
assert_eq!(
format!("{}", name),
"mx.example.com.org."
);
// the "mx" label should fit into the place "www" used before,
// make sure the buffer was reused and the name not moved within
assert_eq!(1, name.label_offsets.label_pos(0));
name.push_back(DnsLabelRef::new(b"com").unwrap()).unwrap();
assert_eq!(
format!("{}", name),
"mx.example.com.org.com."
);
}
}

View File

@ -0,0 +1,265 @@
use bytes::{BytesMut,BufMut};
use super::*;
impl DnsName {
/// Remove the front label
///
/// Returns `false` if the name was the root (".")
pub fn try_pop_front(&mut self) -> bool {
match self.label_offsets {
LabelOffsets::Uncompressed(ref mut offs) => {
if offs.is_empty() { return false; }
self.total_len -= self.data[offs[0] as usize] + 1;
offs.remove(0);
},
LabelOffsets::Compressed(ref mut start_pos, ref mut offs) => {
if offs.is_empty() { return false; }
match offs[0] {
LabelOffset::LabelStart(o) => {
let label_space = self.data[*start_pos + o as usize] + 1;
self.total_len -= label_space;
*start_pos += label_space as usize;
},
LabelOffset::PacketStart(o) => {
self.total_len -= self.data[o as usize] + 1;
},
}
offs.remove(0);
},
}
true
}
/// Remove the front label
///
/// # Panics
///
/// Panics if the name was the root (".")
pub fn pop_front(&mut self) {
if !self.try_pop_front() { panic!("Cannot pop label from root name") }
}
/// Insert a new label at the front
///
/// Returns an error if the resulting name would be too long
pub fn push_front<'a, L: Into<DnsLabelRef<'a>>>(&mut self, label: L) -> Result<()> {
let label = label.into();
if label.len() > 254 - self.total_len { bail!("Cannot append label, resulting name too long") }
let (mut data, start) = self.reserve(label.len() as usize + 1, 0);
let new_label_pos = start - (label.len() + 1) as usize;
data[new_label_pos] = label.len();
data[new_label_pos+1..new_label_pos+1+label.len() as usize].copy_from_slice(label.as_raw());
self.data = data.freeze();
self.total_len += label.len() + 1;
match self.label_offsets {
LabelOffsets::Uncompressed(ref mut offs) => {
offs.insert(0, new_label_pos as u8);
},
LabelOffsets::Compressed(_, _) => unreachable!(),
}
Ok(())
}
/// Remove the back label
///
/// Returns `false` if the name was the root (".")
pub fn try_pop_back(&mut self) -> bool {
match self.label_offsets {
LabelOffsets::Uncompressed(ref mut offs) => {
if offs.is_empty() { return false; }
self.total_len -= self.data[offs[offs.len()-1] as usize] + 1;
offs.pop();
},
LabelOffsets::Compressed(ref mut start_pos, ref mut offs) => {
if offs.is_empty() { return false; }
match offs[offs.len()-1] {
LabelOffset::LabelStart(o) => {
self.total_len -= self.data[*start_pos + o as usize] + 1;
},
LabelOffset::PacketStart(o) => {
self.total_len -= self.data[o as usize] + 1;
},
}
offs.pop();
},
}
true
}
/// Remove the back label
///
/// # Panics
///
/// Panics if the name was the root (".")
pub fn pop_back(&mut self) {
if !self.try_pop_back() { panic!("Cannot pop label from root name") }
}
/// Insert a new label at the back
///
/// Returns an error if the resulting name would be too long
pub fn push_back<'a, L: Into<DnsLabelRef<'a>>>(&mut self, label: L) -> Result<()> {
let label = label.into();
if label.len() > 254 - self.total_len { bail!("Cannot append label, resulting name too long") }
let (mut data, start) = self.reserve(0, label.len() as usize + 1);
let new_label_pos = start + self.total_len as usize - 1;
data[new_label_pos] = label.len();
data[new_label_pos+1..new_label_pos+1+label.len() as usize].copy_from_slice(label.as_raw());
data[new_label_pos+1+label.len() as usize] = 0;
self.data = data.freeze();
self.total_len += label.len() + 1;
match self.label_offsets {
LabelOffsets::Uncompressed(ref mut offs) => {
offs.push(new_label_pos as u8);
},
LabelOffsets::Compressed(_, _) => unreachable!(),
}
Ok(())
}
// returns mutable buffer and position of the start of the current
// name (null terminated).
//
// adjusts self.label_offsets accordingly
fn reserve(&mut self, prefix: usize, suffix: usize) -> (BytesMut, usize) {
use std::ptr;
let new_len = self.total_len as usize + prefix + suffix;
assert!(new_len < 256);
self.uncompress(prefix, suffix);
let label_offsets = match self.label_offsets {
LabelOffsets::Uncompressed(ref mut offs) => offs,
LabelOffsets::Compressed(_, _) => unreachable!(),
};
// steal buffer from self (so it has a change to be owned)
let data = self.data.split_off(0).try_mut();
if label_offsets.is_empty() {
// root name
let mut data = data.unwrap_or_else(|_| BytesMut::with_capacity(new_len));
data.put_u8(0);
return (data, 0)
}
let old_start = label_offsets[0] as usize;
// if current "prefix" space (old_start) is bigger than
// requested but fits, just increase the prefix
let (prefix, new_len) = if old_start > prefix && self.total_len as usize + old_start + suffix < 256 {
(old_start, self.total_len as usize + old_start + suffix)
} else {
(prefix, new_len)
};
// check if we need to reallocate
let data = match data {
Ok(data) => {
if data.capacity() < new_len {
// need a new allocation anyway, pretend we couldn't
// own the buffer
Err(data.freeze())
} else {
Ok(data)
}
},
Err(data) => Err(data),
};
match data {
Ok(mut data) => {
if data.len() < new_len {
let add = new_len - data.len();
data.reserve(add);
unsafe { data.set_len(new_len); }
}
if old_start < prefix {
// need more space in front, move back
let p_old = &data[old_start as usize] as *const u8;
let p_new = &mut data[prefix] as *mut u8;
unsafe {
ptr::copy(p_old, p_new, self.total_len as usize);
}
// adjust labels
for o in label_offsets.iter_mut() {
*o = *o + (prefix - old_start) as u8;
}
return (data, prefix);
} else if old_start > prefix {
// too much space in front, (suffix didn't fit into
// length restriction, see check above), move to
// front
let p_old = &data[old_start as usize] as *const u8;
let p_new = &mut data[prefix] as *mut u8;
unsafe {
ptr::copy(p_old, p_new, self.total_len as usize);
}
// adjust labels
for o in label_offsets.iter_mut() {
*o = *o - (old_start - prefix) as u8;
}
return (data, prefix);
} else {
return (data, old_start);
}
},
Err(data) => {
let mut new_data = BytesMut::with_capacity(new_len);
unsafe { new_data.set_len(new_len); }
// copy old data
new_data[prefix..prefix + self.total_len as usize].copy_from_slice(&data[old_start..old_start+self.total_len as usize]);
// adjust labels
for o in label_offsets.iter_mut() {
*o = (*o - old_start as u8) + prefix as u8;
}
return (new_data, prefix);
},
}
}
/// return encoded (uncompressed) representation
///
/// might uncompress the inner representation.
pub fn encode(&mut self) -> Bytes {
self.uncompress(0, 0);
if self.is_root() {
return Bytes::from_static(b"\x00");
}
// at least one label, find first one
let from = self.label_offsets.label_pos(0);
self.data.slice(from, from + self.total_len as usize)
}
fn uncompress(&mut self, prefix_capacity: usize, suffix_capacity: usize) {
if self.label_offsets.is_compressed() {
let name = {
let labels = self.labels();
let label_count = labels.len();
let new_len = self.total_len as usize + prefix_capacity + suffix_capacity;
assert!(new_len < 256);
let mut data = BytesMut::with_capacity(new_len);
let mut offsets = SmallVec::<[u8;16]>::with_capacity(label_count);
unsafe { data.set_len(prefix_capacity) }
let mut pos = prefix_capacity as u8;
for label in labels {
offsets.push(pos);
pos += label.len() + 1;
data.put_u8(label.len());
data.put_slice(label.as_raw());
}
data.put_u8(0);
DnsName{
data: data.freeze(),
label_offsets: LabelOffsets::Uncompressed(offsets),
total_len: self.total_len,
}
};
*self = name
}
}
}

View File

@ -0,0 +1,81 @@
use bytes::Buf;
use super::*;
impl DnsName {
/// `data`: bytes of packet from beginning until at least the end of the name
/// `start_pos`: position of first byte of the name
/// `uncmpr_offsets`: offsets of uncompressed labels so far
/// `label_len`: first compressed label length (`0xc0 | offset`)
/// `total_len`: length of (uncompressed) label encoding so far
fn parse_name_compressed_cont(data: Bytes, start_pos: usize, uncmpr_offsets: SmallVec<[u8;16]>, mut total_len: usize, mut label_len: u8) -> Result<Self> {
let mut label_offsets = uncmpr_offsets.into_iter()
.map(LabelOffset::LabelStart)
.collect::<SmallVec<_>>();
let mut pos = start_pos + total_len;
'next_compressed: loop {
{
let new_pos = (label_len & 0x3f) as usize;
if new_pos >= pos { bail!("Compressed label offset to big") }
pos = new_pos;
}
loop {
label_len = data[pos];
if 0 == label_len {
return Ok(DnsName{
data: data,
label_offsets: LabelOffsets::Compressed(start_pos, label_offsets),
total_len: total_len as u8 + 1,
})
}
if label_len & 0xc == 0xc { continue 'next_compressed; }
if label_len > 63 { bail!("Invalid label length {}", label_len) }
total_len += 1 + label_len as usize;
// max len 255, but there also needs to be an empty label at the end
if total_len > 254 { bail!("DNS name too long") }
label_offsets.push(LabelOffset::PacketStart(pos as u8));
pos += 1 + label_len as usize;
}
}
}
pub(super) fn parse_name(data: &mut Cursor<Bytes>, accept_compressed: bool) -> Result<Self> {
check_enough_data!(data, 1, "DnsName");
let start_pos = data.position() as usize;
let mut total_len : usize = 0;
let mut label_offsets = SmallVec::new();
loop {
check_enough_data!(data, 1, "DnsName label len");
let label_len = data.get_u8() as usize;
if 0 == label_len {
let end_pos = data.position() as usize;
return Ok(DnsName{
data: data.get_ref().slice(start_pos, end_pos),
label_offsets: LabelOffsets::Uncompressed(label_offsets),
total_len: total_len as u8 + 1,
})
}
if label_len & 0xc0 == 0xc0 {
// compressed label
if !accept_compressed { bail!("Invalid label compression {}", label_len) }
let end_pos = data.position() as usize;
let data = data.get_ref().slice(0, end_pos);
return Self::parse_name_compressed_cont(data, start_pos, label_offsets, total_len, label_len as u8);
}
label_offsets.push(total_len as u8);
if label_len > 63 { bail!("Invalid label length {}", label_len) }
total_len += 1 + label_len;
// max len 255, but there also needs to be an empty label at the end
if total_len > 254 { bail!{"DNS name too long"} }
check_enough_data!(data, (label_len), "DnsName label");
data.advance(label_len);
}
}
}

View File

@ -0,0 +1,8 @@
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct Type(pub u16);
impl ::ser::DnsPacketData for Type {
fn deserialize(data: &mut ::std::io::Cursor<::bytes::Bytes>) -> ::errors::Result<Self> {
Ok(Type(::ser::DnsPacketData::deserialize(data)?))
}
}

View File

@ -0,0 +1,21 @@
use bytes::{Bytes, Buf};
use std::io::Cursor;
use ser::DnsPacketData;
use errors::*;
#[derive(Clone, Debug)]
pub struct ShortText(Bytes);
// RFC 1035 names this `<character-string>`
impl DnsPacketData for ShortText {
fn deserialize(data: &mut Cursor<Bytes>) -> Result<Self> {
check_enough_data!(data, 1, "ShortText length");
let label_len = data.get_u8() as usize;
check_enough_data!(data, label_len, "ShortText content");
let pos = data.position() as usize;
let text = data.get_ref().slice(pos, pos + label_len);
data.advance(label_len);
Ok(ShortText(text))
}
}

View File

@ -0,0 +1,42 @@
use bytes::{Buf, Bytes};
use failure;
use std::fmt;
use std::io;
use std::result;
pub type Result<T> = result::Result<T, failure::Error>;
#[derive(Debug)]
pub struct NotEnoughData {
position: u64,
data: Bytes,
}
impl NotEnoughData {
pub fn check(data: &mut io::Cursor<Bytes>, need: usize) -> Result<()> {
if data.remaining() < need {
bail!(NotEnoughData{
position: data.position(),
data: data.get_ref().clone(),
})
}
Ok(())
}
}
impl fmt::Display for NotEnoughData {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "not enough data at position {} in {:?}", self.position, self.data)
}
}
impl failure::Fail for NotEnoughData {}
macro_rules! check_enough_data {
($data:ident, $n:expr, $context:expr) => {
{
use $crate::failure::ResultExt;
$crate::errors::NotEnoughData::check($data, $n).context($context)?;
}
};
}

View File

@ -0,0 +1,22 @@
pub extern crate byteorder;
pub extern crate bytes;
#[macro_use]
pub extern crate failure;
#[macro_use]
extern crate dnsbox_derive;
extern crate smallvec;
#[macro_use]
extern crate lazy_static;
#[macro_use]
pub mod errors;
pub mod common_types;
pub mod records;
pub mod ser;
// dnsbox_derive will use ::dnsbox_base::...; make this work in this crate too.
mod dnsbox_base {
#[allow(unused_imports)]
pub use super::*;
}

View File

@ -0,0 +1,8 @@
mod structs;
pub mod registry;
pub mod types;
pub use self::structs::*;
#[cfg(test)]
mod tests;

View File

@ -0,0 +1,149 @@
use std::collections::HashMap;
use records::structs;
use records::types;
use common_types::Type;
use ser::RRData;
lazy_static!{
static ref REGISTRY: Registry = Registry::init();
}
fn registry() -> &'static Registry {
&*REGISTRY
}
pub fn known_name_to_type(name: &str) -> Option<Type> {
let registry = registry();
let &t = registry.names_to_type.get(name)?;
registry.type_parser.get(&t)?;
Some(t)
}
pub fn name_to_type(name: &str) -> Option<Type> {
let registry = registry();
let &t = registry.names_to_type.get(name)?;
Some(t)
}
struct Registry {
names_to_type: HashMap<String, Type>,
type_parser: HashMap<Type, ()>,
// make sure registrations are in order
prev_type: Option<Type>,
}
impl Registry {
fn init() -> Self {
let mut r = Registry {
names_to_type: HashMap::new(),
type_parser: HashMap::new(),
prev_type: None,
};
r.register_known::<structs::A>();
r.register_known::<structs::NS>();
r.register_known::<structs::MD>();
r.register_known::<structs::MF>();
r.register_known::<structs::CNAME>();
r.register_known::<structs::SOA>();
r.register_known::<structs::MB>();
r.register_known::<structs::MG>();
r.register_known::<structs::MR>();
r.register_known::<structs::NULL>();
r.register_known::<structs::WKS>();
r.register_known::<structs::PTR>();
r.register_known::<structs::HINFO>();
r.register_known::<structs::MINFO>();
r.register_known::<structs::MX>();
r.register_known::<structs::TXT>();
r.register_known::<structs::RP>();
r.register_known::<structs::AFSDB>();
r.register_unknown("X25" , types::X25);
r.register_unknown("ISDN" , types::ISDN);
r.register_unknown("RT" , types::RT);
r.register_unknown("NSAP" , types::NSAP);
r.register_unknown("NSAP-PTR" , types::NSAP_PTR);
r.register_unknown("SIG" , types::SIG);
r.register_known::<structs::KEY>();
r.register_unknown("PX" , types::PX);
r.register_unknown("GPOS" , types::GPOS);
r.register_known::<structs::AAAA>();
r.register_known::<structs::LOC>();
r.register_unknown("NXT" , types::NXT);
r.register_unknown("EID" , types::EID);
r.register_unknown("NIMLOC" , types::NIMLOC);
r.register_known::<structs::SRV>();
r.register_unknown("ATMA" , types::ATMA);
r.register_known::<structs::NAPTR>();
r.register_known::<structs::KX>();
r.register_known::<structs::CERT>();
r.register_unknown("A6" , types::A6);
r.register_known::<structs::DNAME>();
r.register_unknown("SINK" , types::SINK);
r.register_unknown("OPT" , types::OPT);
r.register_unknown("APL" , types::APL);
r.register_known::<structs::DS>();
r.register_known::<structs::SSHFP>();
r.register_unknown("IPSECKEY" , types::IPSECKEY);
r.register_known::<structs::RRSIG>();
r.register_known::<structs::NSEC>();
r.register_known::<structs::DNSKEY>();
r.register_unknown("DHCID" , types::DHCID);
r.register_known::<structs::NSEC3>();
r.register_known::<structs::NSEC3PARAM>();
r.register_unknown("TLSA" , types::TLSA);
r.register_unknown("SMIMEA" , types::SMIMEA);
r.register_unknown("HIP" , types::HIP);
r.register_unknown("NINFO" , types::NINFO);
r.register_unknown("RKEY" , types::RKEY);
r.register_unknown("TALINK" , types::TALINK);
r.register_unknown("CDS" , types::CDS);
r.register_unknown("CDNSKEY" , types::CDNSKEY);
r.register_unknown("OPENPGPKEY", types::OPENPGPKEY);
r.register_unknown("CSYNC" , types::CSYNC);
r.register_known::<structs::SPF>();
r.register_unknown("UINFO" , types::UINFO);
r.register_unknown("UID" , types::UID);
r.register_unknown("GID" , types::GID);
r.register_unknown("UNSPEC" , types::UNSPEC);
r.register_unknown("NID" , types::NID);
r.register_unknown("L32" , types::L32);
r.register_unknown("L64" , types::L64);
r.register_unknown("LP" , types::LP);
r.register_unknown("EUI48" , types::EUI48);
r.register_unknown("EUI64" , types::EUI64);
r.register_unknown("TKEY" , types::TKEY);
r.register_unknown("TSIG" , types::TSIG);
r.register_unknown("IXFR" , types::IXFR);
r.register_unknown("AXFR" , types::AXFR);
r.register_unknown("MAILB" , types::MAILB);
r.register_unknown("MAILA" , types::MAILA);
r.register_unknown("ANY" , types::ANY);
r.register_unknown("URI" , types::URI);
r.register_unknown("CAA" , types::CAA);
r.register_unknown("AVC" , types::AVC);
r.register_unknown("DOA" , types::DOA);
r.register_unknown("DLV" , types::DLV);
r.register_unknown("ADDR" , types::ADDR);
r.register_unknown("ALIAS" , types::ALIAS);
r
}
fn register_unknown(&mut self, name: &'static str, rrtype: Type) {
assert!(self.prev_type < Some(rrtype), "registration not in order");
self.prev_type = Some(rrtype);
assert!(self.names_to_type.insert(name.into(), rrtype).is_none());
}
fn register_known<T: RRData>(&mut self) {
let n = T::rr_type_name();
let t = T::rr_type();
assert!(self.prev_type < Some(t), "registration not in order");
self.prev_type = Some(t);
assert!(self.names_to_type.insert(n.into_owned(), t).is_none());
self.type_parser.insert(t, ());
}
}

View File

@ -0,0 +1,402 @@
use bytes::Bytes;
use ser::DnsPacketData;
use common_types::*;
use std::net::{Ipv4Addr, Ipv6Addr};
// unless otherwise documented, class should probably be IN (0x0001)
// class IN
#[derive(Clone, Debug, DnsPacketData, RRData)]
pub struct A {
addr: Ipv4Addr,
}
// class independent
#[derive(Clone, Debug, DnsPacketData, RRData)]
pub struct NS {
nsdname: DnsCompressedName,
}
// class independent
#[derive(Clone, Debug, DnsPacketData, RRData)]
pub struct MD {
madname: DnsCompressedName,
}
// class independent
#[derive(Clone, Debug, DnsPacketData, RRData)]
pub struct MF {
madname: DnsCompressedName,
}
// class independent
#[derive(Clone, Debug, DnsPacketData, RRData)]
pub struct CNAME {
cname: DnsCompressedName,
}
// class independent
#[derive(Clone, Debug, DnsPacketData, RRData)]
pub struct SOA {
mname: DnsCompressedName,
rname: DnsCompressedName,
serial: u32,
refresh: u32,
retry: u32,
expire: u32,
minimum: u32,
}
// class independent
#[derive(Clone, Debug, DnsPacketData, RRData)]
pub struct MB {
madname: DnsCompressedName,
}
// class independent
#[derive(Clone, Debug, DnsPacketData, RRData)]
pub struct MG {
mgmname: DnsCompressedName,
}
// class independent
#[derive(Clone, Debug, DnsPacketData, RRData)]
pub struct MR {
newname: DnsCompressedName,
}
// class independent
#[derive(Clone, Debug, DnsPacketData, RRData)]
pub struct NULL {
anything: RemainingText,
}
// class IN
#[derive(Clone, Debug, DnsPacketData, RRData)]
pub struct WKS {
address: Ipv4Addr,
protocol: u8,
bitmap: RemainingText,
}
// class independent
#[derive(Clone, Debug, DnsPacketData, RRData)]
pub struct PTR {
ptrdname: DnsCompressedName,
}
// class independent
#[derive(Clone, Debug, DnsPacketData, RRData)]
pub struct HINFO {
cpu: ShortText,
os: ShortText,
}
// class independent
#[derive(Clone, Debug, DnsPacketData, RRData)]
pub struct MINFO {
rmailbx: DnsCompressedName,
emailbx: DnsCompressedName,
}
// class independent
#[derive(Clone, Debug, DnsPacketData, RRData)]
pub struct MX {
preference: u16,
mxname: DnsCompressedName,
}
// class independent
#[derive(Clone, Debug, DnsPacketData, RRData)]
pub struct TXT {
text: LongText,
}
// end of RFC 1035: no DnsCompressedName below!
// class independent
#[derive(Clone, Debug, DnsPacketData, RRData)]
pub struct RP {
mbox: DnsName,
txt: DnsName,
}
// class independent
#[derive(Clone, Debug, DnsPacketData, RRData)]
pub struct AFSDB {
subtype: u16,
hostname: DnsName,
}
// class independent
// pub struct X25;
// class independent
// pub struct ISDN;
// class independent
// pub struct RT;
// pub struct NSAP;
// pub struct NSAP_PTR;
// pub struct SIG;
// class independent
#[derive(Clone, Debug, DnsPacketData, RRData)]
pub struct KEY {
flags: u16,
protocol: u8,
algorithm: u8,
certificate: RemainingText,
}
// pub struct PX;
// pub struct GPOS;
// class IN
#[derive(Clone, Debug, DnsPacketData, RRData)]
pub struct AAAA {
addr: Ipv6Addr,
}
// class independent
#[derive(Clone, Debug, RRData)]
pub enum LOC {
Version0(LOC0),
UnknownVersion{
version: u8,
data: RemainingText
},
}
impl DnsPacketData for LOC {
fn deserialize(data: &mut ::std::io::Cursor<Bytes>) -> ::errors::Result<Self> {
let version: u8 = DnsPacketData::deserialize(data)?;
if 0 == version {
Ok(LOC::Version0(DnsPacketData::deserialize(data)?))
} else {
Ok(LOC::UnknownVersion{
version: version,
data: DnsPacketData::deserialize(data)?,
})
}
}
}
#[derive(Clone, Debug, DnsPacketData)]
pub struct LOC0 {
size: u8,
horizontal_precision: u8,
vertical_precision: u8,
latitude: u32,
longitude: u32,
altitude: u32,
}
// pub struct NXT;
// pub struct EID;
// pub struct NIMLOC;
// class IN
#[derive(Clone, Debug, DnsPacketData, RRData)]
pub struct SRV {
preference: u16,
weight: u16,
port: u16,
target: DnsName,
}
// pub struct ATMA;
// class independent
#[derive(Clone, Debug, DnsPacketData, RRData)]
pub struct NAPTR {
order: u16,
preference: u16,
flags: ShortText,
service: ShortText,
regexp: ShortText,
replacement: DnsName,
}
// class IN
#[derive(Clone, Debug, DnsPacketData, RRData)]
pub struct KX {
preference: u16,
exchanger: DnsName,
}
// class ??
#[derive(Clone, Debug, DnsPacketData, RRData)]
pub struct CERT {
cert_type: u16,
key_tag: u16,
algorithm: u8,
certificate: RemainingText,
}
// pub struct A6;
// class independent
#[derive(Clone, Debug, DnsPacketData, RRData)]
pub struct DNAME {
target: DnsName,
}
// pub struct SINK;
// OPT should be decoded at "transport level", abuses ttl and class
// fields too.
// pub struct OPT;
// pub struct APL;
#[derive(Clone, Debug, DnsPacketData, RRData)]
pub struct DS {
key_tag: u16,
algorithm: u8,
digest_type: u8,
digest: HexRemainingBlob,
}
#[derive(Clone, Debug, DnsPacketData, RRData)]
pub struct SSHFP {
algorithm: u8,
fingerprint_type: u8,
fingerprint: HexRemainingBlob,
}
// #[derive(Clone, Debug, DnsPacketData, RRData)]
// pub struct IPSECKEY;
// class independent
#[derive(Clone, Debug, DnsPacketData, RRData)]
pub struct RRSIG {
rr_type: Type,
algorithm: u8,
labels: u8,
original_ttl: u32,
signature_expiration: u32,
signature_inception: u32,
key_tag: u16,
signers_name: DnsName,
signature: RemainingText,
}
#[derive(Clone, Debug, DnsPacketData)]
pub struct NsecTypeBitmap {
types: RemainingText, // TODO: actually parse it
}
// class independent
#[derive(Clone, Debug, DnsPacketData, RRData)]
pub struct NSEC {
next: DnsName,
bitmap: NsecTypeBitmap,
}
// class independent
#[derive(Clone, Debug, DnsPacketData, RRData)]
pub struct DNSKEY {
flags: u16,
protocol: u8,
algorithm: u8,
public_key: RemainingText,
}
// #[derive(Clone, Debug, DnsPacketData, RRData)]
// pub struct DHCID;
// class independent
#[derive(Clone, Debug, DnsPacketData, RRData)]
pub struct NSEC3 {
hash_algorithm: u8,
flags: u8,
iterations: u16,
salt: HexShortBlob,
next_hashed: Base32ShortBlob,
bitmap: NsecTypeBitmap,
}
// class independent
#[derive(Clone, Debug, DnsPacketData, RRData)]
pub struct NSEC3PARAM {
hash_algorithm: u8,
flags: u8,
iterations: u16,
salt: HexShortBlob,
}
// #[derive(Clone, Debug, DnsPacketData, RRData)]
// pub struct TLSA;
// #[derive(Clone, Debug, DnsPacketData, RRData)]
// pub struct SMIMEA;
// pub struct HIP;
// pub struct NINFO;
// #[derive(Clone, Debug, DnsPacketData, RRData)]
// pub struct RKEY;
// pub struct TALINK;
// #[derive(Clone, Debug, DnsPacketData, RRData)]
// pub struct CDS;
// #[derive(Clone, Debug, DnsPacketData, RRData)]
// pub struct CDNSKEY;
// #[derive(Clone, Debug, DnsPacketData, RRData)]
// pub struct OPENPGPKEY;
// pub struct CSYNC;
#[derive(Clone, Debug, DnsPacketData, RRData)]
pub struct SPF {
text: LongText,
}
// pub struct UINFO;
// pub struct UID;
// pub struct GID;
// pub struct UNSPEC;
// pub struct NID;
// pub struct L32;
// pub struct L64;
// pub struct LP;
// #[derive(Clone, Debug, DnsPacketData, RRData)]
// pub struct EUI48;
// #[derive(Clone, Debug, DnsPacketData, RRData)]
// pub struct EUI64;
// #[derive(Clone, Debug, DnsPacketData, RRData)]
// pub struct TKEY;
// #[derive(Clone, Debug, DnsPacketData, RRData)]
// pub struct TSIG;
// pub struct IXFR; // qtype only?
// pub struct AXFR; // qtype only?
// pub struct MAILB; // qtype only?
// pub struct MAILA; // qtype only?
// pub struct ANY; // qtype only?
// #[derive(Clone, Debug, DnsPacketData, RRData)]
// pub struct URI;
// #[derive(Clone, Debug, DnsPacketData, RRData)]
// pub struct CAA;
// pub struct AVC;
// pub struct DOA;
// #[derive(Clone, Debug, DnsPacketData, RRData)]
// pub struct DLV;
// pub struct ADDR;
// #[derive(Clone, Debug, DnsPacketData, RRData)]
// pub struct ALIAS;

View File

@ -0,0 +1,16 @@
use bytes::Bytes;
use records::structs;
use ser::packet::deserialize;
use ser::RRData;
fn check<T>(txt: &str, data: &'static [u8])
where
T: RRData
{
let d: T = deserialize(Bytes::from_static(data)).expect("couldn't parse record");
}
#[test]
fn test_a() {
check::<structs::A>("127.0.0.1", b"\x7f\x00\x00\x01");
}

View File

@ -0,0 +1,294 @@
use common_types::Type;
pub const A : Type = Type(KnownType::A as u16);
pub const NS : Type = Type(KnownType::NS as u16);
pub const MD : Type = Type(KnownType::MD as u16);
pub const MF : Type = Type(KnownType::MF as u16);
pub const CNAME : Type = Type(KnownType::CNAME as u16);
pub const SOA : Type = Type(KnownType::SOA as u16);
pub const MB : Type = Type(KnownType::MB as u16);
pub const MG : Type = Type(KnownType::MG as u16);
pub const MR : Type = Type(KnownType::MR as u16);
pub const NULL : Type = Type(KnownType::NULL as u16);
pub const WKS : Type = Type(KnownType::WKS as u16);
pub const PTR : Type = Type(KnownType::PTR as u16);
pub const HINFO : Type = Type(KnownType::HINFO as u16);
pub const MINFO : Type = Type(KnownType::MINFO as u16);
pub const MX : Type = Type(KnownType::MX as u16);
pub const TXT : Type = Type(KnownType::TXT as u16);
pub const RP : Type = Type(KnownType::RP as u16);
pub const AFSDB : Type = Type(KnownType::AFSDB as u16);
pub const X25 : Type = Type(KnownType::X25 as u16);
pub const ISDN : Type = Type(KnownType::ISDN as u16);
pub const RT : Type = Type(KnownType::RT as u16);
pub const NSAP : Type = Type(KnownType::NSAP as u16);
pub const NSAP_PTR : Type = Type(KnownType::NSAP_PTR as u16);
pub const SIG : Type = Type(KnownType::SIG as u16);
pub const KEY : Type = Type(KnownType::KEY as u16);
pub const PX : Type = Type(KnownType::PX as u16);
pub const GPOS : Type = Type(KnownType::GPOS as u16);
pub const AAAA : Type = Type(KnownType::AAAA as u16);
pub const LOC : Type = Type(KnownType::LOC as u16);
pub const NXT : Type = Type(KnownType::NXT as u16);
pub const EID : Type = Type(KnownType::EID as u16);
pub const NIMLOC : Type = Type(KnownType::NIMLOC as u16);
pub const SRV : Type = Type(KnownType::SRV as u16);
pub const ATMA : Type = Type(KnownType::ATMA as u16);
pub const NAPTR : Type = Type(KnownType::NAPTR as u16);
pub const KX : Type = Type(KnownType::KX as u16);
pub const CERT : Type = Type(KnownType::CERT as u16);
pub const A6 : Type = Type(KnownType::A6 as u16);
pub const DNAME : Type = Type(KnownType::DNAME as u16);
pub const SINK : Type = Type(KnownType::SINK as u16);
pub const OPT : Type = Type(KnownType::OPT as u16);
pub const APL : Type = Type(KnownType::APL as u16);
pub const DS : Type = Type(KnownType::DS as u16);
pub const SSHFP : Type = Type(KnownType::SSHFP as u16);
pub const IPSECKEY : Type = Type(KnownType::IPSECKEY as u16);
pub const RRSIG : Type = Type(KnownType::RRSIG as u16);
pub const NSEC : Type = Type(KnownType::NSEC as u16);
pub const DNSKEY : Type = Type(KnownType::DNSKEY as u16);
pub const DHCID : Type = Type(KnownType::DHCID as u16);
pub const NSEC3 : Type = Type(KnownType::NSEC3 as u16);
pub const NSEC3PARAM : Type = Type(KnownType::NSEC3PARAM as u16);
pub const TLSA : Type = Type(KnownType::TLSA as u16);
pub const SMIMEA : Type = Type(KnownType::SMIMEA as u16);
pub const HIP : Type = Type(KnownType::HIP as u16);
pub const NINFO : Type = Type(KnownType::NINFO as u16);
pub const RKEY : Type = Type(KnownType::RKEY as u16);
pub const TALINK : Type = Type(KnownType::TALINK as u16);
pub const CDS : Type = Type(KnownType::CDS as u16);
pub const CDNSKEY : Type = Type(KnownType::CDNSKEY as u16);
pub const OPENPGPKEY : Type = Type(KnownType::OPENPGPKEY as u16);
pub const CSYNC : Type = Type(KnownType::CSYNC as u16);
pub const SPF : Type = Type(KnownType::SPF as u16);
pub const UINFO : Type = Type(KnownType::UINFO as u16);
pub const UID : Type = Type(KnownType::UID as u16);
pub const GID : Type = Type(KnownType::GID as u16);
pub const UNSPEC : Type = Type(KnownType::UNSPEC as u16);
pub const NID : Type = Type(KnownType::NID as u16);
pub const L32 : Type = Type(KnownType::L32 as u16);
pub const L64 : Type = Type(KnownType::L64 as u16);
pub const LP : Type = Type(KnownType::LP as u16);
pub const EUI48 : Type = Type(KnownType::EUI48 as u16);
pub const EUI64 : Type = Type(KnownType::EUI64 as u16);
pub const TKEY : Type = Type(KnownType::TKEY as u16);
pub const TSIG : Type = Type(KnownType::TSIG as u16);
pub const IXFR : Type = Type(KnownType::IXFR as u16);
pub const AXFR : Type = Type(KnownType::AXFR as u16);
pub const MAILB : Type = Type(KnownType::MAILB as u16);
pub const MAILA : Type = Type(KnownType::MAILA as u16);
pub const ANY : Type = Type(KnownType::ANY as u16);
pub const URI : Type = Type(KnownType::URI as u16);
pub const CAA : Type = Type(KnownType::CAA as u16);
pub const AVC : Type = Type(KnownType::AVC as u16);
pub const DOA : Type = Type(KnownType::DOA as u16);
pub const DLV : Type = Type(KnownType::DLV as u16);
pub const ADDR : Type = Type(KnownType::ADDR as u16);
pub const ALIAS : Type = Type(KnownType::ALIAS as u16);
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[repr(u16)]
#[allow(non_camel_case_types)]
pub enum KnownType {
// try to list "original" rfc
A = 0x0001, // RFC 1035
NS = 0x0002, // RFC 1035
MD = 0x0003, // RFC 1035
MF = 0x0004, // RFC 1035
CNAME = 0x0005, // RFC 1035
SOA = 0x0006, // RFC 1035
MB = 0x0007, // RFC 1035
MG = 0x0008, // RFC 1035
MR = 0x0009, // RFC 1035
NULL = 0x000a, // RFC 1035
WKS = 0x000b, // RFC 1035
PTR = 0x000c, // RFC 1035
HINFO = 0x000d, // RFC 1035
MINFO = 0x000e, // RFC 1035
MX = 0x000f, // RFC 1035
TXT = 0x0010, // RFC 1035
RP = 0x0011, // RFC 1183
AFSDB = 0x0012, // RFC 1183
X25 = 0x0013, // RFC 1183
ISDN = 0x0014, // RFC 1183
RT = 0x0015, // RFC 1183
NSAP = 0x0016, // RFC 1706
NSAP_PTR = 0x0017, // RFC 1348
SIG = 0x0018, // RFC 2535
KEY = 0x0019, // RFC 2535
PX = 0x001a, // RFC 2163
GPOS = 0x001b, // RFC 1712
AAAA = 0x001c, // RFC 3596
LOC = 0x001d, // RFC 1876
NXT = 0x001e, // RFC 2535
EID = 0x001f, // Michael Patton: http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt
NIMLOC = 0x0020, // Michael Patton: http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt
SRV = 0x0021, // RFC 2782
ATMA = 0x0022, // http://www.broadband-forum.org/ftp/pub/approved-specs/af-dans-0152.000.pdf
NAPTR = 0x0023, // RFC 2168
KX = 0x0024, // RFC 2230
CERT = 0x0025, // RFC 4398
A6 = 0x0026, // RFC 2874
DNAME = 0x0027, // RFC 6672
SINK = 0x0028, // Donald E Eastlake: http://tools.ietf.org/html/draft-eastlake-kitchen-sink
OPT = 0x0029, // RFC 6891
APL = 0x002a, // RFC 3123
DS = 0x002b, // RFC 3658
SSHFP = 0x002c, // RFC 4255
IPSECKEY = 0x002d, // RFC 4025
RRSIG = 0x002e, // RFC 4034
NSEC = 0x002f, // RFC 4034
DNSKEY = 0x0030, // RFC 4034
DHCID = 0x0031, // RFC 4701
NSEC3 = 0x0032, // RFC 5155
NSEC3PARAM = 0x0033, // RFC 5155
TLSA = 0x0034, // RFC 6698
SMIMEA = 0x0035, // RFC 8162
HIP = 0x0037, // RFC 8005
NINFO = 0x0038, // Jim Reid
RKEY = 0x0039, // Jim Reid
TALINK = 0x003a, // Wouter Wijngaards
CDS = 0x003b, // RFC 7344
CDNSKEY = 0x003c, // RFC 7344
OPENPGPKEY = 0x003d, // RFC 7929
CSYNC = 0x003e, // RFC 7477
SPF = 0x0063, // RFC 7208
UINFO = 0x0064, // IANA-Reserved
UID = 0x0065, // IANA-Reserved
GID = 0x0066, // IANA-Reserved
UNSPEC = 0x0067, // IANA-Reserved
NID = 0x0068, // RFC 6742
L32 = 0x0069, // RFC 6742
L64 = 0x006a, // RFC 6742
LP = 0x006b, // RFC 6742
EUI48 = 0x006c, // RFC 7043
EUI64 = 0x006d, // RFC 7043
TKEY = 0x00f9, // RFC 2930
TSIG = 0x00fa, // RFC 2845
IXFR = 0x00fb, // RFC 1995
AXFR = 0x00fc, // RFC 1035
MAILB = 0x00fd, // RFC 1035
MAILA = 0x00fe, // RFC 1035
ANY = 0x00ff, // RFC 1035, "*"
URI = 0x0100, // RFC 7553
CAA = 0x0101, // RFC 6844
AVC = 0x0102, // Wolfgang Riedel
DOA = 0x0103, // http://www.iana.org/go/draft-durand-doa-over-dns
DLV = 0x8001, // RFC 4431
ADDR = 0xff78, // powerdns?
ALIAS = 0xff79, // powerdns?
}
#[cfg(test)]
macro_rules! check_type {
($t:ident, $dec:expr) => {
{
use records::registry;
// compare decimal value
assert_eq!($t, Type($dec), "wrong decimal value for {}", stringify!($t));
// make sure it's registered
assert_eq!(registry::name_to_type(stringify!($t)), Some($t), "{} not registered", stringify!($t));
}
};
($t:ident, $dec:expr, $name:expr) => {
{
use records::registry;
// compare decimal value
assert_eq!($t, Type($dec), "wrong decimal value for {}", stringify!($t));
// make sure it's registered
assert_eq!(registry::name_to_type($name), Some($t), "{} not registered as {:?}", stringify!($t), $name);
}
};
}
#[cfg(test)]
#[test]
fn check_types() {
check_type!(A , 1);
check_type!(NS , 2);
check_type!(MD , 3);
check_type!(MF , 4);
check_type!(CNAME , 5);
check_type!(SOA , 6);
check_type!(MB , 7);
check_type!(MG , 8);
check_type!(MR , 9);
check_type!(NULL , 10);
check_type!(WKS , 11);
check_type!(PTR , 12);
check_type!(HINFO , 13);
check_type!(MINFO , 14);
check_type!(MX , 15);
check_type!(TXT , 16);
check_type!(RP , 17);
check_type!(AFSDB , 18);
check_type!(X25 , 19);
check_type!(ISDN , 20);
check_type!(RT , 21);
check_type!(NSAP , 22);
check_type!(NSAP_PTR , 23, "NSAP-PTR");
check_type!(SIG , 24);
check_type!(KEY , 25);
check_type!(PX , 26);
check_type!(GPOS , 27);
check_type!(AAAA , 28);
check_type!(LOC , 29);
check_type!(NXT , 30);
check_type!(EID , 31);
check_type!(NIMLOC , 32);
check_type!(SRV , 33);
check_type!(ATMA , 34);
check_type!(NAPTR , 35);
check_type!(KX , 36);
check_type!(CERT , 37);
check_type!(A6 , 38);
check_type!(DNAME , 39);
check_type!(SINK , 40);
check_type!(OPT , 41);
check_type!(APL , 42);
check_type!(DS , 43);
check_type!(SSHFP , 44);
check_type!(IPSECKEY , 45);
check_type!(RRSIG , 46);
check_type!(NSEC , 47);
check_type!(DNSKEY , 48);
check_type!(DHCID , 49);
check_type!(NSEC3 , 50);
check_type!(NSEC3PARAM, 51);
check_type!(TLSA , 52);
check_type!(SMIMEA , 53);
check_type!(HIP , 55);
check_type!(NINFO , 56);
check_type!(RKEY , 57);
check_type!(TALINK , 58);
check_type!(CDS , 59);
check_type!(CDNSKEY , 60);
check_type!(OPENPGPKEY, 61);
check_type!(CSYNC , 62);
check_type!(SPF , 99);
check_type!(UINFO , 100);
check_type!(UID , 101);
check_type!(GID , 102);
check_type!(UNSPEC , 103);
check_type!(NID , 104);
check_type!(L32 , 105);
check_type!(L64 , 106);
check_type!(LP , 107);
check_type!(EUI48 , 108);
check_type!(EUI64 , 109);
check_type!(TKEY , 249);
check_type!(TSIG , 250);
check_type!(IXFR , 251);
check_type!(AXFR , 252);
check_type!(MAILB , 253);
check_type!(MAILA , 254);
check_type!(ANY , 255);
check_type!(URI , 256);
check_type!(CAA , 257);
check_type!(AVC , 258);
check_type!(DOA , 259);
check_type!(DLV , 32769);
check_type!(ADDR , 65400);
check_type!(ALIAS , 65401);
}

View File

@ -0,0 +1,7 @@
pub mod packet;
pub mod text;
mod rrdata;
pub use self::packet::DnsPacketData;
pub use self::text::DnsTextData;
pub use self::rrdata::RRData;

View File

@ -0,0 +1,21 @@
use bytes::{Bytes, Buf};
use errors::*;
use std::io::Cursor;
mod std_impls;
pub trait DnsPacketData: Sized {
fn deserialize(data: &mut Cursor<Bytes>) -> Result<Self>;
}
pub fn deserialize<T>(data: Bytes) -> Result<T>
where
T: DnsPacketData
{
let mut c = Cursor::new(data);
let result = T::deserialize(&mut c)?;
if c.remaining() != 0 {
bail!("data remaining: {}", c.remaining())
}
Ok(result)
}

View File

@ -0,0 +1,64 @@
use bytes::{Bytes,Buf,BigEndian};
use errors::*;
use ser::packet::DnsPacketData;
use std::io::{Cursor, Read};
use std::mem::size_of;
use std::net::{Ipv4Addr, Ipv6Addr};
impl DnsPacketData for u8 {
fn deserialize(data: &mut Cursor<Bytes>) -> Result<Self> {
check_enough_data!(data, size_of::<Self>(), "u8");
Ok(data.get_u8())
}
}
impl DnsPacketData for u16 {
fn deserialize(data: &mut Cursor<Bytes>) -> Result<Self> {
check_enough_data!(data, size_of::<Self>(), "u16");
Ok(data.get_u16::<BigEndian>())
}
}
impl DnsPacketData for u32 {
fn deserialize(data: &mut Cursor<Bytes>) -> Result<Self> {
check_enough_data!(data, size_of::<Self>(), "u32");
Ok(data.get_u32::<BigEndian>())
}
}
impl DnsPacketData for Ipv4Addr {
fn deserialize(data: &mut Cursor<Bytes>) -> Result<Self> {
check_enough_data!(data, size_of::<u32>(), "Ipv4Addr");
Ok(data.get_u32::<BigEndian>().into())
}
}
impl DnsPacketData for Ipv6Addr {
fn deserialize(data: &mut Cursor<Bytes>) -> Result<Self> {
check_enough_data!(data, 16, "Ipv6Addr");
let mut buf = [0u8; 16];
data.read_exact(&mut buf)?;
Ok(buf.into())
}
}
#[cfg(test)]
mod tests {
use errors::*;
fn deserialize<T: super::DnsPacketData>(data: &'static [u8]) -> Result<T> {
use bytes::{Bytes,Buf};
use std::io::Cursor;
let mut c = Cursor::new(Bytes::from_static(data));
let result = T::deserialize(&mut c)?;
if c.remaining() != 0 {
bail!("data remaining: {}", c.remaining())
}
Ok(result)
}
#[test]
fn test_u16() {
assert!(deserialize::<u16>(b"\x80\x08").unwrap() == 0x8008);
}
}

View File

@ -0,0 +1,8 @@
use std::borrow::Cow;
use common_types::Type;
pub trait RRData: super::DnsPacketData {
fn rr_type() -> Type;
fn rr_type_name() -> Cow<'static, str>;
}

View File

@ -0,0 +1,4 @@
pub trait DnsTextData: Sized {
fn dns_parse(data: &str) -> ::errors::Result<Self>;
fn dns_format(&self) -> ::errors::Result<String>;
}

View File

@ -0,0 +1,11 @@
[package]
name = "dnsbox-derive"
version = "0.1.0"
authors = ["Stefan Bühler <stbuehler@web.de>"]
[lib]
proc-macro = true
[dependencies]
syn = "0.11"
quote = "0.3"

View File

@ -0,0 +1,44 @@
use syn;
use quote;
pub fn impl_hello_world(ast: &syn::DeriveInput) -> quote::Tokens {
let fields = match ast.body {
syn::Body::Enum(_) => panic!("Deriving DnsPacketData not supported for enum types"),
syn::Body::Struct(syn::VariantData::Struct(ref s)) => s,
syn::Body::Struct(_) => panic!("Deriving DnsPacketData not supported for unit / tuple struct types"),
};
if !ast.generics.ty_params.is_empty() {
panic!("Type parameters not supported for deriving DnsPacketData");
}
if !ast.generics.where_clause.predicates.is_empty() {
panic!("Where clauses not supported for deriving DnsPacketData");
}
if !ast.generics.lifetimes.is_empty() {
panic!("Lifetimes not supported for deriving DnsPacketData");
}
let name = &ast.ident;
let mut parse_fields = quote!{};
for field in fields {
let field_name = field.ident.as_ref().unwrap();
parse_fields = quote!{#parse_fields
#field_name: DnsPacketData::deserialize(_data).with_context(|_| format!("failed parsing field {}::{}", stringify!(#name), stringify!(#field_name)))?,
};
}
quote!{
impl ::dnsbox_base::ser::DnsPacketData for #name {
#[allow(unused_imports)]
fn deserialize(_data: &mut ::std::io::Cursor<::dnsbox_base::bytes::Bytes>) -> ::dnsbox_base::errors::Result<Self> {
use ::dnsbox_base::failure::ResultExt;
use ::dnsbox_base::ser::DnsPacketData;
Ok(#name{ #parse_fields })
}
}
}
}

View File

@ -0,0 +1,31 @@
extern crate proc_macro;
extern crate syn;
#[macro_use]
extern crate quote;
use proc_macro::TokenStream;
mod dns_packet_data;
mod rrdata;
#[proc_macro_derive(DnsPacketData)]
pub fn derive_dns_packet_data(input: TokenStream) -> TokenStream {
let s = input.to_string();
let ast = syn::parse_derive_input(&s).unwrap();
let gen = dns_packet_data::impl_hello_world(&ast);
gen.parse().unwrap()
}
#[proc_macro_derive(RRData)]
pub fn derive_rr_data(input: TokenStream) -> TokenStream {
let s = input.to_string();
let ast = syn::parse_derive_input(&s).unwrap();
let gen = rrdata::impl_rr_data(&ast);
gen.parse().unwrap()
}

View File

@ -0,0 +1,48 @@
use syn;
use quote;
pub fn impl_rr_data(ast: &syn::DeriveInput) -> quote::Tokens {
if !ast.generics.ty_params.is_empty() {
panic!("Type parameters not supported for deriving RRData");
}
if !ast.generics.where_clause.predicates.is_empty() {
panic!("Where clauses not supported for deriving RRData");
}
if !ast.generics.lifetimes.is_empty() {
panic!("Lifetimes not supported for deriving RRData");
}
let name = &ast.ident;
let name_str: &str = name.as_ref();
let test_mod_name = syn::Ident::from(format!("test_rr_{}", name));
quote!{
impl ::dnsbox_base::ser::RRData for #name {
fn rr_type() -> ::dnsbox_base::common_types::Type {
::dnsbox_base::records::types::#name
}
fn rr_type_name() -> ::std::borrow::Cow<'static, str> {
::std::borrow::Cow::Borrowed(#name_str)
}
}
// #[cfg(test)]
#[allow(non_snake_case, unused_imports)]
mod #test_mod_name {
use ::dnsbox_base::records::registry;
use ::dnsbox_base::records::types;
#[test]
fn test_registry() {
assert_eq!(
registry::known_name_to_type(#name_str),
Some(types::#name)
);
}
}
}
}

584
old/name_old.rs Normal file
View File

@ -0,0 +1,584 @@
#![deny(missing_docs)]
//! Various structs to represents DNS names and labels
use bytes::{Bytes,Buf,BytesMut};
use errors::*;
use std::fmt;
use std::io::Cursor;
use packet_data::{DnsPacketData,deserialize};
#[inline]
fn check_label(label: &[u8]) -> Result<()> {
if label.len() == 0 {
bail!("label must not be empty")
}
if label.len() > 63 {
bail!("label must not be longer than 63 bytes")
}
Ok(())
}
/// A DNS label (any binary string with `0 < length < 64`)
#[derive(Clone,PartialEq,Eq,PartialOrd,Ord,Hash)]
pub struct DnsLabel {
label: Bytes, // 0 < len < 64
}
impl DnsLabel {
/// Create new label from existing storage
///
/// Fails when the length doesn't match the requirement `0 < length < 64`.
pub fn new(label: Bytes) -> Result<Self> {
check_label(&label)?;
Ok(DnsLabel{label})
}
/// Convert to a representation without storage
pub fn as_ref<'a>(&'a self) -> DnsLabelRef<'a> {
DnsLabelRef{label: self.label.as_ref()}
}
/// Access as raw bytes
pub fn as_bytes(&self) -> &Bytes {
&self.label
}
/// Access as raw bytes
pub fn as_raw(&self) -> &[u8] {
&self.label
}
}
impl<'a> From<DnsLabelRef<'a>> for DnsLabel {
fn from(label_ref: DnsLabelRef<'a>) -> Self {
DnsLabel{
label: Bytes::from(label_ref.label),
}
}
}
impl fmt::Debug for DnsLabel {
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
self.as_ref().fmt(w)
}
}
impl fmt::Display for DnsLabel {
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
self.as_ref().fmt(w)
}
}
/// A DNS label (any binary string with `0 < length < 64`)
///
/// Storage is provided through lifetime.
#[derive(Clone,Copy,PartialEq,Eq,PartialOrd,Ord,Hash)]
pub struct DnsLabelRef<'a> {
label: &'a [u8], // 0 < len < 64
}
impl<'a> DnsLabelRef<'a> {
/// Create new label from existing storage
///
/// Fails when the length doesn't match the requirement `0 < length < 64`.
pub fn new(label: &'a [u8]) -> Result<Self> {
check_label(label)?;
Ok(DnsLabelRef{label})
}
/// Access as raw bytes
pub fn as_raw(&self) -> &'a [u8] {
self.label
}
}
impl<'a> From<&'a DnsLabel> for DnsLabelRef<'a> {
fn from(label: &'a DnsLabel) -> Self {
label.as_ref()
}
}
impl<'a> fmt::Debug for DnsLabelRef<'a> {
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
// just escape the display version
format!("{}", self).fmt(w)
}
}
impl<'a> fmt::Display for DnsLabelRef<'a> {
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
use std::str;
let mut done = 0;
for pos in 0..self.label.len() {
let c = self.label[pos];
if c <= 0x21 || c >= 0x7e || b'.' == c || b'\\' == c {
// flush
if done < pos {
w.write_str(unsafe {str::from_utf8_unchecked(&self.label[done..pos])})?;
}
match c {
b'.' => w.write_str(r#"\."#)?,
b'\\' => w.write_str(r#"\\"#)?,
_ => write!(w, r"\{:03o}", c)?,
}
done = pos + 1;
}
}
// final flush
if done < self.label.len() {
w.write_str(unsafe {str::from_utf8_unchecked(&self.label[done..])})?;
}
Ok(())
}
}
/// Customize formatting of DNS labels
///
/// The default uses "." as separator and adds a trailing separator.
///
/// The `Debug` formatters just format as `Display` to a String and then
/// format that string as `Debug`.
#[derive(Clone,Copy,PartialEq,Eq,PartialOrd,Ord,Hash,Debug)]
pub struct DisplayLabelsOptions {
/// separator to insert between (escaped) labels
pub separator: &'static str,
/// whether a trailing separator is added.
///
/// without a trailing separator the root zone is represented as
/// empty string!
pub trailing: bool,
}
impl Default for DisplayLabelsOptions {
fn default() -> Self {
DisplayLabelsOptions{
separator: ".",
trailing: true,
}
}
}
/// Wrap anything representing a collection of labels (`DnsLabelRef`) to
/// format using the given `options`.
///
/// As name you can pass any cloneable `(Into)Iterator` with
/// `DnsLabelRef` items, e.g:
///
/// * `&DnsPlainName`
/// * `&DnsName`
/// * `DnsNameIterator`
/// * `DnsPlainNameIterator`
#[derive(Clone)]
pub struct DisplayLabels<'a, I>
where
I: IntoIterator<Item=DnsLabelRef<'a>>+Clone
{
/// Label collection to iterate over
pub labels: I,
/// Options
pub options: DisplayLabelsOptions,
}
impl<'a, I> fmt::Debug for DisplayLabels<'a, I>
where
I: IntoIterator<Item=DnsLabelRef<'a>>+Clone
{
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
// just escape the display version1
format!("{}", self).fmt(w)
}
}
impl<'a, I> fmt::Display for DisplayLabels<'a, I>
where
I: IntoIterator<Item=DnsLabelRef<'a>>+Clone
{
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
let mut i = self.labels.clone().into_iter();
if let Some(first_label) = i.next() {
// first label
fmt::Display::fmt(&first_label, w)?;
// remaining labels
while let Some(label) = i.next() {
w.write_str(self.options.separator)?;
fmt::Display::fmt(&label, w)?;
}
}
if self.options.trailing {
w.write_str(self.options.separator)?;
}
Ok(())
}
}
/// A DNS name
///
/// Uses the "original" uncompressed raw representation for storage
/// (i.e. can share memory with a parsed packet)
#[derive(Clone,PartialEq,Eq,PartialOrd,Ord,Hash)]
pub struct DnsPlainName {
// uncompressed raw representation
data: Bytes, // at most 255 bytes
label_count: u8, // at most 127 labels; doesn't count the final (empty) label
}
impl DnsPlainName {
/// Parse a name from raw bytes
pub fn new(raw: Bytes) -> Result<Self> {
deserialize(raw)
}
/// How many labels the name has (without the trailing empty label,
/// at most 127)
pub fn label_count(&self) -> u8 {
self.label_count
}
/// Iterator over the labels (in the order they are stored in memory,
/// i.e. top-level name last).
pub fn labels<'a>(&'a self) -> DnsPlainNameIterator<'a> {
DnsPlainNameIterator{
name_data: self.data.as_ref(),
position: 0,
labels_done: 0,
label_count: self.label_count,
}
}
}
impl<'a> IntoIterator for &'a DnsPlainName {
type Item = DnsLabelRef<'a>;
type IntoIter = DnsPlainNameIterator<'a>;
fn into_iter(self) -> Self::IntoIter {
self.labels()
}
}
impl fmt::Debug for DnsPlainName {
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
DisplayLabels{
labels: self,
options: Default::default(),
}.fmt(w)
}
}
impl fmt::Display for DnsPlainName {
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
DisplayLabels{
labels: self,
options: Default::default(),
}.fmt(w)
}
}
impl DnsPacketData for DnsPlainName {
fn deserialize(data: &mut Cursor<Bytes>) -> Result<Self> {
check_enough_data!(data, 1, "DnsPlainName");
let start_pos = data.position() as usize;
let mut total_len : usize = 0;
let mut label_count: u8 = 0;
loop {
check_enough_data!(data, 1, "DnsPlainName label len");
let label_len = data.get_u8() as usize;
total_len += 1;
if total_len > 255 { bail!{"DNS name too long"} }
if 0 == label_len { break; }
label_count += 1; // can't overflow: total_len <= 255, and each label so far was not empty, i.e. used at least two bytes.
if label_len > 63 { bail!("Invalid label length {}", label_len) }
total_len += label_len;
if total_len > 255 { bail!{"DNS name too long"} }
check_enough_data!(data, (label_len), "DnsPlainName label");
data.advance(label_len);
}
let end_pos = data.position() as usize;
Ok(DnsPlainName{
data: data.get_ref().slice(start_pos, end_pos),
label_count: label_count,
})
}
}
/// Iterator type for [`DnsPlainName::labels`]
///
/// [`DnsPlainName::labels`]: struct.DnsPlainName.html#method.labels
#[derive(Clone)]
pub struct DnsPlainNameIterator<'a> {
name_data: &'a [u8],
position: u8,
labels_done: u8,
label_count: u8,
}
impl<'a> Iterator for DnsPlainNameIterator<'a> {
type Item = DnsLabelRef<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.labels_done >= self.label_count { return None }
self.labels_done += 1;
let label_len = self.name_data[self.position as usize];
let end = self.position+1+label_len;
let label = DnsLabelRef{label: &self.name_data[(self.position+1) as usize..end as usize]};
self.position = end;
Some(label)
}
fn size_hint(&self) -> (usize, Option<usize>) {
let count = self.len();
(count, Some(count))
}
fn count(self) -> usize {
self.len()
}
}
impl<'a> ExactSizeIterator for DnsPlainNameIterator<'a> {
fn len(&self) -> usize {
(self.label_count - self.labels_done) as usize
}
}
/// A DNS name
///
/// Uses a modified representation for storage (i.e. can NOT share
/// memory with a parsed packet), but supports a `DoubleEndedIterator`
/// to iterate over the labels.
#[derive(Clone,PartialEq,Eq,PartialOrd,Ord,Hash)]
pub struct DnsName {
// similar to the DNS encoding; but each "length" octet is the "XOR"
// of the length of the surrounding labels.
data: Bytes, // at most 255 bytes
label_count: u8, // at most 127 labels; doesn't count the final (empty) label
}
impl DnsName {
/// Parse a name from raw bytes
pub fn new(raw: Bytes) -> Result<Self> {
deserialize(raw)
}
/// How many labels the name has (without the trailing empty label,
/// at most 127)
pub fn label_count(&self) -> u8 {
self.label_count
}
/// Iterator over the labels (in the order they are stored in memory,
/// i.e. top-level name last).
pub fn labels(&self) -> DnsNameIterator {
DnsNameIterator{
name_data: self.data.as_ref(),
front_position: 0,
front_prev_label_len: 0,
back_position: self.data.len() as u8 - 1,
back_next_label_len: 0,
labels_done: 0,
label_count: self.label_count,
}
}
}
impl From<DnsPlainName> for DnsName {
fn from(plain: DnsPlainName) -> Self {
let mut data = plain.data.try_mut().unwrap_or_else(BytesMut::from);
let mut pos : u8 = 0;
let mut prev_len : u8 = 0;
loop {
let label_len = data[pos as usize];
data[pos as usize] ^= prev_len;
if 0 == label_len { break; }
pos += label_len + 1;
prev_len = label_len;
}
DnsName{
data: data.freeze(),
label_count: plain.label_count,
}
}
}
impl From<DnsName> for DnsPlainName {
fn from(name: DnsName) -> DnsPlainName {
let mut data = name.data.try_mut().unwrap_or_else(BytesMut::from);
let mut pos : u8 = 0;
let mut prev_len : u8 = 0;
loop {
let label_len = data[pos as usize] ^ prev_len;
data[pos as usize] = label_len;
if 0 == label_len { break; }
pos += label_len + 1;
prev_len = label_len;
}
DnsPlainName{
data: data.freeze(),
label_count: name.label_count,
}
}
}
impl<'a> IntoIterator for &'a DnsName {
type Item = DnsLabelRef<'a>;
type IntoIter = DnsNameIterator<'a>;
fn into_iter(self) -> Self::IntoIter {
self.labels()
}
}
impl fmt::Debug for DnsName {
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
DisplayLabels{
labels: self,
options: Default::default(),
}.fmt(w)
}
}
impl fmt::Display for DnsName {
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
DisplayLabels{
labels: self,
options: Default::default(),
}.fmt(w)
}
}
impl DnsPacketData for DnsName {
fn deserialize(data: &mut Cursor<Bytes>) -> Result<Self> {
Ok(DnsName::from(DnsPlainName::deserialize(data)?))
}
}
/// Iterator type for [`DnsName::labels`]
///
/// [`DnsName::labels`]: struct.DnsName.html#method.labels
#[derive(Clone)]
pub struct DnsNameIterator<'a> {
name_data: &'a [u8],
front_position: u8,
front_prev_label_len: u8,
back_position: u8,
back_next_label_len: u8,
labels_done: u8,
label_count: u8,
}
impl<'a> Iterator for DnsNameIterator<'a> {
type Item = DnsLabelRef<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.labels_done >= self.label_count { return None }
self.labels_done += 1;
let label_len = self.name_data[self.front_position as usize] ^ self.front_prev_label_len;
self.front_prev_label_len = label_len;
let end = self.front_position+1+label_len;
let label = DnsLabelRef{label: &self.name_data[(self.front_position+1) as usize..end as usize]};
self.front_position = end;
Some(label)
}
fn size_hint(&self) -> (usize, Option<usize>) {
let count = self.len();
(count, Some(count))
}
fn count(self) -> usize {
self.len()
}
}
impl<'a> ExactSizeIterator for DnsNameIterator<'a> {
fn len(&self) -> usize {
(self.label_count - self.labels_done) as usize
}
}
impl<'a> DoubleEndedIterator for DnsNameIterator<'a> {
fn next_back(&mut self) -> Option<Self::Item> {
if self.labels_done >= self.label_count { return None }
self.labels_done += 1;
let label_len = self.name_data[self.back_position as usize] ^ self.back_next_label_len;
self.back_next_label_len = label_len;
let end = self.back_position;
self.back_position -= 1 + label_len;
let label = DnsLabelRef{label: &self.name_data[(self.back_position+1) as usize..end as usize]};
Some(label)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn do_parse_and_display_name<T>()
where
T: fmt::Display+fmt::Debug+DnsPacketData,
for<'a> &'a T: IntoIterator<Item=DnsLabelRef<'a>>,
{
{
let name = deserialize::<T>(Bytes::from_static(b"\x07example\x03com\x00")).unwrap();
assert_eq!(
format!("{}", name
),
"example.com."
);
assert_eq!(
name.into_iter().count(),
2
);
}
assert_eq!(
format!(
"{}",
deserialize::<T>(Bytes::from_static(b"\x07e!am.l\\\x03com\x00")).unwrap()
),
"e\\041am\\.l\\\\.com."
);
assert_eq!(
format!(
"{:?}",
deserialize::<T>(Bytes::from_static(b"\x07e!am.l\\\x03com\x00")).unwrap()
),
r#""e\\041am\\.l\\\\.com.""#
);
}
#[test]
fn parse_and_display_plain_name() {
do_parse_and_display_name::<DnsPlainName>();
}
#[test]
fn parse_and_display_name() {
do_parse_and_display_name::<DnsName>();
}
#[test]
fn parse_and_reverse_name() {
let name = deserialize::<DnsName>(Bytes::from_static(b"\x03www\x07example\x03com\x00")).unwrap();
assert_eq!(
format!(
"{}",
DisplayLabels{
labels: name.labels().rev(),
options: DisplayLabelsOptions{
separator: " ",
trailing: false,
},
}
),
"com example www"
);
}
#[test]
fn parse_and_convert_names() {
let name = deserialize::<DnsPlainName>(Bytes::from_static(b"\x03www\x07example\x03com\x00")).unwrap();
assert_eq!(
DnsPlainName::from(DnsName::from(name.clone())),
name
);
}
}

5
src/main.rs Normal file
View File

@ -0,0 +1,5 @@
extern crate dnsbox_base;
fn main() {
println!("Test");
}