Bfry/split client server (#68)
* initial split of server * split client and server crates * create scripts for running tests across crates * fix target location in tests * trying different travis settings for getting kcov working * fix directory change in kcov setup * fixing for loop for the kcov tests * added some env config for config tests * adding back sudo settings, and additional coveralls merging config * fix exclude paths * merging coverage reports * fixing results kcov paths
This commit is contained in:
parent
fb23b277b6
commit
2b705dc300
1
.gitignore
vendored
1
.gitignore
vendored
@ -12,3 +12,4 @@
|
||||
|
||||
# Generated by Cargo
|
||||
/target/
|
||||
**/target/
|
||||
|
12
.travis.yml
12
.travis.yml
@ -1,3 +1,5 @@
|
||||
sudo: required
|
||||
dist: trusty
|
||||
language: rust
|
||||
os:
|
||||
- linux
|
||||
@ -9,9 +11,7 @@ rust:
|
||||
matrix:
|
||||
allow_failure:
|
||||
- rust: nightly
|
||||
after_success: |
|
||||
sudo apt-get install libcurl4-openssl-dev libelf-dev libdw-dev &&
|
||||
wget https://github.com/SimonKagstrom/kcov/archive/master.tar.gz &&
|
||||
tar xzf master.tar.gz && mkdir kcov-master/build && cd kcov-master/build && cmake .. && make &&
|
||||
sudo make install && cd ../.. &&
|
||||
kcov --coveralls-id=$TRAVIS_JOB_ID --exclude-pattern=/.cargo target/kcov target/debug/trust_dns-*
|
||||
script: scripts/run_tests.sh
|
||||
after_success: scripts/run_kcov.sh
|
||||
notifications:
|
||||
webhooks: https://coveralls.io/webhook?repo_token=0fXi81cx3Khi8g0Q0mGqsABePoEdJfMcf
|
||||
|
@ -2,6 +2,11 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## unreleased (0.9.0)
|
||||
### Changed
|
||||
- Split Server and Client into separate crates, #43
|
||||
- Moved many integration tests to `tests` from `src`, #52
|
||||
|
||||
## 0.8.1
|
||||
### Fixed
|
||||
- Fix build on rustc 1.11, #66
|
||||
|
24
Cargo.lock
generated
24
Cargo.lock
generated
@ -1,10 +1,9 @@
|
||||
[root]
|
||||
name = "trust-dns"
|
||||
name = "trust-dns-server"
|
||||
version = "0.8.1"
|
||||
dependencies = [
|
||||
"backtrace 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"chrono 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"data-encoding 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"docopt 0.6.86 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"error-chain 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -18,6 +17,7 @@ dependencies = [
|
||||
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"toml 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"trust-dns 0.8.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -482,6 +482,26 @@ dependencies = [
|
||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "trust-dns"
|
||||
version = "0.8.1"
|
||||
dependencies = [
|
||||
"backtrace 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"chrono 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"data-encoding 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"error-chain 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"openssl 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"trust-dns-server 0.8.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "user32-sys"
|
||||
version = "0.2.0"
|
||||
|
84
Cargo.toml
84
Cargo.toml
@ -1,82 +1,2 @@
|
||||
[package]
|
||||
name = "trust-dns"
|
||||
version = "0.8.1"
|
||||
authors = ["Benjamin Fry <benjaminfry@me.com>"]
|
||||
|
||||
# A short blurb about the package. This is not rendered in any format when
|
||||
# uploaded to crates.io (aka this is not markdown)
|
||||
description = """
|
||||
TRust-DNS is a safe and secure DNS server and client with DNSec support.
|
||||
Eventually this could be a replacement for BIND9. DNSSec on the client side,
|
||||
with NSEC validation for negative records, is complete. The client and
|
||||
server both support dynamic DNS with authenticated requests.
|
||||
"""
|
||||
|
||||
# These URLs point to more information about the repository
|
||||
documentation = "https://docs.rs/trust-dns"
|
||||
homepage = "http://www.trust-dns.org/index.html"
|
||||
repository = "https://github.com/bluejekyll/trust-dns"
|
||||
|
||||
# This points to a file in the repository (relative to this Cargo.toml). The
|
||||
# contents of this file are stored and indexed in the registry.
|
||||
readme = "README.md"
|
||||
|
||||
# This is a small list of keywords used to categorize and search for this
|
||||
# package.
|
||||
keywords = ["DNS", "BIND", "dig", "named", "dnssec"]
|
||||
|
||||
# This is a string description of the license for this package. Currently
|
||||
# crates.io will validate the license provided against a whitelist of known
|
||||
# license identifiers from http://spdx.org/licenses/. Multiple licenses can
|
||||
# be separated with a `/`
|
||||
license = "MIT/Apache-2.0"
|
||||
|
||||
# custom build steps
|
||||
build = "build.rs"
|
||||
|
||||
[features]
|
||||
|
||||
[lib]
|
||||
name = "trust_dns"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "named"
|
||||
path = "src/named.rs"
|
||||
|
||||
|
||||
[profile.dev]
|
||||
opt-level = 0 # Controls the --opt-level the compiler builds with
|
||||
debug = true # Controls whether the compiler passes `-g`
|
||||
rpath = false # Controls whether the compiler passes `-C rpath`
|
||||
lto = false # Controls `-C lto` for binaries and staticlibs
|
||||
debug-assertions = true # Controls whether debug assertions are enabled
|
||||
codegen-units = 1 # Controls whether the compiler passes `-C codegen-units`
|
||||
# `codegen-units` is ignored when `lto = true`
|
||||
|
||||
# The testing profile, used for `cargo test`
|
||||
[profile.test]
|
||||
opt-level = 0
|
||||
debug = true
|
||||
rpath = false
|
||||
lto = false
|
||||
debug-assertions = true
|
||||
codegen-units = 1
|
||||
|
||||
[dependencies]
|
||||
backtrace = "^0.2.1"
|
||||
chrono = "^0.2.21"
|
||||
data-encoding = "^1.1.2"
|
||||
docopt = "^0.6.78"
|
||||
error-chain = "0.1.12"
|
||||
futures = "^0.1"
|
||||
lazy_static = "^0.2.1"
|
||||
log = "^0.3.5"
|
||||
mio = "^0.5.1"
|
||||
openssl = "^0.8.3"
|
||||
rand = "^0.3"
|
||||
rustc-serialize = "^0.3.18"
|
||||
rusqlite = "^0.7.3"
|
||||
time = "^0.1"
|
||||
tokio-core = "^0.1"
|
||||
toml = "^0.1"
|
||||
[workspace]
|
||||
members = ["client", "server"]
|
||||
|
606
client/Cargo.lock
generated
Normal file
606
client/Cargo.lock
generated
Normal file
@ -0,0 +1,606 @@
|
||||
[root]
|
||||
name = "trust-dns"
|
||||
version = "0.8.1"
|
||||
dependencies = [
|
||||
"backtrace 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"chrono 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"data-encoding 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"error-chain 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"openssl 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"trust-dns-server 0.8.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"backtrace-sys 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cfg-if 0.1.0 (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.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-demangle 0.1.2 (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.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"gcc 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.2.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"num 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "data-encoding"
|
||||
version = "1.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 = "docopt"
|
||||
version = "0.6.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"strsim 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "error-chain"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"backtrace 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gcc"
|
||||
version = "0.3.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "gdi32-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 = "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 = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "lazycell"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "libressl-pnacl-sys"
|
||||
version = "2.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"pnacl-build-helper 1.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libsqlite3-sys"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
version = "0.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "lru-cache"
|
||||
version = "0.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"linked-hash-map 0.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"net2 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nix 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazycell 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"net2 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nix 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miow"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"net2 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "net2"
|
||||
version = "0.2.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.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.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num"
|
||||
version = "0.1.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"num-integer 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-iter 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-iter"
|
||||
version = "0.1.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"num-integer 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.1.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"openssl-sys 0.7.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.7.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"gdi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libressl-pnacl-sys 2.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"user32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "pnacl-build-helper"
|
||||
version = "1.4.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "0.1.80"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "rusqlite"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libsqlite3-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lru-cache 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-serialize"
|
||||
version = "0.3.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scoped-tls"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "0.1.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "tempdir"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread-id"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.1.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-core"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"futures 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mio 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.1.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "trust-dns-server"
|
||||
version = "0.8.1"
|
||||
dependencies = [
|
||||
"backtrace 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"chrono 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"docopt 0.6.86 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"error-chain 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"openssl 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rusqlite 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"toml 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"trust-dns 0.8.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "user32-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 = "utf8-ranges"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "void"
|
||||
version = "1.0.2"
|
||||
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"
|
||||
|
||||
[[package]]
|
||||
name = "ws2_32-sys"
|
||||
version = "0.2.1"
|
||||
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)",
|
||||
]
|
||||
|
||||
[metadata]
|
||||
"checksum aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ca972c2ea5f742bfce5687b9aef75506a764f61d37f8f649047846a9686ddb66"
|
||||
"checksum backtrace 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "346d7644f0b5f9bc73082d3b2236b69a05fd35cce0cfa3724e184e6a5c9e2a2f"
|
||||
"checksum backtrace-sys 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ff73785ae8e06bb4a7b09e09f06d7434f9748b86d2f67bdf334b603354497e08"
|
||||
"checksum bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8dead7461c1127cf637931a1e50934eb6eee8bff2f74433ac7909e9afcee04a3"
|
||||
"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
|
||||
"checksum bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c129aff112dcc562970abb69e2508b40850dd24c274761bb50fb8a0067ba6c27"
|
||||
"checksum cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de1e760d7b6535af4241fca8bd8adf68e2e7edacc6b29f5d399050c5e48cf88c"
|
||||
"checksum chrono 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)" = "9213f7cd7c27e95c2b57c49f0e69b1ea65b27138da84a170133fd21b07659c00"
|
||||
"checksum data-encoding 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f13f03d68d1906eb3222536f5756953e30de4dff4417e5d428414fb8edb26723"
|
||||
"checksum dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97590ba53bcb8ac28279161ca943a924d1fd4a8fb3fa63302591647c4fc5b850"
|
||||
"checksum docopt 0.6.86 (registry+https://github.com/rust-lang/crates.io-index)" = "4a7ef30445607f6fc8720f0a0a2c7442284b629cf0d049286860fae23e71c4d9"
|
||||
"checksum error-chain 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "faa976b4fd2e4c2b2f3f486874b19e61944d3de3de8b61c9fcf835d583871bcc"
|
||||
"checksum futures 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dd89497091f8c5d3a65c6b4baf6d2f0731937a7c9217d2f89141b21437a9d96"
|
||||
"checksum gcc 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)" = "553f11439bdefe755bf366b264820f1da70f3aaf3924e594b886beb9c831bcf5"
|
||||
"checksum gdi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0912515a8ff24ba900422ecda800b52f4016a56251922d397c576bf92c690518"
|
||||
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||
"checksum lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "49247ec2a285bb3dcb23cbd9c35193c025e7251bfce77c1d5da97e6362dffe7f"
|
||||
"checksum lazycell 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce12306c4739d86ee97c23139f3a34ddf0387bbf181bc7929d287025a8c3ef6b"
|
||||
"checksum libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)" = "044d1360593a78f5c8e5e710beccdc24ab71d1f01bc19a29bcacdba22e8475d8"
|
||||
"checksum libressl-pnacl-sys 2.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "cbc058951ab6a3ef35ca16462d7642c4867e6403520811f28537a4e2f2db3e71"
|
||||
"checksum libsqlite3-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "663508cb9c1e23363aea1a8b1f7d6340394ebc3bc3a6daebfb9cc99b8feaf2ec"
|
||||
"checksum linked-hash-map 0.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "83f7ff3baae999fdf921cccf54b61842bb3b26868d50d02dff48052ebec8dd79"
|
||||
"checksum log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ab83497bf8bf4ed2a74259c1c802351fcd67a65baa86394b6ba73c36f4838054"
|
||||
"checksum lru-cache 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "42d50dcb5d9f145df83b1043207e1ac0c37c9c779c4e128ca4655abc3f3cbf8c"
|
||||
"checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20"
|
||||
"checksum mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a637d1ca14eacae06296a008fa7ad955347e34efcb5891cfd8ba05491a37907e"
|
||||
"checksum mio 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "410a1a0ff76f5a226f1e4e3ff1756128e65cd30166e39c3892283e2ac09d5b67"
|
||||
"checksum miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d5bfc6782530ac8ace97af10a540054a37126b63b0702ddaaa243b73b5745b9a"
|
||||
"checksum net2 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)" = "5edf9cb6be97212423aed9413dd4729d62b370b5e1c571750e882cebbbc1e3e2"
|
||||
"checksum nix 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bfb3ddedaa14746434a02041940495bf11325c22f6d36125d3bdd56090d50a79"
|
||||
"checksum nix 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a0d95c5fa8b641c10ad0b8887454ebaafa3c92b5cd5350f8fc693adafd178e7b"
|
||||
"checksum num 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)" = "bde7c03b09e7c6a301ee81f6ddf66d7a28ec305699e3d3b056d2fc56470e3120"
|
||||
"checksum num-integer 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "fb24d9bfb3f222010df27995441ded1e954f8f69cd35021f6bef02ca9552fb92"
|
||||
"checksum num-iter 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "287a1c9969a847055e1122ec0ea7a5c5d6f72aad97934e131c83d5c08ab4e45c"
|
||||
"checksum num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)" = "a16a42856a256b39c6d3484f097f6713e14feacd9bfb02290917904fae46c81c"
|
||||
"checksum openssl 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b11754cb6c81bb9e62faaf0eb6d94dde2aab0928c04db5078b74242880f35eb1"
|
||||
"checksum openssl-sys 0.7.17 (registry+https://github.com/rust-lang/crates.io-index)" = "89c47ee94c352eea9ddaf8e364be7f978a3bb6d66d73176572484238dd5a5c3f"
|
||||
"checksum pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8cee804ecc7eaf201a4a207241472cc870e825206f6c031e3ee2a72fa425f2fa"
|
||||
"checksum pnacl-build-helper 1.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "61c9231d31aea845007443d62fcbb58bb6949ab9c18081ee1e09920e0cf1118b"
|
||||
"checksum rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "2791d88c6defac799c3f20d74f094ca33b9332612d9aef9078519c82e4fe04a5"
|
||||
"checksum regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4fd4ace6a8cf7860714a2c2280d6c1f7e6a413486c13298bbc86fd3da019402f"
|
||||
"checksum regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f9ec002c35e86791825ed294b50008eea9ddfc8def4420124fbc6b08db834957"
|
||||
"checksum rusqlite 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e9b3854687228334d8a579cd2f666ddd7fb46a5f68ac0460da2898394c4679d2"
|
||||
"checksum rustc-demangle 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "662b2795f21ff17df7c4c50c1d955c8b5fa9eaddd1cf1bb32f8de2372ffd989b"
|
||||
"checksum rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)" = "6159e4e6e559c81bd706afe9c8fd68f547d3e851ce12e76b1de7914bab61691b"
|
||||
"checksum rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084"
|
||||
"checksum scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f417c22df063e9450888a7561788e9bd46d3bb3c1466435b4eccb903807f147d"
|
||||
"checksum semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)" = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac"
|
||||
"checksum slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d807fd58c4181bbabed77cb3b891ba9748241a552bcc5be698faaebefc54f46e"
|
||||
"checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23"
|
||||
"checksum strsim 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "50c069df92e4b01425a8bf3576d5d417943a6a7272fbabaf5bd80b1aaa76442e"
|
||||
"checksum tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "87974a6f5c1dfb344d733055601650059a3363de2a6104819293baff662132d6"
|
||||
"checksum thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9539db560102d1cef46b8b78ce737ff0bb64e7e18d35b2a5688f7d097d0ff03"
|
||||
"checksum thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8576dbbfcaef9641452d5cf0df9b0e7eeab7694956dd33bb61515fb8f18cfdd5"
|
||||
"checksum time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "3c7ec6d62a20df54e07ab3b78b9a3932972f4b7981de295563686849eb3989af"
|
||||
"checksum tokio-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "659cbae6c954dee37352853816c6a52180e47feb70be73bbfeec6d58c4da4a71"
|
||||
"checksum toml 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)" = "0590d72182e50e879c4da3b11c6488dae18fccb1ae0c7a3eda18e16795844796"
|
||||
"checksum user32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ef4711d107b21b410a3a974b1204d9accc8b10dad75d8324b5d755de1617d47"
|
||||
"checksum utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1ca13c08c41c9c3e04224ed9ff80461d97e121589ff27c753a16cb10830ae0f"
|
||||
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
||||
"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"
|
||||
"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
|
59
client/Cargo.toml
Normal file
59
client/Cargo.toml
Normal file
@ -0,0 +1,59 @@
|
||||
[package]
|
||||
name = "trust-dns"
|
||||
version = "0.8.1"
|
||||
authors = ["Benjamin Fry <benjaminfry@me.com>"]
|
||||
|
||||
# A short blurb about the package. This is not rendered in any format when
|
||||
# uploaded to crates.io (aka this is not markdown)
|
||||
description = """
|
||||
TRust-DNS is a safe and secure DNS server and client with DNSec support.
|
||||
Eventually this could be a replacement for BIND9. DNSSec on the client side,
|
||||
with NSEC validation for negative records, is complete. The client and
|
||||
server both support dynamic DNS with authenticated requests.
|
||||
"""
|
||||
|
||||
# These URLs point to more information about the repository
|
||||
documentation = "https://docs.rs/trust-dns"
|
||||
homepage = "http://www.trust-dns.org/index.html"
|
||||
repository = "https://github.com/bluejekyll/trust-dns"
|
||||
|
||||
# This points to a file in the repository (relative to this Cargo.toml). The
|
||||
# contents of this file are stored and indexed in the registry.
|
||||
readme = "README.md"
|
||||
|
||||
# This is a small list of keywords used to categorize and search for this
|
||||
# package.
|
||||
keywords = ["DNS", "BIND", "dig", "named", "dnssec"]
|
||||
|
||||
# This is a string description of the license for this package. Currently
|
||||
# crates.io will validate the license provided against a whitelist of known
|
||||
# license identifiers from http://spdx.org/licenses/. Multiple licenses can
|
||||
# be separated with a `/`
|
||||
license = "MIT/Apache-2.0"
|
||||
|
||||
# custom build steps
|
||||
build = "build.rs"
|
||||
|
||||
[features]
|
||||
|
||||
[lib]
|
||||
name = "trust_dns"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
backtrace = "^0.2.1"
|
||||
chrono = "^0.2.21"
|
||||
data-encoding = "^1.1.2"
|
||||
error-chain = "0.1.12"
|
||||
futures = "^0.1"
|
||||
lazy_static = "^0.2.1"
|
||||
log = "^0.3.5"
|
||||
mio = "^0.5.1"
|
||||
openssl = "^0.8.3"
|
||||
rand = "^0.3"
|
||||
rustc-serialize = "^0.3.18"
|
||||
time = "^0.1"
|
||||
tokio-core = "^0.1"
|
||||
|
||||
[dev-dependencies]
|
||||
trust-dns-server = { version = "*", path = "../server" }
|
@ -1013,558 +1013,3 @@ impl<C: ClientConnection> Client<C> {
|
||||
Ok(response)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::net::*;
|
||||
|
||||
use chrono::Duration;
|
||||
use openssl::crypto::rsa::RSA;
|
||||
|
||||
use ::authority::Catalog;
|
||||
use ::authority::authority_tests::{create_example, create_secure_example};
|
||||
#[allow(deprecated)]
|
||||
use ::client::{Client, ClientConnection, TestClientConnection};
|
||||
use ::op::ResponseCode;
|
||||
use ::rr::{DNSClass, Record, RecordType, domain, RData};
|
||||
use ::rr::dnssec::{Algorithm, Signer, TrustAnchor};
|
||||
use ::tcp::TcpClientConnection;
|
||||
use ::udp::UdpClientConnection;
|
||||
|
||||
#[test]
|
||||
fn test_query_nonet() {
|
||||
let authority = create_example();
|
||||
let mut catalog = Catalog::new();
|
||||
catalog.upsert(authority.get_origin().clone(), authority);
|
||||
|
||||
let client = Client::new(TestClientConnection::new(&catalog));
|
||||
|
||||
test_query(client);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_query_udp() {
|
||||
let addr: SocketAddr = ("8.8.8.8",53).to_socket_addrs().unwrap().next().unwrap();
|
||||
let conn = UdpClientConnection::new(addr).unwrap();
|
||||
let client = Client::new(conn);
|
||||
|
||||
test_query(client);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_query_tcp() {
|
||||
let addr: SocketAddr = ("8.8.8.8",53).to_socket_addrs().unwrap().next().unwrap();
|
||||
let conn = TcpClientConnection::new(addr).unwrap();
|
||||
let client = Client::new(conn);
|
||||
|
||||
test_query(client);
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
#[cfg(test)]
|
||||
fn test_query<C: ClientConnection>(client: Client<C>) {
|
||||
use std::cmp::Ordering;
|
||||
let name = domain::Name::with_labels(vec!["WWW".to_string(), "example".to_string(), "com".to_string()]);
|
||||
|
||||
let response = client.query(&name, DNSClass::IN, RecordType::A);
|
||||
assert!(response.is_ok(), "query failed: {}", response.unwrap_err());
|
||||
|
||||
let response = response.unwrap();
|
||||
|
||||
println!("response records: {:?}", response);
|
||||
assert_eq!(response.get_queries().first().expect("expected query").get_name().cmp_with_case(&name, false), Ordering::Equal);
|
||||
|
||||
let record = &response.get_answers()[0];
|
||||
assert_eq!(record.get_name(), &name);
|
||||
assert_eq!(record.get_rr_type(), RecordType::A);
|
||||
assert_eq!(record.get_dns_class(), DNSClass::IN);
|
||||
|
||||
if let &RData::A(ref address) = record.get_rdata() {
|
||||
assert_eq!(address, &Ipv4Addr::new(93,184,216,34))
|
||||
} else {
|
||||
assert!(false);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_secure_query_example_nonet() {
|
||||
use ::client::client_connection::test::TestClientConnection;
|
||||
|
||||
let authority = create_secure_example();
|
||||
|
||||
let public_key = {
|
||||
let signers = authority.get_secure_keys();
|
||||
signers.first().expect("expected a key in the authority").get_public_key()
|
||||
};
|
||||
|
||||
let mut catalog = Catalog::new();
|
||||
catalog.upsert(authority.get_origin().clone(), authority);
|
||||
|
||||
let mut trust_anchor = TrustAnchor::new();
|
||||
trust_anchor.insert_trust_anchor(public_key);
|
||||
|
||||
let client = Client::with_trust_anchor(TestClientConnection::new(&catalog), trust_anchor);
|
||||
|
||||
test_secure_query_example(client);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_secure_query_example_udp() {
|
||||
let addr: SocketAddr = ("8.8.8.8",53).to_socket_addrs().unwrap().next().unwrap();
|
||||
let conn = UdpClientConnection::new(addr).unwrap();
|
||||
let client = Client::new(conn);
|
||||
|
||||
test_secure_query_example(client);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_secure_query_example_tcp() {
|
||||
let addr: SocketAddr = ("8.8.8.8",53).to_socket_addrs().unwrap().next().unwrap();
|
||||
let conn = TcpClientConnection::new(addr).unwrap();
|
||||
let client = Client::new(conn);
|
||||
|
||||
test_secure_query_example(client);
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
#[cfg(test)]
|
||||
fn test_secure_query_example<C: ClientConnection>(client: Client<C>) {
|
||||
use log::LogLevel;
|
||||
use ::logger::TrustDnsLogger;
|
||||
TrustDnsLogger::enable_logging(LogLevel::Debug);
|
||||
|
||||
let name = domain::Name::with_labels(vec!["www".to_string(), "example".to_string(), "com".to_string()]);
|
||||
let response = client.secure_query(&name, DNSClass::IN, RecordType::A);
|
||||
|
||||
assert!(response.is_ok(), "query for {} failed: {}", name, response.unwrap_err());
|
||||
|
||||
let response = response.unwrap();
|
||||
|
||||
println!("response records: {:?}", response);
|
||||
assert!(response.get_edns().expect("edns not here").is_dnssec_ok());
|
||||
|
||||
let record = &response.get_answers()[0];
|
||||
assert_eq!(record.get_name(), &name);
|
||||
assert_eq!(record.get_rr_type(), RecordType::A);
|
||||
assert_eq!(record.get_dns_class(), DNSClass::IN);
|
||||
|
||||
if let &RData::A(ref address) = record.get_rdata() {
|
||||
assert_eq!(address, &Ipv4Addr::new(93,184,216,34))
|
||||
} else {
|
||||
assert!(false);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_dnssec_rollernet_td_udp() {
|
||||
use ::logger::TrustDnsLogger;
|
||||
use log::LogLevel;
|
||||
|
||||
TrustDnsLogger::enable_logging(LogLevel::Debug);
|
||||
|
||||
let c = Client::new(UdpClientConnection::new("8.8.8.8:53".parse().unwrap()).unwrap());
|
||||
c.secure_query(
|
||||
&domain::Name::parse("rollernet.us.", None).unwrap(),
|
||||
DNSClass::IN,
|
||||
RecordType::DS,
|
||||
).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_dnssec_rollernet_td_tcp() {
|
||||
use ::logger::TrustDnsLogger;
|
||||
use log::LogLevel;
|
||||
|
||||
TrustDnsLogger::enable_logging(LogLevel::Debug);
|
||||
|
||||
let c = Client::new(TcpClientConnection::new("8.8.8.8:53".parse().unwrap()).unwrap());
|
||||
c.secure_query(
|
||||
&domain::Name::parse("rollernet.us.", None).unwrap(),
|
||||
DNSClass::IN,
|
||||
RecordType::DS,
|
||||
).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_dnssec_rollernet_td_tcp_mixed_case() {
|
||||
use ::logger::TrustDnsLogger;
|
||||
use log::LogLevel;
|
||||
|
||||
TrustDnsLogger::enable_logging(LogLevel::Debug);
|
||||
|
||||
let c = Client::new(TcpClientConnection::new("8.8.8.8:53".parse().unwrap()).unwrap());
|
||||
c.secure_query(
|
||||
&domain::Name::parse("RollErnet.Us.", None).unwrap(),
|
||||
DNSClass::IN,
|
||||
RecordType::DS,
|
||||
).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nsec_query_example_nonet() {
|
||||
use ::client::client_connection::test::TestClientConnection;
|
||||
|
||||
let authority = create_secure_example();
|
||||
|
||||
let public_key = {
|
||||
let signers = authority.get_secure_keys();
|
||||
signers.first().expect("expected a key in the authority").get_public_key()
|
||||
};
|
||||
|
||||
let mut catalog = Catalog::new();
|
||||
catalog.upsert(authority.get_origin().clone(), authority);
|
||||
|
||||
let mut trust_anchor = TrustAnchor::new();
|
||||
trust_anchor.insert_trust_anchor(public_key);
|
||||
|
||||
let client = Client::with_trust_anchor(TestClientConnection::new(&catalog), trust_anchor);
|
||||
|
||||
test_nsec_query_example(client);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_nsec_query_example_udp() {
|
||||
let addr: SocketAddr = ("8.8.8.8",53).to_socket_addrs().unwrap().next().unwrap();
|
||||
let conn = UdpClientConnection::new(addr).unwrap();
|
||||
let client = Client::new(conn);
|
||||
test_nsec_query_example(client);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_nsec_query_example_tcp() {
|
||||
let addr: SocketAddr = ("8.8.8.8",53).to_socket_addrs().unwrap().next().unwrap();
|
||||
let conn = TcpClientConnection::new(addr).unwrap();
|
||||
let client = Client::new(conn);
|
||||
test_nsec_query_example(client);
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
#[cfg(test)]
|
||||
fn test_nsec_query_example<C: ClientConnection>(client: Client<C>) {
|
||||
let name = domain::Name::with_labels(vec!["none".to_string(), "example".to_string(), "com".to_string()]);
|
||||
|
||||
let response = client.secure_query(&name, DNSClass::IN, RecordType::A);
|
||||
assert!(response.is_ok(), "query failed: {}", response.unwrap_err());
|
||||
|
||||
let response = response.unwrap();
|
||||
assert_eq!(response.get_response_code(), ResponseCode::NXDomain);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_nsec_query_type() {
|
||||
let name = domain::Name::with_labels(vec!["www".to_string(), "example".to_string(), "com".to_string()]);
|
||||
|
||||
let addr: SocketAddr = ("8.8.8.8",53).to_socket_addrs().unwrap().next().unwrap();
|
||||
let conn = TcpClientConnection::new(addr).unwrap();
|
||||
let client = Client::new(conn);
|
||||
|
||||
let response = client.secure_query(&name, DNSClass::IN, RecordType::NS);
|
||||
assert!(response.is_ok(), "query failed: {}", response.unwrap_err());
|
||||
|
||||
let response = response.unwrap();
|
||||
// TODO: it would be nice to verify that the NSEC records were validated...
|
||||
assert_eq!(response.get_response_code(), ResponseCode::NoError);
|
||||
assert!(response.get_answers().is_empty());
|
||||
}
|
||||
|
||||
// TODO: disabled until I decide what to do with NSEC3 see issue #10
|
||||
//
|
||||
// TODO these NSEC3 tests don't work, it seems that the zone is not signed properly.
|
||||
// #[test]
|
||||
// #[ignore]
|
||||
// fn test_nsec3_sdsmt() {
|
||||
// let addr: SocketAddr = ("75.75.75.75",53).to_socket_addrs().unwrap().next().unwrap();
|
||||
// let conn = TcpClientConnection::new(addr).unwrap();
|
||||
// let name = domain::Name::with_labels(vec!["none".to_string(), "sdsmt".to_string(), "edu".to_string()]);
|
||||
// let client = Client::new(conn);
|
||||
//
|
||||
// let response = client.secure_query(&name, DNSClass::IN, RecordType::NS);
|
||||
// assert!(response.is_ok(), "query failed: {}", response.unwrap_err());
|
||||
//
|
||||
// let response = response.unwrap();
|
||||
// assert_eq!(response.get_response_code(), ResponseCode::NXDomain);
|
||||
// }
|
||||
|
||||
// TODO: disabled until I decide what to do with NSEC3 see issue #10
|
||||
//
|
||||
// #[test]
|
||||
// #[ignore]
|
||||
// fn test_nsec3_sdsmt_type() {
|
||||
// let addr: SocketAddr = ("75.75.75.75",53).to_socket_addrs().unwrap().next().unwrap();
|
||||
// let conn = TcpClientConnection::new(addr).unwrap();
|
||||
// let name = domain::Name::with_labels(vec!["www".to_string(), "sdsmt".to_string(), "edu".to_string()]);
|
||||
// let client = Client::new(conn);
|
||||
//
|
||||
// let response = client.secure_query(&name, DNSClass::IN, RecordType::NS);
|
||||
// assert!(response.is_ok(), "query failed: {}", response.unwrap_err());
|
||||
//
|
||||
// let response = response.unwrap();
|
||||
// assert_eq!(response.get_response_code(), ResponseCode::NXDomain);
|
||||
// }
|
||||
|
||||
#[allow(deprecated)]
|
||||
#[cfg(test)]
|
||||
fn create_sig0_ready_client<'a>(catalog: &'a mut Catalog) -> (Client<TestClientConnection<'a>>, Signer, domain::Name) {
|
||||
use chrono::Duration;
|
||||
use ::rr::rdata::DNSKEY;
|
||||
|
||||
let mut authority = create_example();
|
||||
authority.set_allow_update(true);
|
||||
let origin = authority.get_origin().clone();
|
||||
|
||||
let rsa = RSA::generate(512).unwrap();
|
||||
|
||||
let signer = Signer::new(Algorithm::RSASHA256,
|
||||
rsa,
|
||||
domain::Name::with_labels(vec!["trusted".to_string(), "example".to_string(), "com".to_string()]),
|
||||
Duration::max_value());
|
||||
|
||||
// insert the KEY for the trusted.example.com
|
||||
let mut auth_key = Record::with(domain::Name::with_labels(vec!["trusted".to_string(), "example".to_string(), "com".to_string()]),
|
||||
RecordType::KEY,
|
||||
Duration::minutes(5).num_seconds() as u32);
|
||||
auth_key.rdata(RData::KEY(DNSKEY::new(false, false, false, signer.get_algorithm(), signer.get_public_key())));
|
||||
authority.upsert(auth_key, 0);
|
||||
|
||||
catalog.upsert(authority.get_origin().clone(), authority);
|
||||
let client = Client::new(TestClientConnection::new(catalog));
|
||||
|
||||
(client, signer, origin)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create() {
|
||||
let mut catalog = Catalog::new();
|
||||
let (client, signer, origin) = create_sig0_ready_client(&mut catalog);
|
||||
|
||||
// create a record
|
||||
let mut record = Record::with(domain::Name::with_labels(vec!["new".to_string(), "example".to_string(), "com".to_string()]),
|
||||
RecordType::A,
|
||||
Duration::minutes(5).num_seconds() as u32);
|
||||
record.rdata(RData::A(Ipv4Addr::new(100,10,100,10)));
|
||||
|
||||
|
||||
let result = client.create(record.clone(), origin.clone(), &signer).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
let result = client.query(record.get_name(), record.get_dns_class(), record.get_rr_type()).expect("query failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
assert_eq!(result.get_answers().len(), 1);
|
||||
assert_eq!(result.get_answers()[0], record);
|
||||
|
||||
// trying to create again should error
|
||||
// TODO: it would be cool to make this
|
||||
let result = client.create(record.clone(), origin.clone(), &signer).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::YXRRSet);
|
||||
|
||||
// will fail if already set and not the same value.
|
||||
let mut record = record.clone();
|
||||
record.rdata(RData::A(Ipv4Addr::new(101,11,101,11)));
|
||||
|
||||
let result = client.create(record.clone(), origin.clone(), &signer).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::YXRRSet);
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_append() {
|
||||
let mut catalog = Catalog::new();
|
||||
let (client, signer, origin) = create_sig0_ready_client(&mut catalog);
|
||||
|
||||
// append a record
|
||||
let mut record = Record::with(domain::Name::with_labels(vec!["new".to_string(), "example".to_string(), "com".to_string()]),
|
||||
RecordType::A,
|
||||
Duration::minutes(5).num_seconds() as u32);
|
||||
record.rdata(RData::A(Ipv4Addr::new(100,10,100,10)));
|
||||
|
||||
// first check the must_exist option
|
||||
let result = client.append(record.clone(), origin.clone(), true, &signer).expect("append failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NXRRSet);
|
||||
|
||||
// next append to a non-existent RRset
|
||||
let result = client.append(record.clone(), origin.clone(), false, &signer).expect("append failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
// verify record contents
|
||||
let result = client.query(record.get_name(), record.get_dns_class(), record.get_rr_type()).expect("query failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
assert_eq!(result.get_answers().len(), 1);
|
||||
assert_eq!(result.get_answers()[0], record);
|
||||
|
||||
// will fail if already set and not the same value.
|
||||
let mut record = record.clone();
|
||||
record.rdata(RData::A(Ipv4Addr::new(101,11,101,11)));
|
||||
|
||||
let result = client.append(record.clone(), origin.clone(), true, &signer).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
let result = client.query(record.get_name(), record.get_dns_class(), record.get_rr_type()).expect("query failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
assert_eq!(result.get_answers().len(), 2);
|
||||
|
||||
assert!(result.get_answers().iter().any(|rr| if let &RData::A(ref ip) = rr.get_rdata() { *ip == Ipv4Addr::new(100,10,100,10) } else { false }));
|
||||
assert!(result.get_answers().iter().any(|rr| if let &RData::A(ref ip) = rr.get_rdata() { *ip == Ipv4Addr::new(101,11,101,11) } else { false }));
|
||||
|
||||
// show that appending the same thing again is ok, but doesn't add any records
|
||||
let result = client.append(record.clone(), origin.clone(), true, &signer).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
let result = client.query(record.get_name(), record.get_dns_class(), record.get_rr_type()).expect("query failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
assert_eq!(result.get_answers().len(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_compare_and_swap() {
|
||||
let mut catalog = Catalog::new();
|
||||
let (client, signer, origin) = create_sig0_ready_client(&mut catalog);
|
||||
|
||||
// create a record
|
||||
let mut record = Record::with(domain::Name::with_labels(vec!["new".to_string(), "example".to_string(), "com".to_string()]),
|
||||
RecordType::A,
|
||||
Duration::minutes(5).num_seconds() as u32);
|
||||
record.rdata(RData::A(Ipv4Addr::new(100,10,100,10)));
|
||||
|
||||
let result = client.create(record.clone(), origin.clone(), &signer).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
let current = record;
|
||||
let mut new = current.clone();
|
||||
new.rdata(RData::A(Ipv4Addr::new(101,11,101,11)));
|
||||
|
||||
let result = client.compare_and_swap(current.clone(), new.clone(), origin.clone(), &signer).expect("compare_and_swap failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
let result = client.query(new.get_name(), new.get_dns_class(), new.get_rr_type()).expect("query failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
assert_eq!(result.get_answers().len(), 1);
|
||||
assert!(result.get_answers().iter().any(|rr| if let &RData::A(ref ip) = rr.get_rdata() { *ip == Ipv4Addr::new(101,11,101,11) } else { false }));
|
||||
|
||||
// check the it fails if tried again.
|
||||
let mut new = new;
|
||||
new.rdata(RData::A(Ipv4Addr::new(102,12,102,12)));
|
||||
|
||||
let result = client.compare_and_swap(current, new.clone(), origin.clone(), &signer).expect("compare_and_swap failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NXRRSet);
|
||||
|
||||
let result = client.query(new.get_name(), new.get_dns_class(), new.get_rr_type()).expect("query failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
assert_eq!(result.get_answers().len(), 1);
|
||||
assert!(result.get_answers().iter().any(|rr| if let &RData::A(ref ip) = rr.get_rdata() { *ip == Ipv4Addr::new(101,11,101,11) } else { false }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delete_by_rdata() {
|
||||
let mut catalog = Catalog::new();
|
||||
let (client, signer, origin) = create_sig0_ready_client(&mut catalog);
|
||||
|
||||
// append a record
|
||||
let mut record = Record::with(domain::Name::with_labels(vec!["new".to_string(), "example".to_string(), "com".to_string()]),
|
||||
RecordType::A,
|
||||
Duration::minutes(5).num_seconds() as u32);
|
||||
record.rdata(RData::A(Ipv4Addr::new(100,10,100,10)));
|
||||
|
||||
// first check the must_exist option
|
||||
let result = client.delete_by_rdata(record.clone(), origin.clone(), &signer).expect("delete failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
// next create to a non-existent RRset
|
||||
let result = client.create(record.clone(), origin.clone(), &signer).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
let mut record = record.clone();
|
||||
record.rdata(RData::A(Ipv4Addr::new(101,11,101,11)));
|
||||
let result = client.append(record.clone(), origin.clone(), true, &signer).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
// verify record contents
|
||||
let result = client.delete_by_rdata(record.clone(), origin.clone(), &signer).expect("delete failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
let result = client.query(record.get_name(), record.get_dns_class(), record.get_rr_type()).expect("query failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
assert_eq!(result.get_answers().len(), 1);
|
||||
assert!(result.get_answers().iter().any(|rr| if let &RData::A(ref ip) = rr.get_rdata() { *ip == Ipv4Addr::new(100,10,100,10) } else { false }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delete_rrset() {
|
||||
let mut catalog = Catalog::new();
|
||||
let (client, signer, origin) = create_sig0_ready_client(&mut catalog);
|
||||
|
||||
// append a record
|
||||
let mut record = Record::with(domain::Name::with_labels(vec!["new".to_string(), "example".to_string(), "com".to_string()]),
|
||||
RecordType::A,
|
||||
Duration::minutes(5).num_seconds() as u32);
|
||||
record.rdata(RData::A(Ipv4Addr::new(100,10,100,10)));
|
||||
|
||||
// first check the must_exist option
|
||||
let result = client.delete_rrset(record.clone(), origin.clone(), &signer).expect("delete failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
// next create to a non-existent RRset
|
||||
let result = client.create(record.clone(), origin.clone(), &signer).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
let mut record = record.clone();
|
||||
record.rdata(RData::A(Ipv4Addr::new(101,11,101,11)));
|
||||
let result = client.append(record.clone(), origin.clone(), true, &signer).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
// verify record contents
|
||||
let result = client.delete_rrset(record.clone(), origin.clone(), &signer).expect("delete failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
let result = client.query(record.get_name(), record.get_dns_class(), record.get_rr_type()).expect("query failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NXDomain);
|
||||
assert_eq!(result.get_answers().len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delete_all() {
|
||||
let mut catalog = Catalog::new();
|
||||
let (client, signer, origin) = create_sig0_ready_client(&mut catalog);
|
||||
|
||||
// append a record
|
||||
let mut record = Record::with(domain::Name::with_labels(vec!["new".to_string(), "example".to_string(), "com".to_string()]),
|
||||
RecordType::A,
|
||||
Duration::minutes(5).num_seconds() as u32);
|
||||
record.rdata(RData::A(Ipv4Addr::new(100,10,100,10)));
|
||||
|
||||
// first check the must_exist option
|
||||
let result = client.delete_all(record.get_name().clone(), origin.clone(), DNSClass::IN, &signer).expect("delete failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
// next create to a non-existent RRset
|
||||
let result = client.create(record.clone(), origin.clone(), &signer).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
let mut record = record.clone();
|
||||
record.rr_type(RecordType::AAAA);
|
||||
record.rdata(RData::AAAA(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8)));
|
||||
let result = client.create(record.clone(), origin.clone(), &signer).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
// verify record contents
|
||||
let result = client.delete_all(record.get_name().clone(), origin.clone(), DNSClass::IN, &signer).expect("delete failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
let result = client.query(record.get_name(), record.get_dns_class(), RecordType::A).expect("query failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NXDomain);
|
||||
assert_eq!(result.get_answers().len(), 0);
|
||||
|
||||
let result = client.query(record.get_name(), record.get_dns_class(), RecordType::AAAA).expect("query failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NXDomain);
|
||||
assert_eq!(result.get_answers().len(), 0);
|
||||
}
|
||||
}
|
30
client/src/client/client_connection.rs
Normal file
30
client/src/client/client_connection.rs
Normal file
@ -0,0 +1,30 @@
|
||||
// Copyright (C) 2016 Benjamin Fry <benjaminfry@me.com>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Trait for client connections
|
||||
|
||||
use std::fmt::Debug;
|
||||
|
||||
use ::error::*;
|
||||
|
||||
/// Trait for client connections
|
||||
pub trait ClientConnection: Sized+Debug {
|
||||
/// Sends a serialized message to via this connection, returning the serialized response.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `bytes` - the serialized Message
|
||||
fn send(&mut self, bytes: Vec<u8>) -> ClientResult<Vec<u8>>;
|
||||
// TODO: split connect, send and read...
|
||||
}
|
@ -777,524 +777,3 @@ pub trait ClientHandle: Clone {
|
||||
self.send(message)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
use std;
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
use std::net::*;
|
||||
|
||||
use chrono::Duration;
|
||||
use futures::{Async, Future, finished, Poll};
|
||||
use futures::stream::{Fuse, Stream};
|
||||
use futures::task::park;
|
||||
use openssl::crypto::rsa::RSA;
|
||||
use tokio_core::reactor::{Core, Handle};
|
||||
use tokio_core::channel::{channel, Receiver};
|
||||
|
||||
use super::{ClientFuture, BasicClientHandle, ClientHandle, StreamHandle};
|
||||
use ::error::*;
|
||||
use ::op::{Message, ResponseCode};
|
||||
use ::authority::Catalog;
|
||||
use ::authority::authority_tests::{create_example};
|
||||
use ::rr::domain;
|
||||
use ::rr::{DNSClass, RData, Record, RecordType};
|
||||
use ::rr::dnssec::{Algorithm, Signer};
|
||||
use ::serialize::binary::{BinDecoder, BinEncoder, BinSerializable};
|
||||
use ::udp::UdpClientStream;
|
||||
use ::tcp::TcpClientStream;
|
||||
|
||||
pub struct TestClientStream {
|
||||
catalog: Catalog,
|
||||
outbound_messages: Fuse<Receiver<Vec<u8>>>,
|
||||
}
|
||||
|
||||
impl TestClientStream {
|
||||
pub fn new(catalog: Catalog, loop_handle: Handle) -> (Box<Future<Item=Self, Error=io::Error>>, StreamHandle) {
|
||||
let (message_sender, outbound_messages) = channel(&loop_handle).expect("somethings wrong with the event loop");
|
||||
|
||||
let stream: Box<Future<Item=TestClientStream, Error=io::Error>> = Box::new(finished(
|
||||
TestClientStream { catalog: catalog, outbound_messages: outbound_messages.fuse() }
|
||||
));
|
||||
|
||||
(stream, message_sender)
|
||||
}
|
||||
}
|
||||
|
||||
impl Stream for TestClientStream {
|
||||
type Item = Vec<u8>;
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
||||
match try!(self.outbound_messages.poll()) {
|
||||
// already handled above, here to make sure the poll() pops the next message
|
||||
Async::Ready(Some(bytes)) => {
|
||||
let mut decoder = BinDecoder::new(&bytes);
|
||||
|
||||
let message = Message::read(&mut decoder).expect("could not decode message");
|
||||
let response = self.catalog.handle_request(&message);
|
||||
|
||||
let mut buf = Vec::with_capacity(512);
|
||||
{
|
||||
let mut encoder = BinEncoder::new(&mut buf);
|
||||
response.emit(&mut encoder).expect("could not encode");
|
||||
}
|
||||
|
||||
Ok(Async::Ready(Some(buf)))
|
||||
},
|
||||
// now we get to drop through to the receives...
|
||||
// TODO: should we also return None if there are no more messages to send?
|
||||
_ => {
|
||||
park().unpark();
|
||||
Ok(Async::NotReady)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for TestClientStream {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "TestClientStream catalog")
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_query_nonet() {
|
||||
let authority = create_example();
|
||||
let mut catalog = Catalog::new();
|
||||
catalog.upsert(authority.get_origin().clone(), authority);
|
||||
|
||||
let mut io_loop = Core::new().unwrap();
|
||||
let (stream, sender) = TestClientStream::new(catalog, io_loop.handle());
|
||||
let client = ClientFuture::new(stream, sender, io_loop.handle(), None);
|
||||
|
||||
io_loop.run(test_query(&client)).unwrap();
|
||||
io_loop.run(test_query(&client)).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_query_udp_ipv4() {
|
||||
use std::net::{SocketAddr, ToSocketAddrs};
|
||||
use tokio_core::reactor::Core;
|
||||
|
||||
let mut io_loop = Core::new().unwrap();
|
||||
let addr: SocketAddr = ("8.8.8.8",53).to_socket_addrs().unwrap().next().unwrap();
|
||||
let (stream, sender) = UdpClientStream::new(addr, io_loop.handle());
|
||||
let client = ClientFuture::new(stream, sender, io_loop.handle(), None);
|
||||
|
||||
// TODO: timeouts on these requests so that the test doesn't hang
|
||||
io_loop.run(test_query(&client)).unwrap();
|
||||
io_loop.run(test_query(&client)).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_query_udp_ipv6() {
|
||||
use std::net::{SocketAddr, ToSocketAddrs};
|
||||
use tokio_core::reactor::Core;
|
||||
|
||||
let mut io_loop = Core::new().unwrap();
|
||||
let addr: SocketAddr = ("2001:4860:4860::8888",53).to_socket_addrs().unwrap().next().unwrap();
|
||||
let (stream, sender) = UdpClientStream::new(addr, io_loop.handle());
|
||||
let client = ClientFuture::new(stream, sender, io_loop.handle(), None);
|
||||
|
||||
// TODO: timeouts on these requests so that the test doesn't hang
|
||||
io_loop.run(test_query(&client)).unwrap();
|
||||
io_loop.run(test_query(&client)).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_query_tcp_ipv4() {
|
||||
use std::net::{SocketAddr, ToSocketAddrs};
|
||||
use tokio_core::reactor::Core;
|
||||
|
||||
let mut io_loop = Core::new().unwrap();
|
||||
let addr: SocketAddr = ("8.8.8.8",53).to_socket_addrs().unwrap().next().unwrap();
|
||||
let (stream, sender) = TcpClientStream::new(addr, io_loop.handle());
|
||||
let client = ClientFuture::new(stream, sender, io_loop.handle(), None);
|
||||
|
||||
// TODO: timeouts on these requests so that the test doesn't hang
|
||||
io_loop.run(test_query(&client)).unwrap();
|
||||
io_loop.run(test_query(&client)).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_query_tcp_ipv6() {
|
||||
use std::net::{SocketAddr, ToSocketAddrs};
|
||||
use tokio_core::reactor::Core;
|
||||
|
||||
let mut io_loop = Core::new().unwrap();
|
||||
let addr: SocketAddr = ("2001:4860:4860::8888",53).to_socket_addrs().unwrap().next().unwrap();
|
||||
let (stream, sender) = TcpClientStream::new(addr, io_loop.handle());
|
||||
let client = ClientFuture::new(stream, sender, io_loop.handle(), None);
|
||||
|
||||
// TODO: timeouts on these requests so that the test doesn't hang
|
||||
io_loop.run(test_query(&client)).unwrap();
|
||||
io_loop.run(test_query(&client)).unwrap();
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn test_query(client: &BasicClientHandle) -> Box<Future<Item=(), Error=()>> {
|
||||
use std::net::Ipv4Addr;
|
||||
use std::cmp::Ordering;
|
||||
use ::rr::RData;
|
||||
|
||||
use log::LogLevel;
|
||||
use ::logger::TrustDnsLogger;
|
||||
|
||||
TrustDnsLogger::enable_logging(LogLevel::Debug);
|
||||
|
||||
let name = domain::Name::with_labels(vec!["WWW".to_string(), "example".to_string(), "com".to_string()]);
|
||||
|
||||
Box::new(client.query(name.clone(), DNSClass::IN, RecordType::A)
|
||||
.map(move |response| {
|
||||
println!("response records: {:?}", response);
|
||||
assert_eq!(response.get_queries().first().expect("expected query").get_name().cmp_with_case(&name, false), Ordering::Equal);
|
||||
|
||||
let record = &response.get_answers()[0];
|
||||
assert_eq!(record.get_name(), &name);
|
||||
assert_eq!(record.get_rr_type(), RecordType::A);
|
||||
assert_eq!(record.get_dns_class(), DNSClass::IN);
|
||||
|
||||
if let &RData::A(ref address) = record.get_rdata() {
|
||||
assert_eq!(address, &Ipv4Addr::new(93,184,216,34))
|
||||
} else {
|
||||
assert!(false);
|
||||
}
|
||||
})
|
||||
.map_err(|e| {
|
||||
assert!(false, "query failed: {}", e);
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
// update tests
|
||||
//
|
||||
|
||||
/// create a client with a sig0 section
|
||||
fn create_sig0_ready_client(io_loop: &Core) -> (BasicClientHandle, domain::Name) {
|
||||
use chrono::Duration;
|
||||
use ::rr::rdata::DNSKEY;
|
||||
|
||||
let mut authority = create_example();
|
||||
authority.set_allow_update(true);
|
||||
let origin = authority.get_origin().clone();
|
||||
|
||||
let rsa = RSA::generate(512).unwrap();
|
||||
|
||||
let signer = Signer::new(Algorithm::RSASHA256,
|
||||
rsa,
|
||||
domain::Name::with_labels(vec!["trusted".to_string(), "example".to_string(), "com".to_string()]),
|
||||
Duration::max_value());
|
||||
|
||||
// insert the KEY for the trusted.example.com
|
||||
let mut auth_key = Record::with(domain::Name::with_labels(vec!["trusted".to_string(), "example".to_string(), "com".to_string()]),
|
||||
RecordType::KEY,
|
||||
Duration::minutes(5).num_seconds() as u32);
|
||||
auth_key.rdata(RData::KEY(DNSKEY::new(false, false, false, signer.get_algorithm(), signer.get_public_key())));
|
||||
authority.upsert(auth_key, 0);
|
||||
|
||||
// setup the catalog
|
||||
let mut catalog = Catalog::new();
|
||||
catalog.upsert(authority.get_origin().clone(), authority);
|
||||
|
||||
let (stream, sender) = TestClientStream::new(catalog, io_loop.handle());
|
||||
let client = ClientFuture::new(stream, sender, io_loop.handle(), Some(signer));
|
||||
|
||||
(client, origin)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create() {
|
||||
let mut io_loop = Core::new().unwrap();
|
||||
let (client, origin) = create_sig0_ready_client(&io_loop);
|
||||
|
||||
// create a record
|
||||
let mut record = Record::with(domain::Name::with_labels(vec!["new".to_string(), "example".to_string(), "com".to_string()]),
|
||||
RecordType::A,
|
||||
Duration::minutes(5).num_seconds() as u32);
|
||||
record.rdata(RData::A(Ipv4Addr::new(100,10,100,10)));
|
||||
|
||||
|
||||
let result = io_loop.run(client.create(record.clone(), origin.clone())).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
let result = io_loop.run(client.query(record.get_name().clone(), record.get_dns_class(), record.get_rr_type())).expect("query failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
assert_eq!(result.get_answers().len(), 1);
|
||||
assert_eq!(result.get_answers()[0], record);
|
||||
|
||||
// trying to create again should error
|
||||
// TODO: it would be cool to make this
|
||||
let result = io_loop.run(client.create(record.clone(), origin.clone())).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::YXRRSet);
|
||||
|
||||
// will fail if already set and not the same value.
|
||||
let mut record = record.clone();
|
||||
record.rdata(RData::A(Ipv4Addr::new(101,11,101,11)));
|
||||
|
||||
let result = io_loop.run(client.create(record.clone(), origin.clone())).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::YXRRSet);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_append() {
|
||||
let mut io_loop = Core::new().unwrap();
|
||||
let (client, origin) = create_sig0_ready_client(&io_loop);
|
||||
|
||||
// append a record
|
||||
let mut record = Record::with(domain::Name::with_labels(vec!["new".to_string(), "example".to_string(), "com".to_string()]),
|
||||
RecordType::A,
|
||||
Duration::minutes(5).num_seconds() as u32);
|
||||
record.rdata(RData::A(Ipv4Addr::new(100,10,100,10)));
|
||||
|
||||
// first check the must_exist option
|
||||
let result = io_loop.run(client.append(record.clone(), origin.clone(), true)).expect("append failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NXRRSet);
|
||||
|
||||
// next append to a non-existent RRset
|
||||
let result = io_loop.run(client.append(record.clone(), origin.clone(), false)).expect("append failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
// verify record contents
|
||||
let result = io_loop.run(client.query(record.get_name().clone(), record.get_dns_class(), record.get_rr_type())).expect("query failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
assert_eq!(result.get_answers().len(), 1);
|
||||
assert_eq!(result.get_answers()[0], record);
|
||||
|
||||
// will fail if already set and not the same value.
|
||||
let mut record = record.clone();
|
||||
record.rdata(RData::A(Ipv4Addr::new(101,11,101,11)));
|
||||
|
||||
let result = io_loop.run(client.append(record.clone(), origin.clone(), true)).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
let result = io_loop.run(client.query(record.get_name().clone(), record.get_dns_class(), record.get_rr_type())).expect("query failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
assert_eq!(result.get_answers().len(), 2);
|
||||
|
||||
assert!(result.get_answers().iter().any(|rr| if let &RData::A(ref ip) = rr.get_rdata() { *ip == Ipv4Addr::new(100,10,100,10) } else { false }));
|
||||
assert!(result.get_answers().iter().any(|rr| if let &RData::A(ref ip) = rr.get_rdata() { *ip == Ipv4Addr::new(101,11,101,11) } else { false }));
|
||||
|
||||
// show that appending the same thing again is ok, but doesn't add any records
|
||||
let result = io_loop.run(client.append(record.clone(), origin.clone(), true)).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
let result = io_loop.run(client.query(record.get_name().clone(), record.get_dns_class(), record.get_rr_type())).expect("query failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
assert_eq!(result.get_answers().len(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_compare_and_swap() {
|
||||
let mut io_loop = Core::new().unwrap();
|
||||
let (client, origin) = create_sig0_ready_client(&io_loop);
|
||||
|
||||
// create a record
|
||||
let mut record = Record::with(domain::Name::with_labels(vec!["new".to_string(), "example".to_string(), "com".to_string()]),
|
||||
RecordType::A,
|
||||
Duration::minutes(5).num_seconds() as u32);
|
||||
record.rdata(RData::A(Ipv4Addr::new(100,10,100,10)));
|
||||
|
||||
let result = io_loop.run(client.create(record.clone(), origin.clone())).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
let current = record;
|
||||
let mut new = current.clone();
|
||||
new.rdata(RData::A(Ipv4Addr::new(101,11,101,11)));
|
||||
|
||||
let result = io_loop.run(client.compare_and_swap(current.clone(), new.clone(), origin.clone())).expect("compare_and_swap failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
let result = io_loop.run(client.query(new.get_name().clone(), new.get_dns_class(), new.get_rr_type())).expect("query failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
assert_eq!(result.get_answers().len(), 1);
|
||||
assert!(result.get_answers().iter().any(|rr| if let &RData::A(ref ip) = rr.get_rdata() { *ip == Ipv4Addr::new(101,11,101,11) } else { false }));
|
||||
|
||||
// check the it fails if tried again.
|
||||
let mut new = new;
|
||||
new.rdata(RData::A(Ipv4Addr::new(102,12,102,12)));
|
||||
|
||||
let result = io_loop.run(client.compare_and_swap(current, new.clone(), origin.clone())).expect("compare_and_swap failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NXRRSet);
|
||||
|
||||
let result = io_loop.run(client.query(new.get_name().clone(), new.get_dns_class(), new.get_rr_type())).expect("query failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
assert_eq!(result.get_answers().len(), 1);
|
||||
assert!(result.get_answers().iter().any(|rr| if let &RData::A(ref ip) = rr.get_rdata() { *ip == Ipv4Addr::new(101,11,101,11) } else { false }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delete_by_rdata() {
|
||||
let mut io_loop = Core::new().unwrap();
|
||||
let (client, origin) = create_sig0_ready_client(&io_loop);
|
||||
|
||||
// append a record
|
||||
let mut record = Record::with(domain::Name::with_labels(vec!["new".to_string(), "example".to_string(), "com".to_string()]),
|
||||
RecordType::A,
|
||||
Duration::minutes(5).num_seconds() as u32);
|
||||
record.rdata(RData::A(Ipv4Addr::new(100,10,100,10)));
|
||||
|
||||
// first check the must_exist option
|
||||
let result = io_loop.run(client.delete_by_rdata(record.clone(), origin.clone())).expect("delete failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
// next create to a non-existent RRset
|
||||
let result = io_loop.run(client.create(record.clone(), origin.clone())).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
let mut record = record.clone();
|
||||
record.rdata(RData::A(Ipv4Addr::new(101,11,101,11)));
|
||||
let result = io_loop.run(client.append(record.clone(), origin.clone(), true)).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
// verify record contents
|
||||
let result = io_loop.run(client.delete_by_rdata(record.clone(), origin.clone())).expect("delete failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
let result = io_loop.run(client.query(record.get_name().clone(), record.get_dns_class(), record.get_rr_type())).expect("query failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
assert_eq!(result.get_answers().len(), 1);
|
||||
assert!(result.get_answers().iter().any(|rr| if let &RData::A(ref ip) = rr.get_rdata() { *ip == Ipv4Addr::new(100,10,100,10) } else { false }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delete_rrset() {
|
||||
let mut io_loop = Core::new().unwrap();
|
||||
let (client, origin) = create_sig0_ready_client(&io_loop);
|
||||
|
||||
// append a record
|
||||
let mut record = Record::with(domain::Name::with_labels(vec!["new".to_string(), "example".to_string(), "com".to_string()]),
|
||||
RecordType::A,
|
||||
Duration::minutes(5).num_seconds() as u32);
|
||||
record.rdata(RData::A(Ipv4Addr::new(100,10,100,10)));
|
||||
|
||||
// first check the must_exist option
|
||||
let result = io_loop.run(client.delete_rrset(record.clone(), origin.clone())).expect("delete failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
// next create to a non-existent RRset
|
||||
let result = io_loop.run(client.create(record.clone(), origin.clone())).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
let mut record = record.clone();
|
||||
record.rdata(RData::A(Ipv4Addr::new(101,11,101,11)));
|
||||
let result = io_loop.run(client.append(record.clone(), origin.clone(), true)).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
// verify record contents
|
||||
let result = io_loop.run(client.delete_rrset(record.clone(), origin.clone())).expect("delete failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
let result = io_loop.run(client.query(record.get_name().clone(), record.get_dns_class(), record.get_rr_type())).expect("query failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NXDomain);
|
||||
assert_eq!(result.get_answers().len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delete_all() {
|
||||
let mut io_loop = Core::new().unwrap();
|
||||
let (client, origin) = create_sig0_ready_client(&io_loop);
|
||||
|
||||
// append a record
|
||||
let mut record = Record::with(domain::Name::with_labels(vec!["new".to_string(), "example".to_string(), "com".to_string()]),
|
||||
RecordType::A,
|
||||
Duration::minutes(5).num_seconds() as u32);
|
||||
record.rdata(RData::A(Ipv4Addr::new(100,10,100,10)));
|
||||
|
||||
// first check the must_exist option
|
||||
let result = io_loop.run(client.delete_all(record.get_name().clone(), origin.clone(), DNSClass::IN)).expect("delete failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
// next create to a non-existent RRset
|
||||
let result = io_loop.run(client.create(record.clone(), origin.clone())).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
let mut record = record.clone();
|
||||
record.rr_type(RecordType::AAAA);
|
||||
record.rdata(RData::AAAA(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8)));
|
||||
let result = io_loop.run(client.create(record.clone(), origin.clone())).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
// verify record contents
|
||||
let result = io_loop.run(client.delete_all(record.get_name().clone(), origin.clone(), DNSClass::IN)).expect("delete failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
let result = io_loop.run(client.query(record.get_name().clone(), record.get_dns_class(), RecordType::A)).expect("query failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NXDomain);
|
||||
assert_eq!(result.get_answers().len(), 0);
|
||||
|
||||
let result = io_loop.run(client.query(record.get_name().clone(), record.get_dns_class(), RecordType::AAAA)).expect("query failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NXDomain);
|
||||
assert_eq!(result.get_answers().len(), 0);
|
||||
}
|
||||
|
||||
// need to do something with the message channel, otherwise the ClientFuture will think there
|
||||
// is no one listening to messages and shutdown...
|
||||
#[allow(dead_code)]
|
||||
pub struct NeverReturnsClientStream {
|
||||
outbound_messages: Fuse<Receiver<Vec<u8>>>,
|
||||
}
|
||||
|
||||
impl NeverReturnsClientStream {
|
||||
pub fn new(loop_handle: Handle) -> (Box<Future<Item=Self, Error=io::Error>>, StreamHandle) {
|
||||
let (message_sender, outbound_messages) = channel(&loop_handle).expect("somethings wrong with the event loop");
|
||||
|
||||
let stream: Box<Future<Item=NeverReturnsClientStream, Error=io::Error>> = Box::new(finished(
|
||||
NeverReturnsClientStream {
|
||||
outbound_messages: outbound_messages.fuse()
|
||||
}
|
||||
));
|
||||
|
||||
(stream, message_sender)
|
||||
}
|
||||
}
|
||||
|
||||
impl Stream for NeverReturnsClientStream {
|
||||
type Item = Vec<u8>;
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
||||
// always not ready...
|
||||
park().unpark();
|
||||
Ok(Async::NotReady)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for NeverReturnsClientStream {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "TestClientStream catalog")
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_timeout_query_nonet() {
|
||||
let authority = create_example();
|
||||
let mut catalog = Catalog::new();
|
||||
catalog.upsert(authority.get_origin().clone(), authority);
|
||||
|
||||
let mut io_loop = Core::new().unwrap();
|
||||
let (stream, sender) = NeverReturnsClientStream::new(io_loop.handle());
|
||||
let client = ClientFuture::with_timeout(stream, sender, io_loop.handle(),
|
||||
std::time::Duration::from_millis(1), None);
|
||||
|
||||
let name = domain::Name::with_labels(vec!["www".to_string(), "example".to_string(), "com".to_string()]);
|
||||
|
||||
if let &ClientErrorKind::Timeout = io_loop.run(client.query(name.clone(), DNSClass::IN, RecordType::A)).unwrap_err().kind() {
|
||||
()
|
||||
} else {
|
||||
assert!(false);
|
||||
}
|
||||
|
||||
|
||||
// test that we don't have any thing funky with registering new timeouts, etc...
|
||||
if let &ClientErrorKind::Timeout = io_loop.run(client.query(name.clone(), DNSClass::IN, RecordType::AAAA)).unwrap_err().kind() {
|
||||
()
|
||||
} else {
|
||||
assert!(false);
|
||||
}
|
||||
}
|
||||
}
|
@ -28,12 +28,7 @@ mod secure_client_handle;
|
||||
#[allow(deprecated)]
|
||||
pub use self::client::Client;
|
||||
pub use self::client_connection::ClientConnection;
|
||||
pub use self::client_future::{ClientFuture, BasicClientHandle, ClientHandle};
|
||||
pub use self::client_future::{ClientFuture, BasicClientHandle, ClientHandle, StreamHandle};
|
||||
pub use self::memoize_client_handle::MemoizeClientHandle;
|
||||
pub use self::retry_client_handle::RetryClientHandle;
|
||||
pub use self::secure_client_handle::SecureClientHandle;
|
||||
|
||||
#[cfg(test)]
|
||||
pub use self::client_connection::test::TestClientConnection;
|
||||
#[cfg(test)]
|
||||
pub use self::client_future::test::TestClientStream;
|
@ -748,259 +748,3 @@ fn verify_nsec(query: &Query, nsecs: Vec<&Record>) -> bool {
|
||||
// if we got here, then there are no matching NSEC records, no validation
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
use std::net::*;
|
||||
|
||||
use tokio_core::reactor::Core;
|
||||
|
||||
use ::authority::Catalog;
|
||||
use ::authority::authority_tests::create_secure_example;
|
||||
use ::client::{BasicClientHandle, ClientFuture, ClientHandle, MemoizeClientHandle, SecureClientHandle, TestClientStream};
|
||||
use ::op::ResponseCode;
|
||||
use ::rr::domain;
|
||||
use ::rr::{DNSClass, RData, RecordType};
|
||||
use ::rr::dnssec::TrustAnchor;
|
||||
use ::tcp::TcpClientStream;
|
||||
use ::udp::UdpClientStream;
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_secure_query_example_nonet() {
|
||||
with_nonet(test_secure_query_example);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_secure_query_example_udp() {
|
||||
with_udp(test_secure_query_example);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_secure_query_example_tcp() {
|
||||
with_tcp(test_secure_query_example);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn test_secure_query_example<H>(client: SecureClientHandle<H>, mut io_loop: Core)
|
||||
where H: ClientHandle + 'static {
|
||||
let name = domain::Name::with_labels(vec!["www".to_string(), "example".to_string(), "com".to_string()]);
|
||||
let response = io_loop.run(client.query(name.clone(), DNSClass::IN, RecordType::A)).expect("query failed");
|
||||
|
||||
println!("response records: {:?}", response);
|
||||
assert!(response.get_edns().expect("edns not here").is_dnssec_ok());
|
||||
|
||||
assert!(!response.get_answers().is_empty());
|
||||
let record = &response.get_answers()[0];
|
||||
assert_eq!(record.get_name(), &name);
|
||||
assert_eq!(record.get_rr_type(), RecordType::A);
|
||||
assert_eq!(record.get_dns_class(), DNSClass::IN);
|
||||
|
||||
if let &RData::A(ref address) = record.get_rdata() {
|
||||
assert_eq!(address, &Ipv4Addr::new(93,184,216,34))
|
||||
} else {
|
||||
assert!(false);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nsec_query_example_nonet() {
|
||||
with_nonet(test_nsec_query_example);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_nsec_query_example_udp() {
|
||||
with_udp(test_nsec_query_example);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_nsec_query_example_tcp() {
|
||||
with_tcp(test_nsec_query_example);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn test_nsec_query_example<H>(client: SecureClientHandle<H>, mut io_loop: Core)
|
||||
where H: ClientHandle + 'static {
|
||||
let name = domain::Name::with_labels(vec!["none".to_string(), "example".to_string(), "com".to_string()]);
|
||||
|
||||
let response = io_loop.run(client.query(name.clone(), DNSClass::IN, RecordType::A)).expect("query failed");
|
||||
assert_eq!(response.get_response_code(), ResponseCode::NXDomain);
|
||||
}
|
||||
|
||||
// TODO: NSEC response code wrong in Trust-DNS? Issue #53
|
||||
// #[test]
|
||||
// fn test_nsec_query_type_nonet() {
|
||||
// with_nonet(test_nsec_query_type);
|
||||
// }
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_nsec_query_type_udp() {
|
||||
with_udp(test_nsec_query_type);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_nsec_query_type_tcp() {
|
||||
with_tcp(test_nsec_query_type);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn test_nsec_query_type<H>(client: SecureClientHandle<H>, mut io_loop: Core)
|
||||
where H: ClientHandle + 'static {
|
||||
let name = domain::Name::with_labels(vec!["www".to_string(), "example".to_string(), "com".to_string()]);
|
||||
|
||||
let response = io_loop.run(client.query(name.clone(), DNSClass::IN, RecordType::NS)).expect("query failed");
|
||||
|
||||
assert_eq!(response.get_response_code(), ResponseCode::NoError);
|
||||
assert!(response.get_answers().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_dnssec_rollernet_td_udp() {
|
||||
with_udp(dnssec_rollernet_td_test);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_dnssec_rollernet_td_tcp() {
|
||||
with_udp(dnssec_rollernet_td_test);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_dnssec_rollernet_td_tcp_mixed_case() {
|
||||
with_tcp(dnssec_rollernet_td_mixed_case_test);
|
||||
}
|
||||
|
||||
fn dnssec_rollernet_td_test<H>(client: SecureClientHandle<H>, mut io_loop: Core)
|
||||
where H: ClientHandle + 'static {
|
||||
let name = domain::Name::parse("rollernet.us.", None).unwrap();
|
||||
|
||||
let response = io_loop.run(client.query(name.clone(), DNSClass::IN, RecordType::DS)).expect("query failed");
|
||||
|
||||
assert_eq!(response.get_response_code(), ResponseCode::NoError);
|
||||
// rollernet doesn't have any DS records...
|
||||
// would have failed validation
|
||||
assert!(response.get_answers().is_empty());
|
||||
}
|
||||
|
||||
fn dnssec_rollernet_td_mixed_case_test<H>(client: SecureClientHandle<H>, mut io_loop: Core)
|
||||
where H: ClientHandle + 'static {
|
||||
let name = domain::Name::parse("RollErnet.Us.", None).unwrap();
|
||||
|
||||
let response = io_loop.run(client.query(name.clone(), DNSClass::IN, RecordType::DS)).expect("query failed");
|
||||
|
||||
assert_eq!(response.get_response_code(), ResponseCode::NoError);
|
||||
// rollernet doesn't have any DS records...
|
||||
// would have failed validation
|
||||
assert!(response.get_answers().is_empty());
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn with_nonet<F>(test: F) where F: Fn(SecureClientHandle<MemoizeClientHandle<BasicClientHandle>>, Core) {
|
||||
use log::LogLevel;
|
||||
use ::logger::TrustDnsLogger;
|
||||
TrustDnsLogger::enable_logging(LogLevel::Debug);
|
||||
|
||||
use std;
|
||||
let succeeded = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
|
||||
let succeeded_clone = succeeded.clone();
|
||||
std::thread::Builder::new().name("thread_killer".to_string()).spawn(move || {
|
||||
let succeeded = succeeded_clone.clone();
|
||||
for _ in 0..15 {
|
||||
std::thread::sleep(std::time::Duration::from_secs(1));
|
||||
if succeeded.load(std::sync::atomic::Ordering::Relaxed) { return }
|
||||
}
|
||||
|
||||
panic!("timeout");
|
||||
}).unwrap();
|
||||
|
||||
let authority = create_secure_example();
|
||||
|
||||
let public_key = {
|
||||
let signers = authority.get_secure_keys();
|
||||
signers.first().expect("expected a key in the authority").get_public_key()
|
||||
};
|
||||
|
||||
let mut catalog = Catalog::new();
|
||||
catalog.upsert(authority.get_origin().clone(), authority);
|
||||
|
||||
let mut trust_anchor = TrustAnchor::new();
|
||||
trust_anchor.insert_trust_anchor(public_key);
|
||||
|
||||
let io_loop = Core::new().unwrap();
|
||||
let (stream, sender) = TestClientStream::new(catalog, io_loop.handle());
|
||||
let client = ClientFuture::new(stream, sender, io_loop.handle(), None);
|
||||
let client = MemoizeClientHandle::new(client);
|
||||
let secure_client = SecureClientHandle::with_trust_anchor(client, trust_anchor);
|
||||
|
||||
test(secure_client, io_loop);
|
||||
succeeded.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn with_udp<F>(test: F) where F: Fn(SecureClientHandle<MemoizeClientHandle<BasicClientHandle>>, Core) {
|
||||
use log::LogLevel;
|
||||
use ::logger::TrustDnsLogger;
|
||||
TrustDnsLogger::enable_logging(LogLevel::Debug);
|
||||
|
||||
use std;
|
||||
let succeeded = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
|
||||
let succeeded_clone = succeeded.clone();
|
||||
std::thread::Builder::new().name("thread_killer".to_string()).spawn(move || {
|
||||
let succeeded = succeeded_clone.clone();
|
||||
for _ in 0..15 {
|
||||
std::thread::sleep(std::time::Duration::from_secs(1));
|
||||
if succeeded.load(std::sync::atomic::Ordering::Relaxed) { return }
|
||||
}
|
||||
|
||||
panic!("timeout");
|
||||
}).unwrap();
|
||||
|
||||
let io_loop = Core::new().unwrap();
|
||||
let addr: SocketAddr = ("8.8.8.8",53).to_socket_addrs().unwrap().next().unwrap();
|
||||
let (stream, sender) = UdpClientStream::new(addr, io_loop.handle());
|
||||
let client = ClientFuture::new(stream, sender, io_loop.handle(), None);
|
||||
let client = MemoizeClientHandle::new(client);
|
||||
let secure_client = SecureClientHandle::new(client);
|
||||
|
||||
test(secure_client, io_loop);
|
||||
succeeded.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn with_tcp<F>(test: F) where F: Fn(SecureClientHandle<MemoizeClientHandle<BasicClientHandle>>, Core) {
|
||||
use log::LogLevel;
|
||||
use ::logger::TrustDnsLogger;
|
||||
TrustDnsLogger::enable_logging(LogLevel::Debug);
|
||||
|
||||
use std;
|
||||
let succeeded = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
|
||||
let succeeded_clone = succeeded.clone();
|
||||
std::thread::Builder::new().name("thread_killer".to_string()).spawn(move || {
|
||||
let succeeded = succeeded_clone.clone();
|
||||
for _ in 0..15 {
|
||||
std::thread::sleep(std::time::Duration::from_secs(1));
|
||||
if succeeded.load(std::sync::atomic::Ordering::Relaxed) { return }
|
||||
}
|
||||
|
||||
panic!("timeout");
|
||||
}).unwrap();
|
||||
|
||||
let io_loop = Core::new().unwrap();
|
||||
let addr: SocketAddr = ("8.8.8.8",53).to_socket_addrs().unwrap().next().unwrap();
|
||||
let (stream, sender) = TcpClientStream::new(addr, io_loop.handle());
|
||||
let client = ClientFuture::new(stream, sender, io_loop.handle(), None);
|
||||
let client = MemoizeClientHandle::new(client);
|
||||
let secure_client = SecureClientHandle::new(client);
|
||||
|
||||
test(secure_client, io_loop);
|
||||
succeeded.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
}
|
@ -21,37 +21,27 @@ mod encode_error;
|
||||
mod client_error;
|
||||
mod lexer_error;
|
||||
mod parse_error;
|
||||
mod persistence_error;
|
||||
mod config_error;
|
||||
|
||||
pub use self::decode_error::Error as DecodeError;
|
||||
pub use self::encode_error::Error as EncodeError;
|
||||
pub use self::client_error::Error as ClientError;
|
||||
pub use self::lexer_error::Error as LexerError;
|
||||
pub use self::parse_error::Error as ParseError;
|
||||
pub use self::persistence_error::Error as PersistenceError;
|
||||
pub use self::config_error::Error as ConfigError;
|
||||
|
||||
pub use self::decode_error::ErrorKind as DecodeErrorKind;
|
||||
pub use self::encode_error::ErrorKind as EncodeErrorKind;
|
||||
pub use self::client_error::ErrorKind as ClientErrorKind;
|
||||
pub use self::lexer_error::ErrorKind as LexerErrorKind;
|
||||
pub use self::parse_error::ErrorKind as ParseErrorKind;
|
||||
pub use self::persistence_error::ErrorKind as PersistenceErrorKind;
|
||||
pub use self::config_error::ErrorKind as ConfigErrorKind;
|
||||
|
||||
pub use self::decode_error::ChainErr as DecodeChainErr;
|
||||
pub use self::encode_error::ChainErr as EncodeChainErr;
|
||||
pub use self::client_error::ChainErr as ClientChainErr;
|
||||
pub use self::lexer_error::ChainErr as LexerChainErr;
|
||||
pub use self::parse_error::ChainErr as ParseChainErr;
|
||||
pub use self::persistence_error::ChainErr as PersistenceChainErr;
|
||||
pub use self::config_error::ChainErr as ConfigChainErr;
|
||||
|
||||
pub type DecodeResult<T> = Result<T, DecodeError>;
|
||||
pub type EncodeResult = Result<(), EncodeError>;
|
||||
pub type ClientResult<T> = Result<T, ClientError>;
|
||||
pub type LexerResult<T> = Result<T, LexerError>;
|
||||
pub type ParseResult<T> = Result<T, ParseError>;
|
||||
pub type PersistenceResult<T> = Result<T, PersistenceError>;
|
||||
pub type ConfigResult<T> = Result<T, ConfigError>;
|
@ -36,27 +36,23 @@ extern crate data_encoding;
|
||||
extern crate mio;
|
||||
extern crate openssl;
|
||||
extern crate rand;
|
||||
extern crate rusqlite;
|
||||
extern crate rustc_serialize;
|
||||
extern crate time;
|
||||
#[macro_use] extern crate tokio_core;
|
||||
extern crate toml;
|
||||
|
||||
pub mod error;
|
||||
pub mod logger;
|
||||
pub mod rr;
|
||||
pub mod authority;
|
||||
pub mod op;
|
||||
pub mod udp;
|
||||
pub mod tcp;
|
||||
pub mod client;
|
||||
pub mod server;
|
||||
pub mod serialize;
|
||||
pub mod config;
|
||||
|
||||
/// this exposes a version function which gives access to the access
|
||||
include!(concat!(env!("OUT_DIR"), "/version.rs"));
|
||||
|
||||
// TODO switch env_logger and remove this
|
||||
#[test]
|
||||
fn enable_logging_for_tests() {
|
||||
use log::LogLevel;
|
@ -82,6 +82,16 @@ impl Message {
|
||||
name_servers: Vec::new(), additionals: Vec::new(), sig0: Vec::new(), edns: None }
|
||||
}
|
||||
|
||||
pub fn error_msg(id: u16, op_code: OpCode, response_code: ResponseCode) -> Message {
|
||||
let mut message: Message = Message::new();
|
||||
message.message_type(MessageType::Response);
|
||||
message.id(id);
|
||||
message.response_code(response_code);
|
||||
message.op_code(op_code);
|
||||
|
||||
message
|
||||
}
|
||||
|
||||
pub fn truncate(&self) -> Self {
|
||||
let mut truncated: Message = Message::new();
|
||||
truncated.id(self.get_id());
|
@ -17,17 +17,19 @@
|
||||
//! Operations to send with a `Client` or server, e.g. `Query`, `Message`, or `UpdateMessage` can
|
||||
//! be used to gether to either query or update resource records sets.
|
||||
|
||||
pub mod op_code;
|
||||
pub mod response_code;
|
||||
pub mod message;
|
||||
pub mod header;
|
||||
pub mod query;
|
||||
mod edns;
|
||||
pub mod header;
|
||||
pub mod message;
|
||||
pub mod op_code;
|
||||
pub mod query;
|
||||
pub mod request_handler;
|
||||
pub mod response_code;
|
||||
|
||||
pub use self::message::{Message, UpdateMessage};
|
||||
pub use self::query::Query;
|
||||
pub use self::edns::Edns;
|
||||
pub use self::header::Header;
|
||||
pub use self::header::MessageType;
|
||||
pub use self::message::{Message, UpdateMessage};
|
||||
pub use self::op_code::OpCode;
|
||||
pub use self::query::Query;
|
||||
pub use self::request_handler::RequestHandler;
|
||||
pub use self::response_code::ResponseCode;
|
||||
pub use self::edns::Edns;
|
17
client/src/op/request_handler.rs
Normal file
17
client/src/op/request_handler.rs
Normal file
@ -0,0 +1,17 @@
|
||||
use ::op::Message;
|
||||
|
||||
/// Trait for handling incoming requests, and providing a message response.
|
||||
///
|
||||
/// *note* this probably belongs in the server crate and may move there in the future.
|
||||
pub trait RequestHandler {
|
||||
/// Determine's what needs to happen given the type of request, i.e. Query or Update.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `request` - the requested action to perform.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// The derived response to the the request
|
||||
fn handle_request(&self, request: &Message) -> Message;
|
||||
}
|
@ -16,16 +16,20 @@
|
||||
|
||||
//! Resource record related components, e.g. `Name` aka label, `Record`, `RData`, ...
|
||||
|
||||
pub mod record_type;
|
||||
pub mod dns_class;
|
||||
pub mod resource;
|
||||
pub mod record_data;
|
||||
pub mod dnssec;
|
||||
pub mod domain;
|
||||
pub mod rdata;
|
||||
pub mod dnssec;
|
||||
pub mod record_data;
|
||||
pub mod record_type;
|
||||
pub mod resource;
|
||||
mod rr_key;
|
||||
mod rr_set;
|
||||
|
||||
pub use self::record_type::RecordType;
|
||||
pub use self::resource::Record;
|
||||
pub use self::domain::Name;
|
||||
pub use self::dns_class::DNSClass;
|
||||
pub use self::record_data::RData;
|
||||
pub use self::record_type::RecordType;
|
||||
pub use self::resource::Record;
|
||||
pub use self::rr_key::RrKey;
|
||||
pub use self::rr_set::RrSet;
|
41
client/src/rr/rr_key.rs
Normal file
41
client/src/rr/rr_key.rs
Normal file
@ -0,0 +1,41 @@
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use ::rr::{Name, RecordType};
|
||||
|
||||
/// Accessor key for RRSets in the Authority.
|
||||
#[derive(Eq, PartialEq, Debug, Hash, Clone)]
|
||||
pub struct RrKey { pub name: Name, pub record_type: RecordType }
|
||||
|
||||
impl RrKey {
|
||||
/// Creates a new key to access the Authority.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `name` - domain name to lookup.
|
||||
/// * `record_type` - the `RecordType` to lookup.
|
||||
///
|
||||
/// # Return value
|
||||
///
|
||||
/// A new key to access the Authorities.
|
||||
/// TODO: make all cloned params pass by value.
|
||||
pub fn new(name: &Name, record_type: RecordType) -> RrKey {
|
||||
RrKey{ name: name.clone(), record_type: record_type }
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for RrKey {
|
||||
fn partial_cmp(&self, other: &RrKey) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for RrKey {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
let order = self.name.cmp(&other.name);
|
||||
if order == Ordering::Equal {
|
||||
self.record_type.cmp(&other.record_type)
|
||||
} else {
|
||||
order
|
||||
}
|
||||
}
|
||||
}
|
@ -10,7 +10,7 @@ use ::rr::{Name, Record, RecordType, RData};
|
||||
|
||||
/// Set of resource records associated to a name and type
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct RRSet {
|
||||
pub struct RrSet {
|
||||
name: Name,
|
||||
record_type: RecordType,
|
||||
ttl: u32,
|
||||
@ -19,13 +19,13 @@ pub struct RRSet {
|
||||
serial: u32, // serial number at which this record was modified
|
||||
}
|
||||
|
||||
impl RRSet {
|
||||
impl RrSet {
|
||||
/// Creates a new Resource Record Set.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `name` - The label for the `RRSet`
|
||||
/// * `record_type` - `RecordType` of this `RRSet`, all records in the `RRSet` must be of the
|
||||
/// * `name` - The label for the `RrSet`
|
||||
/// * `record_type` - `RecordType` of this `RrSet`, all records in the `RrSet` must be of the
|
||||
/// specified `RecordType`.
|
||||
/// * `serial` - current serial number of the `SOA` record, this is to be used for `IXFR` and
|
||||
/// signing for DNSSec after updates.
|
||||
@ -33,8 +33,9 @@ impl RRSet {
|
||||
/// # Return value
|
||||
///
|
||||
/// The newly created Resource Record Set
|
||||
pub fn new(name: &Name, record_type: RecordType, serial: u32) -> RRSet {
|
||||
RRSet{name: name.clone(), record_type: record_type, ttl: 0, records: Vec::new(), rrsigs: Vec::new(), serial: serial}
|
||||
/// TODO: make all cloned params pass by value
|
||||
pub fn new(name: &Name, record_type: RecordType, serial: u32) -> RrSet {
|
||||
RrSet{name: name.clone(), record_type: record_type, ttl: 0, records: Vec::new(), rrsigs: Vec::new(), serial: serial}
|
||||
}
|
||||
|
||||
/// # Return value
|
||||
@ -54,7 +55,7 @@ impl RRSet {
|
||||
/// # Return value
|
||||
///
|
||||
/// TTL, time-to-live, of the Resource Record Set, this is the maximum length of time that an
|
||||
/// RRSet should be cached.
|
||||
/// RrSet should be cached.
|
||||
pub fn get_ttl(&self) -> u32 {
|
||||
self.ttl
|
||||
}
|
||||
@ -129,7 +130,7 @@ impl RRSet {
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `record` - `Record` asserts that the `name` and `record_type` match the `RRSet`.
|
||||
/// * `record` - `Record` asserts that the `name` and `record_type` match the `RrSet`.
|
||||
/// * `serial` - current serial number of the `SOA` record, this is to be used for `IXFR` and
|
||||
/// signing for DNSSec after updates. The serial will only be updated if the
|
||||
/// record was added.
|
||||
@ -219,7 +220,7 @@ impl RRSet {
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `record` - `Record` asserts that the `name` and `record_type` match the `RRSet`. Removes
|
||||
/// * `record` - `Record` asserts that the `name` and `record_type` match the `RrSet`. Removes
|
||||
/// any `record` if the record data, `RData`, match.
|
||||
/// * `serial` - current serial number of the `SOA` record, this is to be used for `IXFR` and
|
||||
/// signing for DNSSec after updates. The serial will only be updated if the
|
||||
@ -270,13 +271,12 @@ mod test {
|
||||
use std::net::Ipv4Addr;
|
||||
use ::rr::*;
|
||||
use ::rr::rdata::SOA;
|
||||
use super::RRSet;
|
||||
|
||||
#[test]
|
||||
fn test_insert() {
|
||||
let name = Name::new().label("www").label("example").label("com");
|
||||
let record_type = RecordType::A;
|
||||
let mut rr_set = RRSet::new(&name, record_type, 0);
|
||||
let mut rr_set = RrSet::new(&name, record_type, 0);
|
||||
|
||||
let insert = Record::new().name(name.clone()).ttl(86400).rr_type(record_type).dns_class(DNSClass::IN).rdata(RData::A(Ipv4Addr::new(93,184,216,24))).clone();
|
||||
|
||||
@ -301,7 +301,7 @@ mod test {
|
||||
fn test_insert_soa() {
|
||||
let name = Name::new().label("example").label("com");
|
||||
let record_type = RecordType::SOA;
|
||||
let mut rr_set = RRSet::new(&name, record_type, 0);
|
||||
let mut rr_set = RrSet::new(&name, record_type, 0);
|
||||
|
||||
let insert = Record::new().name(name.clone()).ttl(3600).rr_type(RecordType::SOA).dns_class(DNSClass::IN).rdata(RData::SOA(SOA::new(Name::parse("sns.dns.icann.org.", None).unwrap(), Name::parse("noc.dns.icann.org.", None).unwrap(), 2015082403, 7200, 3600, 1209600, 3600 ))).clone();
|
||||
let same_serial = Record::new().name(name.clone()).ttl(3600).rr_type(RecordType::SOA).dns_class(DNSClass::IN).rdata(RData::SOA(SOA::new(Name::parse("sns.dns.icann.net.", None).unwrap(), Name::parse("noc.dns.icann.net.", None).unwrap(), 2015082403, 7200, 3600, 1209600, 3600 ))).clone();
|
||||
@ -330,7 +330,7 @@ mod test {
|
||||
let new_cname = Name::new().label("w2").label("example").label("com");
|
||||
|
||||
let record_type = RecordType::CNAME;
|
||||
let mut rr_set = RRSet::new(&name, record_type, 0);
|
||||
let mut rr_set = RrSet::new(&name, record_type, 0);
|
||||
|
||||
let insert = Record::new().name(name.clone()).ttl(3600).rr_type(RecordType::CNAME).dns_class(DNSClass::IN).rdata(RData::CNAME(cname.clone()) ).clone();
|
||||
let new_record = Record::new().name(name.clone()).ttl(3600).rr_type(RecordType::CNAME).dns_class(DNSClass::IN).rdata(RData::CNAME(new_cname.clone()) ).clone();
|
||||
@ -348,7 +348,7 @@ mod test {
|
||||
fn test_remove() {
|
||||
let name = Name::new().label("www").label("example").label("com");
|
||||
let record_type = RecordType::A;
|
||||
let mut rr_set = RRSet::new(&name, record_type, 0);
|
||||
let mut rr_set = RrSet::new(&name, record_type, 0);
|
||||
|
||||
let insert = Record::new().name(name.clone()).ttl(86400).rr_type(record_type).dns_class(DNSClass::IN).rdata(RData::A(Ipv4Addr::new(93,184,216,24))).clone();
|
||||
let insert1 = Record::new().name(name.clone()).ttl(86400).rr_type(record_type).dns_class(DNSClass::IN).rdata(RData::A(Ipv4Addr::new(93,184,216,25))).clone();
|
||||
@ -366,7 +366,7 @@ mod test {
|
||||
fn test_remove_soa() {
|
||||
let name = Name::new().label("example").label("com");
|
||||
let record_type = RecordType::SOA;
|
||||
let mut rr_set = RRSet::new(&name, record_type, 0);
|
||||
let mut rr_set = RrSet::new(&name, record_type, 0);
|
||||
|
||||
let insert = Record::new().name(name.clone()).ttl(3600).rr_type(RecordType::SOA).dns_class(DNSClass::IN).rdata(RData::SOA(SOA::new(Name::parse("sns.dns.icann.org.", None).unwrap(), Name::parse("noc.dns.icann.org.", None).unwrap(), 2015082403, 7200, 3600, 1209600, 3600 ))).clone();
|
||||
|
||||
@ -379,7 +379,7 @@ mod test {
|
||||
fn test_remove_ns() {
|
||||
let name = Name::new().label("example").label("com");
|
||||
let record_type = RecordType::NS;
|
||||
let mut rr_set = RRSet::new(&name, record_type, 0);
|
||||
let mut rr_set = RrSet::new(&name, record_type, 0);
|
||||
|
||||
let ns1 = Record::new().name(name.clone()).ttl(86400).rr_type(RecordType::NS).dns_class(DNSClass::IN).rdata(RData::NS(Name::parse("a.iana-servers.net.", None).unwrap()) ).clone();
|
||||
let ns2 = Record::new().name(name.clone()).ttl(86400).rr_type(RecordType::NS).dns_class(DNSClass::IN).rdata(RData::NS(Name::parse("b.iana-servers.net.", None).unwrap()) ).clone();
|
@ -14,12 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
use std::collections::BTreeMap;
|
||||
use std::io::Read;
|
||||
use std::fs::File;
|
||||
|
||||
use ::error::*;
|
||||
use ::rr::{ Name, RecordType, Record, DNSClass, RData};
|
||||
use ::authority::{Authority, RrKey, ZoneType, RRSet};
|
||||
use ::rr::{ Name, RecordType, Record, DNSClass, RData, RrKey, RrSet};
|
||||
|
||||
use super::master_lex::{Lexer, Token};
|
||||
|
||||
@ -130,21 +127,10 @@ impl Parser {
|
||||
Parser
|
||||
}
|
||||
|
||||
pub fn parse_file(file: File, origin: Option<Name>, zone_type: ZoneType, allow_update: bool) -> ParseResult<Authority> {
|
||||
let mut file = file;
|
||||
let mut buf = String::new();
|
||||
|
||||
// TODO, this should really use something to read line by line or some other method to
|
||||
// keep the usage down. and be a custom lexer...
|
||||
try!(file.read_to_string(&mut buf));
|
||||
let lexer = Lexer::new(&buf);
|
||||
Self::new().parse(lexer, origin, zone_type, allow_update)
|
||||
}
|
||||
|
||||
// TODO: change this function to load into an Authority, using the update_records() method
|
||||
pub fn parse(&mut self, lexer: Lexer, origin: Option<Name>, zone_type: ZoneType, allow_update: bool) -> ParseResult<Authority> {
|
||||
pub fn parse(&mut self, lexer: Lexer, origin: Option<Name>) -> ParseResult<(Name, BTreeMap<RrKey, RrSet>)> {
|
||||
let mut lexer = lexer;
|
||||
let mut records: BTreeMap<RrKey, RRSet> = BTreeMap::new();
|
||||
let mut records: BTreeMap<RrKey, RrSet> = BTreeMap::new();
|
||||
|
||||
let mut origin: Option<Name> = origin;
|
||||
let mut current_name: Option<Name> = None;
|
||||
@ -283,7 +269,7 @@ impl Parser {
|
||||
|
||||
match rtype.unwrap() {
|
||||
RecordType::SOA => {
|
||||
let mut set = RRSet::new(record.get_name(), record.get_rr_type(), 0);
|
||||
let mut set = RrSet::new(record.get_name(), record.get_rr_type(), 0);
|
||||
set.insert(record, 0);
|
||||
if records.insert(key, set).is_some() {
|
||||
return Err(ParseErrorKind::Message("SOA is already specified").into());
|
||||
@ -291,7 +277,7 @@ impl Parser {
|
||||
},
|
||||
_ => {
|
||||
// add a Vec if it's not there, then add the record to the list
|
||||
let mut set = records.entry(key).or_insert(RRSet::new(record.get_name(), record.get_rr_type(), 0));
|
||||
let mut set = records.entry(key).or_insert(RrSet::new(record.get_name(), record.get_rr_type(), 0));
|
||||
set.insert(record, 0);
|
||||
},
|
||||
}
|
||||
@ -306,7 +292,8 @@ impl Parser {
|
||||
|
||||
//
|
||||
// build the Authority and return.
|
||||
Ok(Authority::new(try!(origin.ok_or(ParseError::from(ParseErrorKind::Message("$ORIGIN was not specified")))), records, zone_type, allow_update))
|
||||
let origin = try!(origin.ok_or(ParseError::from(ParseErrorKind::Message("$ORIGIN was not specified"))));
|
||||
Ok((origin, records))
|
||||
}
|
||||
|
||||
/// parses the string following the rules from:
|
@ -19,6 +19,3 @@ mod master;
|
||||
pub use self::master::Parser;
|
||||
pub use self::master_lex::Lexer;
|
||||
pub use self::master_lex::Token;
|
||||
|
||||
#[cfg(test)]
|
||||
mod txt_tests;
|
@ -18,9 +18,8 @@ use std::io;
|
||||
use std::sync::Arc;
|
||||
|
||||
use mio::udp::UdpSocket;
|
||||
use mio::EventSet; // not * b/c don't want confusion with std::net
|
||||
use mio::EventSet;
|
||||
|
||||
use ::authority::Catalog;
|
||||
use ::op::*;
|
||||
use ::serialize::binary::*;
|
||||
|
||||
@ -42,7 +41,7 @@ impl UdpHandler {
|
||||
UdpHandler{ state: UdpState::Writing, addr: server_addr, message: request, buffer: bytes}
|
||||
}
|
||||
|
||||
pub fn new_server(socket: &UdpSocket, catalog: Arc<Catalog>) -> Option<Self> {
|
||||
pub fn new_server<H>(socket: &UdpSocket, catalog: Arc<H>) -> Option<Self> where H: RequestHandler {
|
||||
//let mut buf: Vec<u8> = Vec::with_capacity(512);
|
||||
let mut buf: [u8; 4096] = [0u8; 4096];
|
||||
let recv_result = socket.recv_from(&mut buf);
|
||||
@ -58,7 +57,7 @@ impl UdpHandler {
|
||||
let response = match request {
|
||||
Err(ref decode_error) => {
|
||||
warn!("unable to decode request from client: {:?}: {}", addr, decode_error);
|
||||
Catalog::error_msg(0/* id is in the message... */, OpCode::Query/* right default? */, ResponseCode::FormErr)
|
||||
Message::error_msg(0/* id is in the message... */, OpCode::Query/* right default? */, ResponseCode::FormErr)
|
||||
},
|
||||
Ok(ref req) => catalog.handle_request(req), // this is a buf if the unwrap() fails
|
||||
};
|
||||
@ -101,7 +100,7 @@ impl UdpHandler {
|
||||
// otherwise we'll blow the stack, which is ok, there's something horribly wrong in that
|
||||
// case with the code.
|
||||
error!("error encoding response to client: {}", encode_error);
|
||||
Self::serialize_msg(buf, &Catalog::error_msg(response.get_id(), response.get_op_code(), ResponseCode::ServFail))
|
||||
Self::serialize_msg(buf, &Message::error_msg(response.get_id(), response.get_op_code(), ResponseCode::ServFail))
|
||||
} else {
|
||||
buf
|
||||
}
|
461
client/tests/client_future_tests.rs
Normal file
461
client/tests/client_future_tests.rs
Normal file
@ -0,0 +1,461 @@
|
||||
extern crate chrono;
|
||||
extern crate futures;
|
||||
extern crate openssl;
|
||||
extern crate tokio_core;
|
||||
extern crate trust_dns;
|
||||
extern crate trust_dns_server;
|
||||
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
use std::net::*;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use chrono::Duration;
|
||||
use futures::{Async, Future, finished, Poll};
|
||||
use futures::stream::{Fuse, Stream};
|
||||
use futures::task::park;
|
||||
use openssl::crypto::rsa::RSA;
|
||||
use tokio_core::reactor::{Core, Handle};
|
||||
use tokio_core::channel::{channel, Receiver};
|
||||
|
||||
use trust_dns::client::{ClientFuture, BasicClientHandle, ClientHandle, StreamHandle};
|
||||
use trust_dns::error::*;
|
||||
use trust_dns::op::ResponseCode;
|
||||
use trust_dns::rr::domain;
|
||||
use trust_dns::rr::{DNSClass, RData, Record, RecordType};
|
||||
use trust_dns::rr::dnssec::{Algorithm, Signer};
|
||||
use trust_dns::rr::rdata::*;
|
||||
use trust_dns::udp::UdpClientStream;
|
||||
use trust_dns::tcp::TcpClientStream;
|
||||
use trust_dns_server::authority::Catalog;
|
||||
use trust_dns_server::authority::authority::{create_example};
|
||||
|
||||
mod common;
|
||||
use common::TestClientStream;
|
||||
|
||||
#[test]
|
||||
fn test_query_nonet() {
|
||||
let authority = create_example();
|
||||
let mut catalog = Catalog::new();
|
||||
catalog.upsert(authority.get_origin().clone(), authority);
|
||||
|
||||
let mut io_loop = Core::new().unwrap();
|
||||
let (stream, sender) = TestClientStream::new(catalog, io_loop.handle());
|
||||
let client = ClientFuture::new(stream, sender, io_loop.handle(), None);
|
||||
|
||||
io_loop.run(test_query(&client)).unwrap();
|
||||
io_loop.run(test_query(&client)).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_query_udp_ipv4() {
|
||||
use std::net::{SocketAddr, ToSocketAddrs};
|
||||
use tokio_core::reactor::Core;
|
||||
|
||||
let mut io_loop = Core::new().unwrap();
|
||||
let addr: SocketAddr = ("8.8.8.8",53).to_socket_addrs().unwrap().next().unwrap();
|
||||
let (stream, sender) = UdpClientStream::new(addr, io_loop.handle());
|
||||
let client = ClientFuture::new(stream, sender, io_loop.handle(), None);
|
||||
|
||||
// TODO: timeouts on these requests so that the test doesn't hang
|
||||
io_loop.run(test_query(&client)).unwrap();
|
||||
io_loop.run(test_query(&client)).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_query_udp_ipv6() {
|
||||
use std::net::{SocketAddr, ToSocketAddrs};
|
||||
use tokio_core::reactor::Core;
|
||||
|
||||
let mut io_loop = Core::new().unwrap();
|
||||
let addr: SocketAddr = ("2001:4860:4860::8888",53).to_socket_addrs().unwrap().next().unwrap();
|
||||
let (stream, sender) = UdpClientStream::new(addr, io_loop.handle());
|
||||
let client = ClientFuture::new(stream, sender, io_loop.handle(), None);
|
||||
|
||||
// TODO: timeouts on these requests so that the test doesn't hang
|
||||
io_loop.run(test_query(&client)).unwrap();
|
||||
io_loop.run(test_query(&client)).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_query_tcp_ipv4() {
|
||||
use std::net::{SocketAddr, ToSocketAddrs};
|
||||
use tokio_core::reactor::Core;
|
||||
|
||||
let mut io_loop = Core::new().unwrap();
|
||||
let addr: SocketAddr = ("8.8.8.8",53).to_socket_addrs().unwrap().next().unwrap();
|
||||
let (stream, sender) = TcpClientStream::new(addr, io_loop.handle());
|
||||
let client = ClientFuture::new(stream, sender, io_loop.handle(), None);
|
||||
|
||||
// TODO: timeouts on these requests so that the test doesn't hang
|
||||
io_loop.run(test_query(&client)).unwrap();
|
||||
io_loop.run(test_query(&client)).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_query_tcp_ipv6() {
|
||||
use std::net::{SocketAddr, ToSocketAddrs};
|
||||
use tokio_core::reactor::Core;
|
||||
|
||||
let mut io_loop = Core::new().unwrap();
|
||||
let addr: SocketAddr = ("2001:4860:4860::8888",53).to_socket_addrs().unwrap().next().unwrap();
|
||||
let (stream, sender) = TcpClientStream::new(addr, io_loop.handle());
|
||||
let client = ClientFuture::new(stream, sender, io_loop.handle(), None);
|
||||
|
||||
// TODO: timeouts on these requests so that the test doesn't hang
|
||||
io_loop.run(test_query(&client)).unwrap();
|
||||
io_loop.run(test_query(&client)).unwrap();
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn test_query(client: &BasicClientHandle) -> Box<Future<Item=(), Error=()>> {
|
||||
let name = domain::Name::with_labels(vec!["WWW".to_string(), "example".to_string(), "com".to_string()]);
|
||||
|
||||
Box::new(client.query(name.clone(), DNSClass::IN, RecordType::A)
|
||||
.map(move |response| {
|
||||
println!("response records: {:?}", response);
|
||||
assert_eq!(response.get_queries().first().expect("expected query").get_name().cmp_with_case(&name, false), Ordering::Equal);
|
||||
|
||||
let record = &response.get_answers()[0];
|
||||
assert_eq!(record.get_name(), &name);
|
||||
assert_eq!(record.get_rr_type(), RecordType::A);
|
||||
assert_eq!(record.get_dns_class(), DNSClass::IN);
|
||||
|
||||
if let &RData::A(ref address) = record.get_rdata() {
|
||||
assert_eq!(address, &Ipv4Addr::new(93,184,216,34))
|
||||
} else {
|
||||
assert!(false);
|
||||
}
|
||||
})
|
||||
.map_err(|e| {
|
||||
assert!(false, "query failed: {}", e);
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
// update tests
|
||||
//
|
||||
|
||||
/// create a client with a sig0 section
|
||||
fn create_sig0_ready_client(io_loop: &Core) -> (BasicClientHandle, domain::Name) {
|
||||
let mut authority = create_example();
|
||||
authority.set_allow_update(true);
|
||||
let origin = authority.get_origin().clone();
|
||||
|
||||
let rsa = RSA::generate(512).unwrap();
|
||||
|
||||
let signer = Signer::new(Algorithm::RSASHA256,
|
||||
rsa,
|
||||
domain::Name::with_labels(vec!["trusted".to_string(), "example".to_string(), "com".to_string()]),
|
||||
Duration::max_value());
|
||||
|
||||
// insert the KEY for the trusted.example.com
|
||||
let mut auth_key = Record::with(domain::Name::with_labels(vec!["trusted".to_string(), "example".to_string(), "com".to_string()]),
|
||||
RecordType::KEY,
|
||||
Duration::minutes(5).num_seconds() as u32);
|
||||
auth_key.rdata(RData::KEY(DNSKEY::new(false, false, false, signer.get_algorithm(), signer.get_public_key())));
|
||||
authority.upsert(auth_key, 0);
|
||||
|
||||
// setup the catalog
|
||||
let mut catalog = Catalog::new();
|
||||
catalog.upsert(authority.get_origin().clone(), authority);
|
||||
|
||||
let (stream, sender) = TestClientStream::new(catalog, io_loop.handle());
|
||||
let client = ClientFuture::new(stream, sender, io_loop.handle(), Some(signer));
|
||||
|
||||
(client, origin)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create() {
|
||||
let mut io_loop = Core::new().unwrap();
|
||||
let (client, origin) = create_sig0_ready_client(&io_loop);
|
||||
|
||||
// create a record
|
||||
let mut record = Record::with(domain::Name::with_labels(vec!["new".to_string(), "example".to_string(), "com".to_string()]),
|
||||
RecordType::A,
|
||||
Duration::minutes(5).num_seconds() as u32);
|
||||
record.rdata(RData::A(Ipv4Addr::new(100,10,100,10)));
|
||||
|
||||
|
||||
let result = io_loop.run(client.create(record.clone(), origin.clone())).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
let result = io_loop.run(client.query(record.get_name().clone(), record.get_dns_class(), record.get_rr_type())).expect("query failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
assert_eq!(result.get_answers().len(), 1);
|
||||
assert_eq!(result.get_answers()[0], record);
|
||||
|
||||
// trying to create again should error
|
||||
// TODO: it would be cool to make this
|
||||
let result = io_loop.run(client.create(record.clone(), origin.clone())).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::YXRRSet);
|
||||
|
||||
// will fail if already set and not the same value.
|
||||
let mut record = record.clone();
|
||||
record.rdata(RData::A(Ipv4Addr::new(101,11,101,11)));
|
||||
|
||||
let result = io_loop.run(client.create(record.clone(), origin.clone())).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::YXRRSet);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_append() {
|
||||
let mut io_loop = Core::new().unwrap();
|
||||
let (client, origin) = create_sig0_ready_client(&io_loop);
|
||||
|
||||
// append a record
|
||||
let mut record = Record::with(domain::Name::with_labels(vec!["new".to_string(), "example".to_string(), "com".to_string()]),
|
||||
RecordType::A,
|
||||
Duration::minutes(5).num_seconds() as u32);
|
||||
record.rdata(RData::A(Ipv4Addr::new(100,10,100,10)));
|
||||
|
||||
// first check the must_exist option
|
||||
let result = io_loop.run(client.append(record.clone(), origin.clone(), true)).expect("append failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NXRRSet);
|
||||
|
||||
// next append to a non-existent RRset
|
||||
let result = io_loop.run(client.append(record.clone(), origin.clone(), false)).expect("append failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
// verify record contents
|
||||
let result = io_loop.run(client.query(record.get_name().clone(), record.get_dns_class(), record.get_rr_type())).expect("query failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
assert_eq!(result.get_answers().len(), 1);
|
||||
assert_eq!(result.get_answers()[0], record);
|
||||
|
||||
// will fail if already set and not the same value.
|
||||
let mut record = record.clone();
|
||||
record.rdata(RData::A(Ipv4Addr::new(101,11,101,11)));
|
||||
|
||||
let result = io_loop.run(client.append(record.clone(), origin.clone(), true)).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
let result = io_loop.run(client.query(record.get_name().clone(), record.get_dns_class(), record.get_rr_type())).expect("query failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
assert_eq!(result.get_answers().len(), 2);
|
||||
|
||||
assert!(result.get_answers().iter().any(|rr| if let &RData::A(ref ip) = rr.get_rdata() { *ip == Ipv4Addr::new(100,10,100,10) } else { false }));
|
||||
assert!(result.get_answers().iter().any(|rr| if let &RData::A(ref ip) = rr.get_rdata() { *ip == Ipv4Addr::new(101,11,101,11) } else { false }));
|
||||
|
||||
// show that appending the same thing again is ok, but doesn't add any records
|
||||
let result = io_loop.run(client.append(record.clone(), origin.clone(), true)).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
let result = io_loop.run(client.query(record.get_name().clone(), record.get_dns_class(), record.get_rr_type())).expect("query failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
assert_eq!(result.get_answers().len(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_compare_and_swap() {
|
||||
let mut io_loop = Core::new().unwrap();
|
||||
let (client, origin) = create_sig0_ready_client(&io_loop);
|
||||
|
||||
// create a record
|
||||
let mut record = Record::with(domain::Name::with_labels(vec!["new".to_string(), "example".to_string(), "com".to_string()]),
|
||||
RecordType::A,
|
||||
Duration::minutes(5).num_seconds() as u32);
|
||||
record.rdata(RData::A(Ipv4Addr::new(100,10,100,10)));
|
||||
|
||||
let result = io_loop.run(client.create(record.clone(), origin.clone())).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
let current = record;
|
||||
let mut new = current.clone();
|
||||
new.rdata(RData::A(Ipv4Addr::new(101,11,101,11)));
|
||||
|
||||
let result = io_loop.run(client.compare_and_swap(current.clone(), new.clone(), origin.clone())).expect("compare_and_swap failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
let result = io_loop.run(client.query(new.get_name().clone(), new.get_dns_class(), new.get_rr_type())).expect("query failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
assert_eq!(result.get_answers().len(), 1);
|
||||
assert!(result.get_answers().iter().any(|rr| if let &RData::A(ref ip) = rr.get_rdata() { *ip == Ipv4Addr::new(101,11,101,11) } else { false }));
|
||||
|
||||
// check the it fails if tried again.
|
||||
let mut new = new;
|
||||
new.rdata(RData::A(Ipv4Addr::new(102,12,102,12)));
|
||||
|
||||
let result = io_loop.run(client.compare_and_swap(current, new.clone(), origin.clone())).expect("compare_and_swap failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NXRRSet);
|
||||
|
||||
let result = io_loop.run(client.query(new.get_name().clone(), new.get_dns_class(), new.get_rr_type())).expect("query failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
assert_eq!(result.get_answers().len(), 1);
|
||||
assert!(result.get_answers().iter().any(|rr| if let &RData::A(ref ip) = rr.get_rdata() { *ip == Ipv4Addr::new(101,11,101,11) } else { false }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delete_by_rdata() {
|
||||
let mut io_loop = Core::new().unwrap();
|
||||
let (client, origin) = create_sig0_ready_client(&io_loop);
|
||||
|
||||
// append a record
|
||||
let mut record = Record::with(domain::Name::with_labels(vec!["new".to_string(), "example".to_string(), "com".to_string()]),
|
||||
RecordType::A,
|
||||
Duration::minutes(5).num_seconds() as u32);
|
||||
record.rdata(RData::A(Ipv4Addr::new(100,10,100,10)));
|
||||
|
||||
// first check the must_exist option
|
||||
let result = io_loop.run(client.delete_by_rdata(record.clone(), origin.clone())).expect("delete failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
// next create to a non-existent RRset
|
||||
let result = io_loop.run(client.create(record.clone(), origin.clone())).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
let mut record = record.clone();
|
||||
record.rdata(RData::A(Ipv4Addr::new(101,11,101,11)));
|
||||
let result = io_loop.run(client.append(record.clone(), origin.clone(), true)).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
// verify record contents
|
||||
let result = io_loop.run(client.delete_by_rdata(record.clone(), origin.clone())).expect("delete failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
let result = io_loop.run(client.query(record.get_name().clone(), record.get_dns_class(), record.get_rr_type())).expect("query failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
assert_eq!(result.get_answers().len(), 1);
|
||||
assert!(result.get_answers().iter().any(|rr| if let &RData::A(ref ip) = rr.get_rdata() { *ip == Ipv4Addr::new(100,10,100,10) } else { false }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delete_rrset() {
|
||||
let mut io_loop = Core::new().unwrap();
|
||||
let (client, origin) = create_sig0_ready_client(&io_loop);
|
||||
|
||||
// append a record
|
||||
let mut record = Record::with(domain::Name::with_labels(vec!["new".to_string(), "example".to_string(), "com".to_string()]),
|
||||
RecordType::A,
|
||||
Duration::minutes(5).num_seconds() as u32);
|
||||
record.rdata(RData::A(Ipv4Addr::new(100,10,100,10)));
|
||||
|
||||
// first check the must_exist option
|
||||
let result = io_loop.run(client.delete_rrset(record.clone(), origin.clone())).expect("delete failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
// next create to a non-existent RRset
|
||||
let result = io_loop.run(client.create(record.clone(), origin.clone())).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
let mut record = record.clone();
|
||||
record.rdata(RData::A(Ipv4Addr::new(101,11,101,11)));
|
||||
let result = io_loop.run(client.append(record.clone(), origin.clone(), true)).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
// verify record contents
|
||||
let result = io_loop.run(client.delete_rrset(record.clone(), origin.clone())).expect("delete failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
let result = io_loop.run(client.query(record.get_name().clone(), record.get_dns_class(), record.get_rr_type())).expect("query failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NXDomain);
|
||||
assert_eq!(result.get_answers().len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delete_all() {
|
||||
let mut io_loop = Core::new().unwrap();
|
||||
let (client, origin) = create_sig0_ready_client(&io_loop);
|
||||
|
||||
// append a record
|
||||
let mut record = Record::with(domain::Name::with_labels(vec!["new".to_string(), "example".to_string(), "com".to_string()]),
|
||||
RecordType::A,
|
||||
Duration::minutes(5).num_seconds() as u32);
|
||||
record.rdata(RData::A(Ipv4Addr::new(100,10,100,10)));
|
||||
|
||||
// first check the must_exist option
|
||||
let result = io_loop.run(client.delete_all(record.get_name().clone(), origin.clone(), DNSClass::IN)).expect("delete failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
// next create to a non-existent RRset
|
||||
let result = io_loop.run(client.create(record.clone(), origin.clone())).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
let mut record = record.clone();
|
||||
record.rr_type(RecordType::AAAA);
|
||||
record.rdata(RData::AAAA(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8)));
|
||||
let result = io_loop.run(client.create(record.clone(), origin.clone())).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
// verify record contents
|
||||
let result = io_loop.run(client.delete_all(record.get_name().clone(), origin.clone(), DNSClass::IN)).expect("delete failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
let result = io_loop.run(client.query(record.get_name().clone(), record.get_dns_class(), RecordType::A)).expect("query failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NXDomain);
|
||||
assert_eq!(result.get_answers().len(), 0);
|
||||
|
||||
let result = io_loop.run(client.query(record.get_name().clone(), record.get_dns_class(), RecordType::AAAA)).expect("query failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NXDomain);
|
||||
assert_eq!(result.get_answers().len(), 0);
|
||||
}
|
||||
|
||||
// need to do something with the message channel, otherwise the ClientFuture will think there
|
||||
// is no one listening to messages and shutdown...
|
||||
#[allow(dead_code)]
|
||||
pub struct NeverReturnsClientStream {
|
||||
outbound_messages: Fuse<Receiver<Vec<u8>>>,
|
||||
}
|
||||
|
||||
impl NeverReturnsClientStream {
|
||||
pub fn new(loop_handle: Handle) -> (Box<Future<Item=Self, Error=io::Error>>, StreamHandle) {
|
||||
let (message_sender, outbound_messages) = channel(&loop_handle).expect("somethings wrong with the event loop");
|
||||
|
||||
let stream: Box<Future<Item=NeverReturnsClientStream, Error=io::Error>> = Box::new(finished(
|
||||
NeverReturnsClientStream {
|
||||
outbound_messages: outbound_messages.fuse()
|
||||
}
|
||||
));
|
||||
|
||||
(stream, message_sender)
|
||||
}
|
||||
}
|
||||
|
||||
impl Stream for NeverReturnsClientStream {
|
||||
type Item = Vec<u8>;
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
||||
// always not ready...
|
||||
park().unpark();
|
||||
Ok(Async::NotReady)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for NeverReturnsClientStream {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "TestClientStream catalog")
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_timeout_query_nonet() {
|
||||
let authority = create_example();
|
||||
let mut catalog = Catalog::new();
|
||||
catalog.upsert(authority.get_origin().clone(), authority);
|
||||
|
||||
let mut io_loop = Core::new().unwrap();
|
||||
let (stream, sender) = NeverReturnsClientStream::new(io_loop.handle());
|
||||
let client = ClientFuture::with_timeout(stream, sender, io_loop.handle(),
|
||||
std::time::Duration::from_millis(1), None);
|
||||
|
||||
let name = domain::Name::with_labels(vec!["www".to_string(), "example".to_string(), "com".to_string()]);
|
||||
|
||||
if let &ClientErrorKind::Timeout = io_loop.run(client.query(name.clone(), DNSClass::IN, RecordType::A)).unwrap_err().kind() {
|
||||
()
|
||||
} else {
|
||||
assert!(false);
|
||||
}
|
||||
|
||||
|
||||
// test that we don't have any thing funky with registering new timeouts, etc...
|
||||
if let &ClientErrorKind::Timeout = io_loop.run(client.query(name.clone(), DNSClass::IN, RecordType::AAAA)).unwrap_err().kind() {
|
||||
()
|
||||
} else {
|
||||
assert!(false);
|
||||
}
|
||||
}
|
563
client/tests/client_tests.rs
Normal file
563
client/tests/client_tests.rs
Normal file
@ -0,0 +1,563 @@
|
||||
extern crate chrono;
|
||||
extern crate openssl;
|
||||
extern crate trust_dns;
|
||||
extern crate trust_dns_server;
|
||||
|
||||
use std::net::*;
|
||||
use std::fmt;
|
||||
|
||||
use chrono::Duration;
|
||||
use openssl::crypto::rsa::RSA;
|
||||
|
||||
#[allow(deprecated)]
|
||||
use trust_dns::client::{Client, ClientConnection};
|
||||
use trust_dns::error::*;
|
||||
use trust_dns::op::*;
|
||||
use trust_dns::rr::{DNSClass, Record, RecordType, domain, RData};
|
||||
use trust_dns::rr::dnssec::{Algorithm, Signer, TrustAnchor};
|
||||
use trust_dns::rr::rdata::*;
|
||||
use trust_dns::serialize::binary::{BinDecoder, BinEncoder, BinSerializable};
|
||||
use trust_dns::tcp::TcpClientConnection;
|
||||
use trust_dns::udp::UdpClientConnection;
|
||||
|
||||
use trust_dns_server::authority::Catalog;
|
||||
use trust_dns_server::authority::authority::{create_example, create_secure_example};
|
||||
|
||||
pub struct TestClientConnection<'a> {
|
||||
catalog: &'a Catalog
|
||||
}
|
||||
|
||||
impl<'a> TestClientConnection<'a> {
|
||||
pub fn new(catalog: &'a Catalog) -> TestClientConnection<'a> {
|
||||
TestClientConnection { catalog: catalog }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ClientConnection for TestClientConnection<'a> {
|
||||
fn send(&mut self, bytes: Vec<u8>) -> ClientResult<Vec<u8>> {
|
||||
let mut decoder = BinDecoder::new(&bytes);
|
||||
|
||||
let message = try!(Message::read(&mut decoder));
|
||||
let response = self.catalog.handle_request(&message);
|
||||
|
||||
let mut buf = Vec::with_capacity(512);
|
||||
{
|
||||
let mut encoder = BinEncoder::new(&mut buf);
|
||||
try!(response.emit(&mut encoder));
|
||||
}
|
||||
|
||||
Ok(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> fmt::Debug for TestClientConnection<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "TestClientConnection catalog")
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_query_nonet() {
|
||||
let authority = create_example();
|
||||
let mut catalog = Catalog::new();
|
||||
catalog.upsert(authority.get_origin().clone(), authority);
|
||||
|
||||
let client = Client::new(TestClientConnection::new(&catalog));
|
||||
|
||||
test_query(client);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_query_udp() {
|
||||
let addr: SocketAddr = ("8.8.8.8",53).to_socket_addrs().unwrap().next().unwrap();
|
||||
let conn = UdpClientConnection::new(addr).unwrap();
|
||||
let client = Client::new(conn);
|
||||
|
||||
test_query(client);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_query_tcp() {
|
||||
let addr: SocketAddr = ("8.8.8.8",53).to_socket_addrs().unwrap().next().unwrap();
|
||||
let conn = TcpClientConnection::new(addr).unwrap();
|
||||
let client = Client::new(conn);
|
||||
|
||||
test_query(client);
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
fn test_query<C: ClientConnection>(client: Client<C>) {
|
||||
use std::cmp::Ordering;
|
||||
let name = domain::Name::with_labels(vec!["WWW".to_string(), "example".to_string(), "com".to_string()]);
|
||||
|
||||
let response = client.query(&name, DNSClass::IN, RecordType::A);
|
||||
assert!(response.is_ok(), "query failed: {}", response.unwrap_err());
|
||||
|
||||
let response = response.unwrap();
|
||||
|
||||
println!("response records: {:?}", response);
|
||||
assert_eq!(response.get_queries().first().expect("expected query").get_name().cmp_with_case(&name, false), Ordering::Equal);
|
||||
|
||||
let record = &response.get_answers()[0];
|
||||
assert_eq!(record.get_name(), &name);
|
||||
assert_eq!(record.get_rr_type(), RecordType::A);
|
||||
assert_eq!(record.get_dns_class(), DNSClass::IN);
|
||||
|
||||
if let &RData::A(ref address) = record.get_rdata() {
|
||||
assert_eq!(address, &Ipv4Addr::new(93,184,216,34))
|
||||
} else {
|
||||
assert!(false);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_secure_query_example_nonet() {
|
||||
let authority = create_secure_example();
|
||||
|
||||
let public_key = {
|
||||
let signers = authority.get_secure_keys();
|
||||
signers.first().expect("expected a key in the authority").get_public_key()
|
||||
};
|
||||
|
||||
let mut catalog = Catalog::new();
|
||||
catalog.upsert(authority.get_origin().clone(), authority);
|
||||
|
||||
let mut trust_anchor = TrustAnchor::new();
|
||||
trust_anchor.insert_trust_anchor(public_key);
|
||||
|
||||
let client = Client::with_trust_anchor(TestClientConnection::new(&catalog), trust_anchor);
|
||||
|
||||
test_secure_query_example(client);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_secure_query_example_udp() {
|
||||
let addr: SocketAddr = ("8.8.8.8",53).to_socket_addrs().unwrap().next().unwrap();
|
||||
let conn = UdpClientConnection::new(addr).unwrap();
|
||||
let client = Client::new(conn);
|
||||
|
||||
test_secure_query_example(client);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_secure_query_example_tcp() {
|
||||
let addr: SocketAddr = ("8.8.8.8",53).to_socket_addrs().unwrap().next().unwrap();
|
||||
let conn = TcpClientConnection::new(addr).unwrap();
|
||||
let client = Client::new(conn);
|
||||
|
||||
test_secure_query_example(client);
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
fn test_secure_query_example<C: ClientConnection>(client: Client<C>) {
|
||||
let name = domain::Name::with_labels(vec!["www".to_string(), "example".to_string(), "com".to_string()]);
|
||||
let response = client.secure_query(&name, DNSClass::IN, RecordType::A);
|
||||
|
||||
assert!(response.is_ok(), "query for {} failed: {}", name, response.unwrap_err());
|
||||
|
||||
let response = response.unwrap();
|
||||
|
||||
println!("response records: {:?}", response);
|
||||
assert!(response.get_edns().expect("edns not here").is_dnssec_ok());
|
||||
|
||||
let record = &response.get_answers()[0];
|
||||
assert_eq!(record.get_name(), &name);
|
||||
assert_eq!(record.get_rr_type(), RecordType::A);
|
||||
assert_eq!(record.get_dns_class(), DNSClass::IN);
|
||||
|
||||
if let &RData::A(ref address) = record.get_rdata() {
|
||||
assert_eq!(address, &Ipv4Addr::new(93,184,216,34))
|
||||
} else {
|
||||
assert!(false);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_dnssec_rollernet_td_udp() {
|
||||
let c = Client::new(UdpClientConnection::new("8.8.8.8:53".parse().unwrap()).unwrap());
|
||||
c.secure_query(
|
||||
&domain::Name::parse("rollernet.us.", None).unwrap(),
|
||||
DNSClass::IN,
|
||||
RecordType::DS,
|
||||
).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_dnssec_rollernet_td_tcp() {
|
||||
let c = Client::new(TcpClientConnection::new("8.8.8.8:53".parse().unwrap()).unwrap());
|
||||
c.secure_query(
|
||||
&domain::Name::parse("rollernet.us.", None).unwrap(),
|
||||
DNSClass::IN,
|
||||
RecordType::DS,
|
||||
).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_dnssec_rollernet_td_tcp_mixed_case() {
|
||||
let c = Client::new(TcpClientConnection::new("8.8.8.8:53".parse().unwrap()).unwrap());
|
||||
c.secure_query(
|
||||
&domain::Name::parse("RollErnet.Us.", None).unwrap(),
|
||||
DNSClass::IN,
|
||||
RecordType::DS,
|
||||
).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nsec_query_example_nonet() {
|
||||
let authority = create_secure_example();
|
||||
|
||||
let public_key = {
|
||||
let signers = authority.get_secure_keys();
|
||||
signers.first().expect("expected a key in the authority").get_public_key()
|
||||
};
|
||||
|
||||
let mut catalog = Catalog::new();
|
||||
catalog.upsert(authority.get_origin().clone(), authority);
|
||||
|
||||
let mut trust_anchor = TrustAnchor::new();
|
||||
trust_anchor.insert_trust_anchor(public_key);
|
||||
|
||||
let client = Client::with_trust_anchor(TestClientConnection::new(&catalog), trust_anchor);
|
||||
|
||||
test_nsec_query_example(client);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_nsec_query_example_udp() {
|
||||
let addr: SocketAddr = ("8.8.8.8",53).to_socket_addrs().unwrap().next().unwrap();
|
||||
let conn = UdpClientConnection::new(addr).unwrap();
|
||||
let client = Client::new(conn);
|
||||
test_nsec_query_example(client);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_nsec_query_example_tcp() {
|
||||
let addr: SocketAddr = ("8.8.8.8",53).to_socket_addrs().unwrap().next().unwrap();
|
||||
let conn = TcpClientConnection::new(addr).unwrap();
|
||||
let client = Client::new(conn);
|
||||
test_nsec_query_example(client);
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
fn test_nsec_query_example<C: ClientConnection>(client: Client<C>) {
|
||||
let name = domain::Name::with_labels(vec!["none".to_string(), "example".to_string(), "com".to_string()]);
|
||||
|
||||
let response = client.secure_query(&name, DNSClass::IN, RecordType::A);
|
||||
assert!(response.is_ok(), "query failed: {}", response.unwrap_err());
|
||||
|
||||
let response = response.unwrap();
|
||||
assert_eq!(response.get_response_code(), ResponseCode::NXDomain);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_nsec_query_type() {
|
||||
let name = domain::Name::with_labels(vec!["www".to_string(), "example".to_string(), "com".to_string()]);
|
||||
|
||||
let addr: SocketAddr = ("8.8.8.8",53).to_socket_addrs().unwrap().next().unwrap();
|
||||
let conn = TcpClientConnection::new(addr).unwrap();
|
||||
let client = Client::new(conn);
|
||||
|
||||
let response = client.secure_query(&name, DNSClass::IN, RecordType::NS);
|
||||
assert!(response.is_ok(), "query failed: {}", response.unwrap_err());
|
||||
|
||||
let response = response.unwrap();
|
||||
// TODO: it would be nice to verify that the NSEC records were validated...
|
||||
assert_eq!(response.get_response_code(), ResponseCode::NoError);
|
||||
assert!(response.get_answers().is_empty());
|
||||
}
|
||||
|
||||
// TODO: disabled until I decide what to do with NSEC3 see issue #10
|
||||
//
|
||||
// TODO these NSEC3 tests don't work, it seems that the zone is not signed properly.
|
||||
// #[test]
|
||||
// #[ignore]
|
||||
// fn test_nsec3_sdsmt() {
|
||||
// let addr: SocketAddr = ("75.75.75.75",53).to_socket_addrs().unwrap().next().unwrap();
|
||||
// let conn = TcpClientConnection::new(addr).unwrap();
|
||||
// let name = domain::Name::with_labels(vec!["none".to_string(), "sdsmt".to_string(), "edu".to_string()]);
|
||||
// let client = Client::new(conn);
|
||||
//
|
||||
// let response = client.secure_query(&name, DNSClass::IN, RecordType::NS);
|
||||
// assert!(response.is_ok(), "query failed: {}", response.unwrap_err());
|
||||
//
|
||||
// let response = response.unwrap();
|
||||
// assert_eq!(response.get_response_code(), ResponseCode::NXDomain);
|
||||
// }
|
||||
|
||||
// TODO: disabled until I decide what to do with NSEC3 see issue #10
|
||||
//
|
||||
// #[test]
|
||||
// #[ignore]
|
||||
// fn test_nsec3_sdsmt_type() {
|
||||
// let addr: SocketAddr = ("75.75.75.75",53).to_socket_addrs().unwrap().next().unwrap();
|
||||
// let conn = TcpClientConnection::new(addr).unwrap();
|
||||
// let name = domain::Name::with_labels(vec!["www".to_string(), "sdsmt".to_string(), "edu".to_string()]);
|
||||
// let client = Client::new(conn);
|
||||
//
|
||||
// let response = client.secure_query(&name, DNSClass::IN, RecordType::NS);
|
||||
// assert!(response.is_ok(), "query failed: {}", response.unwrap_err());
|
||||
//
|
||||
// let response = response.unwrap();
|
||||
// assert_eq!(response.get_response_code(), ResponseCode::NXDomain);
|
||||
// }
|
||||
|
||||
#[allow(deprecated)]
|
||||
fn create_sig0_ready_client<'a>(catalog: &'a mut Catalog) -> (Client<TestClientConnection<'a>>, Signer, domain::Name) {
|
||||
let mut authority = create_example();
|
||||
authority.set_allow_update(true);
|
||||
let origin = authority.get_origin().clone();
|
||||
|
||||
let rsa = RSA::generate(512).unwrap();
|
||||
|
||||
let signer = Signer::new(Algorithm::RSASHA256, rsa,
|
||||
domain::Name::with_labels(vec!["trusted".to_string(), "example".to_string(), "com".to_string()]),
|
||||
Duration::max_value());
|
||||
|
||||
// insert the KEY for the trusted.example.com
|
||||
let mut auth_key = Record::with(domain::Name::with_labels(vec!["trusted".to_string(), "example".to_string(), "com".to_string()]),
|
||||
RecordType::KEY,
|
||||
Duration::minutes(5).num_seconds() as u32);
|
||||
auth_key.rdata(RData::KEY(DNSKEY::new(false, false, false, signer.get_algorithm(), signer.get_public_key())));
|
||||
authority.upsert(auth_key, 0);
|
||||
|
||||
catalog.upsert(authority.get_origin().clone(), authority);
|
||||
let client = Client::new(TestClientConnection::new(catalog));
|
||||
|
||||
(client, signer, origin)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create() {
|
||||
let mut catalog = Catalog::new();
|
||||
let (client, signer, origin) = create_sig0_ready_client(&mut catalog);
|
||||
|
||||
// create a record
|
||||
let mut record = Record::with(domain::Name::with_labels(vec!["new".to_string(), "example".to_string(), "com".to_string()]),
|
||||
RecordType::A,
|
||||
Duration::minutes(5).num_seconds() as u32);
|
||||
record.rdata(RData::A(Ipv4Addr::new(100,10,100,10)));
|
||||
|
||||
|
||||
let result = client.create(record.clone(), origin.clone(), &signer).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
let result = client.query(record.get_name(), record.get_dns_class(), record.get_rr_type()).expect("query failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
assert_eq!(result.get_answers().len(), 1);
|
||||
assert_eq!(result.get_answers()[0], record);
|
||||
|
||||
// trying to create again should error
|
||||
// TODO: it would be cool to make this
|
||||
let result = client.create(record.clone(), origin.clone(), &signer).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::YXRRSet);
|
||||
|
||||
// will fail if already set and not the same value.
|
||||
let mut record = record.clone();
|
||||
record.rdata(RData::A(Ipv4Addr::new(101,11,101,11)));
|
||||
|
||||
let result = client.create(record.clone(), origin.clone(), &signer).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::YXRRSet);
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_append() {
|
||||
let mut catalog = Catalog::new();
|
||||
let (client, signer, origin) = create_sig0_ready_client(&mut catalog);
|
||||
|
||||
// append a record
|
||||
let mut record = Record::with(domain::Name::with_labels(vec!["new".to_string(), "example".to_string(), "com".to_string()]),
|
||||
RecordType::A,
|
||||
Duration::minutes(5).num_seconds() as u32);
|
||||
record.rdata(RData::A(Ipv4Addr::new(100,10,100,10)));
|
||||
|
||||
// first check the must_exist option
|
||||
let result = client.append(record.clone(), origin.clone(), true, &signer).expect("append failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NXRRSet);
|
||||
|
||||
// next append to a non-existent RRset
|
||||
let result = client.append(record.clone(), origin.clone(), false, &signer).expect("append failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
// verify record contents
|
||||
let result = client.query(record.get_name(), record.get_dns_class(), record.get_rr_type()).expect("query failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
assert_eq!(result.get_answers().len(), 1);
|
||||
assert_eq!(result.get_answers()[0], record);
|
||||
|
||||
// will fail if already set and not the same value.
|
||||
let mut record = record.clone();
|
||||
record.rdata(RData::A(Ipv4Addr::new(101,11,101,11)));
|
||||
|
||||
let result = client.append(record.clone(), origin.clone(), true, &signer).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
let result = client.query(record.get_name(), record.get_dns_class(), record.get_rr_type()).expect("query failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
assert_eq!(result.get_answers().len(), 2);
|
||||
|
||||
assert!(result.get_answers().iter().any(|rr| if let &RData::A(ref ip) = rr.get_rdata() { *ip == Ipv4Addr::new(100,10,100,10) } else { false }));
|
||||
assert!(result.get_answers().iter().any(|rr| if let &RData::A(ref ip) = rr.get_rdata() { *ip == Ipv4Addr::new(101,11,101,11) } else { false }));
|
||||
|
||||
// show that appending the same thing again is ok, but doesn't add any records
|
||||
let result = client.append(record.clone(), origin.clone(), true, &signer).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
let result = client.query(record.get_name(), record.get_dns_class(), record.get_rr_type()).expect("query failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
assert_eq!(result.get_answers().len(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_compare_and_swap() {
|
||||
let mut catalog = Catalog::new();
|
||||
let (client, signer, origin) = create_sig0_ready_client(&mut catalog);
|
||||
|
||||
// create a record
|
||||
let mut record = Record::with(domain::Name::with_labels(vec!["new".to_string(), "example".to_string(), "com".to_string()]),
|
||||
RecordType::A,
|
||||
Duration::minutes(5).num_seconds() as u32);
|
||||
record.rdata(RData::A(Ipv4Addr::new(100,10,100,10)));
|
||||
|
||||
let result = client.create(record.clone(), origin.clone(), &signer).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
let current = record;
|
||||
let mut new = current.clone();
|
||||
new.rdata(RData::A(Ipv4Addr::new(101,11,101,11)));
|
||||
|
||||
let result = client.compare_and_swap(current.clone(), new.clone(), origin.clone(), &signer).expect("compare_and_swap failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
let result = client.query(new.get_name(), new.get_dns_class(), new.get_rr_type()).expect("query failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
assert_eq!(result.get_answers().len(), 1);
|
||||
assert!(result.get_answers().iter().any(|rr| if let &RData::A(ref ip) = rr.get_rdata() { *ip == Ipv4Addr::new(101,11,101,11) } else { false }));
|
||||
|
||||
// check the it fails if tried again.
|
||||
let mut new = new;
|
||||
new.rdata(RData::A(Ipv4Addr::new(102,12,102,12)));
|
||||
|
||||
let result = client.compare_and_swap(current, new.clone(), origin.clone(), &signer).expect("compare_and_swap failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NXRRSet);
|
||||
|
||||
let result = client.query(new.get_name(), new.get_dns_class(), new.get_rr_type()).expect("query failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
assert_eq!(result.get_answers().len(), 1);
|
||||
assert!(result.get_answers().iter().any(|rr| if let &RData::A(ref ip) = rr.get_rdata() { *ip == Ipv4Addr::new(101,11,101,11) } else { false }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delete_by_rdata() {
|
||||
let mut catalog = Catalog::new();
|
||||
let (client, signer, origin) = create_sig0_ready_client(&mut catalog);
|
||||
|
||||
// append a record
|
||||
let mut record = Record::with(domain::Name::with_labels(vec!["new".to_string(), "example".to_string(), "com".to_string()]),
|
||||
RecordType::A,
|
||||
Duration::minutes(5).num_seconds() as u32);
|
||||
record.rdata(RData::A(Ipv4Addr::new(100,10,100,10)));
|
||||
|
||||
// first check the must_exist option
|
||||
let result = client.delete_by_rdata(record.clone(), origin.clone(), &signer).expect("delete failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
// next create to a non-existent RRset
|
||||
let result = client.create(record.clone(), origin.clone(), &signer).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
let mut record = record.clone();
|
||||
record.rdata(RData::A(Ipv4Addr::new(101,11,101,11)));
|
||||
let result = client.append(record.clone(), origin.clone(), true, &signer).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
// verify record contents
|
||||
let result = client.delete_by_rdata(record.clone(), origin.clone(), &signer).expect("delete failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
let result = client.query(record.get_name(), record.get_dns_class(), record.get_rr_type()).expect("query failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
assert_eq!(result.get_answers().len(), 1);
|
||||
assert!(result.get_answers().iter().any(|rr| if let &RData::A(ref ip) = rr.get_rdata() { *ip == Ipv4Addr::new(100,10,100,10) } else { false }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delete_rrset() {
|
||||
let mut catalog = Catalog::new();
|
||||
let (client, signer, origin) = create_sig0_ready_client(&mut catalog);
|
||||
|
||||
// append a record
|
||||
let mut record = Record::with(domain::Name::with_labels(vec!["new".to_string(), "example".to_string(), "com".to_string()]),
|
||||
RecordType::A,
|
||||
Duration::minutes(5).num_seconds() as u32);
|
||||
record.rdata(RData::A(Ipv4Addr::new(100,10,100,10)));
|
||||
|
||||
// first check the must_exist option
|
||||
let result = client.delete_rrset(record.clone(), origin.clone(), &signer).expect("delete failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
// next create to a non-existent RRset
|
||||
let result = client.create(record.clone(), origin.clone(), &signer).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
let mut record = record.clone();
|
||||
record.rdata(RData::A(Ipv4Addr::new(101,11,101,11)));
|
||||
let result = client.append(record.clone(), origin.clone(), true, &signer).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
// verify record contents
|
||||
let result = client.delete_rrset(record.clone(), origin.clone(), &signer).expect("delete failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
let result = client.query(record.get_name(), record.get_dns_class(), record.get_rr_type()).expect("query failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NXDomain);
|
||||
assert_eq!(result.get_answers().len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delete_all() {
|
||||
let mut catalog = Catalog::new();
|
||||
let (client, signer, origin) = create_sig0_ready_client(&mut catalog);
|
||||
|
||||
// append a record
|
||||
let mut record = Record::with(domain::Name::with_labels(vec!["new".to_string(), "example".to_string(), "com".to_string()]),
|
||||
RecordType::A,
|
||||
Duration::minutes(5).num_seconds() as u32);
|
||||
record.rdata(RData::A(Ipv4Addr::new(100,10,100,10)));
|
||||
|
||||
// first check the must_exist option
|
||||
let result = client.delete_all(record.get_name().clone(), origin.clone(), DNSClass::IN, &signer).expect("delete failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
// next create to a non-existent RRset
|
||||
let result = client.create(record.clone(), origin.clone(), &signer).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
let mut record = record.clone();
|
||||
record.rr_type(RecordType::AAAA);
|
||||
record.rdata(RData::AAAA(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8)));
|
||||
let result = client.create(record.clone(), origin.clone(), &signer).expect("create failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
// verify record contents
|
||||
let result = client.delete_all(record.get_name().clone(), origin.clone(), DNSClass::IN, &signer).expect("delete failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
|
||||
let result = client.query(record.get_name(), record.get_dns_class(), RecordType::A).expect("query failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NXDomain);
|
||||
assert_eq!(result.get_answers().len(), 0);
|
||||
|
||||
let result = client.query(record.get_name(), record.get_dns_class(), RecordType::AAAA).expect("query failed");
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NXDomain);
|
||||
assert_eq!(result.get_answers().len(), 0);
|
||||
}
|
68
client/tests/common/mod.rs
Normal file
68
client/tests/common/mod.rs
Normal file
@ -0,0 +1,68 @@
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
|
||||
use futures::{Async, Future, finished, Poll};
|
||||
use futures::stream::{Fuse, Stream};
|
||||
use futures::task::park;
|
||||
use tokio_core::reactor::*;
|
||||
use tokio_core::channel::*;
|
||||
|
||||
use trust_dns::client::StreamHandle;
|
||||
use trust_dns::op::*;
|
||||
use trust_dns::serialize::binary::*;
|
||||
|
||||
use trust_dns_server::authority::Catalog;
|
||||
|
||||
pub struct TestClientStream {
|
||||
catalog: Catalog,
|
||||
outbound_messages: Fuse<Receiver<Vec<u8>>>,
|
||||
}
|
||||
|
||||
impl TestClientStream {
|
||||
pub fn new(catalog: Catalog, loop_handle: Handle) -> (Box<Future<Item=Self, Error=io::Error>>, StreamHandle) {
|
||||
let (message_sender, outbound_messages) = channel(&loop_handle).expect("somethings wrong with the event loop");
|
||||
|
||||
let stream: Box<Future<Item=TestClientStream, Error=io::Error>> = Box::new(finished(
|
||||
TestClientStream { catalog: catalog, outbound_messages: outbound_messages.fuse() }
|
||||
));
|
||||
|
||||
(stream, message_sender)
|
||||
}
|
||||
}
|
||||
|
||||
impl Stream for TestClientStream {
|
||||
type Item = Vec<u8>;
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
||||
match try!(self.outbound_messages.poll()) {
|
||||
// already handled above, here to make sure the poll() pops the next message
|
||||
Async::Ready(Some(bytes)) => {
|
||||
let mut decoder = BinDecoder::new(&bytes);
|
||||
|
||||
let message = Message::read(&mut decoder).expect("could not decode message");
|
||||
let response = self.catalog.handle_request(&message);
|
||||
|
||||
let mut buf = Vec::with_capacity(512);
|
||||
{
|
||||
let mut encoder = BinEncoder::new(&mut buf);
|
||||
response.emit(&mut encoder).expect("could not encode");
|
||||
}
|
||||
|
||||
Ok(Async::Ready(Some(buf)))
|
||||
},
|
||||
// now we get to drop through to the receives...
|
||||
// TODO: should we also return None if there are no more messages to send?
|
||||
_ => {
|
||||
park().unpark();
|
||||
Ok(Async::NotReady)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for TestClientStream {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "TestClientStream catalog")
|
||||
}
|
||||
}
|
241
client/tests/secure_client_handle_tests.rs
Normal file
241
client/tests/secure_client_handle_tests.rs
Normal file
@ -0,0 +1,241 @@
|
||||
extern crate chrono;
|
||||
extern crate futures;
|
||||
extern crate openssl;
|
||||
extern crate tokio_core;
|
||||
extern crate trust_dns;
|
||||
extern crate trust_dns_server;
|
||||
|
||||
use std::net::*;
|
||||
|
||||
use tokio_core::reactor::Core;
|
||||
|
||||
use trust_dns::client::{BasicClientHandle, ClientFuture, ClientHandle, MemoizeClientHandle, SecureClientHandle};
|
||||
use trust_dns::op::ResponseCode;
|
||||
use trust_dns::rr::domain;
|
||||
use trust_dns::rr::{DNSClass, RData, RecordType};
|
||||
use trust_dns::rr::dnssec::TrustAnchor;
|
||||
use trust_dns::tcp::TcpClientStream;
|
||||
use trust_dns::udp::UdpClientStream;
|
||||
|
||||
use trust_dns_server::authority::Catalog;
|
||||
use trust_dns_server::authority::authority::create_secure_example;
|
||||
|
||||
mod common;
|
||||
use common::TestClientStream;
|
||||
|
||||
#[test]
|
||||
fn test_secure_query_example_nonet() {
|
||||
with_nonet(test_secure_query_example);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_secure_query_example_udp() {
|
||||
with_udp(test_secure_query_example);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_secure_query_example_tcp() {
|
||||
with_tcp(test_secure_query_example);
|
||||
}
|
||||
|
||||
fn test_secure_query_example<H>(client: SecureClientHandle<H>, mut io_loop: Core)
|
||||
where H: ClientHandle + 'static {
|
||||
let name = domain::Name::with_labels(vec!["www".to_string(), "example".to_string(), "com".to_string()]);
|
||||
let response = io_loop.run(client.query(name.clone(), DNSClass::IN, RecordType::A)).expect("query failed");
|
||||
|
||||
println!("response records: {:?}", response);
|
||||
assert!(response.get_edns().expect("edns not here").is_dnssec_ok());
|
||||
|
||||
assert!(!response.get_answers().is_empty());
|
||||
let record = &response.get_answers()[0];
|
||||
assert_eq!(record.get_name(), &name);
|
||||
assert_eq!(record.get_rr_type(), RecordType::A);
|
||||
assert_eq!(record.get_dns_class(), DNSClass::IN);
|
||||
|
||||
if let &RData::A(ref address) = record.get_rdata() {
|
||||
assert_eq!(address, &Ipv4Addr::new(93,184,216,34))
|
||||
} else {
|
||||
assert!(false);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nsec_query_example_nonet() {
|
||||
with_nonet(test_nsec_query_example);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_nsec_query_example_udp() {
|
||||
with_udp(test_nsec_query_example);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_nsec_query_example_tcp() {
|
||||
with_tcp(test_nsec_query_example);
|
||||
}
|
||||
|
||||
fn test_nsec_query_example<H>(client: SecureClientHandle<H>, mut io_loop: Core)
|
||||
where H: ClientHandle + 'static {
|
||||
let name = domain::Name::with_labels(vec!["none".to_string(), "example".to_string(), "com".to_string()]);
|
||||
|
||||
let response = io_loop.run(client.query(name.clone(), DNSClass::IN, RecordType::A)).expect("query failed");
|
||||
assert_eq!(response.get_response_code(), ResponseCode::NXDomain);
|
||||
}
|
||||
|
||||
// TODO: NSEC response code wrong in Trust-DNS? Issue #53
|
||||
// #[test]
|
||||
// fn test_nsec_query_type_nonet() {
|
||||
// with_nonet(test_nsec_query_type);
|
||||
// }
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_nsec_query_type_udp() {
|
||||
with_udp(test_nsec_query_type);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_nsec_query_type_tcp() {
|
||||
with_tcp(test_nsec_query_type);
|
||||
}
|
||||
|
||||
fn test_nsec_query_type<H>(client: SecureClientHandle<H>, mut io_loop: Core)
|
||||
where H: ClientHandle + 'static {
|
||||
let name = domain::Name::with_labels(vec!["www".to_string(), "example".to_string(), "com".to_string()]);
|
||||
|
||||
let response = io_loop.run(client.query(name.clone(), DNSClass::IN, RecordType::NS)).expect("query failed");
|
||||
|
||||
assert_eq!(response.get_response_code(), ResponseCode::NoError);
|
||||
assert!(response.get_answers().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_dnssec_rollernet_td_udp() {
|
||||
with_udp(dnssec_rollernet_td_test);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_dnssec_rollernet_td_tcp() {
|
||||
with_udp(dnssec_rollernet_td_test);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_dnssec_rollernet_td_tcp_mixed_case() {
|
||||
with_tcp(dnssec_rollernet_td_mixed_case_test);
|
||||
}
|
||||
|
||||
fn dnssec_rollernet_td_test<H>(client: SecureClientHandle<H>, mut io_loop: Core)
|
||||
where H: ClientHandle + 'static {
|
||||
let name = domain::Name::parse("rollernet.us.", None).unwrap();
|
||||
|
||||
let response = io_loop.run(client.query(name.clone(), DNSClass::IN, RecordType::DS)).expect("query failed");
|
||||
|
||||
assert_eq!(response.get_response_code(), ResponseCode::NoError);
|
||||
// rollernet doesn't have any DS records...
|
||||
// would have failed validation
|
||||
assert!(response.get_answers().is_empty());
|
||||
}
|
||||
|
||||
fn dnssec_rollernet_td_mixed_case_test<H>(client: SecureClientHandle<H>, mut io_loop: Core)
|
||||
where H: ClientHandle + 'static {
|
||||
let name = domain::Name::parse("RollErnet.Us.", None).unwrap();
|
||||
|
||||
let response = io_loop.run(client.query(name.clone(), DNSClass::IN, RecordType::DS)).expect("query failed");
|
||||
|
||||
assert_eq!(response.get_response_code(), ResponseCode::NoError);
|
||||
// rollernet doesn't have any DS records...
|
||||
// would have failed validation
|
||||
assert!(response.get_answers().is_empty());
|
||||
}
|
||||
|
||||
fn with_nonet<F>(test: F) where F: Fn(SecureClientHandle<MemoizeClientHandle<BasicClientHandle>>, Core) {
|
||||
let succeeded = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
|
||||
let succeeded_clone = succeeded.clone();
|
||||
std::thread::Builder::new().name("thread_killer".to_string()).spawn(move || {
|
||||
let succeeded = succeeded_clone.clone();
|
||||
for _ in 0..15 {
|
||||
std::thread::sleep(std::time::Duration::from_secs(1));
|
||||
if succeeded.load(std::sync::atomic::Ordering::Relaxed) { return }
|
||||
}
|
||||
|
||||
panic!("timeout");
|
||||
}).unwrap();
|
||||
|
||||
let authority = create_secure_example();
|
||||
|
||||
let public_key = {
|
||||
let signers = authority.get_secure_keys();
|
||||
signers.first().expect("expected a key in the authority").get_public_key()
|
||||
};
|
||||
|
||||
let mut catalog = Catalog::new();
|
||||
catalog.upsert(authority.get_origin().clone(), authority);
|
||||
|
||||
let mut trust_anchor = TrustAnchor::new();
|
||||
trust_anchor.insert_trust_anchor(public_key);
|
||||
|
||||
let io_loop = Core::new().unwrap();
|
||||
let (stream, sender) = TestClientStream::new(catalog, io_loop.handle());
|
||||
let client = ClientFuture::new(stream, sender, io_loop.handle(), None);
|
||||
let client = MemoizeClientHandle::new(client);
|
||||
let secure_client = SecureClientHandle::with_trust_anchor(client, trust_anchor);
|
||||
|
||||
test(secure_client, io_loop);
|
||||
succeeded.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
|
||||
fn with_udp<F>(test: F) where F: Fn(SecureClientHandle<MemoizeClientHandle<BasicClientHandle>>, Core) {
|
||||
let succeeded = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
|
||||
let succeeded_clone = succeeded.clone();
|
||||
std::thread::Builder::new().name("thread_killer".to_string()).spawn(move || {
|
||||
let succeeded = succeeded_clone.clone();
|
||||
for _ in 0..15 {
|
||||
std::thread::sleep(std::time::Duration::from_secs(1));
|
||||
if succeeded.load(std::sync::atomic::Ordering::Relaxed) { return }
|
||||
}
|
||||
|
||||
panic!("timeout");
|
||||
}).unwrap();
|
||||
|
||||
let io_loop = Core::new().unwrap();
|
||||
let addr: SocketAddr = ("8.8.8.8",53).to_socket_addrs().unwrap().next().unwrap();
|
||||
let (stream, sender) = UdpClientStream::new(addr, io_loop.handle());
|
||||
let client = ClientFuture::new(stream, sender, io_loop.handle(), None);
|
||||
let client = MemoizeClientHandle::new(client);
|
||||
let secure_client = SecureClientHandle::new(client);
|
||||
|
||||
test(secure_client, io_loop);
|
||||
succeeded.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
|
||||
fn with_tcp<F>(test: F) where F: Fn(SecureClientHandle<MemoizeClientHandle<BasicClientHandle>>, Core) {
|
||||
let succeeded = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
|
||||
let succeeded_clone = succeeded.clone();
|
||||
std::thread::Builder::new().name("thread_killer".to_string()).spawn(move || {
|
||||
let succeeded = succeeded_clone.clone();
|
||||
for _ in 0..15 {
|
||||
std::thread::sleep(std::time::Duration::from_secs(1));
|
||||
if succeeded.load(std::sync::atomic::Ordering::Relaxed) { return }
|
||||
}
|
||||
|
||||
panic!("timeout");
|
||||
}).unwrap();
|
||||
|
||||
let io_loop = Core::new().unwrap();
|
||||
let addr: SocketAddr = ("8.8.8.8",53).to_socket_addrs().unwrap().next().unwrap();
|
||||
let (stream, sender) = TcpClientStream::new(addr, io_loop.handle());
|
||||
let client = ClientFuture::new(stream, sender, io_loop.handle(), None);
|
||||
let client = MemoizeClientHandle::new(client);
|
||||
let secure_client = SecureClientHandle::new(client);
|
||||
|
||||
test(secure_client, io_loop);
|
||||
succeeded.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
@ -1,10 +1,13 @@
|
||||
extern crate trust_dns;
|
||||
extern crate trust_dns_server;
|
||||
|
||||
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||
use std::str::FromStr;
|
||||
|
||||
use ::rr::*;
|
||||
use ::authority::ZoneType;
|
||||
use trust_dns::rr::*;
|
||||
use trust_dns::serialize::txt::*;
|
||||
use trust_dns_server::authority::*;
|
||||
|
||||
use super::*;
|
||||
|
||||
|
||||
#[test]
|
||||
@ -38,10 +41,11 @@ short 70 A 26.3.0.104
|
||||
venera A 10.1.0.52
|
||||
A 128.9.0.32");
|
||||
|
||||
let authority = Parser::new().parse(lexer, Some(Name::new().label("isi").label("edu")), ZoneType::Master, false);
|
||||
if authority.is_err() { panic!("failed to parse: {:?}", authority.err()) }
|
||||
let records = Parser::new().parse(lexer, Some(Name::new().label("isi").label("edu")));
|
||||
if records.is_err() { panic!("failed to parse: {:?}", records.err()) }
|
||||
|
||||
let authority = authority.unwrap();
|
||||
let (origin, records) = records.unwrap();
|
||||
let authority = Authority::new(origin, records, ZoneType::Master, false);
|
||||
|
||||
// not validating everything, just one of each...
|
||||
|
@ -3,6 +3,6 @@
|
||||
trust_dns_dir=$(dirname $0)/..
|
||||
|
||||
pushd ${trust_dns_dir}
|
||||
docker run -a STDERR -a STDOUT --rm -v ${PWD}:/src fnichol/rust:1.11.0 cargo test "$@" | tee target/linux_output.txt
|
||||
docker run -a STDERR -a STDOUT --rm -v ${PWD}:/src fnichol/rust:1.12.0 bash scripts/run_tests.sh "$@" | tee target/linux_output.txt
|
||||
|
||||
popd
|
||||
|
52
scripts/run_kcov.sh
Executable file
52
scripts/run_kcov.sh
Executable file
@ -0,0 +1,52 @@
|
||||
#!/bin/bash -e
|
||||
|
||||
trust_dns_dir=$(dirname $0)/..
|
||||
cd ${trust_dns_dir:?}
|
||||
|
||||
case $(uname) in
|
||||
Darwin) exit 0;;
|
||||
*) KCOV=true;;
|
||||
esac
|
||||
|
||||
# don't run on nightly or beta
|
||||
rustc --version | grep beta && exit 0;
|
||||
rustc --version | grep nightly && exit 0;
|
||||
|
||||
rm -rf kcov-master master.tar.gz*
|
||||
|
||||
# install kcov
|
||||
# sudo apt-get install libcurl4-openssl-dev libelf-dev libdw-dev
|
||||
sudo apt-get install cmake libcurl4-openssl-dev libelf-dev libdw-dev
|
||||
wget https://github.com/SimonKagstrom/kcov/archive/master.tar.gz
|
||||
tar xzf master.tar.gz
|
||||
mkdir kcov-master/build
|
||||
cd kcov-master/build
|
||||
cmake ..
|
||||
make
|
||||
sudo make install
|
||||
cd ../..
|
||||
|
||||
# run kcov on all tests, rerunning all tests with coverage report
|
||||
mkdir -p target
|
||||
|
||||
# needed to tell some config tests where the server root directory is
|
||||
export TDNS_SERVER_SRC_ROOT=./server
|
||||
export COVERALLS_PARALLEL=true
|
||||
|
||||
SRC_PATHS=client/src,server/src
|
||||
EXCLUDE_PATHS=client/src/error,server/src/error
|
||||
|
||||
for i in target/debug/trust_dns*-* target/debug/*_tests-* ; do
|
||||
if [ -f $i ] && [ -x $i ]; then
|
||||
# submit the report... what's the executable since there are many?
|
||||
echo "executing kcov on $i"
|
||||
kcov --exclude-pattern=/.cargo \
|
||||
--include-path=${SRC_PATHS} \
|
||||
--exclude-path=${EXCLUDE_PATHS} \
|
||||
target/kcov-$(basename $i) $i
|
||||
fi
|
||||
done
|
||||
|
||||
echo "merging and uploading to coveralls.io"
|
||||
kcov --coveralls-id=${TRAVIS_JOB_ID} \
|
||||
--merge target/kcov-*
|
12
scripts/run_tests.sh
Executable file
12
scripts/run_tests.sh
Executable file
@ -0,0 +1,12 @@
|
||||
#!/bin/bash -e
|
||||
|
||||
trust_dns_dir=$(dirname $0)/..
|
||||
cd ${trust_dns_dir:?}
|
||||
|
||||
for i in client server; do
|
||||
pushd $i
|
||||
echo "executing cargo on $i"
|
||||
cargo build --verbose
|
||||
cargo test --verbose
|
||||
popd
|
||||
done
|
543
server/Cargo.lock
generated
Normal file
543
server/Cargo.lock
generated
Normal file
@ -0,0 +1,543 @@
|
||||
[root]
|
||||
name = "trust-dns-server"
|
||||
version = "0.8.1"
|
||||
dependencies = [
|
||||
"backtrace 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"chrono 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"docopt 0.6.86 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"error-chain 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"openssl 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rusqlite 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"toml 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"trust-dns 0.8.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"backtrace-sys 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cfg-if 0.1.0 (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.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-demangle 0.1.2 (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.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"gcc 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.2.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"num 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "data-encoding"
|
||||
version = "1.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 = "docopt"
|
||||
version = "0.6.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"strsim 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "error-chain"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"backtrace 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gcc"
|
||||
version = "0.3.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "gdi32-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 = "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 = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "lazycell"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "libressl-pnacl-sys"
|
||||
version = "2.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"pnacl-build-helper 1.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libsqlite3-sys"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
version = "0.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "lru-cache"
|
||||
version = "0.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"linked-hash-map 0.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"net2 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nix 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazycell 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"net2 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nix 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miow"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"net2 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "net2"
|
||||
version = "0.2.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.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.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num"
|
||||
version = "0.1.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"num-integer 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-iter 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-iter"
|
||||
version = "0.1.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"num-integer 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.1.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"openssl-sys 0.7.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.7.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"gdi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libressl-pnacl-sys 2.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"user32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "pnacl-build-helper"
|
||||
version = "1.4.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "0.1.80"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "rusqlite"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libsqlite3-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lru-cache 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-serialize"
|
||||
version = "0.3.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scoped-tls"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "0.1.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "tempdir"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread-id"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.1.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-core"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"futures 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mio 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.1.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "trust-dns"
|
||||
version = "0.8.1"
|
||||
dependencies = [
|
||||
"backtrace 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"chrono 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"data-encoding 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"error-chain 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"openssl 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rusqlite 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"toml 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "user32-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 = "utf8-ranges"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "void"
|
||||
version = "1.0.2"
|
||||
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"
|
||||
|
||||
[[package]]
|
||||
name = "ws2_32-sys"
|
||||
version = "0.2.1"
|
||||
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)",
|
||||
]
|
||||
|
63
server/Cargo.toml
Normal file
63
server/Cargo.toml
Normal file
@ -0,0 +1,63 @@
|
||||
[package]
|
||||
name = "trust-dns-server"
|
||||
version = "0.8.1"
|
||||
authors = ["Benjamin Fry <benjaminfry@me.com>"]
|
||||
|
||||
# A short blurb about the package. This is not rendered in any format when
|
||||
# uploaded to crates.io (aka this is not markdown)
|
||||
description = """
|
||||
TRust-DNS is a safe and secure DNS server and client with DNSec support.
|
||||
Eventually this could be a replacement for BIND9. DNSSec on the client side,
|
||||
with NSEC validation for negative records, is complete. The client and
|
||||
server both support dynamic DNS with authenticated requests.
|
||||
"""
|
||||
|
||||
# These URLs point to more information about the repository
|
||||
documentation = "https://docs.rs/trust-dns"
|
||||
homepage = "http://www.trust-dns.org/index.html"
|
||||
repository = "https://github.com/bluejekyll/trust-dns"
|
||||
|
||||
# This points to a file in the repository (relative to this Cargo.toml). The
|
||||
# contents of this file are stored and indexed in the registry.
|
||||
readme = "README.md"
|
||||
|
||||
# This is a small list of keywords used to categorize and search for this
|
||||
# package.
|
||||
keywords = ["DNS", "BIND", "dig", "named", "dnssec"]
|
||||
|
||||
# This is a string description of the license for this package. Currently
|
||||
# crates.io will validate the license provided against a whitelist of known
|
||||
# license identifiers from http://spdx.org/licenses/. Multiple licenses can
|
||||
# be separated with a `/`
|
||||
license = "MIT/Apache-2.0"
|
||||
|
||||
# custom build steps
|
||||
build = "build.rs"
|
||||
|
||||
[features]
|
||||
|
||||
[lib]
|
||||
name = "trust_dns_server"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "named"
|
||||
path = "src/named.rs"
|
||||
|
||||
[dependencies]
|
||||
backtrace = "^0.2.1"
|
||||
chrono = "^0.2.21"
|
||||
docopt = "^0.6.78"
|
||||
error-chain = "0.1.12"
|
||||
futures = "^0.1"
|
||||
lazy_static = "^0.2.1"
|
||||
log = "^0.3.5"
|
||||
mio = "^0.5.1"
|
||||
openssl = "^0.8.3"
|
||||
rand = "^0.3"
|
||||
rustc-serialize = "^0.3.18"
|
||||
rusqlite = "^0.7.3"
|
||||
time = "^0.1"
|
||||
tokio-core = "^0.1"
|
||||
toml = "^0.1"
|
||||
trust-dns = { version = "*", path = "../client" }
|
32
server/build.rs
Normal file
32
server/build.rs
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Benjamin Fry <benjaminfry@me.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
|
||||
fn main() {
|
||||
// write out a version file to link against for version information
|
||||
let out_dir = env::var("OUT_DIR").unwrap();
|
||||
let version = env::var("CARGO_PKG_VERSION").unwrap();
|
||||
let dest_path = Path::new(&out_dir).join("version.rs");
|
||||
let mut f = File::create(&dest_path).unwrap();
|
||||
|
||||
|
||||
f.write_all(b"pub fn version() -> &'static str {").unwrap();
|
||||
write!(f, " \"{}\" ", version).unwrap();
|
||||
f.write_all(b" }").unwrap();
|
||||
}
|
@ -14,53 +14,17 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
use std::collections::BTreeMap;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use chrono::UTC;
|
||||
|
||||
use ::authority::{Journal, RRSet, UpdateResult, ZoneType};
|
||||
use trust_dns::op::{Message, UpdateMessage, ResponseCode, Query};
|
||||
use trust_dns::rr::{DNSClass, Name, RData, Record, RecordType, RrKey, RrSet};
|
||||
use trust_dns::rr::rdata::{NSEC, SIG};
|
||||
use trust_dns::rr::dnssec::Signer;
|
||||
|
||||
use ::authority::{Journal, UpdateResult, ZoneType};
|
||||
use ::error::{PersistenceErrorKind, PersistenceResult};
|
||||
use ::op::{Message, UpdateMessage, ResponseCode, Query};
|
||||
use ::rr::{DNSClass, Name, RData, Record, RecordType};
|
||||
use ::rr::rdata::{NSEC, SIG};
|
||||
use ::rr::dnssec::Signer;
|
||||
|
||||
/// Accessor key for RRSets in the Authority.
|
||||
#[derive(Eq, PartialEq, Debug, Hash, Clone)]
|
||||
pub struct RrKey { name: Name, record_type: RecordType }
|
||||
|
||||
impl RrKey {
|
||||
/// Creates a new key to access the Authority.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `name` - domain name to lookup.
|
||||
/// * `record_type` - the `RecordType` to lookup.
|
||||
///
|
||||
/// # Return value
|
||||
///
|
||||
/// A new key to access the Authorities.
|
||||
pub fn new(name: &Name, record_type: RecordType) -> RrKey {
|
||||
RrKey{ name: name.clone(), record_type: record_type }
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for RrKey {
|
||||
fn partial_cmp(&self, other: &RrKey) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for RrKey {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
let order = self.name.cmp(&other.name);
|
||||
if order == Ordering::Equal {
|
||||
self.record_type.cmp(&other.record_type)
|
||||
} else {
|
||||
order
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Authority is responsible for storing the resource records for a particular zone.
|
||||
///
|
||||
@ -70,7 +34,7 @@ pub struct Authority {
|
||||
origin: Name,
|
||||
class: DNSClass,
|
||||
journal: Option<Journal>,
|
||||
records: BTreeMap<RrKey, RRSet>,
|
||||
records: BTreeMap<RrKey, RrSet>,
|
||||
zone_type: ZoneType,
|
||||
allow_update: bool,
|
||||
// Private key mapped to the Record of the DNSKey
|
||||
@ -95,7 +59,7 @@ impl Authority {
|
||||
/// # Return value
|
||||
///
|
||||
/// The new `Authority`.
|
||||
pub fn new(origin: Name, records: BTreeMap<RrKey, RRSet>, zone_type: ZoneType, allow_update: bool) -> Authority {
|
||||
pub fn new(origin: Name, records: BTreeMap<RrKey, RrSet>, zone_type: ZoneType, allow_update: bool) -> Authority {
|
||||
Authority{ origin: origin, class: DNSClass::IN, journal: None, records: records, zone_type: zone_type,
|
||||
allow_update: allow_update, secure_keys: Vec::new() }
|
||||
}
|
||||
@ -178,25 +142,28 @@ impl Authority {
|
||||
self.journal.as_ref()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
/// Enables the zone for dynamic DNS updates
|
||||
pub fn set_allow_update(&mut self, allow_update: bool) {
|
||||
self.allow_update = allow_update;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
/// Retrieve the Signer, which contains the private keys, for this zone
|
||||
pub fn get_secure_keys(&self) -> &[Signer] {
|
||||
&self.secure_keys
|
||||
}
|
||||
|
||||
/// Get the origin of this zone, i.e. example.com is the origin for www.example.com
|
||||
pub fn get_origin(&self) -> &Name {
|
||||
&self.origin
|
||||
}
|
||||
|
||||
/// What type is this zone
|
||||
pub fn get_zone_type(&self) -> ZoneType {
|
||||
self.zone_type
|
||||
}
|
||||
|
||||
pub fn get_records(&self) -> &BTreeMap<RrKey, RRSet> {
|
||||
/// Get all the
|
||||
pub fn get_records(&self) -> &BTreeMap<RrKey, RrSet> {
|
||||
&self.records
|
||||
}
|
||||
|
||||
@ -209,18 +176,18 @@ impl Authority {
|
||||
self.lookup(&self.origin, RecordType::SOA, false).first().map(|v| *v)
|
||||
}
|
||||
|
||||
/// Returns the SOA record
|
||||
///
|
||||
///
|
||||
/// Returns the SOA record for the zone
|
||||
pub fn get_soa_secure(&self, is_secure: bool) -> Vec<&Record> {
|
||||
self.lookup(&self.origin, RecordType::SOA, is_secure)
|
||||
}
|
||||
|
||||
/// Returns the minimum ttl (as used in the SOA record)
|
||||
pub fn get_minimum_ttl(&self) -> u32 {
|
||||
self.get_soa().map_or(0, |soa| if let &RData::SOA(ref rdata) = soa.get_rdata() { rdata.get_minimum() } else { 0 })
|
||||
}
|
||||
|
||||
fn get_serial(&self) -> u32 {
|
||||
/// get the current serial number for the zone.
|
||||
pub fn get_serial(&self) -> u32 {
|
||||
let soa = if let Some(ref soa_record) = self.get_soa() {
|
||||
soa_record.clone()
|
||||
} else {
|
||||
@ -307,7 +274,7 @@ impl Authority {
|
||||
/// NONE rrset empty RRset does not exist
|
||||
/// zone rrset rr RRset exists (value dependent)
|
||||
/// ```
|
||||
fn verify_prerequisites(&self, pre_requisites: &[Record]) -> UpdateResult<()> {
|
||||
pub fn verify_prerequisites(&self, pre_requisites: &[Record]) -> UpdateResult<()> {
|
||||
// 3.2.5 - Pseudocode for Prerequisite Section Processing
|
||||
//
|
||||
// for rr in prerequisites
|
||||
@ -443,7 +410,7 @@ impl Authority {
|
||||
/// and restore the zone to its original state before answering the
|
||||
/// requestor.
|
||||
/// ```
|
||||
fn authorize(&self, update_message: &Message) -> UpdateResult<()> {
|
||||
pub fn authorize(&self, update_message: &Message) -> UpdateResult<()> {
|
||||
// 3.3.3 - Pseudocode for Permission Checking
|
||||
//
|
||||
// if (security policy exists)
|
||||
@ -527,7 +494,7 @@ impl Authority {
|
||||
/// MAILB, or any other QUERY metatype besides ANY, or any unrecognized
|
||||
/// type, else signal FORMERR to the requestor.
|
||||
/// ```
|
||||
fn pre_scan(&self, records: &[Record]) -> UpdateResult<()> {
|
||||
pub fn pre_scan(&self, records: &[Record]) -> UpdateResult<()> {
|
||||
// 3.4.1.3 - Pseudocode For Update Section Prescan
|
||||
//
|
||||
// [rr] for rr in updates
|
||||
@ -603,7 +570,7 @@ impl Authority {
|
||||
/// * `records` - set of record instructions for update following above rules
|
||||
/// * `auto_sign` - if true, the zone will auto_sign (assuming there are signers present), this
|
||||
/// should be disabled during recovery.
|
||||
fn update_records(&mut self, records: &[Record], auto_sign: bool) -> UpdateResult<bool> {
|
||||
pub fn update_records(&mut self, records: &[Record], auto_sign: bool) -> UpdateResult<bool> {
|
||||
let mut updated = false;
|
||||
let serial: u32 = self.get_serial();
|
||||
|
||||
@ -760,7 +727,7 @@ impl Authority {
|
||||
assert_eq!(self.class, record.get_dns_class());
|
||||
|
||||
let rr_key = RrKey::new(record.get_name(), record.get_rr_type());
|
||||
let records: &mut RRSet = self.records.entry(rr_key).or_insert(RRSet::new(record.get_name(), record.get_rr_type(), serial));
|
||||
let records: &mut RrSet = self.records.entry(rr_key).or_insert(RrSet::new(record.get_name(), record.get_rr_type(), serial));
|
||||
|
||||
records.insert(record, serial)
|
||||
}
|
||||
@ -1073,448 +1040,75 @@ impl Authority {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod authority_tests {
|
||||
use std::collections::BTreeMap;
|
||||
use std::net::{Ipv4Addr,Ipv6Addr};
|
||||
|
||||
use ::authority::ZoneType;
|
||||
use ::rr::*;
|
||||
use ::rr::rdata::{ NULL, SOA, TXT };
|
||||
use ::op::*;
|
||||
use super::*;
|
||||
|
||||
pub fn create_example() -> Authority {
|
||||
let origin: Name = Name::parse("example.com.", None,).unwrap();
|
||||
let mut records: Authority = Authority::new(origin.clone(), BTreeMap::new(), ZoneType::Master, false);
|
||||
// example.com. 3600 IN SOA sns.dns.icann.org. noc.dns.icann.org. 2015082403 7200 3600 1209600 3600
|
||||
records.upsert(Record::new().name(origin.clone()).ttl(3600).rr_type(RecordType::SOA).dns_class(DNSClass::IN).rdata(RData::SOA(SOA::new(Name::parse("sns.dns.icann.org.", None).unwrap(), Name::parse("noc.dns.icann.org.", None).unwrap(), 2015082403, 7200, 3600, 1209600, 3600 ))).clone(), 0);
|
||||
|
||||
records.upsert(Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::NS).dns_class(DNSClass::IN).rdata(RData::NS(Name::parse("a.iana-servers.net.", None).unwrap()) ).clone(), 0);
|
||||
records.upsert(Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::NS).dns_class(DNSClass::IN).rdata(RData::NS(Name::parse("b.iana-servers.net.", None).unwrap()) ).clone(), 0);
|
||||
|
||||
// example.com. 60 IN TXT "v=spf1 -all"
|
||||
//records.upsert(origin.clone(), Record::new().name(origin.clone()).ttl(60).rr_type(RecordType::TXT).dns_class(DNSClass::IN).rdata(RData::TXT{ txt_data: vec!["v=spf1 -all".to_string()] }).clone());
|
||||
// example.com. 60 IN TXT "$Id: example.com 4415 2015-08-24 20:12:23Z davids $"
|
||||
records.upsert(Record::new().name(origin.clone()).ttl(60).rr_type(RecordType::TXT).dns_class(DNSClass::IN).rdata(RData::TXT(TXT::new(vec!["$Id: example.com 4415 2015-08-24 20:12:23Z davids $".to_string()]))).clone(), 0);
|
||||
|
||||
// example.com. 86400 IN A 93.184.216.34
|
||||
records.upsert(Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::IN).rdata(RData::A(Ipv4Addr::new(93,184,216,34))).clone(), 0);
|
||||
|
||||
// example.com. 86400 IN AAAA 2606:2800:220:1:248:1893:25c8:1946
|
||||
records.upsert(Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::AAAA).dns_class(DNSClass::IN).rdata(RData::AAAA(Ipv6Addr::new(0x2606,0x2800,0x220,0x1,0x248,0x1893,0x25c8,0x1946))).clone(), 0);
|
||||
|
||||
// TODO support these later...
|
||||
|
||||
// example.com. 3600 IN RRSIG NSEC 8 2 3600 20150926015219 20150905040848 54108 example.com. d0AXd6QRITqLeiYbQUlJ5O0Og9tSjk7IlxQr9aJO+r+rc1g0dW9i9OCc XXQxdC1/zyubecjD6kSs3vwxzzEEupivaKHKtNPXdnDZ5UUiaIC1VU9l 9h/ik+AR4rCTY6dYPCI6lafD/TlqQLbpEnb34ywkRpl5G3pasPrwEY7b nrAndEY=
|
||||
// example.com. 3600 IN NSEC www.example.com. A NS SOA TXT AAAA RRSIG NSEC DNSKEY
|
||||
// example.com. 86400 IN RRSIG NS 8 2 86400 20150915033148 20150824191224 54108 example.com. O2TCB5/v/b1XGlTQEj0/oGKp7dTueQ7zRmCtADDEDWrzLdWrKcmDGF37 mgKejcAlSYVhWLxyLlet7KqJhLu+oQcDTNf/BT3vNX/Ivx3sKhUUMpfi 8Mn5zhRqM9gbzZVCS/toJIYqOBqvAkS7UpkmpLzl0Zt2h4j0Gp/8GwRb ZU67l6M=
|
||||
// example.com. 86400 IN RRSIG AAAA 8 2 86400 20150914212400 20150824191224 54108 example.com. AHd2BDNjtg4jPRQwyT4FHtlVTZDZ6IIusYVGCzWfnt5SZOoizyXnJhqX 44MeVTqi1/2cskpKvRkK3bkYnVUcjZiFgSaa9xJHmXrslaTr5mOmXt9s 6k95N1daYKhDKKcr0M4TXLUgdnBr+/pMFiLsyOoDb8GJDT8Llmpk52Ie ysJX8BY=
|
||||
// example.com. 86400 IN RRSIG A 8 2 86400 20150914083326 20150824191224 54108 example.com. La1p2R7GPMrXEm3kcznSJ70sOspmfSDsgOZ74GlzgaFfMRveA20IDUnZ /HI9M95/tBWbHdHBtm9aCK+4n7EluhNPTAT1+88V6xK7Lc7pcBfBXIHg DAdUoj26VIh7NRml/0QR0dFu4PriA/wLNe+d1Q961qf0JZP80TU4IMBC X/W6Ijk=
|
||||
// example.com. 60 IN RRSIG TXT 8 2 60 20150914201612 20150824191224 54108 example.com. Be/bPvaVVK/o66QOHJZMFBDCQVhP44jptS9sZe8Vpfmzd72/v+1gwn1z u2+xisePSpAMtDZsFJgqsCjpbLFvmhNdh8ktlq/kuCME5hZs7qY7DZIB VwkSTsJPIq8qhX22clfIbqzaypuIX9ajWr+5i0nGQLNekMB07t4/GCoJ q5QpQoE=
|
||||
// example.com. 3600 IN RRSIG DNSKEY 8 2 3600 20150914090528 20150824071818 31406 example.com. rZJRBwHhYzCDwkDEXqECHNWezTNj2A683I/yHHqD1j9ytGHGskGEEyJC i5fk70YCm64GqDYKu70kgv7hCFqc4OM3aD88QDe3L4Uv7ZXqouNbjTEO 3BEBI13GetRkK5qLndl30Y/urOBASQFELQUJsvQBR2gJMdQsb6G0mHIW rubY2SxAGa9rQW7yehRQNK4ME37FqINBDuIV9o7kULPhn9Ux1Qx62prd 9nikzamGxFL+9dFDOfnYVw2C/OgGJNIXh5QyKMG4qXmXb6sB/V3P+FE+ +vkt3RToE2xPN5bf1vVIlEJof6LtojrowwnZpiphTXFJF/BJrgiotGt3 Gsd8Cw==
|
||||
// example.com. 3600 IN DNSKEY 256 3 8 AwEAAcZMEndf6/+kG6Dp7re/grJ9f5CP5bQplBGokyxbM4oPNeBfWMIC +xY+ICgTyJarVB4aPYNMV7znsHM4XwU8hfpZ3ZcmT+69KyGqs+tt2pc/ si30dnUpPo/AMnN7Kul2SgqT9g1bb5O0D/CH2txo6YXr/BbuNHLqAh/x mof1QYkl6GoP
|
||||
// example.com. 3600 IN DNSKEY 256 3 8 AwEAAeZFCLkW/sztmJmpmZo/udvAyqshiLO34zHzzkVPrhuUBA/xb3wk YeCvMO6iBxCD+/Dk7fWEAT1NR21bDKHySVHE5cre+fqnXI+9NCjkMoBE 193j8G5HscIpWpG1qgkelBhmucfUPv+R4AIhpfjc352eh1q/SniYUGR4 fytlDZVXCLhL
|
||||
// example.com. 3600 IN DNSKEY 257 3 8 AwEAAbOFAxl+Lkt0UMglZizKEC1AxUu8zlj65KYatR5wBWMrh18TYzK/ ig6Y1t5YTWCO68bynorpNu9fqNFALX7bVl9/gybA0v0EhF+dgXmoUfRX 7ksMGgBvtfa2/Y9a3klXNLqkTszIQ4PEMVCjtryl19Be9/PkFeC9ITjg MRQsQhmB39eyMYnal+f3bUxKk4fq7cuEU0dbRpue4H/N6jPucXWOwiMA kTJhghqgy+o9FfIp+tR/emKao94/wpVXDcPf5B18j7xz2SvTTxiuqCzC MtsxnikZHcoh1j4g+Y1B8zIMIvrEM+pZGhh/Yuf4RwCBgaYCi9hpiMWV vS4WBzx0/lU=
|
||||
// example.com. 3600 IN RRSIG SOA 8 2 3600 20150926132522 20150905040848 54108 example.com. q8psdDPaJVo9KPVgMNR2N1by3LMEci+3HyTmN/Xv3DgDFG5MqNlX9Dfj dUBIMbvYmkUUPQ9fIWYA+ldmDHiRBiHIcvvk/LYD8mODWL6RoF+GEsW0 zm43RNBnbE41wtNrch5WU/q1ko2svB98ooqePWWuFzmdyPpidtLCgSCz FCiCiVQ=
|
||||
|
||||
// www
|
||||
let www_name: Name = Name::parse("www.example.com.", None).unwrap();
|
||||
|
||||
// www.example.com. 86400 IN TXT "v=spf1 -all"
|
||||
records.upsert(Record::new().name(www_name.clone()).ttl(86400).rr_type(RecordType::TXT).dns_class(DNSClass::IN).rdata(RData::TXT(TXT::new(vec!["v=spf1 -all".to_string()]))).clone(), 0);
|
||||
|
||||
// www.example.com. 86400 IN A 93.184.216.34
|
||||
records.upsert(Record::new().name(www_name.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::IN).rdata(RData::A(Ipv4Addr::new(93,184,216,34))).clone(), 0);
|
||||
|
||||
// www.example.com. 86400 IN AAAA 2606:2800:220:1:248:1893:25c8:1946
|
||||
records.upsert(Record::new().name(www_name.clone()).ttl(86400).rr_type(RecordType::AAAA).dns_class(DNSClass::IN).rdata(RData::AAAA(Ipv6Addr::new(0x2606,0x2800,0x220,0x1,0x248,0x1893,0x25c8,0x1946))).clone(), 0);
|
||||
|
||||
// www.example.com. 3600 IN RRSIG NSEC 8 3 3600 20150925215757 20150905040848 54108 example.com. ZKIVt1IN3O1FWZPSfrQAH7nHt7RUFDjcbh7NxnEqd/uTGCnZ6SrAEgrY E9GMmBwvRjoucphGtjkYOpPJPe5MlnTHoYCjxL4qmG3LsD2KD0bfPufa ibtlQZRrPglxZ92hBKK3ZiPnPRe7I9yni2UQSQA7XDi7CQySYyo490It AxdXjAo=
|
||||
// www.example.com. 3600 IN NSEC example.com. A TXT AAAA RRSIG NSEC
|
||||
// www.example.com. 86400 IN RRSIG TXT 8 3 86400 20150914142952 20150824191224 54108 example.com. LvODnPb7NLDZfHPBOrr/qLnOKA670vVYKQSk5Qkz3MPNKDVAFJqsP2Y6 UYcypSJZfcSjfIk2mU9dUiansU2ZL80OZJUsUobqJt5De748ovITYDJ7 afbohQzPg+4E1GIWMkJZ/VQD3B2pmr7J5rPn+vejxSQSoI93AIQaTpCU L5O/Bac=
|
||||
// www.example.com. 86400 IN RRSIG AAAA 8 3 86400 20150914082216 20150824191224 54108 example.com. kje4FKE+7d/j4OzWQelcKkePq6DxCRY/5btAiUcZNf+zVNlHK+o57h1r Y76ZviWChQB8Np2TjA1DrXGi/kHr2KKE60H5822mFZ2b9O+sgW4q6o3G kO2E1CQxbYe+nI1Z8lVfjdCNm81zfvYqDjo2/tGqagehxG1V9MBZO6br 4KKdoa4=
|
||||
// www.example.com. 86400 IN RRSIG A 8 3 86400 20150915023456 20150824191224 54108 example.com. cWtw0nMvcXcYNnxejB3Le3KBfoPPQZLmbaJ8ybdmzBDefQOm1ZjZZMOP wHEIxzdjRhG9mLt1mpyo1H7OezKTGX+mDtskcECTl/+jB/YSZyvbwRxj e88Lrg4D+D2MiajQn3XSWf+6LQVe1J67gdbKTXezvux0tRxBNHHqWXRk pxCILes=
|
||||
|
||||
return records;
|
||||
}
|
||||
|
||||
pub fn create_secure_example() -> Authority {
|
||||
use chrono::Duration;
|
||||
use openssl::crypto::rsa::RSA;
|
||||
use ::rr::dnssec::{Algorithm, Signer};
|
||||
|
||||
let mut authority: Authority = create_example();
|
||||
let rsa = RSA::generate(2048).unwrap();
|
||||
let signer = Signer::new(Algorithm::RSASHA256, rsa, authority.get_origin().clone(), Duration::weeks(1));
|
||||
|
||||
authority.add_secure_key(signer);
|
||||
authority.secure_zone();
|
||||
|
||||
authority
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_search() {
|
||||
let example = create_example();
|
||||
let origin = example.get_origin().clone();
|
||||
|
||||
let mut query: Query = Query::new();
|
||||
query.name(origin.clone());
|
||||
|
||||
let result = example.search(&query, false);
|
||||
if !result.is_empty() {
|
||||
assert_eq!(result.first().unwrap().get_rr_type(), RecordType::A);
|
||||
assert_eq!(result.first().unwrap().get_dns_class(), DNSClass::IN);
|
||||
assert_eq!(result.first().unwrap().get_rdata(), &RData::A(Ipv4Addr::new(93,184,216,34)));
|
||||
} else {
|
||||
panic!("expected a result"); // valid panic, in test
|
||||
}
|
||||
}
|
||||
|
||||
/// this is a litte more interesting b/c it requires a recursive lookup for the origin
|
||||
#[test]
|
||||
fn test_search_www() {
|
||||
let example = create_example();
|
||||
let www_name = Name::parse("www.example.com.", None).unwrap();
|
||||
|
||||
let mut query: Query = Query::new();
|
||||
query.name(www_name.clone());
|
||||
|
||||
let result = example.search(&query, false);
|
||||
if !result.is_empty() {
|
||||
assert_eq!(result.first().unwrap().get_rr_type(), RecordType::A);
|
||||
assert_eq!(result.first().unwrap().get_dns_class(), DNSClass::IN);
|
||||
assert_eq!(result.first().unwrap().get_rdata(), &RData::A(Ipv4Addr::new(93,184,216,34)));
|
||||
} else {
|
||||
panic!("expected a result"); // valid panic, in test
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_authority() {
|
||||
let authority: Authority = create_example();
|
||||
|
||||
assert!(authority.get_soa().is_some());
|
||||
assert_eq!(authority.get_soa().unwrap().get_dns_class(), DNSClass::IN);
|
||||
|
||||
assert!(!authority.lookup(authority.get_origin(), RecordType::NS, false).is_empty());
|
||||
|
||||
let mut lookup: Vec<_> = authority.get_ns(false);
|
||||
lookup.sort();
|
||||
|
||||
assert_eq!(**lookup.first().unwrap(), Record::new().name(authority.get_origin().clone()).ttl(86400).rr_type(RecordType::NS).dns_class(DNSClass::IN).rdata(RData::NS(Name::parse("a.iana-servers.net.", None).unwrap()) ).clone());
|
||||
assert_eq!(**lookup.last().unwrap(), Record::new().name(authority.get_origin().clone()).ttl(86400).rr_type(RecordType::NS).dns_class(DNSClass::IN).rdata(RData::NS(Name::parse("b.iana-servers.net.", None).unwrap()) ).clone());
|
||||
|
||||
assert!(!authority.lookup(authority.get_origin(), RecordType::TXT, false).is_empty());
|
||||
|
||||
let mut lookup: Vec<_> = authority.lookup(authority.get_origin(), RecordType::TXT, false);
|
||||
lookup.sort();
|
||||
|
||||
assert_eq!(**lookup.first().unwrap(), Record::new().name(authority.get_origin().clone()).ttl(60).rr_type(RecordType::TXT).dns_class(DNSClass::IN).rdata(RData::TXT(TXT::new(vec!["$Id: example.com 4415 2015-08-24 20:12:23Z davids $".to_string()]))).clone());
|
||||
|
||||
assert_eq!(**authority.lookup(authority.get_origin(), RecordType::A, false).first().unwrap(), Record::new().name(authority.get_origin().clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::IN).rdata(RData::A(Ipv4Addr::new(93,184,216,34))).clone());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_authorize() {
|
||||
let authority: Authority = create_example();
|
||||
|
||||
let mut message = Message::new();
|
||||
message.id(10).message_type(MessageType::Query).op_code(OpCode::Update);
|
||||
|
||||
assert_eq!(authority.authorize(&message), Err(ResponseCode::Refused));
|
||||
|
||||
// TODO: this will nee to be more complex as additional policies are added
|
||||
// authority.set_allow_update(true);
|
||||
// assert!(authority.authorize(&message).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_prerequisites() {
|
||||
let not_zone = Name::new().label("not").label("a").label("domain").label("com");
|
||||
let not_in_zone = Name::new().label("not").label("example").label("com");
|
||||
|
||||
let mut authority: Authority = create_example();
|
||||
authority.set_allow_update(true);
|
||||
|
||||
// first check the initial negatives, ttl = 0, and the zone is the same
|
||||
assert_eq!(authority.verify_prerequisites(&[Record::new().name(not_in_zone.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::IN).rdata(RData::NULL(NULL::new())).clone()]), Err(ResponseCode::FormErr));
|
||||
assert_eq!(authority.verify_prerequisites(&[Record::new().name(not_zone.clone()).ttl(0).rr_type(RecordType::A).dns_class(DNSClass::IN).rdata(RData::NULL(NULL::new())).clone()]), Err(ResponseCode::NotZone));
|
||||
|
||||
// * ANY ANY empty Name is in use
|
||||
assert!(authority.verify_prerequisites(&[Record::new().name(authority.get_origin().clone()).ttl(0).dns_class(DNSClass::ANY).rr_type(RecordType::ANY).rdata(RData::NULL(NULL::new())).clone()]).is_ok());
|
||||
assert_eq!(authority.verify_prerequisites(&[Record::new().name(not_in_zone.clone()).ttl(0).dns_class(DNSClass::ANY).rr_type(RecordType::ANY).rdata(RData::NULL(NULL::new())).clone()]), Err(ResponseCode::NXDomain));
|
||||
|
||||
// * ANY rrset empty RRset exists (value independent)
|
||||
assert!(authority.verify_prerequisites(&[Record::new().name(authority.get_origin().clone()).ttl(0).dns_class(DNSClass::ANY).rr_type(RecordType::A).rdata(RData::NULL(NULL::new())).clone()]).is_ok());
|
||||
assert_eq!(authority.verify_prerequisites(&[Record::new().name(not_in_zone.clone()).ttl(0).dns_class(DNSClass::ANY).rr_type(RecordType::A).rdata(RData::NULL(NULL::new())).clone()]), Err(ResponseCode::NXRRSet));
|
||||
|
||||
// * NONE ANY empty Name is not in use
|
||||
assert!(authority.verify_prerequisites(&[Record::new().name(not_in_zone.clone()).ttl(0).dns_class(DNSClass::NONE).rr_type(RecordType::ANY).rdata(RData::NULL(NULL::new())).clone()]).is_ok());
|
||||
assert_eq!(authority.verify_prerequisites(&[Record::new().name(authority.get_origin().clone()).ttl(0).dns_class(DNSClass::NONE).rr_type(RecordType::ANY).rdata(RData::NULL(NULL::new())).clone()]), Err(ResponseCode::YXDomain));
|
||||
|
||||
// * NONE rrset empty RRset does not exist
|
||||
assert!(authority.verify_prerequisites(&[Record::new().name(not_in_zone.clone()).ttl(0).dns_class(DNSClass::NONE).rr_type(RecordType::A).rdata(RData::NULL(NULL::new())).clone()]).is_ok());
|
||||
assert_eq!(authority.verify_prerequisites(&[Record::new().name(authority.get_origin().clone()).ttl(0).dns_class(DNSClass::NONE).rr_type(RecordType::A).rdata(RData::NULL(NULL::new())).clone()]), Err(ResponseCode::YXRRSet));
|
||||
|
||||
// * zone rrset rr RRset exists (value dependent)
|
||||
assert!(authority.verify_prerequisites(&[Record::new().name(authority.get_origin().clone()).ttl(0).dns_class(DNSClass::IN).rr_type(RecordType::A).rdata(RData::A(Ipv4Addr::new(93,184,216,34))).clone()]).is_ok());
|
||||
// wrong class
|
||||
assert_eq!(authority.verify_prerequisites(&[Record::new().name(authority.get_origin().clone()).ttl(0).dns_class(DNSClass::CH).rr_type(RecordType::A).rdata(RData::A(Ipv4Addr::new(93,184,216,34))).clone()]), Err(ResponseCode::FormErr));
|
||||
// wrong Name
|
||||
assert_eq!(authority.verify_prerequisites(&[Record::new().name(not_in_zone.clone()).ttl(0).dns_class(DNSClass::IN).rr_type(RecordType::A).rdata(RData::A(Ipv4Addr::new(93,184,216,24))).clone()]), Err(ResponseCode::NXRRSet));
|
||||
// wrong IP
|
||||
assert_eq!(authority.verify_prerequisites(&[Record::new().name(authority.get_origin().clone()).ttl(0).dns_class(DNSClass::IN).rr_type(RecordType::A).rdata(RData::A(Ipv4Addr::new(93,184,216,24))).clone()]), Err(ResponseCode::NXRRSet));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pre_scan() {
|
||||
let up_name = Name::new().label("www").label("example").label("com");
|
||||
let not_zone = Name::new().label("not").label("zone").label("com");
|
||||
|
||||
let authority: Authority = create_example();
|
||||
|
||||
assert_eq!(authority.pre_scan(&[Record::new().name(not_zone.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::IN).rdata(RData::A(Ipv4Addr::new(93,184,216,24))).clone()]), Err(ResponseCode::NotZone));
|
||||
|
||||
assert_eq!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(86400).rr_type(RecordType::ANY).dns_class(DNSClass::IN).rdata(RData::NULL(NULL::new()) ).clone()]), Err(ResponseCode::FormErr));
|
||||
assert_eq!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(86400).rr_type(RecordType::AXFR).dns_class(DNSClass::IN).rdata(RData::NULL(NULL::new()) ).clone()]), Err(ResponseCode::FormErr));
|
||||
assert_eq!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(86400).rr_type(RecordType::IXFR).dns_class(DNSClass::IN).rdata(RData::NULL(NULL::new()) ).clone()]), Err(ResponseCode::FormErr));
|
||||
assert!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::IN).rdata(RData::A(Ipv4Addr::new(93,184,216,24))).clone()]).is_ok());
|
||||
assert!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::IN).rdata(RData::NULL(NULL::new())).clone()]).is_ok());
|
||||
|
||||
assert_eq!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::ANY).rdata(RData::A(Ipv4Addr::new(93,184,216,24))).clone()]), Err(ResponseCode::FormErr));
|
||||
assert_eq!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(0).rr_type(RecordType::A).dns_class(DNSClass::ANY).rdata(RData::A(Ipv4Addr::new(93,184,216,24))).clone()]), Err(ResponseCode::FormErr));
|
||||
assert_eq!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(0).rr_type(RecordType::AXFR).dns_class(DNSClass::ANY).rdata(RData::NULL(NULL::new())).clone()]), Err(ResponseCode::FormErr));
|
||||
assert_eq!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(0).rr_type(RecordType::IXFR).dns_class(DNSClass::ANY).rdata(RData::NULL(NULL::new())).clone()]), Err(ResponseCode::FormErr));
|
||||
assert!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(0).rr_type(RecordType::ANY).dns_class(DNSClass::ANY).rdata(RData::NULL(NULL::new())).clone()]).is_ok());
|
||||
assert!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(0).rr_type(RecordType::A).dns_class(DNSClass::ANY).rdata(RData::NULL(NULL::new())).clone()]).is_ok());
|
||||
|
||||
assert_eq!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::NONE).rdata(RData::NULL(NULL::new())).clone()]), Err(ResponseCode::FormErr));
|
||||
assert_eq!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(0).rr_type(RecordType::ANY).dns_class(DNSClass::NONE).rdata(RData::NULL(NULL::new())).clone()]), Err(ResponseCode::FormErr));
|
||||
assert_eq!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(0).rr_type(RecordType::AXFR).dns_class(DNSClass::NONE).rdata(RData::NULL(NULL::new())).clone()]), Err(ResponseCode::FormErr));
|
||||
assert_eq!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(0).rr_type(RecordType::IXFR).dns_class(DNSClass::NONE).rdata(RData::NULL(NULL::new())).clone()]), Err(ResponseCode::FormErr));
|
||||
assert!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(0).rr_type(RecordType::A).dns_class(DNSClass::NONE).rdata(RData::NULL(NULL::new())).clone()]).is_ok());
|
||||
assert!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(0).rr_type(RecordType::A).dns_class(DNSClass::NONE).rdata(RData::A(Ipv4Addr::new(93,184,216,24))).clone()]).is_ok());
|
||||
|
||||
assert_eq!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::CH).rdata(RData::NULL(NULL::new())).clone()]), Err(ResponseCode::FormErr));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_update() {
|
||||
let new_name = Name::new().label("new").label("example").label("com");
|
||||
let www_name = Name::new().label("www").label("example").label("com");
|
||||
let mut authority: Authority = create_example();
|
||||
let serial = authority.get_serial();
|
||||
|
||||
authority.set_allow_update(true);
|
||||
|
||||
let mut original_vec: Vec<Record> = vec![
|
||||
Record::new().name(www_name.clone()).ttl(86400).rr_type(RecordType::TXT).dns_class(DNSClass::IN).rdata(RData::TXT(TXT::new(vec!["v=spf1 -all".to_string()]))).clone(),
|
||||
Record::new().name(www_name.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::IN).rdata(RData::A(Ipv4Addr::new(93,184,216,34))).clone(),
|
||||
Record::new().name(www_name.clone()).ttl(86400).rr_type(RecordType::AAAA).dns_class(DNSClass::IN).rdata(RData::AAAA(Ipv6Addr::new(0x2606,0x2800,0x220,0x1,0x248,0x1893,0x25c8,0x1946))).clone(),
|
||||
];
|
||||
|
||||
original_vec.sort();
|
||||
|
||||
{
|
||||
// assert that the correct set of records is there.
|
||||
let mut www_rrset: Vec<&Record> = authority.lookup(&www_name, RecordType::ANY, false);
|
||||
www_rrset.sort();
|
||||
|
||||
assert_eq!(www_rrset, original_vec.iter().collect::<Vec<&Record>>());
|
||||
|
||||
// assert new record doesn't exist
|
||||
assert!(authority.lookup(&new_name, RecordType::ANY, false).is_empty());
|
||||
}
|
||||
|
||||
//
|
||||
// zone rrset rr Add to an RRset
|
||||
let add_record = &[Record::new().name(new_name.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::IN).rdata(RData::A(Ipv4Addr::new(93,184,216,24))).clone()];
|
||||
assert!(authority.update_records(add_record, true).expect("update failed"));
|
||||
assert_eq!(authority.lookup(&new_name, RecordType::ANY, false), add_record.iter().collect::<Vec<&Record>>());
|
||||
assert_eq!(serial + 1, authority.get_serial());
|
||||
|
||||
let add_www_record = &[Record::new().name(www_name.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::IN).rdata(RData::A(Ipv4Addr::new(10,0,0,1))).clone()];
|
||||
assert!(authority.update_records(add_www_record, true).expect("update failed"));
|
||||
assert_eq!(serial + 2, authority.get_serial());
|
||||
|
||||
{
|
||||
let mut www_rrset = authority.lookup(&www_name, RecordType::ANY, false);
|
||||
www_rrset.sort();
|
||||
|
||||
let mut plus_10 = original_vec.clone();
|
||||
plus_10.push(add_www_record[0].clone());
|
||||
plus_10.sort();
|
||||
assert_eq!(www_rrset, plus_10.iter().collect::<Vec<&Record>>());
|
||||
}
|
||||
|
||||
//
|
||||
// NONE rrset rr Delete an RR from an RRset
|
||||
let del_record = &[Record::new().name(new_name.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::NONE).rdata(RData::A(Ipv4Addr::new(93,184,216,24))).clone()];
|
||||
assert!(authority.update_records(del_record, true).expect("update failed"));
|
||||
assert_eq!(serial + 3, authority.get_serial());
|
||||
{
|
||||
println!("after delete of specific record: {:?}", authority.lookup(&new_name, RecordType::ANY, false));
|
||||
assert!(authority.lookup(&new_name, RecordType::ANY, false).is_empty());
|
||||
}
|
||||
|
||||
// remove one from www
|
||||
let del_record = &[Record::new().name(www_name.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::NONE).rdata(RData::A(Ipv4Addr::new(10,0,0,1))).clone()];
|
||||
assert!(authority.update_records(del_record, true).expect("update failed"));
|
||||
assert_eq!(serial + 4, authority.get_serial());
|
||||
{
|
||||
let mut www_rrset = authority.lookup(&www_name, RecordType::ANY, false);
|
||||
www_rrset.sort();
|
||||
|
||||
assert_eq!(www_rrset, original_vec.iter().collect::<Vec<&Record>>());
|
||||
}
|
||||
|
||||
//
|
||||
// ANY rrset empty Delete an RRset
|
||||
let del_record = &[Record::new().name(www_name.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::ANY).rdata(RData::NULL(NULL::new())).clone()];
|
||||
assert!(authority.update_records(del_record, true).expect("update failed"));
|
||||
assert_eq!(serial + 5, authority.get_serial());
|
||||
let mut removed_a_vec: Vec<_> = vec![
|
||||
Record::new().name(www_name.clone()).ttl(86400).rr_type(RecordType::TXT).dns_class(DNSClass::IN).rdata(RData::TXT(TXT::new(vec!["v=spf1 -all".to_string()]))).clone(),
|
||||
Record::new().name(www_name.clone()).ttl(86400).rr_type(RecordType::AAAA).dns_class(DNSClass::IN).rdata(RData::AAAA(Ipv6Addr::new(0x2606,0x2800,0x220,0x1,0x248,0x1893,0x25c8,0x1946))).clone(),
|
||||
];
|
||||
removed_a_vec.sort();
|
||||
|
||||
{
|
||||
let mut www_rrset = authority.lookup(&www_name, RecordType::ANY, false);
|
||||
www_rrset.sort();
|
||||
|
||||
assert_eq!(www_rrset, removed_a_vec.iter().collect::<Vec<&Record>>());
|
||||
}
|
||||
|
||||
//
|
||||
// ANY ANY empty Delete all RRsets from a name
|
||||
println!("deleting all records");
|
||||
let del_record = &[Record::new().name(www_name.clone()).ttl(86400).rr_type(RecordType::ANY).dns_class(DNSClass::ANY).rdata(RData::NULL(NULL::new())).clone()];
|
||||
assert!(authority.update_records(del_record, true).expect("update failed"));
|
||||
assert!(authority.lookup(&www_name, RecordType::ANY, false).is_empty());
|
||||
assert_eq!(serial + 6, authority.get_serial());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_zone_signing() {
|
||||
use ::rr::{RData};
|
||||
|
||||
let authority: Authority = create_secure_example();
|
||||
|
||||
let results = authority.lookup(&authority.get_origin(), RecordType::AXFR, true);
|
||||
|
||||
assert!(results.iter().any(|r| r.get_rr_type() == RecordType::DNSKEY), "must contain a DNSKEY");
|
||||
|
||||
for record in results.iter() {
|
||||
if record.get_rr_type() == RecordType::RRSIG { continue }
|
||||
if record.get_rr_type() == RecordType::DNSKEY { continue }
|
||||
|
||||
// validate all records have associated RRSIGs after signing
|
||||
assert!(results.iter().any(|r| r.get_rr_type() == RecordType::RRSIG &&
|
||||
r.get_name() == record.get_name() &&
|
||||
if let &RData::SIG(ref rrsig) = r.get_rdata() {
|
||||
rrsig.get_type_covered() == record.get_rr_type()
|
||||
} else {
|
||||
false
|
||||
} ), "record type not covered: {:?}", record);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_nsec() {
|
||||
let name = Name::new().label("zzz").label("example").label("com");
|
||||
let authority: Authority = create_secure_example();
|
||||
|
||||
let results = authority.get_nsec_records(&name, true);
|
||||
|
||||
for record in results.iter() {
|
||||
assert!(record.get_name() < &name);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_journal() {
|
||||
use std::net::Ipv4Addr;
|
||||
use rusqlite::Connection;
|
||||
use ::authority::Journal;
|
||||
|
||||
// test that this message can be inserted
|
||||
let conn = Connection::open_in_memory().expect("could not create in memory DB");
|
||||
let mut journal = Journal::new(conn).unwrap();
|
||||
journal.schema_up().unwrap();
|
||||
|
||||
let mut authority = create_example();
|
||||
authority.journal(journal);
|
||||
authority.persist_to_journal().unwrap();
|
||||
|
||||
let new_name = Name::new().label("new").label("example").label("com");
|
||||
let delete_name = Name::new().label("www").label("example").label("com");
|
||||
let new_record = Record::new().name(new_name.clone()).rdata(RData::A(Ipv4Addr::new(10,11,12,13))).clone();
|
||||
let delete_record = Record::new().name(delete_name.clone()).rdata(RData::A(Ipv4Addr::new(93,184,216,34))).dns_class(DNSClass::NONE).clone();
|
||||
authority.update_records(&[new_record.clone(), delete_record], true).unwrap();
|
||||
|
||||
// assert that the correct set of records is there.
|
||||
let new_rrset: Vec<&Record> = authority.lookup(&new_name, RecordType::A, false);
|
||||
assert!(new_rrset.iter().all(|r| *r == &new_record));
|
||||
|
||||
let delete_rrset: Vec<&Record> = authority.lookup(&delete_name, RecordType::A, false);
|
||||
assert!(delete_rrset.is_empty());
|
||||
|
||||
// that record should have been recorded... let's reload the journal and see if we get it.
|
||||
let mut recovered_authority = Authority::new(authority.get_origin().clone(),
|
||||
BTreeMap::new(),
|
||||
ZoneType::Master,
|
||||
false);
|
||||
recovered_authority.recover_with_journal(authority.get_journal().expect("journal not Some")).expect("recovery");
|
||||
|
||||
// assert that the correct set of records is there.
|
||||
let new_rrset: Vec<&Record> = recovered_authority.lookup(&new_name, RecordType::A, false);
|
||||
assert!(new_rrset.iter().all(|r| *r == &new_record));
|
||||
|
||||
let delete_rrset: Vec<&Record> = authority.lookup(&delete_name, RecordType::A, false);
|
||||
assert!(delete_rrset.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_recovery() {
|
||||
use rusqlite::Connection;
|
||||
use ::authority::Journal;
|
||||
|
||||
// test that this message can be inserted
|
||||
let conn = Connection::open_in_memory().expect("could not create in memory DB");
|
||||
let mut journal = Journal::new(conn).unwrap();
|
||||
journal.schema_up().unwrap();
|
||||
|
||||
let mut authority = create_example();
|
||||
authority.journal(journal);
|
||||
authority.persist_to_journal().unwrap();
|
||||
|
||||
let journal = authority.get_journal().unwrap();
|
||||
let mut recovered_authority = Authority::new(authority.get_origin().clone(),
|
||||
BTreeMap::new(),
|
||||
ZoneType::Master,
|
||||
false);
|
||||
|
||||
recovered_authority.recover_with_journal(journal).expect("recovery");
|
||||
|
||||
assert_eq!(recovered_authority.get_records().len(), authority.get_records().len());
|
||||
assert_eq!(recovered_authority.get_soa(), authority.get_soa());
|
||||
assert!(recovered_authority.get_records().iter().all(|(rr_key, rr_set)| {
|
||||
let other_rr_set = authority.get_records().get(rr_key).expect(&format!("key doesn't exist: {:?}", rr_key));
|
||||
rr_set.iter().zip(other_rr_set.iter()).all(|(record, other_record)| {
|
||||
record.get_ttl() == other_record.get_ttl() &&
|
||||
record.get_rdata() == other_record.get_rdata()
|
||||
})
|
||||
}));
|
||||
|
||||
assert!(authority.get_records().iter().all(|(rr_key, rr_set)| {
|
||||
let other_rr_set = recovered_authority.get_records().get(rr_key).expect(&format!("key doesn't exist: {:?}", rr_key));
|
||||
rr_set.iter().zip(other_rr_set.iter()).all(|(record, other_record)| {
|
||||
record.get_ttl() == other_record.get_ttl() &&
|
||||
record.get_rdata() == other_record.get_rdata()
|
||||
})
|
||||
}));
|
||||
}
|
||||
pub fn create_example() -> Authority {
|
||||
use std::net::*;
|
||||
use trust_dns::rr::rdata::*;
|
||||
|
||||
let origin: Name = Name::parse("example.com.", None,).unwrap();
|
||||
let mut records: Authority = Authority::new(origin.clone(), BTreeMap::new(), ZoneType::Master, false);
|
||||
// example.com. 3600 IN SOA sns.dns.icann.org. noc.dns.icann.org. 2015082403 7200 3600 1209600 3600
|
||||
records.upsert(Record::new().name(origin.clone()).ttl(3600).rr_type(RecordType::SOA).dns_class(DNSClass::IN).rdata(RData::SOA(SOA::new(Name::parse("sns.dns.icann.org.", None).unwrap(), Name::parse("noc.dns.icann.org.", None).unwrap(), 2015082403, 7200, 3600, 1209600, 3600 ))).clone(), 0);
|
||||
|
||||
records.upsert(Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::NS).dns_class(DNSClass::IN).rdata(RData::NS(Name::parse("a.iana-servers.net.", None).unwrap()) ).clone(), 0);
|
||||
records.upsert(Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::NS).dns_class(DNSClass::IN).rdata(RData::NS(Name::parse("b.iana-servers.net.", None).unwrap()) ).clone(), 0);
|
||||
|
||||
// example.com. 60 IN TXT "v=spf1 -all"
|
||||
//records.upsert(origin.clone(), Record::new().name(origin.clone()).ttl(60).rr_type(RecordType::TXT).dns_class(DNSClass::IN).rdata(RData::TXT{ txt_data: vec!["v=spf1 -all".to_string()] }).clone());
|
||||
// example.com. 60 IN TXT "$Id: example.com 4415 2015-08-24 20:12:23Z davids $"
|
||||
records.upsert(Record::new().name(origin.clone()).ttl(60).rr_type(RecordType::TXT).dns_class(DNSClass::IN).rdata(RData::TXT(TXT::new(vec!["$Id: example.com 4415 2015-08-24 20:12:23Z davids $".to_string()]))).clone(), 0);
|
||||
|
||||
// example.com. 86400 IN A 93.184.216.34
|
||||
records.upsert(Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::IN).rdata(RData::A(Ipv4Addr::new(93,184,216,34))).clone(), 0);
|
||||
|
||||
// example.com. 86400 IN AAAA 2606:2800:220:1:248:1893:25c8:1946
|
||||
records.upsert(Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::AAAA).dns_class(DNSClass::IN).rdata(RData::AAAA(Ipv6Addr::new(0x2606,0x2800,0x220,0x1,0x248,0x1893,0x25c8,0x1946))).clone(), 0);
|
||||
|
||||
// TODO support these later...
|
||||
|
||||
// example.com. 3600 IN RRSIG NSEC 8 2 3600 20150926015219 20150905040848 54108 example.com. d0AXd6QRITqLeiYbQUlJ5O0Og9tSjk7IlxQr9aJO+r+rc1g0dW9i9OCc XXQxdC1/zyubecjD6kSs3vwxzzEEupivaKHKtNPXdnDZ5UUiaIC1VU9l 9h/ik+AR4rCTY6dYPCI6lafD/TlqQLbpEnb34ywkRpl5G3pasPrwEY7b nrAndEY=
|
||||
// example.com. 3600 IN NSEC www.example.com. A NS SOA TXT AAAA RRSIG NSEC DNSKEY
|
||||
// example.com. 86400 IN RRSIG NS 8 2 86400 20150915033148 20150824191224 54108 example.com. O2TCB5/v/b1XGlTQEj0/oGKp7dTueQ7zRmCtADDEDWrzLdWrKcmDGF37 mgKejcAlSYVhWLxyLlet7KqJhLu+oQcDTNf/BT3vNX/Ivx3sKhUUMpfi 8Mn5zhRqM9gbzZVCS/toJIYqOBqvAkS7UpkmpLzl0Zt2h4j0Gp/8GwRb ZU67l6M=
|
||||
// example.com. 86400 IN RRSIG AAAA 8 2 86400 20150914212400 20150824191224 54108 example.com. AHd2BDNjtg4jPRQwyT4FHtlVTZDZ6IIusYVGCzWfnt5SZOoizyXnJhqX 44MeVTqi1/2cskpKvRkK3bkYnVUcjZiFgSaa9xJHmXrslaTr5mOmXt9s 6k95N1daYKhDKKcr0M4TXLUgdnBr+/pMFiLsyOoDb8GJDT8Llmpk52Ie ysJX8BY=
|
||||
// example.com. 86400 IN RRSIG A 8 2 86400 20150914083326 20150824191224 54108 example.com. La1p2R7GPMrXEm3kcznSJ70sOspmfSDsgOZ74GlzgaFfMRveA20IDUnZ /HI9M95/tBWbHdHBtm9aCK+4n7EluhNPTAT1+88V6xK7Lc7pcBfBXIHg DAdUoj26VIh7NRml/0QR0dFu4PriA/wLNe+d1Q961qf0JZP80TU4IMBC X/W6Ijk=
|
||||
// example.com. 60 IN RRSIG TXT 8 2 60 20150914201612 20150824191224 54108 example.com. Be/bPvaVVK/o66QOHJZMFBDCQVhP44jptS9sZe8Vpfmzd72/v+1gwn1z u2+xisePSpAMtDZsFJgqsCjpbLFvmhNdh8ktlq/kuCME5hZs7qY7DZIB VwkSTsJPIq8qhX22clfIbqzaypuIX9ajWr+5i0nGQLNekMB07t4/GCoJ q5QpQoE=
|
||||
// example.com. 3600 IN RRSIG DNSKEY 8 2 3600 20150914090528 20150824071818 31406 example.com. rZJRBwHhYzCDwkDEXqECHNWezTNj2A683I/yHHqD1j9ytGHGskGEEyJC i5fk70YCm64GqDYKu70kgv7hCFqc4OM3aD88QDe3L4Uv7ZXqouNbjTEO 3BEBI13GetRkK5qLndl30Y/urOBASQFELQUJsvQBR2gJMdQsb6G0mHIW rubY2SxAGa9rQW7yehRQNK4ME37FqINBDuIV9o7kULPhn9Ux1Qx62prd 9nikzamGxFL+9dFDOfnYVw2C/OgGJNIXh5QyKMG4qXmXb6sB/V3P+FE+ +vkt3RToE2xPN5bf1vVIlEJof6LtojrowwnZpiphTXFJF/BJrgiotGt3 Gsd8Cw==
|
||||
// example.com. 3600 IN DNSKEY 256 3 8 AwEAAcZMEndf6/+kG6Dp7re/grJ9f5CP5bQplBGokyxbM4oPNeBfWMIC +xY+ICgTyJarVB4aPYNMV7znsHM4XwU8hfpZ3ZcmT+69KyGqs+tt2pc/ si30dnUpPo/AMnN7Kul2SgqT9g1bb5O0D/CH2txo6YXr/BbuNHLqAh/x mof1QYkl6GoP
|
||||
// example.com. 3600 IN DNSKEY 256 3 8 AwEAAeZFCLkW/sztmJmpmZo/udvAyqshiLO34zHzzkVPrhuUBA/xb3wk YeCvMO6iBxCD+/Dk7fWEAT1NR21bDKHySVHE5cre+fqnXI+9NCjkMoBE 193j8G5HscIpWpG1qgkelBhmucfUPv+R4AIhpfjc352eh1q/SniYUGR4 fytlDZVXCLhL
|
||||
// example.com. 3600 IN DNSKEY 257 3 8 AwEAAbOFAxl+Lkt0UMglZizKEC1AxUu8zlj65KYatR5wBWMrh18TYzK/ ig6Y1t5YTWCO68bynorpNu9fqNFALX7bVl9/gybA0v0EhF+dgXmoUfRX 7ksMGgBvtfa2/Y9a3klXNLqkTszIQ4PEMVCjtryl19Be9/PkFeC9ITjg MRQsQhmB39eyMYnal+f3bUxKk4fq7cuEU0dbRpue4H/N6jPucXWOwiMA kTJhghqgy+o9FfIp+tR/emKao94/wpVXDcPf5B18j7xz2SvTTxiuqCzC MtsxnikZHcoh1j4g+Y1B8zIMIvrEM+pZGhh/Yuf4RwCBgaYCi9hpiMWV vS4WBzx0/lU=
|
||||
// example.com. 3600 IN RRSIG SOA 8 2 3600 20150926132522 20150905040848 54108 example.com. q8psdDPaJVo9KPVgMNR2N1by3LMEci+3HyTmN/Xv3DgDFG5MqNlX9Dfj dUBIMbvYmkUUPQ9fIWYA+ldmDHiRBiHIcvvk/LYD8mODWL6RoF+GEsW0 zm43RNBnbE41wtNrch5WU/q1ko2svB98ooqePWWuFzmdyPpidtLCgSCz FCiCiVQ=
|
||||
|
||||
// www
|
||||
let www_name: Name = Name::parse("www.example.com.", None).unwrap();
|
||||
|
||||
// www.example.com. 86400 IN TXT "v=spf1 -all"
|
||||
records.upsert(Record::new().name(www_name.clone()).ttl(86400).rr_type(RecordType::TXT).dns_class(DNSClass::IN).rdata(RData::TXT(TXT::new(vec!["v=spf1 -all".to_string()]))).clone(), 0);
|
||||
|
||||
// www.example.com. 86400 IN A 93.184.216.34
|
||||
records.upsert(Record::new().name(www_name.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::IN).rdata(RData::A(Ipv4Addr::new(93,184,216,34))).clone(), 0);
|
||||
|
||||
// www.example.com. 86400 IN AAAA 2606:2800:220:1:248:1893:25c8:1946
|
||||
records.upsert(Record::new().name(www_name.clone()).ttl(86400).rr_type(RecordType::AAAA).dns_class(DNSClass::IN).rdata(RData::AAAA(Ipv6Addr::new(0x2606,0x2800,0x220,0x1,0x248,0x1893,0x25c8,0x1946))).clone(), 0);
|
||||
|
||||
// www.example.com. 3600 IN RRSIG NSEC 8 3 3600 20150925215757 20150905040848 54108 example.com. ZKIVt1IN3O1FWZPSfrQAH7nHt7RUFDjcbh7NxnEqd/uTGCnZ6SrAEgrY E9GMmBwvRjoucphGtjkYOpPJPe5MlnTHoYCjxL4qmG3LsD2KD0bfPufa ibtlQZRrPglxZ92hBKK3ZiPnPRe7I9yni2UQSQA7XDi7CQySYyo490It AxdXjAo=
|
||||
// www.example.com. 3600 IN NSEC example.com. A TXT AAAA RRSIG NSEC
|
||||
// www.example.com. 86400 IN RRSIG TXT 8 3 86400 20150914142952 20150824191224 54108 example.com. LvODnPb7NLDZfHPBOrr/qLnOKA670vVYKQSk5Qkz3MPNKDVAFJqsP2Y6 UYcypSJZfcSjfIk2mU9dUiansU2ZL80OZJUsUobqJt5De748ovITYDJ7 afbohQzPg+4E1GIWMkJZ/VQD3B2pmr7J5rPn+vejxSQSoI93AIQaTpCU L5O/Bac=
|
||||
// www.example.com. 86400 IN RRSIG AAAA 8 3 86400 20150914082216 20150824191224 54108 example.com. kje4FKE+7d/j4OzWQelcKkePq6DxCRY/5btAiUcZNf+zVNlHK+o57h1r Y76ZviWChQB8Np2TjA1DrXGi/kHr2KKE60H5822mFZ2b9O+sgW4q6o3G kO2E1CQxbYe+nI1Z8lVfjdCNm81zfvYqDjo2/tGqagehxG1V9MBZO6br 4KKdoa4=
|
||||
// www.example.com. 86400 IN RRSIG A 8 3 86400 20150915023456 20150824191224 54108 example.com. cWtw0nMvcXcYNnxejB3Le3KBfoPPQZLmbaJ8ybdmzBDefQOm1ZjZZMOP wHEIxzdjRhG9mLt1mpyo1H7OezKTGX+mDtskcECTl/+jB/YSZyvbwRxj e88Lrg4D+D2MiajQn3XSWf+6LQVe1J67gdbKTXezvux0tRxBNHHqWXRk pxCILes=
|
||||
|
||||
return records;
|
||||
}
|
||||
|
||||
pub fn create_secure_example() -> Authority {
|
||||
use chrono::Duration;
|
||||
use openssl::crypto::rsa::RSA;
|
||||
use trust_dns::rr::dnssec::*;
|
||||
|
||||
let mut authority: Authority = create_example();
|
||||
let rsa = RSA::generate(2048).unwrap();
|
||||
let signer = Signer::new(Algorithm::RSASHA256, rsa, authority.get_origin().clone(), Duration::weeks(1));
|
||||
|
||||
authority.add_secure_key(signer);
|
||||
authority.secure_zone();
|
||||
|
||||
authority
|
||||
}
|
@ -19,26 +19,23 @@
|
||||
use std::collections::HashMap;
|
||||
use std::sync::RwLock;
|
||||
|
||||
use ::rr::{Name, RecordType};
|
||||
use trust_dns::op::{Edns, Message, MessageType, OpCode, Query, UpdateMessage, RequestHandler, ResponseCode};
|
||||
use trust_dns::rr::{Name, RecordType};
|
||||
|
||||
use ::authority::{Authority, ZoneType};
|
||||
use ::op::{Edns, Message, MessageType, OpCode, Query, UpdateMessage, ResponseCode};
|
||||
|
||||
/// Set of authorities, zones, available to this server.
|
||||
pub struct Catalog {
|
||||
authorities: HashMap<Name, RwLock<Authority>>,
|
||||
}
|
||||
|
||||
impl Catalog {
|
||||
pub fn new() -> Self {
|
||||
Catalog{ authorities: HashMap::new() }
|
||||
}
|
||||
|
||||
impl RequestHandler for Catalog {
|
||||
/// Determine's what needs to happen given the type of request, i.e. Query or Update.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `request` - the requested action to perform.
|
||||
pub fn handle_request(&self, request: &Message) -> Message {
|
||||
fn handle_request(&self, request: &Message) -> Message {
|
||||
info!("id: {} type: {:?} op_code: {:?}", request.get_id(), request.get_message_type(), request.get_op_code());
|
||||
debug!("request: {:?}", request);
|
||||
|
||||
@ -92,13 +89,13 @@ impl Catalog {
|
||||
}
|
||||
c @ _ => {
|
||||
error!("unimplemented op_code: {:?}", c);
|
||||
Self::error_msg(request.get_id(), request.get_op_code(), ResponseCode::NotImp)
|
||||
Message::error_msg(request.get_id(), request.get_op_code(), ResponseCode::NotImp)
|
||||
},
|
||||
}
|
||||
},
|
||||
MessageType::Response => {
|
||||
warn!("got a response as a request from id: {}", request.get_id());
|
||||
Self::error_msg(request.get_id(), request.get_op_code(), ResponseCode::NotImp)
|
||||
Message::error_msg(request.get_id(), request.get_op_code(), ResponseCode::NotImp)
|
||||
},
|
||||
};
|
||||
|
||||
@ -112,15 +109,11 @@ impl Catalog {
|
||||
|
||||
response
|
||||
}
|
||||
}
|
||||
|
||||
pub fn error_msg(id: u16, op_code: OpCode, response_code: ResponseCode) -> Message {
|
||||
let mut message: Message = Message::new();
|
||||
message.message_type(MessageType::Response);
|
||||
message.id(id);
|
||||
message.response_code(response_code);
|
||||
message.op_code(op_code);
|
||||
|
||||
return message;
|
||||
impl Catalog {
|
||||
pub fn new() -> Self {
|
||||
Catalog{ authorities: HashMap::new() }
|
||||
}
|
||||
|
||||
pub fn upsert(&mut self, name: Name, authority: Authority) {
|
||||
@ -294,159 +287,3 @@ impl Catalog {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod catalog_tests {
|
||||
use std::net::*;
|
||||
use std::collections::*;
|
||||
|
||||
use ::authority::{Authority, ZoneType};
|
||||
use ::authority::authority_tests::create_example;
|
||||
use ::op::*;
|
||||
use ::rr::*;
|
||||
use ::rr::rdata::SOA;
|
||||
|
||||
use super::*;
|
||||
|
||||
pub fn create_test() -> Authority {
|
||||
let origin: Name = Name::parse("test.com.", None).unwrap();
|
||||
let mut records: Authority = Authority::new(origin.clone(), BTreeMap::new(), ZoneType::Master, false);
|
||||
records.upsert(Record::new().name(origin.clone()).ttl(3600).rr_type(RecordType::SOA).dns_class(DNSClass::IN).rdata(RData::SOA(SOA::new(Name::parse("sns.dns.icann.org.", None).unwrap(), Name::parse("noc.dns.icann.org.", None).unwrap(), 2015082403, 7200, 3600, 1209600, 3600 ))).clone(), 0);
|
||||
|
||||
records.upsert(Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::NS).dns_class(DNSClass::IN).rdata(RData::NS(Name::parse("a.iana-servers.net.", None).unwrap()) ).clone(), 0);
|
||||
records.upsert(Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::NS).dns_class(DNSClass::IN).rdata(RData::NS(Name::parse("b.iana-servers.net.", None).unwrap()) ).clone(), 0);
|
||||
|
||||
records.upsert(Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::IN).rdata(RData::A(Ipv4Addr::new(94,184,216,34))).clone(), 0);
|
||||
records.upsert(Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::AAAA).dns_class(DNSClass::IN).rdata(RData::AAAA(Ipv6Addr::new(0x2606,0x2800,0x220,0x1,0x248,0x1893,0x25c8,0x1946))).clone(), 0);
|
||||
|
||||
let www_name: Name = Name::parse("www.test.com.", None).unwrap();
|
||||
records.upsert(Record::new().name(www_name.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::IN).rdata(RData::A(Ipv4Addr::new(94,184,216,34))).clone(), 0);
|
||||
records.upsert(Record::new().name(www_name.clone()).ttl(86400).rr_type(RecordType::AAAA).dns_class(DNSClass::IN).rdata(RData::AAAA(Ipv6Addr::new(0x2606,0x2800,0x220,0x1,0x248,0x1893,0x25c8,0x1946))).clone(), 0);
|
||||
|
||||
records
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_catalog_lookup() {
|
||||
let example = create_example();
|
||||
let test = create_test();
|
||||
let origin = example.get_origin().clone();
|
||||
let test_origin = test.get_origin().clone();
|
||||
|
||||
let mut catalog: Catalog = Catalog::new();
|
||||
catalog.upsert(origin.clone(), example);
|
||||
catalog.upsert(test_origin.clone(), test);
|
||||
|
||||
let mut question: Message = Message::new();
|
||||
|
||||
let mut query: Query = Query::new();
|
||||
query.name(origin.clone());
|
||||
|
||||
question.add_query(query);
|
||||
|
||||
let result: Message = catalog.lookup(&question);
|
||||
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
assert_eq!(result.get_message_type(), MessageType::Response);
|
||||
|
||||
let answers: &[Record] = result.get_answers();
|
||||
|
||||
assert!(!answers.is_empty());
|
||||
assert_eq!(answers.first().unwrap().get_rr_type(), RecordType::A);
|
||||
assert_eq!(answers.first().unwrap().get_rdata(), &RData::A(Ipv4Addr::new(93,184,216,34)));
|
||||
|
||||
let mut ns: Vec<Record> = result.get_name_servers().to_vec();
|
||||
ns.sort();
|
||||
|
||||
assert_eq!(ns.len(), 2);
|
||||
assert_eq!(ns.first().unwrap().get_rr_type(), RecordType::NS);
|
||||
assert_eq!(ns.first().unwrap().get_rdata(), &RData::NS(Name::parse("a.iana-servers.net.", None).unwrap()) );
|
||||
assert_eq!(ns.last().unwrap().get_rr_type(), RecordType::NS);
|
||||
assert_eq!(ns.last().unwrap().get_rdata(), &RData::NS(Name::parse("b.iana-servers.net.", None).unwrap()) );
|
||||
|
||||
// other zone
|
||||
let mut query: Query = Query::new();
|
||||
query.name(test_origin.clone());
|
||||
|
||||
question.add_query(query);
|
||||
|
||||
let result: Message = catalog.lookup(&question);
|
||||
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NoError);
|
||||
assert_eq!(result.get_message_type(), MessageType::Response);
|
||||
|
||||
let answers: &[Record] = result.get_answers();
|
||||
|
||||
assert!(!answers.is_empty());
|
||||
assert_eq!(answers.first().unwrap().get_rr_type(), RecordType::A);
|
||||
assert_eq!(answers.first().unwrap().get_rdata(), &RData::A(Ipv4Addr::new(93,184,216,34)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_catalog_nx_soa() {
|
||||
let example = create_example();
|
||||
let origin = example.get_origin().clone();
|
||||
|
||||
let mut catalog: Catalog = Catalog::new();
|
||||
catalog.upsert(origin.clone(), example);
|
||||
|
||||
let mut question: Message = Message::new();
|
||||
|
||||
let mut query: Query = Query::new();
|
||||
query.name(Name::parse("nx.example.com.", None).unwrap());
|
||||
|
||||
question.add_query(query);
|
||||
|
||||
let result: Message = catalog.lookup(&question);
|
||||
|
||||
assert_eq!(result.get_response_code(), ResponseCode::NXDomain);
|
||||
assert_eq!(result.get_message_type(), MessageType::Response);
|
||||
|
||||
let ns: &[Record] = result.get_name_servers();
|
||||
|
||||
assert_eq!(ns.len(), 1);
|
||||
assert_eq!(ns.first().unwrap().get_rr_type(), RecordType::SOA);
|
||||
assert_eq!(ns.first().unwrap().get_rdata(), &RData::SOA(SOA::new(Name::parse("sns.dns.icann.org.", None).unwrap(), Name::parse("noc.dns.icann.org.", None).unwrap(), 2015082403, 7200, 3600, 1209600, 3600 )));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_axfr() {
|
||||
let test = create_test();
|
||||
let origin = test.get_origin().clone();
|
||||
let soa = Record::new().name(origin.clone()).ttl(3600).rr_type(RecordType::SOA).dns_class(DNSClass::IN).rdata(RData::SOA(SOA::new(Name::parse("sns.dns.icann.org.", None).unwrap(), Name::parse("noc.dns.icann.org.", None).unwrap(), 2015082403, 7200, 3600, 1209600, 3600 ))).clone();
|
||||
|
||||
let mut catalog: Catalog = Catalog::new();
|
||||
catalog.upsert(origin.clone(), test);
|
||||
|
||||
let mut query: Query = Query::new();
|
||||
query.name(origin.clone());
|
||||
query.query_type(RecordType::AXFR);
|
||||
|
||||
let mut question: Message = Message::new();
|
||||
question.add_query(query);
|
||||
|
||||
let result: Message = catalog.lookup(&question);
|
||||
let mut answers: Vec<Record> = result.get_answers().to_vec();
|
||||
|
||||
assert_eq!(answers.first().unwrap(), &soa);
|
||||
assert_eq!(answers.last().unwrap(), &soa);
|
||||
|
||||
answers.sort();
|
||||
|
||||
let www_name: Name = Name::parse("www.test.com.", None).unwrap();
|
||||
let mut expected_set = vec![
|
||||
Record::new().name(origin.clone()).ttl(3600).rr_type(RecordType::SOA).dns_class(DNSClass::IN).rdata(RData::SOA(SOA::new(Name::parse("sns.dns.icann.org.", None).unwrap(), Name::parse("noc.dns.icann.org.", None).unwrap(), 2015082403, 7200, 3600, 1209600, 3600 ))).clone(),
|
||||
Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::NS).dns_class(DNSClass::IN).rdata(RData::NS(Name::parse("a.iana-servers.net.", None).unwrap()) ).clone(),
|
||||
Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::NS).dns_class(DNSClass::IN).rdata(RData::NS(Name::parse("b.iana-servers.net.", None).unwrap()) ).clone(),
|
||||
Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::IN).rdata(RData::A(Ipv4Addr::new(94,184,216,34))).clone(),
|
||||
Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::AAAA).dns_class(DNSClass::IN).rdata(RData::AAAA(Ipv6Addr::new(0x2606,0x2800,0x220,0x1,0x248,0x1893,0x25c8,0x1946))).clone(),
|
||||
Record::new().name(www_name.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::IN).rdata(RData::A(Ipv4Addr::new(94,184,216,34))).clone(),
|
||||
Record::new().name(www_name.clone()).ttl(86400).rr_type(RecordType::AAAA).dns_class(DNSClass::IN).rdata(RData::AAAA(Ipv6Addr::new(0x2606,0x2800,0x220,0x1,0x248,0x1893,0x25c8,0x1946))).clone(),
|
||||
Record::new().name(origin.clone()).ttl(3600).rr_type(RecordType::SOA).dns_class(DNSClass::IN).rdata(RData::SOA(SOA::new(Name::parse("sns.dns.icann.org.", None).unwrap(), Name::parse("noc.dns.icann.org.", None).unwrap(), 2015082403, 7200, 3600, 1209600, 3600 ))).clone(),
|
||||
];
|
||||
|
||||
expected_set.sort();
|
||||
|
||||
assert_eq!(expected_set, answers);
|
||||
}
|
||||
}
|
@ -16,23 +16,17 @@
|
||||
|
||||
//! Module for `Catalog` of `Authority` zones which are responsible for storing `RRSet` records.
|
||||
|
||||
use ::op::ResponseCode;
|
||||
use trust_dns::op::ResponseCode;
|
||||
|
||||
pub type UpdateResult<T> = Result<T, ResponseCode>;
|
||||
|
||||
#[derive(RustcDecodable, PartialEq, Eq, Debug, Clone, Copy)]
|
||||
pub enum ZoneType { Master, Slave, Hint, Forward }
|
||||
|
||||
mod authority;
|
||||
pub mod authority;
|
||||
mod catalog;
|
||||
mod persistence;
|
||||
mod rr_set;
|
||||
pub mod persistence;
|
||||
|
||||
pub use self::authority::Authority;
|
||||
pub use self::authority::RrKey;
|
||||
pub use self::catalog::Catalog;
|
||||
pub use self::rr_set::RRSet;
|
||||
pub use self::persistence::Journal;
|
||||
|
||||
#[cfg(test)]
|
||||
pub use self::authority::authority_tests;
|
@ -12,12 +12,12 @@ use rusqlite;
|
||||
use rusqlite::Connection;
|
||||
use rusqlite::SqliteError;
|
||||
|
||||
use ::error::PersistenceErrorKind;
|
||||
use ::error::PersistenceResult;
|
||||
use ::rr::Record;
|
||||
use ::serialize::binary::{BinDecoder, BinEncoder, BinSerializable};
|
||||
use trust_dns::rr::Record;
|
||||
use trust_dns::serialize::binary::{BinDecoder, BinEncoder, BinSerializable};
|
||||
|
||||
const CURRENT_VERSION: i64 = 1;
|
||||
use ::error::{PersistenceErrorKind, PersistenceResult};
|
||||
|
||||
pub const CURRENT_VERSION: i64 = 1;
|
||||
|
||||
/// The Journal is the audit log of all changes to a zone after initial creation.
|
||||
pub struct Journal {
|
||||
@ -42,6 +42,10 @@ impl Journal {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_conn(&self) -> &Connection {
|
||||
&self.conn
|
||||
}
|
||||
|
||||
/// gets the current schema version of the journal
|
||||
pub fn get_schema_version(&self) -> i64 {
|
||||
self.version
|
||||
@ -140,7 +144,7 @@ impl Journal {
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `conn` - db connection to use
|
||||
fn select_schema_version(conn: &Connection) -> PersistenceResult<i64> {
|
||||
pub fn select_schema_version(conn: &Connection) -> PersistenceResult<i64> {
|
||||
// first see if our schema is there
|
||||
let mut stmt = try!(conn.prepare("SELECT name
|
||||
FROM sqlite_master
|
||||
@ -258,87 +262,3 @@ impl<'j> Iterator for JournalIter<'j> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_new_journal() {
|
||||
let conn = Connection::open_in_memory().expect("could not create in memory DB");
|
||||
assert_eq!(Journal::new(conn).expect("new Journal").get_schema_version(), -1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_init_journal() {
|
||||
let conn = Connection::open_in_memory().expect("could not create in memory DB");
|
||||
let mut journal = Journal::new(conn).unwrap();
|
||||
let version = journal.schema_up().unwrap();
|
||||
assert_eq!(version, CURRENT_VERSION);
|
||||
assert_eq!(Journal::select_schema_version(&journal.conn).unwrap(), CURRENT_VERSION);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn create_test_journal() -> (Record, Journal) {
|
||||
use std::net::Ipv4Addr;
|
||||
use std::str::FromStr;
|
||||
|
||||
use ::rr::{Name, RData, Record, RecordType};
|
||||
|
||||
let www = Name::with_labels(vec!["www".to_string(),"example".to_string(), "com".to_string()]);
|
||||
|
||||
let mut record = Record::new();
|
||||
record.name(www);
|
||||
record.rr_type(RecordType::A);
|
||||
record.rdata(RData::A(Ipv4Addr::from_str("127.0.0.1").unwrap()));
|
||||
|
||||
// test that this message can be inserted
|
||||
let conn = Connection::open_in_memory().expect("could not create in memory DB");
|
||||
let mut journal = Journal::new(conn).unwrap();
|
||||
journal.schema_up().unwrap();
|
||||
|
||||
// insert the message
|
||||
journal.insert_record(0, &record).unwrap();
|
||||
|
||||
// insert another...
|
||||
record.rdata(RData::A(Ipv4Addr::from_str("127.0.1.1").unwrap()));
|
||||
journal.insert_record(0, &record).unwrap();
|
||||
|
||||
(record, journal)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_insert_and_select_record() {
|
||||
use std::net::Ipv4Addr;
|
||||
use std::str::FromStr;
|
||||
|
||||
use rr::RData;
|
||||
|
||||
let (mut record, journal) = create_test_journal();
|
||||
|
||||
// select the record
|
||||
let (row_id, journal_record) = journal.select_record(0).expect("persistence error").expect("none");
|
||||
record.rdata(RData::A(Ipv4Addr::from_str("127.0.0.1").unwrap()));
|
||||
assert_eq!(journal_record, record);
|
||||
|
||||
// test another
|
||||
let (row_id, journal_record) = journal.select_record(row_id + 1).expect("persistence error").expect("none");
|
||||
record.rdata(RData::A(Ipv4Addr::from_str("127.0.1.1").unwrap()));
|
||||
assert_eq!(journal_record, record);
|
||||
|
||||
// check that we get nothing for id over row_id
|
||||
let option_none = journal.select_record(row_id + 1).expect("persistence error");
|
||||
assert!(option_none.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_iterator() {
|
||||
use std::net::Ipv4Addr;
|
||||
use std::str::FromStr;
|
||||
|
||||
use rr::RData;
|
||||
|
||||
let (mut record, journal) = create_test_journal();
|
||||
|
||||
let mut iter = journal.iter();
|
||||
|
||||
assert_eq!(record.rdata(RData::A(Ipv4Addr::from_str("127.0.0.1").unwrap())), &iter.next().unwrap());
|
||||
assert_eq!(record.rdata(RData::A(Ipv4Addr::from_str("127.0.1.1").unwrap())), &iter.next().unwrap());
|
||||
assert_eq!(None, iter.next());
|
||||
}
|
@ -27,9 +27,11 @@ use rustc_serialize::Decodable;
|
||||
|
||||
use toml::{Decoder, Value};
|
||||
|
||||
use ::error::{ConfigErrorKind, ConfigResult, ConfigError, ParseResult};
|
||||
use ::rr::Name;
|
||||
use trust_dns::error::ParseResult;
|
||||
use trust_dns::rr::Name;
|
||||
|
||||
use ::authority::ZoneType;
|
||||
use ::error::{ConfigErrorKind, ConfigResult, ConfigError};
|
||||
|
||||
static DEFAULT_PORT: u16 = 53;
|
||||
static DEFAULT_PATH: &'static str = "/var/named"; // TODO what about windows (do I care? ;)
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user