add project files
This commit is contained in:
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
.DS_Store
|
||||||
|
.idea/
|
||||||
|
target/
|
||||||
|
todo
|
||||||
67
.travis.yml
Normal file
67
.travis.yml
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
|
||||||
|
language: rust
|
||||||
|
services: docker
|
||||||
|
sudo: required
|
||||||
|
|
||||||
|
env:
|
||||||
|
global:
|
||||||
|
- CRATE_NAME=updns
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
|
||||||
|
- env: TARGET=linux
|
||||||
|
os: linux
|
||||||
|
|
||||||
|
- env: TARGET=osx
|
||||||
|
os: osx
|
||||||
|
|
||||||
|
- env: TARGET=windows
|
||||||
|
os: windows
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- set -e
|
||||||
|
- rustup default nightly
|
||||||
|
- rustup component add rustfmt
|
||||||
|
|
||||||
|
script:
|
||||||
|
- cargo fmt --all -- --check
|
||||||
|
- cargo test
|
||||||
|
- cargo build --release
|
||||||
|
|
||||||
|
after_script: set +e
|
||||||
|
|
||||||
|
before_deploy:
|
||||||
|
- cd ./target/release/
|
||||||
|
- test -r $CRATE_NAME && zip $CRATE_NAME-$TRAVIS_TAG-$TARGET.zip $CRATE_NAME || mv $CRATE_NAME.exe $CRATE_NAME-$TRAVIS_TAG-$TARGET.exe
|
||||||
|
- cd ../../
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
|
||||||
|
- provider: releases
|
||||||
|
api_key:
|
||||||
|
secure: $GITHUB_TOKEN
|
||||||
|
file_glob: true
|
||||||
|
file: ./target/release/$CRATE_NAME-$TRAVIS_TAG-$TARGET.*
|
||||||
|
skip_cleanup: true
|
||||||
|
on:
|
||||||
|
tags: true
|
||||||
|
|
||||||
|
- provider: cargo
|
||||||
|
token: $CARGO_TOKEN
|
||||||
|
on:
|
||||||
|
condition: $TARGET = linux
|
||||||
|
tags: true
|
||||||
|
|
||||||
|
cache: cargo
|
||||||
|
before_cache:
|
||||||
|
- chmod -R a+r $HOME/.cargo
|
||||||
|
|
||||||
|
branches:
|
||||||
|
only:
|
||||||
|
- /^v\d+\.\d+\.\d+.*$/
|
||||||
|
- master
|
||||||
|
|
||||||
|
notifications:
|
||||||
|
email:
|
||||||
|
on_success: never
|
||||||
638
Cargo.lock
generated
Normal file
638
Cargo.lock
generated
Normal file
@@ -0,0 +1,638 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
[[package]]
|
||||||
|
name = "ace"
|
||||||
|
version = "0.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "0.7.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arrayref"
|
||||||
|
version = "0.3.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arrayvec"
|
||||||
|
version = "0.4.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-std"
|
||||||
|
version = "0.99.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"async-task 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"futures-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"futures-timer 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-task"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "backtrace"
|
||||||
|
version = "0.3.35"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "backtrace-sys"
|
||||||
|
version = "0.1.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"cc 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64"
|
||||||
|
version = "0.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "blake2b_simd"
|
||||||
|
version = "0.5.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byteorder"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.0.40"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "0.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cloudabi"
|
||||||
|
version = "0.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "constant_time_eq"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-channel"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-utils"
|
||||||
|
version = "0.6.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dirs"
|
||||||
|
version = "2.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"dirs-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dirs-sys"
|
||||||
|
version = "0.3.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"redox_users 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "failure"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"backtrace 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "failure_derive"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fuchsia-cprng"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fuchsia-zircon"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fuchsia-zircon-sys"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-channel-preview"
|
||||||
|
version = "0.3.0-alpha.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"futures-sink-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-core-preview"
|
||||||
|
version = "0.3.0-alpha.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-executor-preview"
|
||||||
|
version = "0.3.0-alpha.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"futures-util-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-io-preview"
|
||||||
|
version = "0.3.0-alpha.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-preview"
|
||||||
|
version = "0.3.0-alpha.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"futures-channel-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"futures-core-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"futures-executor-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"futures-io-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"futures-sink-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"futures-util-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-sink-preview"
|
||||||
|
version = "0.3.0-alpha.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-timer"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"futures-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-util-preview"
|
||||||
|
version = "0.3.0-alpha.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"futures-channel-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"futures-core-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"futures-io-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"futures-sink-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iovec"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "kernel32-sys"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.62"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mio"
|
||||||
|
version = "0.6.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"iovec 0.1.2 (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.62 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"slab 0.4.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 = "mio-uds"
|
||||||
|
version = "0.6.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "miow"
|
||||||
|
version = "0.2.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)",
|
||||||
|
"net2 0.2.33 (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.33"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nodrop"
|
||||||
|
version = "0.1.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num_cpus"
|
||||||
|
version = "1.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-utils"
|
||||||
|
version = "0.1.0-alpha.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "0.4.30"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "0.6.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_os"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rdrand"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_syscall"
|
||||||
|
version = "0.1.56"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_users"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rust-argon2 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"regex-syntax 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.6.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rust-argon2"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"blake2b_simd 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc-demangle"
|
||||||
|
version = "0.1.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "slab"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "0.15.44"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "synstructure"
|
||||||
|
version = "0.10.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thread_local"
|
||||||
|
version = "0.3.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-xid"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "updns"
|
||||||
|
version = "0.0.1"
|
||||||
|
dependencies = [
|
||||||
|
"ace 0.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"async-std 0.99.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"regex 1.2.1 (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"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"winapi-x86_64-pc-windows-gnu 0.4.0 (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 = "winapi-i686-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
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 ace 0.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1e021d9548a20c4e74f88b5cff290ba225565e3404afcffdc6b275395128a140"
|
||||||
|
"checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d"
|
||||||
|
"checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee"
|
||||||
|
"checksum arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d73f9beda665eaa98ab9e4f7442bd4e7de6652587de55b2525e52e29c1b0ba"
|
||||||
|
"checksum async-std 0.99.4 (registry+https://github.com/rust-lang/crates.io-index)" = "95dbe66a9f8c59a70277214f98d39f25fe1f36f20f6e8412a8b33af0272a2c79"
|
||||||
|
"checksum async-task 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de6bd58f7b9cc49032559422595c81cbfcf04db2f2133592f70af19e258a1ced"
|
||||||
|
"checksum backtrace 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)" = "1371048253fa3bac6704bfd6bbfc922ee9bdcee8881330d40f308b81cc5adc55"
|
||||||
|
"checksum backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "82a830b4ef2d1124a711c71d263c5abdc710ef8e907bd508c88be475cebc422b"
|
||||||
|
"checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e"
|
||||||
|
"checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd"
|
||||||
|
"checksum blake2b_simd 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)" = "bf775a81bb2d464e20ff170ac20316c7b08a43d11dbc72f0f82e8e8d3d6d0499"
|
||||||
|
"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5"
|
||||||
|
"checksum cc 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)" = "b548a4ee81fccb95919d4e22cfea83c7693ebfd78f0495493178db20b3139da7"
|
||||||
|
"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33"
|
||||||
|
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
|
||||||
|
"checksum constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "995a44c877f9212528ccc74b21a232f66ad69001e40ede5bcee2ac9ef2657120"
|
||||||
|
"checksum crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c8ec7fcd21571dc78f96cc96243cab8d8f035247c3efd16c687be154c3fa9efa"
|
||||||
|
"checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6"
|
||||||
|
"checksum dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3"
|
||||||
|
"checksum dirs-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b"
|
||||||
|
"checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2"
|
||||||
|
"checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1"
|
||||||
|
"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
|
||||||
|
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
|
||||||
|
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
|
||||||
|
"checksum futures-channel-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)" = "f477fd0292c4a4ae77044454e7f2b413207942ad405f759bb0b4698b7ace5b12"
|
||||||
|
"checksum futures-core-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)" = "4a2f26f774b81b3847dcda0c81bd4b6313acfb4f69e5a0390c7cb12c058953e9"
|
||||||
|
"checksum futures-executor-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)" = "80705612926df8a1bc05f0057e77460e29318801f988bf7d803a734cf54e7528"
|
||||||
|
"checksum futures-io-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)" = "ee7de0c1c9ed23f9457b0437fec7663ce64d9cc3c906597e714e529377b5ddd1"
|
||||||
|
"checksum futures-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)" = "efa8f90c4fb2328e381f8adfd4255b4a2b696f77d1c63a3dee6700b564c4e4b5"
|
||||||
|
"checksum futures-sink-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)" = "e9b65a2481863d1b78e094a07e9c0eed458cc7dc6e72b22b7138b8a67d924859"
|
||||||
|
"checksum futures-timer 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8f9eb554aa23143abc64ec4d0016f038caf53bb7cbc3d91490835c54edc96550"
|
||||||
|
"checksum futures-util-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)" = "7df53daff1e98cc024bf2720f3ceb0414d96fbb0a94f3cad3a5c3bf3be1d261c"
|
||||||
|
"checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08"
|
||||||
|
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||||
|
"checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14"
|
||||||
|
"checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba"
|
||||||
|
"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
|
||||||
|
"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e"
|
||||||
|
"checksum mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)" = "83f51996a3ed004ef184e16818edc51fadffe8e7ca68be67f9dee67d84d0ff23"
|
||||||
|
"checksum mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125"
|
||||||
|
"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
|
||||||
|
"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88"
|
||||||
|
"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945"
|
||||||
|
"checksum num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcef43580c035376c0705c42792c294b66974abbfd2789b511784023f71f3273"
|
||||||
|
"checksum pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587"
|
||||||
|
"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
|
||||||
|
"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1"
|
||||||
|
"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
|
||||||
|
"checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
|
||||||
|
"checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071"
|
||||||
|
"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
|
||||||
|
"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
|
||||||
|
"checksum redox_users 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4ecedbca3bf205f8d8f5c2b44d83cd0690e39ee84b951ed649e9f1841132b66d"
|
||||||
|
"checksum regex 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88c3d9193984285d544df4a30c23a4e62ead42edf70a4452ceb76dac1ce05c26"
|
||||||
|
"checksum regex-syntax 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b143cceb2ca5e56d5671988ef8b15615733e7ee16cd348e064333b251b89343f"
|
||||||
|
"checksum rust-argon2 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4ca4eaef519b494d1f2848fc602d18816fed808a981aedf4f1f00ceb7c9d32cf"
|
||||||
|
"checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783"
|
||||||
|
"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
|
||||||
|
"checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5"
|
||||||
|
"checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f"
|
||||||
|
"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
|
||||||
|
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
|
||||||
|
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
|
||||||
|
"checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770"
|
||||||
|
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
|
||||||
|
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
|
||||||
25
Cargo.toml
Normal file
25
Cargo.toml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
[package]
|
||||||
|
name = "updns"
|
||||||
|
version = "0.0.1"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
authors = ["wyhaya <wyhaya@gmail.com>"]
|
||||||
|
description = "DNS proxy tool"
|
||||||
|
homepage = "https://github.com/wyhaya/updns"
|
||||||
|
repository = "https://github.com/wyhaya/updns.git"
|
||||||
|
license = "MIT"
|
||||||
|
readme = "README.md"
|
||||||
|
|
||||||
|
keywords = [
|
||||||
|
"dns",
|
||||||
|
"dns-server",
|
||||||
|
"dns-proxy",
|
||||||
|
"udp"
|
||||||
|
]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
ace = "0.0.2"
|
||||||
|
async-std = "0.99.4"
|
||||||
|
dirs = "2.0.2"
|
||||||
|
lazy_static = "1.3.0"
|
||||||
|
regex = "1.2.1"
|
||||||
14
Dockerfile
Normal file
14
Dockerfile
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
|
||||||
|
|
||||||
|
FROM rustlang/rust:nightly as builder
|
||||||
|
WORKDIR /root
|
||||||
|
COPY . /root
|
||||||
|
RUN cargo build --release
|
||||||
|
|
||||||
|
FROM ubuntu
|
||||||
|
EXPOSE 53/udp
|
||||||
|
WORKDIR /root
|
||||||
|
COPY --from=builder ./root/target/release/updns .
|
||||||
|
CMD ["./updns"]
|
||||||
|
|
||||||
|
|
||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) wyhaya <wyhaya@gmail.com>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
91
README.md
Normal file
91
README.md
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
|
||||||
|
|
||||||
|
# updns
|
||||||
|
|
||||||
|
[](https://travis-ci.org/wyhaya/updns)
|
||||||
|
[](https://crates.io/crates/updns)
|
||||||
|
[](https://github.com/wyhaya/updns/blob/master/LICENSE)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
updns is a simple DNS proxy server developed using `Rust`. You can intercept any domain name and return the ip you need.
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
[Download](https://github.com/wyhaya/updns/releases) the binary from the release page
|
||||||
|
|
||||||
|
Or use `cargo` to install
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo install updns
|
||||||
|
```
|
||||||
|
|
||||||
|
## Start to use
|
||||||
|
|
||||||
|
```bash
|
||||||
|
updns
|
||||||
|
# or
|
||||||
|
updns -c /your/hosts
|
||||||
|
```
|
||||||
|
|
||||||
|
You may use `sudo` to run this command because you will use the `53` port, make sure you have sufficient permissions.
|
||||||
|
|
||||||
|
Now change your local DNS server to `127.0.0.1` 🚀
|
||||||
|
|
||||||
|
### Command
|
||||||
|
|
||||||
|
```
|
||||||
|
Usage:
|
||||||
|
updns [COMMAND] [OPTION]
|
||||||
|
|
||||||
|
Command:
|
||||||
|
add Add a DNS record
|
||||||
|
rm Remove a DNS record
|
||||||
|
ls Print all configured DNS records
|
||||||
|
config Call vim to edit the configuration file
|
||||||
|
path Print related directories
|
||||||
|
help Print help information
|
||||||
|
version Print version information
|
||||||
|
|
||||||
|
Option:
|
||||||
|
-c Specify a config file
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running in docker
|
||||||
|
|
||||||
|
Build docker image
|
||||||
|
```bash
|
||||||
|
docker build -t updns .
|
||||||
|
```
|
||||||
|
|
||||||
|
Start up
|
||||||
|
```bash
|
||||||
|
docker run -d --name updns -p 53:53/udp --restart always updns
|
||||||
|
```
|
||||||
|
|
||||||
|
## Config
|
||||||
|
|
||||||
|
You can use `updns config` command and then call `vim` quick edit, or use `updns path` find the updns's installation directory and edit the `.updns` file
|
||||||
|
|
||||||
|
You can specify standard domains, or utilize [regular expressions](https://rustexp.lpil.uk "rustexp") for dynamic matching,
|
||||||
|
You can update the config file at any time, updns will listen for file changes
|
||||||
|
|
||||||
|
```ini
|
||||||
|
bind 0.0.0.0:53 # Binding address
|
||||||
|
proxy 8.8.8.8:53 # Proxy address
|
||||||
|
|
||||||
|
# Domain matching
|
||||||
|
google.com 1.1.1.1
|
||||||
|
^(\w+.)?go+gle.com$ 2.2.2.2
|
||||||
|
|
||||||
|
# Import from other file
|
||||||
|
import /other/hosts
|
||||||
|
```
|
||||||
|
|
||||||
|
## Reference
|
||||||
|
|
||||||
|
[Building a DNS server in Rust](https://github.com/EmilHernvall/dnsguide)
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
[MIT](./LICENSE) license
|
||||||
216
src/config.rs
Normal file
216
src/config.rs
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
use regex::Regex;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io;
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
use std::net::{IpAddr, SocketAddr};
|
||||||
|
use std::path::Path;
|
||||||
|
use std::slice::Iter;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref REG_IGNORE: Regex = Regex::new(r#"^\s*(#.*)?$"#).unwrap();
|
||||||
|
static ref REG_BIND: Regex = Regex::new(r#"^\s*bind\s+(?P<val>[^\s#]+)"#).unwrap();
|
||||||
|
static ref REG_PROXY: Regex = Regex::new(r#"^\s*proxy\s+(?P<val>[^\s#]+)"#).unwrap();
|
||||||
|
// todo
|
||||||
|
// The path will also contain '#' and ' '
|
||||||
|
static ref REG_IMPORT: Regex = Regex::new(r#"\s*import\s+(?P<val>(/.*))"#).unwrap();
|
||||||
|
static ref REG_DOMAIN_IP: Regex = Regex::new(r#"^\s*(?P<val1>[^\s#]+)\s+(?P<val2>[^\s#]+)"#).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cap_socket_addr(reg: &Regex, text: &str) -> Option<Result<SocketAddr, InvalidType>> {
|
||||||
|
if let Some(cap) = reg.captures(text) {
|
||||||
|
return match cap.name("val") {
|
||||||
|
Some(m) => match m.as_str().parse() {
|
||||||
|
Ok(addr) => Some(Ok(addr)),
|
||||||
|
Err(_) => Some(Err(InvalidType::SocketAddr)),
|
||||||
|
},
|
||||||
|
None => Some(Err(InvalidType::SocketAddr)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cap_ip_addr(reg: &Regex, text: &str) -> Option<Result<(Regex, IpAddr), InvalidType>> {
|
||||||
|
if let Some(cap) = reg.captures(text) {
|
||||||
|
if let (Some(val1), Some(val2)) = (cap.name("val1"), cap.name("val2")) {
|
||||||
|
let (val1, val2) = (val1.as_str(), val2.as_str());
|
||||||
|
|
||||||
|
if let Ok(ip) = val1.parse() {
|
||||||
|
return match Regex::new(val2) {
|
||||||
|
Ok(reg) => Some(Ok((reg, ip))),
|
||||||
|
Err(_) => Some(Err(InvalidType::Regex)),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
let ip = match val2.parse() {
|
||||||
|
Ok(ip) => ip,
|
||||||
|
Err(_) => return Some(Err(InvalidType::IpAddr)),
|
||||||
|
};
|
||||||
|
|
||||||
|
let reg = match Regex::new(val1) {
|
||||||
|
Ok(reg) => reg,
|
||||||
|
Err(_) => return Some(Err(InvalidType::Regex)),
|
||||||
|
};
|
||||||
|
|
||||||
|
return Some(Ok((reg, ip)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Some(Err(InvalidType::Other));
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Config {
|
||||||
|
file: File,
|
||||||
|
content: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Invalid {
|
||||||
|
pub line: usize,
|
||||||
|
pub source: String,
|
||||||
|
pub err: InvalidType,
|
||||||
|
}
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum InvalidType {
|
||||||
|
Regex,
|
||||||
|
SocketAddr,
|
||||||
|
IpAddr,
|
||||||
|
Other,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
pub fn new<P: AsRef<Path>>(path: P) -> io::Result<Config> {
|
||||||
|
let mut file = std::fs::OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.append(true)
|
||||||
|
.create(true)
|
||||||
|
.open(path)?;
|
||||||
|
|
||||||
|
let mut content = String::new();
|
||||||
|
file.read_to_string(&mut content)?;
|
||||||
|
|
||||||
|
Ok(Config { file, content })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add(&mut self, domain: &str, ip: &str) -> std::io::Result<()> {
|
||||||
|
if self.content.ends_with("\n") {
|
||||||
|
writeln!(self.file, "{} {}", domain, ip)
|
||||||
|
} else {
|
||||||
|
writeln!(self.file, "\n{} {}", domain, ip)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(&mut self) -> io::Result<(Vec<SocketAddr>, Vec<SocketAddr>, Hosts, Vec<Invalid>)> {
|
||||||
|
let (mut hosts, mut binds, mut proxys, mut errors) =
|
||||||
|
(Hosts::new(), Vec::new(), Vec::new(), Vec::new());
|
||||||
|
|
||||||
|
for (n, line) in self.content.lines().enumerate() {
|
||||||
|
// ignore
|
||||||
|
if REG_IGNORE.is_match(&line) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// bind
|
||||||
|
if let Some(addr) = cap_socket_addr(®_BIND, &line) {
|
||||||
|
match addr {
|
||||||
|
Ok(addr) => binds.push(addr),
|
||||||
|
Err(err) => {
|
||||||
|
errors.push(Invalid {
|
||||||
|
line: n + 1,
|
||||||
|
source: line.to_string(),
|
||||||
|
err,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// proxy
|
||||||
|
if let Some(addr) = cap_socket_addr(®_PROXY, &line) {
|
||||||
|
match addr {
|
||||||
|
Ok(addr) => proxys.push(addr),
|
||||||
|
Err(err) => {
|
||||||
|
errors.push(Invalid {
|
||||||
|
line: n + 1,
|
||||||
|
source: line.to_string(),
|
||||||
|
err,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// import
|
||||||
|
if let Some(cap) = REG_IMPORT.captures(&line) {
|
||||||
|
if let Some(m) = cap.name("val") {
|
||||||
|
let (b, p, h, e) = Config::new(m.as_str())?.parse()?;
|
||||||
|
binds.extend(b);
|
||||||
|
proxys.extend(p);
|
||||||
|
hosts.extend(h);
|
||||||
|
errors.extend(e);
|
||||||
|
} else {
|
||||||
|
// todo
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// host
|
||||||
|
if let Some(d) = cap_ip_addr(®_DOMAIN_IP, &line) {
|
||||||
|
match d {
|
||||||
|
Ok((domain, ip)) => hosts.push(domain, ip),
|
||||||
|
Err(err) => {
|
||||||
|
errors.push(Invalid {
|
||||||
|
line: n + 1,
|
||||||
|
source: line.to_string(),
|
||||||
|
err,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
errors.push(Invalid {
|
||||||
|
line: n + 1,
|
||||||
|
source: line.to_string(),
|
||||||
|
err: InvalidType::Other,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((binds, proxys, hosts, errors))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Hosts {
|
||||||
|
list: Vec<(Regex, IpAddr)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hosts {
|
||||||
|
pub fn new() -> Hosts {
|
||||||
|
Hosts { list: Vec::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push(&mut self, domain: Regex, ip: IpAddr) {
|
||||||
|
self.list.push((domain, ip));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extend(&mut self, hosts: Hosts) {
|
||||||
|
for item in hosts.list {
|
||||||
|
self.list.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iter(&mut self) -> Iter<(Regex, IpAddr)> {
|
||||||
|
self.list.iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, domain: &str) -> Option<&IpAddr> {
|
||||||
|
for (reg, ip) in &self.list {
|
||||||
|
if reg.is_match(domain) {
|
||||||
|
return Some(ip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
769
src/lib.rs
Normal file
769
src/lib.rs
Normal file
@@ -0,0 +1,769 @@
|
|||||||
|
// From : EmilHernvall/dnsguide
|
||||||
|
// GitHub : https://github.com/EmilHernvall/dnsguide
|
||||||
|
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
use std::io::{Error, ErrorKind};
|
||||||
|
use std::io::{Read, Result};
|
||||||
|
|
||||||
|
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||||
|
|
||||||
|
pub struct BytePacketBuffer {
|
||||||
|
pub buf: [u8; 512],
|
||||||
|
pub pos: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BytePacketBuffer {
|
||||||
|
pub fn new() -> BytePacketBuffer {
|
||||||
|
BytePacketBuffer {
|
||||||
|
buf: [0; 512],
|
||||||
|
pos: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pos(&self) -> usize {
|
||||||
|
self.pos
|
||||||
|
}
|
||||||
|
|
||||||
|
fn step(&mut self, steps: usize) -> Result<()> {
|
||||||
|
self.pos += steps;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn seek(&mut self, pos: usize) -> Result<()> {
|
||||||
|
self.pos = pos;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read(&mut self) -> Result<u8> {
|
||||||
|
if self.pos >= 512 {
|
||||||
|
return Err(Error::new(ErrorKind::InvalidInput, "End of buffer"));
|
||||||
|
}
|
||||||
|
let res = self.buf[self.pos];
|
||||||
|
self.pos += 1;
|
||||||
|
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&mut self, pos: usize) -> Result<u8> {
|
||||||
|
if pos >= 512 {
|
||||||
|
return Err(Error::new(ErrorKind::InvalidInput, "End of buffer"));
|
||||||
|
}
|
||||||
|
Ok(self.buf[pos])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_range(&mut self, start: usize, len: usize) -> Result<&[u8]> {
|
||||||
|
if start + len >= 512 {
|
||||||
|
return Err(Error::new(ErrorKind::InvalidInput, "End of buffer"));
|
||||||
|
}
|
||||||
|
Ok(&self.buf[start..start + len as usize])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_u16(&mut self) -> Result<u16> {
|
||||||
|
let res = ((self.read()? as u16) << 8) | (self.read()? as u16);
|
||||||
|
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_u32(&mut self) -> Result<u32> {
|
||||||
|
let res = ((self.read()? as u32) << 24)
|
||||||
|
| ((self.read()? as u32) << 16)
|
||||||
|
| ((self.read()? as u32) << 8)
|
||||||
|
| ((self.read()? as u32) << 0);
|
||||||
|
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_qname(&mut self, outstr: &mut String) -> Result<()> {
|
||||||
|
let mut pos = self.pos();
|
||||||
|
let mut jumped = false;
|
||||||
|
|
||||||
|
let mut delim = "";
|
||||||
|
loop {
|
||||||
|
let len = self.get(pos)?;
|
||||||
|
|
||||||
|
// A two byte sequence, where the two highest bits of the first byte is
|
||||||
|
// set, represents a offset relative to the start of the buffer. We
|
||||||
|
// handle this by jumping to the offset, setting a flag to indicate
|
||||||
|
// that we shouldn't update the shared buffer position once done.
|
||||||
|
if (len & 0xC0) == 0xC0 {
|
||||||
|
// When a jump is performed, we only modify the shared buffer
|
||||||
|
// position once, and avoid making the change later on.
|
||||||
|
if !jumped {
|
||||||
|
self.seek(pos + 2)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let b2 = self.get(pos + 1)? as u16;
|
||||||
|
let offset = (((len as u16) ^ 0xC0) << 8) | b2;
|
||||||
|
pos = offset as usize;
|
||||||
|
jumped = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
pos += 1;
|
||||||
|
|
||||||
|
// Names are terminated by an empty label of length 0
|
||||||
|
if len == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
outstr.push_str(delim);
|
||||||
|
|
||||||
|
let str_buffer = self.get_range(pos, len as usize)?;
|
||||||
|
outstr.push_str(&String::from_utf8_lossy(str_buffer).to_lowercase());
|
||||||
|
|
||||||
|
delim = ".";
|
||||||
|
|
||||||
|
pos += len as usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !jumped {
|
||||||
|
self.seek(pos)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(&mut self, val: u8) -> Result<()> {
|
||||||
|
if self.pos >= 512 {
|
||||||
|
return Err(Error::new(ErrorKind::InvalidInput, "End of buffer"));
|
||||||
|
}
|
||||||
|
self.buf[self.pos] = val;
|
||||||
|
self.pos += 1;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_u8(&mut self, val: u8) -> Result<()> {
|
||||||
|
self.write(val)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_u16(&mut self, val: u16) -> Result<()> {
|
||||||
|
self.write((val >> 8) as u8)?;
|
||||||
|
self.write((val & 0xFF) as u8)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_u32(&mut self, val: u32) -> Result<()> {
|
||||||
|
self.write(((val >> 24) & 0xFF) as u8)?;
|
||||||
|
self.write(((val >> 16) & 0xFF) as u8)?;
|
||||||
|
self.write(((val >> 8) & 0xFF) as u8)?;
|
||||||
|
self.write(((val >> 0) & 0xFF) as u8)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_qname(&mut self, qname: &str) -> Result<()> {
|
||||||
|
let split_str = qname.split('.').collect::<Vec<&str>>();
|
||||||
|
|
||||||
|
for label in split_str {
|
||||||
|
let len = label.len();
|
||||||
|
if len > 0x34 {
|
||||||
|
return Err(Error::new(
|
||||||
|
ErrorKind::InvalidInput,
|
||||||
|
"Single label exceeds 63 characters of length",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.write_u8(len as u8)?;
|
||||||
|
for b in label.as_bytes() {
|
||||||
|
self.write_u8(*b)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.write_u8(0)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set(&mut self, pos: usize, val: u8) -> Result<()> {
|
||||||
|
self.buf[pos] = val;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_u16(&mut self, pos: usize, val: u16) -> Result<()> {
|
||||||
|
self.set(pos, (val >> 8) as u8)?;
|
||||||
|
self.set(pos + 1, (val & 0xFF) as u8)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum ResultCode {
|
||||||
|
NOERROR = 0,
|
||||||
|
FORMERR = 1,
|
||||||
|
SERVFAIL = 2,
|
||||||
|
NXDOMAIN = 3,
|
||||||
|
NOTIMP = 4,
|
||||||
|
REFUSED = 5,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResultCode {
|
||||||
|
pub fn from_num(num: u8) -> ResultCode {
|
||||||
|
match num {
|
||||||
|
1 => ResultCode::FORMERR,
|
||||||
|
2 => ResultCode::SERVFAIL,
|
||||||
|
3 => ResultCode::NXDOMAIN,
|
||||||
|
4 => ResultCode::NOTIMP,
|
||||||
|
5 => ResultCode::REFUSED,
|
||||||
|
0 | _ => ResultCode::NOERROR,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct DnsHeader {
|
||||||
|
pub id: u16, // 16 bits
|
||||||
|
|
||||||
|
pub recursion_desired: bool, // 1 bit
|
||||||
|
pub truncated_message: bool, // 1 bit
|
||||||
|
pub authoritative_answer: bool, // 1 bit
|
||||||
|
pub opcode: u8, // 4 bits
|
||||||
|
pub response: bool, // 1 bit
|
||||||
|
|
||||||
|
pub rescode: ResultCode, // 4 bits
|
||||||
|
pub checking_disabled: bool, // 1 bit
|
||||||
|
pub authed_data: bool, // 1 bit
|
||||||
|
pub z: bool, // 1 bit
|
||||||
|
pub recursion_available: bool, // 1 bit
|
||||||
|
|
||||||
|
pub questions: u16, // 16 bits
|
||||||
|
pub answers: u16, // 16 bits
|
||||||
|
pub authoritative_entries: u16, // 16 bits
|
||||||
|
pub resource_entries: u16, // 16 bits
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DnsHeader {
|
||||||
|
pub fn new() -> DnsHeader {
|
||||||
|
DnsHeader {
|
||||||
|
id: 0,
|
||||||
|
|
||||||
|
recursion_desired: false,
|
||||||
|
truncated_message: false,
|
||||||
|
authoritative_answer: false,
|
||||||
|
opcode: 0,
|
||||||
|
response: false,
|
||||||
|
|
||||||
|
rescode: ResultCode::NOERROR,
|
||||||
|
checking_disabled: false,
|
||||||
|
authed_data: false,
|
||||||
|
z: false,
|
||||||
|
recursion_available: false,
|
||||||
|
|
||||||
|
questions: 0,
|
||||||
|
answers: 0,
|
||||||
|
authoritative_entries: 0,
|
||||||
|
resource_entries: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(&mut self, buffer: &mut BytePacketBuffer) -> Result<()> {
|
||||||
|
self.id = buffer.read_u16()?;
|
||||||
|
|
||||||
|
let flags = buffer.read_u16()?;
|
||||||
|
let a = (flags >> 8) as u8;
|
||||||
|
let b = (flags & 0xFF) as u8;
|
||||||
|
self.recursion_desired = (a & (1 << 0)) > 0;
|
||||||
|
self.truncated_message = (a & (1 << 1)) > 0;
|
||||||
|
self.authoritative_answer = (a & (1 << 2)) > 0;
|
||||||
|
self.opcode = (a >> 3) & 0x0F;
|
||||||
|
self.response = (a & (1 << 7)) > 0;
|
||||||
|
|
||||||
|
self.rescode = ResultCode::from_num(b & 0x0F);
|
||||||
|
self.checking_disabled = (b & (1 << 4)) > 0;
|
||||||
|
self.authed_data = (b & (1 << 5)) > 0;
|
||||||
|
self.z = (b & (1 << 6)) > 0;
|
||||||
|
self.recursion_available = (b & (1 << 7)) > 0;
|
||||||
|
|
||||||
|
self.questions = buffer.read_u16()?;
|
||||||
|
self.answers = buffer.read_u16()?;
|
||||||
|
self.authoritative_entries = buffer.read_u16()?;
|
||||||
|
self.resource_entries = buffer.read_u16()?;
|
||||||
|
|
||||||
|
// Return the constant header size
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(&self, buffer: &mut BytePacketBuffer) -> Result<()> {
|
||||||
|
buffer.write_u16(self.id)?;
|
||||||
|
|
||||||
|
(buffer.write_u8(
|
||||||
|
(self.recursion_desired as u8)
|
||||||
|
| ((self.truncated_message as u8) << 1)
|
||||||
|
| ((self.authoritative_answer as u8) << 2)
|
||||||
|
| (self.opcode << 3)
|
||||||
|
| ((self.response as u8) << 7) as u8,
|
||||||
|
))?;
|
||||||
|
|
||||||
|
(buffer.write_u8(
|
||||||
|
(self.rescode.clone() as u8)
|
||||||
|
| ((self.checking_disabled as u8) << 4)
|
||||||
|
| ((self.authed_data as u8) << 5)
|
||||||
|
| ((self.z as u8) << 6)
|
||||||
|
| ((self.recursion_available as u8) << 7),
|
||||||
|
))?;
|
||||||
|
|
||||||
|
buffer.write_u16(self.questions)?;
|
||||||
|
buffer.write_u16(self.answers)?;
|
||||||
|
buffer.write_u16(self.authoritative_entries)?;
|
||||||
|
buffer.write_u16(self.resource_entries)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Debug, Clone, Hash, Copy)]
|
||||||
|
pub enum QueryType {
|
||||||
|
UNKNOWN(u16),
|
||||||
|
A, // 1
|
||||||
|
NS, // 2
|
||||||
|
CNAME, // 5
|
||||||
|
MX, // 15
|
||||||
|
AAAA, // 28
|
||||||
|
}
|
||||||
|
|
||||||
|
impl QueryType {
|
||||||
|
pub fn to_num(&self) -> u16 {
|
||||||
|
match *self {
|
||||||
|
QueryType::UNKNOWN(x) => x,
|
||||||
|
QueryType::A => 1,
|
||||||
|
QueryType::NS => 2,
|
||||||
|
QueryType::CNAME => 5,
|
||||||
|
QueryType::MX => 15,
|
||||||
|
QueryType::AAAA => 28,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_num(num: u16) -> QueryType {
|
||||||
|
match num {
|
||||||
|
1 => QueryType::A,
|
||||||
|
2 => QueryType::NS,
|
||||||
|
5 => QueryType::CNAME,
|
||||||
|
15 => QueryType::MX,
|
||||||
|
28 => QueryType::AAAA,
|
||||||
|
_ => QueryType::UNKNOWN(num),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct DnsQuestion {
|
||||||
|
pub name: String,
|
||||||
|
pub qtype: QueryType,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DnsQuestion {
|
||||||
|
pub fn new(name: String, qtype: QueryType) -> DnsQuestion {
|
||||||
|
DnsQuestion {
|
||||||
|
name: name,
|
||||||
|
qtype: qtype,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(&mut self, buffer: &mut BytePacketBuffer) -> Result<()> {
|
||||||
|
buffer.read_qname(&mut self.name)?;
|
||||||
|
self.qtype = QueryType::from_num(buffer.read_u16()?); // qtype
|
||||||
|
let _ = buffer.read_u16()?; // class
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(&self, buffer: &mut BytePacketBuffer) -> Result<()> {
|
||||||
|
buffer.write_qname(&self.name)?;
|
||||||
|
|
||||||
|
let typenum = self.qtype.to_num();
|
||||||
|
buffer.write_u16(typenum)?;
|
||||||
|
buffer.write_u16(1)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub enum DnsRecord {
|
||||||
|
UNKNOWN {
|
||||||
|
domain: String,
|
||||||
|
qtype: u16,
|
||||||
|
data_len: u16,
|
||||||
|
ttl: u32,
|
||||||
|
}, // 0
|
||||||
|
A {
|
||||||
|
domain: String,
|
||||||
|
addr: Ipv4Addr,
|
||||||
|
ttl: u32,
|
||||||
|
}, // 1
|
||||||
|
NS {
|
||||||
|
domain: String,
|
||||||
|
host: String,
|
||||||
|
ttl: u32,
|
||||||
|
}, // 2
|
||||||
|
CNAME {
|
||||||
|
domain: String,
|
||||||
|
host: String,
|
||||||
|
ttl: u32,
|
||||||
|
}, // 5
|
||||||
|
MX {
|
||||||
|
domain: String,
|
||||||
|
priority: u16,
|
||||||
|
host: String,
|
||||||
|
ttl: u32,
|
||||||
|
}, // 15
|
||||||
|
AAAA {
|
||||||
|
domain: String,
|
||||||
|
addr: Ipv6Addr,
|
||||||
|
ttl: u32,
|
||||||
|
}, // 28
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DnsRecord {
|
||||||
|
pub fn read(buffer: &mut BytePacketBuffer) -> Result<DnsRecord> {
|
||||||
|
let mut domain = String::new();
|
||||||
|
buffer.read_qname(&mut domain)?;
|
||||||
|
|
||||||
|
let qtype_num = buffer.read_u16()?;
|
||||||
|
let qtype = QueryType::from_num(qtype_num);
|
||||||
|
let _ = buffer.read_u16()?;
|
||||||
|
let ttl = buffer.read_u32()?;
|
||||||
|
let data_len = buffer.read_u16()?;
|
||||||
|
|
||||||
|
match qtype {
|
||||||
|
QueryType::A => {
|
||||||
|
let raw_addr = buffer.read_u32()?;
|
||||||
|
let addr = Ipv4Addr::new(
|
||||||
|
((raw_addr >> 24) & 0xFF) as u8,
|
||||||
|
((raw_addr >> 16) & 0xFF) as u8,
|
||||||
|
((raw_addr >> 8) & 0xFF) as u8,
|
||||||
|
((raw_addr >> 0) & 0xFF) as u8,
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(DnsRecord::A {
|
||||||
|
domain: domain,
|
||||||
|
addr: addr,
|
||||||
|
ttl: ttl,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
QueryType::AAAA => {
|
||||||
|
let raw_addr1 = buffer.read_u32()?;
|
||||||
|
let raw_addr2 = buffer.read_u32()?;
|
||||||
|
let raw_addr3 = buffer.read_u32()?;
|
||||||
|
let raw_addr4 = buffer.read_u32()?;
|
||||||
|
let addr = Ipv6Addr::new(
|
||||||
|
((raw_addr1 >> 16) & 0xFFFF) as u16,
|
||||||
|
((raw_addr1 >> 0) & 0xFFFF) as u16,
|
||||||
|
((raw_addr2 >> 16) & 0xFFFF) as u16,
|
||||||
|
((raw_addr2 >> 0) & 0xFFFF) as u16,
|
||||||
|
((raw_addr3 >> 16) & 0xFFFF) as u16,
|
||||||
|
((raw_addr3 >> 0) & 0xFFFF) as u16,
|
||||||
|
((raw_addr4 >> 16) & 0xFFFF) as u16,
|
||||||
|
((raw_addr4 >> 0) & 0xFFFF) as u16,
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(DnsRecord::AAAA {
|
||||||
|
domain: domain,
|
||||||
|
addr: addr,
|
||||||
|
ttl: ttl,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
QueryType::NS => {
|
||||||
|
let mut ns = String::new();
|
||||||
|
buffer.read_qname(&mut ns)?;
|
||||||
|
|
||||||
|
Ok(DnsRecord::NS {
|
||||||
|
domain: domain,
|
||||||
|
host: ns,
|
||||||
|
ttl: ttl,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
QueryType::CNAME => {
|
||||||
|
let mut cname = String::new();
|
||||||
|
buffer.read_qname(&mut cname)?;
|
||||||
|
|
||||||
|
Ok(DnsRecord::CNAME {
|
||||||
|
domain: domain,
|
||||||
|
host: cname,
|
||||||
|
ttl: ttl,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
QueryType::MX => {
|
||||||
|
let priority = buffer.read_u16()?;
|
||||||
|
let mut mx = String::new();
|
||||||
|
buffer.read_qname(&mut mx)?;
|
||||||
|
|
||||||
|
Ok(DnsRecord::MX {
|
||||||
|
domain: domain,
|
||||||
|
priority: priority,
|
||||||
|
host: mx,
|
||||||
|
ttl: ttl,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
QueryType::UNKNOWN(_) => {
|
||||||
|
buffer.step(data_len as usize)?;
|
||||||
|
|
||||||
|
Ok(DnsRecord::UNKNOWN {
|
||||||
|
domain: domain,
|
||||||
|
qtype: qtype_num,
|
||||||
|
data_len: data_len,
|
||||||
|
ttl: ttl,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(&self, buffer: &mut BytePacketBuffer) -> Result<usize> {
|
||||||
|
let start_pos = buffer.pos();
|
||||||
|
|
||||||
|
match *self {
|
||||||
|
DnsRecord::A {
|
||||||
|
ref domain,
|
||||||
|
ref addr,
|
||||||
|
ttl,
|
||||||
|
} => {
|
||||||
|
buffer.write_qname(domain)?;
|
||||||
|
buffer.write_u16(QueryType::A.to_num())?;
|
||||||
|
buffer.write_u16(1)?;
|
||||||
|
buffer.write_u32(ttl)?;
|
||||||
|
buffer.write_u16(4)?;
|
||||||
|
|
||||||
|
let octets = addr.octets();
|
||||||
|
buffer.write_u8(octets[0])?;
|
||||||
|
buffer.write_u8(octets[1])?;
|
||||||
|
buffer.write_u8(octets[2])?;
|
||||||
|
buffer.write_u8(octets[3])?;
|
||||||
|
}
|
||||||
|
DnsRecord::NS {
|
||||||
|
ref domain,
|
||||||
|
ref host,
|
||||||
|
ttl,
|
||||||
|
} => {
|
||||||
|
buffer.write_qname(domain)?;
|
||||||
|
buffer.write_u16(QueryType::NS.to_num())?;
|
||||||
|
buffer.write_u16(1)?;
|
||||||
|
buffer.write_u32(ttl)?;
|
||||||
|
|
||||||
|
let pos = buffer.pos();
|
||||||
|
buffer.write_u16(0)?;
|
||||||
|
|
||||||
|
buffer.write_qname(host)?;
|
||||||
|
|
||||||
|
let size = buffer.pos() - (pos + 2);
|
||||||
|
buffer.set_u16(pos, size as u16)?;
|
||||||
|
}
|
||||||
|
DnsRecord::CNAME {
|
||||||
|
ref domain,
|
||||||
|
ref host,
|
||||||
|
ttl,
|
||||||
|
} => {
|
||||||
|
buffer.write_qname(domain)?;
|
||||||
|
buffer.write_u16(QueryType::CNAME.to_num())?;
|
||||||
|
buffer.write_u16(1)?;
|
||||||
|
buffer.write_u32(ttl)?;
|
||||||
|
|
||||||
|
let pos = buffer.pos();
|
||||||
|
buffer.write_u16(0)?;
|
||||||
|
|
||||||
|
buffer.write_qname(host)?;
|
||||||
|
|
||||||
|
let size = buffer.pos() - (pos + 2);
|
||||||
|
buffer.set_u16(pos, size as u16)?;
|
||||||
|
}
|
||||||
|
DnsRecord::MX {
|
||||||
|
ref domain,
|
||||||
|
priority,
|
||||||
|
ref host,
|
||||||
|
ttl,
|
||||||
|
} => {
|
||||||
|
buffer.write_qname(domain)?;
|
||||||
|
buffer.write_u16(QueryType::MX.to_num())?;
|
||||||
|
buffer.write_u16(1)?;
|
||||||
|
buffer.write_u32(ttl)?;
|
||||||
|
|
||||||
|
let pos = buffer.pos();
|
||||||
|
buffer.write_u16(0)?;
|
||||||
|
|
||||||
|
buffer.write_u16(priority)?;
|
||||||
|
buffer.write_qname(host)?;
|
||||||
|
|
||||||
|
let size = buffer.pos() - (pos + 2);
|
||||||
|
buffer.set_u16(pos, size as u16)?;
|
||||||
|
}
|
||||||
|
DnsRecord::AAAA {
|
||||||
|
ref domain,
|
||||||
|
ref addr,
|
||||||
|
ttl,
|
||||||
|
} => {
|
||||||
|
buffer.write_qname(domain)?;
|
||||||
|
buffer.write_u16(QueryType::AAAA.to_num())?;
|
||||||
|
buffer.write_u16(1)?;
|
||||||
|
buffer.write_u32(ttl)?;
|
||||||
|
buffer.write_u16(16)?;
|
||||||
|
|
||||||
|
for octet in &addr.segments() {
|
||||||
|
buffer.write_u16(*octet)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DnsRecord::UNKNOWN { .. } => {
|
||||||
|
println!("Skipping record: {:?}", self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(buffer.pos() - start_pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct DnsPacket {
|
||||||
|
pub header: DnsHeader,
|
||||||
|
pub questions: Vec<DnsQuestion>,
|
||||||
|
pub answers: Vec<DnsRecord>,
|
||||||
|
pub authorities: Vec<DnsRecord>,
|
||||||
|
pub resources: Vec<DnsRecord>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DnsPacket {
|
||||||
|
pub fn new() -> DnsPacket {
|
||||||
|
DnsPacket {
|
||||||
|
header: DnsHeader::new(),
|
||||||
|
questions: Vec::new(),
|
||||||
|
answers: Vec::new(),
|
||||||
|
authorities: Vec::new(),
|
||||||
|
resources: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_buffer(buffer: &mut BytePacketBuffer) -> Result<DnsPacket> {
|
||||||
|
let mut result = DnsPacket::new();
|
||||||
|
result.header.read(buffer)?;
|
||||||
|
|
||||||
|
for _ in 0..result.header.questions {
|
||||||
|
let mut question = DnsQuestion::new("".to_string(), QueryType::UNKNOWN(0));
|
||||||
|
question.read(buffer)?;
|
||||||
|
result.questions.push(question);
|
||||||
|
}
|
||||||
|
|
||||||
|
for _ in 0..result.header.answers {
|
||||||
|
let rec = DnsRecord::read(buffer)?;
|
||||||
|
result.answers.push(rec);
|
||||||
|
}
|
||||||
|
for _ in 0..result.header.authoritative_entries {
|
||||||
|
let rec = DnsRecord::read(buffer)?;
|
||||||
|
result.authorities.push(rec);
|
||||||
|
}
|
||||||
|
for _ in 0..result.header.resource_entries {
|
||||||
|
let rec = DnsRecord::read(buffer)?;
|
||||||
|
result.resources.push(rec);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(&mut self, buffer: &mut BytePacketBuffer) -> Result<()> {
|
||||||
|
self.header.questions = self.questions.len() as u16;
|
||||||
|
self.header.answers = self.answers.len() as u16;
|
||||||
|
self.header.authoritative_entries = self.authorities.len() as u16;
|
||||||
|
self.header.resource_entries = self.resources.len() as u16;
|
||||||
|
|
||||||
|
self.header.write(buffer)?;
|
||||||
|
|
||||||
|
for question in &self.questions {
|
||||||
|
question.write(buffer)?;
|
||||||
|
}
|
||||||
|
for rec in &self.answers {
|
||||||
|
rec.write(buffer)?;
|
||||||
|
}
|
||||||
|
for rec in &self.authorities {
|
||||||
|
rec.write(buffer)?;
|
||||||
|
}
|
||||||
|
for rec in &self.resources {
|
||||||
|
rec.write(buffer)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_random_a(&self) -> Option<String> {
|
||||||
|
if !self.answers.is_empty() {
|
||||||
|
let a_record = &self.answers[0];
|
||||||
|
if let DnsRecord::A { ref addr, .. } = *a_record {
|
||||||
|
return Some(addr.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_resolved_ns(&self, qname: &str) -> Option<String> {
|
||||||
|
let mut new_authorities = Vec::new();
|
||||||
|
for auth in &self.authorities {
|
||||||
|
if let DnsRecord::NS {
|
||||||
|
ref domain,
|
||||||
|
ref host,
|
||||||
|
..
|
||||||
|
} = *auth
|
||||||
|
{
|
||||||
|
if !qname.ends_with(domain) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for rsrc in &self.resources {
|
||||||
|
if let DnsRecord::A {
|
||||||
|
ref domain,
|
||||||
|
ref addr,
|
||||||
|
ttl,
|
||||||
|
} = *rsrc
|
||||||
|
{
|
||||||
|
if domain != host {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let rec = DnsRecord::A {
|
||||||
|
domain: host.clone(),
|
||||||
|
addr: *addr,
|
||||||
|
ttl: ttl,
|
||||||
|
};
|
||||||
|
|
||||||
|
new_authorities.push(rec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !new_authorities.is_empty() {
|
||||||
|
if let DnsRecord::A { addr, .. } = new_authorities[0] {
|
||||||
|
return Some(addr.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_unresolved_ns(&self, qname: &str) -> Option<String> {
|
||||||
|
let mut new_authorities = Vec::new();
|
||||||
|
for auth in &self.authorities {
|
||||||
|
if let DnsRecord::NS {
|
||||||
|
ref domain,
|
||||||
|
ref host,
|
||||||
|
..
|
||||||
|
} = *auth
|
||||||
|
{
|
||||||
|
if !qname.ends_with(domain) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_authorities.push(host);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !new_authorities.is_empty() {
|
||||||
|
return Some(new_authorities[0].clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
348
src/main.rs
Normal file
348
src/main.rs
Normal file
@@ -0,0 +1,348 @@
|
|||||||
|
#![feature(const_vec_new)]
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate lazy_static;
|
||||||
|
|
||||||
|
mod config;
|
||||||
|
mod lib;
|
||||||
|
mod watch;
|
||||||
|
|
||||||
|
use ace::App;
|
||||||
|
use async_std::io;
|
||||||
|
use async_std::net::UdpSocket;
|
||||||
|
use async_std::task;
|
||||||
|
use config::{Config, Hosts, Invalid, InvalidType};
|
||||||
|
use dirs;
|
||||||
|
use lib::*;
|
||||||
|
use std::env;
|
||||||
|
use std::net::{IpAddr, SocketAddr};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::process::Command;
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
use watch::Watch;
|
||||||
|
|
||||||
|
const CONFIG_NAME: &str = ".updns";
|
||||||
|
const DEFAULT_BIND: &str = "0.0.0.0:53";
|
||||||
|
const DEFAULT_PROXY: [&str; 2] = ["8.8.8.8:53", "114.114.114.114:53"];
|
||||||
|
const PROXY_TIMEOUT: u64 = 2000;
|
||||||
|
const WATCH_INTERVAL: u64 = 3000;
|
||||||
|
static mut PROXY: Vec<SocketAddr> = Vec::new();
|
||||||
|
static mut HOSTS: Option<Hosts> = None;
|
||||||
|
|
||||||
|
macro_rules! log {
|
||||||
|
($($arg:tt)*) => {
|
||||||
|
println!($($arg)*);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! warn {
|
||||||
|
($($arg:tt)*) => {
|
||||||
|
print!("\x1B[{}m{}\x1B[0m", "1;33", "warning: ");
|
||||||
|
println!($($arg)*);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! error {
|
||||||
|
($($arg:tt)*) => {
|
||||||
|
eprint!("\x1B[{}m{}\x1B[0m", "1;31", "error: ");
|
||||||
|
eprintln!($($arg)*);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! exit {
|
||||||
|
($($arg:tt)*) => {
|
||||||
|
{
|
||||||
|
eprint!("\x1B[{}m{}\x1B[0m", "1;31", "error: ");
|
||||||
|
eprintln!($($arg)*);
|
||||||
|
std::process::exit(1)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let app = App::new(env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"))
|
||||||
|
.cmd("add", "Add a DNS record")
|
||||||
|
.cmd("rm", "Remove a DNS record")
|
||||||
|
.cmd("ls", "Print all configured DNS records")
|
||||||
|
.cmd("config", "Call vim to edit the configuration file")
|
||||||
|
.cmd("path", "Print related directories")
|
||||||
|
.cmd("help", "Print help information")
|
||||||
|
.cmd("version", "Print version information")
|
||||||
|
.opt("-c", "Specify a config file");
|
||||||
|
|
||||||
|
let config_path = match app.value("-c") {
|
||||||
|
Some(values) => {
|
||||||
|
if values.is_empty() {
|
||||||
|
exit!("'-c' value: [CONFIG]");
|
||||||
|
}
|
||||||
|
PathBuf::from(values[0])
|
||||||
|
}
|
||||||
|
None => match dirs::home_dir() {
|
||||||
|
Some(p) => p.join(CONFIG_NAME),
|
||||||
|
None => exit!("Can't get home directory"),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(cmd) = app.command() {
|
||||||
|
match cmd.as_str() {
|
||||||
|
"add" => {
|
||||||
|
let values = app.value("add").unwrap_or(vec![]);
|
||||||
|
if values.len() != 2 {
|
||||||
|
exit!("'add' value: [DOMAIN] [IP]");
|
||||||
|
}
|
||||||
|
let mut config = match Config::new(&config_path) {
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(err) => exit!("Failed to read config file: {:?}\n{:?}", &config_path, err),
|
||||||
|
};
|
||||||
|
if let Err(err) = config.add(&values[0], &values[1]) {
|
||||||
|
exit!("Add record failed\n{:?}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"rm" => {
|
||||||
|
if let Some(value) = app.value("rm") {
|
||||||
|
if value.is_empty() {
|
||||||
|
exit!("'rm' value: [DOMAIN | IP]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"ls" => {
|
||||||
|
let (_, _, _, mut hosts) = config_parse(&config_path);
|
||||||
|
let mut n = 0;
|
||||||
|
for (reg, _) in hosts.iter() {
|
||||||
|
if reg.as_str().len() > n {
|
||||||
|
n = reg.as_str().len();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (domain, ip) in hosts.iter() {
|
||||||
|
println!("{:domain$} {}", domain.as_str(), ip, domain = n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"config" => {
|
||||||
|
let cmd = Command::new("vim").arg(&config_path).status();
|
||||||
|
match cmd {
|
||||||
|
Ok(status) => {
|
||||||
|
if status.success() {
|
||||||
|
config_parse(&config_path);
|
||||||
|
} else {
|
||||||
|
warn!("Non-zero state exit\n{:?}", status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => exit!("Call vim command failed\n{:?}", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"path" => {
|
||||||
|
let binary = match env::current_exe() {
|
||||||
|
Ok(p) => p.display().to_string(),
|
||||||
|
Err(err) => exit!("Failed to get directory\n{:?}", err),
|
||||||
|
};
|
||||||
|
println!("Binary: {}\nConfig: {:?}", binary, config_path);
|
||||||
|
}
|
||||||
|
"help" => {
|
||||||
|
app.help();
|
||||||
|
}
|
||||||
|
"version" => {
|
||||||
|
app.version();
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
app.error_try("help");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (_, mut binds, proxys, hosts) = config_parse(&config_path);
|
||||||
|
if binds.is_empty() {
|
||||||
|
warn!("Will bind the default address '{}'", DEFAULT_BIND);
|
||||||
|
binds.push(DEFAULT_BIND.parse().unwrap());
|
||||||
|
}
|
||||||
|
if proxys.is_empty() {
|
||||||
|
warn!(
|
||||||
|
"Will use the default proxy address '{}'",
|
||||||
|
DEFAULT_PROXY.join(", ")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
update_config(proxys, hosts);
|
||||||
|
|
||||||
|
task::spawn(watch_config(config_path));
|
||||||
|
task::block_on(run_server(binds));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn config_parse(file: &PathBuf) -> (Config, Vec<SocketAddr>, Vec<SocketAddr>, Hosts) {
|
||||||
|
let mut config = match Config::new(file) {
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(err) => exit!("Failed to read config file: {:?}\n{:?}", file, err),
|
||||||
|
};
|
||||||
|
|
||||||
|
let (binds, proxys, hosts, errors) = match config.parse() {
|
||||||
|
Ok(d) => d,
|
||||||
|
Err(err) => exit!("Parsing config file failed\n{:?}", err),
|
||||||
|
};
|
||||||
|
output_invalid(errors);
|
||||||
|
|
||||||
|
(config, binds, proxys, hosts)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn output_invalid(errors: Vec<Invalid>) {
|
||||||
|
if !errors.is_empty() {
|
||||||
|
for invalid in errors {
|
||||||
|
let msg = match invalid.err {
|
||||||
|
InvalidType::SocketAddr => "Cannot parse socket addr",
|
||||||
|
InvalidType::IpAddr => "Cannot parse ip addr",
|
||||||
|
InvalidType::Regex => "Cannot parse Regular expression",
|
||||||
|
InvalidType::Other => "Invalid line",
|
||||||
|
};
|
||||||
|
warn!("{}", msg);
|
||||||
|
log!("Line {}: {}", invalid.line, invalid.source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn watch_config(p: PathBuf) {
|
||||||
|
let mut watch = Watch::new(p, WATCH_INTERVAL);
|
||||||
|
watch
|
||||||
|
.change(|c| {
|
||||||
|
log!("Reload the configuration file: {:?}", &c);
|
||||||
|
if let Ok(mut config) = Config::new(c) {
|
||||||
|
if let Ok((_, proxy, hosts, errors)) = config.parse() {
|
||||||
|
update_config(proxy, hosts);
|
||||||
|
output_invalid(errors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_config(mut proxy: Vec<SocketAddr>, hosts: Hosts) {
|
||||||
|
if proxy.is_empty() {
|
||||||
|
proxy = DEFAULT_PROXY
|
||||||
|
.iter()
|
||||||
|
.map(|p| p.parse().unwrap())
|
||||||
|
.collect::<Vec<SocketAddr>>();
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
PROXY = proxy;
|
||||||
|
HOSTS = Some(hosts);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn run_server(binds: Vec<SocketAddr>) {
|
||||||
|
let mut tasks = vec![];
|
||||||
|
for addr in binds {
|
||||||
|
let task = task::spawn(async move {
|
||||||
|
let socket = match UdpSocket::bind(&addr).await {
|
||||||
|
Ok(socket) => {
|
||||||
|
log!("Start listening to '{}'", addr);
|
||||||
|
socket
|
||||||
|
}
|
||||||
|
Err(err) => exit!("Binding '{}' failed\n{:?}", addr, err),
|
||||||
|
};
|
||||||
|
loop {
|
||||||
|
let mut req = BytePacketBuffer::new();
|
||||||
|
match socket.recv_from(&mut req.buf).await {
|
||||||
|
Ok((len, src)) => {
|
||||||
|
let res = match handle(req, len).await {
|
||||||
|
Ok(data) => data,
|
||||||
|
Err(err) => {
|
||||||
|
error!("Processing request failed\n{:?}", err);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if let Err(err) = socket.send_to(&res, &src).await {
|
||||||
|
error!("Replying to '{}' failed\n{:?}", &src, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
error!("Failed to receive message\n{:?}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
tasks.push(task);
|
||||||
|
}
|
||||||
|
for task in tasks {
|
||||||
|
task.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn proxy(buf: &[u8]) -> io::Result<Vec<u8>> {
|
||||||
|
let proxy = unsafe { &PROXY };
|
||||||
|
|
||||||
|
for addr in proxy.iter() {
|
||||||
|
let socket = UdpSocket::bind(("0.0.0.0", 0)).await?;
|
||||||
|
|
||||||
|
let data = io::timeout(Duration::from_millis(PROXY_TIMEOUT), async {
|
||||||
|
socket.send_to(&buf, addr).await?;
|
||||||
|
let mut res = [0; 512];
|
||||||
|
let len = socket.recv(&mut res).await?;
|
||||||
|
Ok(res[..len].to_vec())
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
match data {
|
||||||
|
Ok(data) => {
|
||||||
|
return Ok(data);
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
error!("Agent request to {}\n{:?}", addr, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
"Proxy server failed to proxy request",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_answer(domain: &str, query: QueryType) -> Option<DnsRecord> {
|
||||||
|
let hosts = unsafe { HOSTS.as_ref().unwrap() };
|
||||||
|
if let Some(ip) = hosts.get(domain) {
|
||||||
|
match query {
|
||||||
|
QueryType::A => {
|
||||||
|
if let IpAddr::V4(addr) = ip {
|
||||||
|
return Some(DnsRecord::A {
|
||||||
|
domain: domain.to_string(),
|
||||||
|
addr: addr.clone(),
|
||||||
|
ttl: 3600,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QueryType::AAAA => {
|
||||||
|
if let IpAddr::V6(addr) = ip {
|
||||||
|
return Some(DnsRecord::AAAA {
|
||||||
|
domain: domain.to_string(),
|
||||||
|
addr: addr.clone(),
|
||||||
|
ttl: 3600,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle(mut req: BytePacketBuffer, len: usize) -> io::Result<Vec<u8>> {
|
||||||
|
let mut request = DnsPacket::from_buffer(&mut req)?;
|
||||||
|
|
||||||
|
let query = match request.questions.get(0) {
|
||||||
|
Some(q) => q,
|
||||||
|
None => return proxy(&req.buf[..len]).await,
|
||||||
|
};
|
||||||
|
|
||||||
|
log!("Query: {} Type: {:?}", query.name, query.qtype);
|
||||||
|
|
||||||
|
if let Some(answer) = get_answer(&query.name, query.qtype) {
|
||||||
|
request.header.recursion_desired = true;
|
||||||
|
request.header.recursion_available = true;
|
||||||
|
request.header.response = true;
|
||||||
|
request.answers.push(answer);
|
||||||
|
let mut res_buffer = BytePacketBuffer::new();
|
||||||
|
request.write(&mut res_buffer)?;
|
||||||
|
let len = res_buffer.pos();
|
||||||
|
let data = res_buffer.get_range(0, len)?;
|
||||||
|
Ok(data.to_vec())
|
||||||
|
} else {
|
||||||
|
proxy(&req.buf[..len]).await
|
||||||
|
}
|
||||||
|
}
|
||||||
46
src/watch.rs
Normal file
46
src/watch.rs
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
use async_std::fs;
|
||||||
|
use async_std::io;
|
||||||
|
use async_std::stream;
|
||||||
|
use async_std::stream::Stream;
|
||||||
|
use async_std::task;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::time::{Duration, SystemTime};
|
||||||
|
|
||||||
|
pub struct Watch {
|
||||||
|
path: PathBuf,
|
||||||
|
interval: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Watch {
|
||||||
|
pub fn new(path: PathBuf, interval: u64) -> Watch {
|
||||||
|
Watch { interval, path }
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn modified(&self) -> io::Result<SystemTime> {
|
||||||
|
let file = fs::File::open(&self.path).await?;
|
||||||
|
let modified = file.metadata().await?.modified()?;
|
||||||
|
Ok(modified)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn change(&mut self, func: fn(path: &PathBuf)) {
|
||||||
|
let mut repeat = stream::repeat(0);
|
||||||
|
let mut before = match self.modified().await {
|
||||||
|
Ok(time) => Some(time),
|
||||||
|
Err(_) => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
while let Some(_) = repeat.next().await {
|
||||||
|
task::sleep(Duration::from_millis(self.interval)).await;
|
||||||
|
|
||||||
|
let after = match self.modified().await {
|
||||||
|
Ok(time) => Some(time),
|
||||||
|
Err(_) => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
if before != after {
|
||||||
|
before = after;
|
||||||
|
func(&self.path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user