init
This commit is contained in:
commit
63cd9196f9
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/target/
|
||||
**/*.rs.bk
|
217
Cargo.lock
generated
Normal file
217
Cargo.lock
generated
Normal 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
9
Cargo.toml
Normal 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]
|
13
lib/dnsbox-base/Cargo.toml
Normal file
13
lib/dnsbox-base/Cargo.toml
Normal 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"
|
90
lib/dnsbox-base/src/common_types/binary/mod.rs
Normal file
90
lib/dnsbox-base/src/common_types/binary/mod.rs
Normal 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))
|
||||
}
|
||||
}
|
9
lib/dnsbox-base/src/common_types/mod.rs
Normal file
9
lib/dnsbox-base/src/common_types/mod.rs
Normal 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;
|
79
lib/dnsbox-base/src/common_types/name/display.rs
Normal file
79
lib/dnsbox-base/src/common_types/name/display.rs
Normal 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(())
|
||||
}
|
||||
}
|
240
lib/dnsbox-base/src/common_types/name/label.rs
Normal file
240
lib/dnsbox-base/src/common_types/name/label.rs
Normal 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(())
|
||||
}
|
||||
}
|
509
lib/dnsbox-base/src/common_types/name/mod.rs
Normal file
509
lib/dnsbox-base/src/common_types/name/mod.rs
Normal 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."
|
||||
);
|
||||
}
|
||||
}
|
265
lib/dnsbox-base/src/common_types/name/name_mutations.rs
Normal file
265
lib/dnsbox-base/src/common_types/name/name_mutations.rs
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
81
lib/dnsbox-base/src/common_types/name/name_parser.rs
Normal file
81
lib/dnsbox-base/src/common_types/name/name_parser.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
8
lib/dnsbox-base/src/common_types/rr_type/mod.rs
Normal file
8
lib/dnsbox-base/src/common_types/rr_type/mod.rs
Normal 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)?))
|
||||
}
|
||||
}
|
21
lib/dnsbox-base/src/common_types/text/mod.rs
Normal file
21
lib/dnsbox-base/src/common_types/text/mod.rs
Normal 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))
|
||||
}
|
||||
}
|
42
lib/dnsbox-base/src/errors.rs
Normal file
42
lib/dnsbox-base/src/errors.rs
Normal 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)?;
|
||||
}
|
||||
};
|
||||
}
|
22
lib/dnsbox-base/src/lib.rs
Normal file
22
lib/dnsbox-base/src/lib.rs
Normal 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::*;
|
||||
}
|
8
lib/dnsbox-base/src/records/mod.rs
Normal file
8
lib/dnsbox-base/src/records/mod.rs
Normal file
@ -0,0 +1,8 @@
|
||||
mod structs;
|
||||
pub mod registry;
|
||||
pub mod types;
|
||||
|
||||
pub use self::structs::*;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
149
lib/dnsbox-base/src/records/registry.rs
Normal file
149
lib/dnsbox-base/src/records/registry.rs
Normal 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, ());
|
||||
}
|
||||
}
|
402
lib/dnsbox-base/src/records/structs.rs
Normal file
402
lib/dnsbox-base/src/records/structs.rs
Normal 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;
|
16
lib/dnsbox-base/src/records/tests.rs
Normal file
16
lib/dnsbox-base/src/records/tests.rs
Normal 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");
|
||||
}
|
294
lib/dnsbox-base/src/records/types.rs
Normal file
294
lib/dnsbox-base/src/records/types.rs
Normal 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);
|
||||
}
|
7
lib/dnsbox-base/src/ser/mod.rs
Normal file
7
lib/dnsbox-base/src/ser/mod.rs
Normal 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;
|
21
lib/dnsbox-base/src/ser/packet/mod.rs
Normal file
21
lib/dnsbox-base/src/ser/packet/mod.rs
Normal 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)
|
||||
}
|
64
lib/dnsbox-base/src/ser/packet/std_impls.rs
Normal file
64
lib/dnsbox-base/src/ser/packet/std_impls.rs
Normal 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);
|
||||
}
|
||||
}
|
8
lib/dnsbox-base/src/ser/rrdata.rs
Normal file
8
lib/dnsbox-base/src/ser/rrdata.rs
Normal 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>;
|
||||
}
|
4
lib/dnsbox-base/src/ser/text/mod.rs
Normal file
4
lib/dnsbox-base/src/ser/text/mod.rs
Normal file
@ -0,0 +1,4 @@
|
||||
pub trait DnsTextData: Sized {
|
||||
fn dns_parse(data: &str) -> ::errors::Result<Self>;
|
||||
fn dns_format(&self) -> ::errors::Result<String>;
|
||||
}
|
11
lib/dnsbox-derive/Cargo.toml
Normal file
11
lib/dnsbox-derive/Cargo.toml
Normal 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"
|
44
lib/dnsbox-derive/src/dns_packet_data.rs
Normal file
44
lib/dnsbox-derive/src/dns_packet_data.rs
Normal 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 })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
31
lib/dnsbox-derive/src/lib.rs
Normal file
31
lib/dnsbox-derive/src/lib.rs
Normal 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()
|
||||
}
|
48
lib/dnsbox-derive/src/rrdata.rs
Normal file
48
lib/dnsbox-derive/src/rrdata.rs
Normal 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
584
old/name_old.rs
Normal 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
5
src/main.rs
Normal file
@ -0,0 +1,5 @@
|
||||
extern crate dnsbox_base;
|
||||
|
||||
fn main() {
|
||||
println!("Test");
|
||||
}
|
Loading…
Reference in New Issue
Block a user