diff --git a/.travis.yml b/.travis.yml index 33b08175..664059c1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,45 +1,45 @@ sudo: false language: rust matrix: - include: - - os: windows - rust: stable + include: + - os: windows + rust: stable - - os: osx - rust: stable + - os: osx + rust: stable - - os: linux - rust: stable - - os: linux - rust: beta - - os: linux - rust: nightly + - os: linux + rust: stable + - os: linux + rust: beta + - os: linux + rust: nightly env: - - RUSTFLAGS="-Ctarget-feature=+aes,+ssse3" RUSTDOCFLAGS="-Ctarget-feature=+aes,+ssse3" RUST_BACKTRACE=1 + - RUSTFLAGS="-Ctarget-feature=+aes,+ssse3" RUSTDOCFLAGS="-Ctarget-feature=+aes,+ssse3" RUST_BACKTRACE=1 addons: - apt: - packages: - - libssl-dev + apt: + packages: + - libssl-dev install: - #- wget https://github.com/jedisct1/libsodium/releases/download/1.0.16/libsodium-1.0.16.tar.gz - #- tar xvfz libsodium-1.0.16.tar.gz - #- cd libsodium-1.0.16 && ./configure --prefix=$HOME/installed_libsodium && make && make install && cd .. - #- export PKG_CONFIG_PATH=$HOME/installed_libsodium/lib/pkgconfig:$PKG_CONFIG_PATH - #- export LD_LIBRARY_PATH=$HOME/installed_libsodium/lib:$LD_LIBRARY_PATH - - export SODIUM_BUILD_STATIC=yes + #- wget https://github.com/jedisct1/libsodium/releases/download/1.0.16/libsodium-1.0.16.tar.gz + #- tar xvfz libsodium-1.0.16.tar.gz + #- cd libsodium-1.0.16 && ./configure --prefix=$HOME/installed_libsodium && make && make install && cd .. + #- export PKG_CONFIG_PATH=$HOME/installed_libsodium/lib/pkgconfig:$PKG_CONFIG_PATH + #- export LD_LIBRARY_PATH=$HOME/installed_libsodium/lib:$LD_LIBRARY_PATH + - export SODIUM_BUILD_STATIC=yes - # Install OpenSSL on Windows - - if [ "${TRAVIS_OS_NAME}" = "windows" ]; then - curl -O http://slproweb.com/download/Win64OpenSSL-1_1_0k.exe; - cmd.exe /C 'Win64OpenSSL-1_1_0k.exe /SILENT /VERYSILENT /SP- /DIR="C:\OpenSSL"'; - export OPENSSL_LIB_DIR=/c/OpenSSL/lib; - export OPENSSL_INCLUDE_DIR=/c/OpenSSL/include; - export OPENSSL_DIR=/c/OpenSSL/bin; - fi + # Install OpenSSL on Windows + - if [ "${TRAVIS_OS_NAME}" = "windows" ]; then + curl -O http://slproweb.com/download/Win64OpenSSL-1_1_0L.exe; + cmd.exe /C 'Win64OpenSSL-1_1_0L.exe /SILENT /VERYSILENT /SP- /DIR="C:\OpenSSL"'; + export OPENSSL_LIB_DIR=/c/OpenSSL/lib; + export OPENSSL_INCLUDE_DIR=/c/OpenSSL/include; + export OPENSSL_DIR=/c/OpenSSL/bin; + fi script: - - cargo build - - cargo test --no-fail-fast --features miscreant + - cargo build + - cargo test --no-fail-fast --features miscreant diff --git a/Cargo.lock b/Cargo.lock index 74c52993..93f95dd2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,7 +2,7 @@ # It is not intended for manual editing. [[package]] name = "adler32" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -27,20 +27,27 @@ name = "ansi_term" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "arc-swap" -version = "0.4.1" +name = "anyhow" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "arrayvec" -version = "0.4.11" +name = "arc-swap" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "async-trait" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -48,33 +55,33 @@ name = "atty" version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "autocfg" -version = "0.1.5" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "backtrace" -version = "0.3.34" +version = "0.3.40" 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.60 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (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" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -85,9 +92,36 @@ dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "base64" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bindgen" +version = "0.51.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cexpr 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "clang-sys 0.28.1 (registry+https://github.com/rust-lang/crates.io-index)", + "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "shlex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "which 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "bitflags" -version = "1.1.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -95,7 +129,7 @@ name = "block-buffer" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -111,12 +145,17 @@ dependencies = [ [[package]] name = "block-padding" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "bumpalo" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "byte-tools" version = "0.3.1" @@ -138,16 +177,21 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "bytes" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "bzip2" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bzip2-sys 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -155,27 +199,34 @@ name = "bzip2-sys" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "c2-chacha" -version = "0.2.2" +version = "0.2.3" 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)", - "ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "cc" -version = "1.0.38" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "cexpr" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "cfg-if" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -185,15 +236,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "chrono" -version = "0.4.7" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "clang-sys" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "clap" version = "2.33.0" @@ -201,10 +261,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -213,7 +273,7 @@ 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)", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -231,37 +291,7 @@ name = "crc32fast" version = "1.2.0" 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 = "crossbeam-deque" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-epoch 0.7.2 (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 = "crossbeam-epoch" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-queue" -version = "0.1.2" -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)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -269,8 +299,8 @@ 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)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -288,7 +318,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "stream-cipher 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "stream-cipher 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -312,15 +342,6 @@ dependencies = [ "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "dns-parser" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "dtoa" version = "0.4.4" @@ -328,12 +349,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "enum-as-inner" -version = "0.2.1" +version = "0.3.0" 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.42 (registry+https://github.com/rust-lang/crates.io-index)", + "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -342,30 +364,42 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", - "humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "humantime 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)", - "regex 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "env_logger" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", + "humantime 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)", + "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "failure" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace 0.3.34 (registry+https://github.com/rust-lang/crates.io-index)", - "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "failure_derive" -version = "0.1.5" +version = "0.1.6" 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.42 (registry+https://github.com/rust-lang/crates.io-index)", - "synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -375,32 +409,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "filetime" -version = "0.2.6" +version = "0.2.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)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "flate2" -version = "0.2.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "miniz-sys 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "flate2" -version = "1.0.9" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "miniz_oxide_c_api 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "miniz_oxide 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -421,17 +447,12 @@ name = "foreign-types-shared" version = "0.1.1" source = "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)", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -442,41 +463,222 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "futures" -version = "0.1.28" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures-channel 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-executor 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-io 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-sink 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-task 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-util 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "futures-channel" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-sink 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "futures-channel-preview" +version = "0.3.0-alpha.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures-core-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-sink-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "futures-core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "futures-core-preview" +version = "0.3.0-alpha.19" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "futures-executor" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-task 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-util 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "futures-executor-preview" +version = "0.3.0-alpha.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures-core-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-util-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "futures-io" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "futures-io-preview" +version = "0.3.0-alpha.19" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "futures-macro" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "futures-preview" +version = "0.3.0-alpha.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures-channel-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-executor-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-io-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-sink-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-util-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "futures-sink" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "futures-sink-preview" +version = "0.3.0-alpha.19" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "futures-task" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "futures-util" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures-channel 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-io 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-macro 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-sink 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-task 0.3.1 (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)", + "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-nested 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "futures-util-preview" +version = "0.3.0-alpha.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures-channel-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-io-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-sink-preview 0.3.0-alpha.19 (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 = "generic-array" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "getrandom" -version = "0.1.8" +version = "0.1.13" 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.60 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "h2" -version = "0.1.26" +version = "0.2.0-alpha.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", - "indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-sink-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-util-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", + "indexmap 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)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "string 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-codec 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-sync 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "heck" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hermit-abi" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hkdf" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hmac" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -484,13 +686,13 @@ name = "hostname" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", "winutil 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "http" -version = "0.1.18" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", @@ -500,22 +702,12 @@ dependencies = [ [[package]] name = "humantime" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "idna" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-normalization 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "idna" version = "0.2.0" @@ -523,21 +715,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-normalization 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-normalization 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "indexmap" -version = "1.0.2" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "iovec" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -545,9 +739,9 @@ name = "ipconfig" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "socket2 0.3.10 (registry+https://github.com/rust-lang/crates.io-index)", + "socket2 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)", "widestring 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)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "winreg 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -556,14 +750,22 @@ name = "itoa" version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "js-sys" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "wasm-bindgen 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "json5" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "pest 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "pest 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "pest_derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -577,26 +779,39 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "libc" -version = "0.2.60" +version = "0.2.66" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "libsodium-ffi" -version = "0.1.17" +name = "libloading" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", - "flate2 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "libsodium-ffi" +version = "0.2.0" +source = "git+https://github.com/zonyitoo/libsodium-ffi#917a5c8de28c9304da58d24682c2e41d09939b7b" +dependencies = [ + "bindgen 0.51.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)", + "flate2 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", "tar 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)", "unwrap 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", - "zip 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "zip 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -606,11 +821,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "lock_api" -version = "0.1.5" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -618,7 +832,7 @@ 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)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -631,7 +845,7 @@ dependencies = [ [[package]] name = "maplit" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -639,6 +853,11 @@ name = "matches" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "maybe-uninit" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "md-5" version = "0.8.0" @@ -654,60 +873,30 @@ name = "memchr" version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "memoffset" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "mime" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unicase 2.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "miniz-sys" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "miniz_oxide" -version = "0.3.0" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "miniz_oxide_c_api" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", - "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "miniz_oxide 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "mio" -version = "0.6.19" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "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)", + "iovec 0.1.4 (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.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (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)", @@ -721,9 +910,9 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", "miow 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -731,9 +920,9 @@ 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.60 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -752,8 +941,8 @@ name = "miow" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "socket2 0.3.10 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "socket2 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -769,58 +958,54 @@ dependencies = [ "dbl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", "pmac 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "stream-cipher 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "stream-cipher 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "zeroize 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "msdos_time" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (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.60 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "nodrop" -version = "0.1.13" +name = "nom" +version = "4.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "num-integer" version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "num-traits" -version = "0.2.8" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "num_cpus" -version = "1.10.1" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "hermit-abi 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -830,61 +1015,56 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "openssl" -version = "0.10.24" +version = "0.10.26" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.48 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.53 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "openssl-sys" -version = "0.9.48" +version = "0.9.53" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", "vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "owning_ref" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "parking_lot" -version = "0.7.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lock_api 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "parking_lot_core" -version = "0.4.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "percent-encoding" -version = "1.0.1" +name = "peeking_take_while" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -894,7 +1074,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "pest" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "ucd-trie 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -905,35 +1085,63 @@ name = "pest_derive" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "pest 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pest_generator 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pest 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "pest_generator 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "pest_generator" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "pest 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pest_meta 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "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.42 (registry+https://github.com/rust-lang/crates.io-index)", + "pest 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "pest_meta 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "pest_meta" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "maplit 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pest 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "pest 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "pin-project" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "pin-project-internal 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pin-project-internal" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pin-project-lite" +version = "0.1.1" +source = "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 = "pkg-config" -version = "0.3.15" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -953,20 +1161,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "ppv-lite86" -version = "0.2.5" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "proc-macro-hack" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "proc-macro-nested" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "proc-macro2" -version = "0.4.30" +version = "1.0.6" 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)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "qrcode" -version = "0.10.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "checked_int_cast 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -979,87 +1202,39 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "quote" -version = "0.6.13" +version = "1.0.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)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rand" -version = "0.6.5" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_xorshift 0.1.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 = "rand" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "getrandom 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", "rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "rand_chacha" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "rand_chacha" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rand_core" -version = "0.3.1" +version = "0.5.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_core" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "getrandom 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_hc" -version = "0.1.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)", + "getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1067,63 +1242,7 @@ name = "rand_hc" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "rand_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_isaac" -version = "0.1.1" -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 = "rand_jitter" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (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.60 (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 = "rand_pcg" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_xorshift" -version = "0.1.1" -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 = "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)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1133,18 +1252,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "regex" -version = "1.2.1" +version = "1.3.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)", + "regex-syntax 0.6.12 (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" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1158,22 +1277,31 @@ dependencies = [ [[package]] name = "ring" -version = "0.14.6" +version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "spin 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "untrusted 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "web-sys 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rustc-demangle" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "rustc-hash" +version = "1.0.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 = "rustc_version" version = "0.2.3" @@ -1184,22 +1312,16 @@ dependencies = [ [[package]] name = "rustls" -version = "0.15.2" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)", - "sct 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "webpki 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ring 0.16.9 (registry+https://github.com/rust-lang/crates.io-index)", + "sct 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "webpki 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "scopeguard" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "scopeguard" version = "1.0.0" @@ -1207,11 +1329,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "sct" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)", - "untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ring 0.16.9 (registry+https://github.com/rust-lang/crates.io-index)", + "untrusted 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1229,31 +1351,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde" -version = "1.0.98" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde_derive 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_derive" -version = "1.0.98" +version = "1.0.103" 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.42 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_urlencoded" -version = "0.5.5" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", + "url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1269,57 +1391,50 @@ dependencies = [ [[package]] name = "shadowsocks-rust" -version = "1.7.2" +version = "1.8.0" dependencies = [ - "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "byte_string 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dns-parser 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "hkdf 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "json5 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "libsodium-ffi 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libsodium-ffi 0.2.0 (git+https://github.com/zonyitoo/libsodium-ffi)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "md-5 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "miscreant 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl 0.10.24 (registry+https://github.com/rust-lang/crates.io-index)", - "qrcode 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_urlencoded 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", - "spin 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl 0.10.26 (registry+https://github.com/rust-lang/crates.io-index)", + "qrcode 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ring 0.16.9 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_urlencoded 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-process 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-signal 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", - "trust-dns-resolver 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", - "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "trust-dns-resolver 0.18.0-alpha.1 (git+https://github.com/bluejekyll/trust-dns)", + "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", "url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "signal-hook" -version = "0.1.10" +name = "shlex" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "signal-hook-registry 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "signal-hook-registry" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "arc-swap 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "arc-swap 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1329,33 +1444,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "smallvec" -version = "0.6.10" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "smallvec" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "socket2" -version = "0.3.10" +version = "0.3.11" 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.60 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "spin" -version = "0.5.0" +name = "sourcefile" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "stable_deref_trait" -version = "1.1.1" +name = "spin" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "stream-cipher" -version = "0.3.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1381,28 +1504,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "subtle" -version = "2.1.1" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "syn" -version = "0.15.42" +version = "1.0.9" 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)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "synstructure" -version = "0.10.2" +version = "0.12.3" 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.42 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1410,8 +1533,8 @@ name = "tar" version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "filetime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "filetime 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "xattr 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1421,7 +1544,7 @@ name = "termcolor" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "wincolor 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1429,7 +1552,7 @@ name = "textwrap" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1437,7 +1560,7 @@ 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)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1445,308 +1568,244 @@ name = "time" version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio" -version = "0.1.22" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-current-thread 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-fs 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-sync 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-threadpool 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-udp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-uds 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (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.21 (registry+https://github.com/rust-lang/crates.io-index)", + "mio-named-pipes 0.1.6 (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.11.1 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-project-lite 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "signal-hook-registry 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-macros 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio-codec" -version = "0.1.1" +version = "0.2.0-alpha.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-current-thread" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-sink-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio-executor" -version = "0.1.8" +version = "0.2.0-alpha.6" 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)", - "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-fs" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-threadpool 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-util-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-sync 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio-io" -version = "0.1.12" +version = "0.2.0-alpha.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core-preview 0.3.0-alpha.19 (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)", + "pin-project 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "tokio-process" -version = "0.2.4" +name = "tokio-macros" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", - "mio-named-pipes 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-signal 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "tokio-reactor" -version = "0.1.9" +name = "tokio-net" +version = "0.2.0-alpha.6" 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)", - "futures 0.1.28 (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)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-util-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-sync 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-codec 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-sync 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tracing 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio-rustls" -version = "0.9.3" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", - "rustls 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "webpki 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-signal" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (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)", - "signal-hook 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rustls 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "webpki 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio-sync" -version = "0.1.6" +version = "0.2.0-alpha.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-tcp" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-threadpool" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-util-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio-timer" -version = "0.2.11" +version = "0.3.0-alpha.6" 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)", - "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-util-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-sync 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "tokio-udp" -version = "0.1.3" +name = "tracing" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tracing-attributes 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "tracing-core 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "tokio-uds" -version = "0.2.5" +name = "tracing-attributes" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (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)", - "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tracing-core" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "trust-dns-https" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" +version = "0.18.0-alpha.1" +source = "git+https://github.com/bluejekyll/trust-dns#b3e729374f13b467e9ba7dd23785aab99af62fd5" dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "data-encoding 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", - "h2 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", + "h2 0.2.0-alpha.3 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rustls 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-rustls 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "trust-dns-proto 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", - "trust-dns-rustls 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "rustls 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-net 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-rustls 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "trust-dns-proto 0.18.0-alpha.1 (git+https://github.com/bluejekyll/trust-dns)", + "trust-dns-rustls 0.18.0-alpha.1 (git+https://github.com/bluejekyll/trust-dns)", "typed-headers 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "webpki 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)", - "webpki-roots 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", + "webpki 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", + "webpki-roots 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "trust-dns-proto" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" +version = "0.18.0-alpha.1" +source = "git+https://github.com/bluejekyll/trust-dns#b3e729374f13b467e9ba7dd23785aab99af62fd5" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "enum-as-inner 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", - "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "async-trait 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", + "enum-as-inner 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", + "idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", - "socket2 0.3.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-udp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "socket2 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-net 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-sync 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-timer 0.3.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", + "url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "trust-dns-resolver" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" +version = "0.18.0-alpha.1" +source = "git+https://github.com/bluejekyll/trust-dns#b3e729374f13b467e9ba7dd23785aab99af62fd5" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", "ipconfig 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "resolv-conf 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustls 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "trust-dns-https 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "trust-dns-proto 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", - "trust-dns-rustls 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "webpki-roots 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustls 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-net 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", + "trust-dns-https 0.18.0-alpha.1 (git+https://github.com/bluejekyll/trust-dns)", + "trust-dns-proto 0.18.0-alpha.1 (git+https://github.com/bluejekyll/trust-dns)", + "trust-dns-rustls 0.18.0-alpha.1 (git+https://github.com/bluejekyll/trust-dns)", + "webpki-roots 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "trust-dns-rustls" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" +version = "0.18.0-alpha.1" +source = "git+https://github.com/bluejekyll/trust-dns#b3e729374f13b467e9ba7dd23785aab99af62fd5" dependencies = [ - "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rustls 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-rustls 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "trust-dns-proto 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", - "webpki 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rustls 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-net 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-rustls 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "trust-dns-proto 0.18.0-alpha.1 (git+https://github.com/bluejekyll/trust-dns)", + "webpki 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1756,14 +1815,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "chrono 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", - "mime 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", + "mime 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "typenum" -version = "1.10.0" +version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1771,14 +1830,6 @@ name = "ucd-trie" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "unicase" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "unicode-bidi" version = "0.3.4" @@ -1789,25 +1840,30 @@ dependencies = [ [[package]] name = "unicode-normalization" -version = "0.1.8" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "unicode-segmentation" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "unicode-width" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "unicode-xid" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "untrusted" -version = "0.6.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1815,16 +1871,6 @@ name = "unwrap" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "url" -version = "1.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "url" version = "2.1.0" @@ -1851,21 +1897,117 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "webpki" -version = "0.19.1" +name = "wasi" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "wasm-bindgen" +version = "0.2.55" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)", - "untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-macro 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bumpalo 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-shared 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-macro-support 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-backend 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-shared 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.55" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "wasm-bindgen-webidl" +version = "0.2.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "anyhow 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)", + "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-backend 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", + "weedle 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "web-sys" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "anyhow 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)", + "js-sys 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)", + "sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-webidl 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "webpki" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ring 0.16.9 (registry+https://github.com/rust-lang/crates.io-index)", + "untrusted 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "webpki-roots" -version = "0.16.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "webpki 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)", + "webpki 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "weedle" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "which" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1880,7 +2022,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "winapi" -version = "0.3.7" +version = "0.3.8" 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)", @@ -1902,7 +2044,7 @@ name = "winapi-util" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1912,10 +2054,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "wincolor" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1924,7 +2066,7 @@ name = "winreg" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1932,7 +2074,7 @@ name = "winutil" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1949,7 +2091,7 @@ name = "xattr" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1959,234 +2101,254 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "zip" -version = "0.2.8" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bzip2 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "flate2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "msdos_time 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "flate2 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", "podio 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [metadata] -"checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c" +"checksum adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2" "checksum aesni 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f70a6b5f971e473091ab7cfb5ffac6cde81666c4556751d8d5620ead8abf100" "checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" -"checksum arc-swap 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1507f9b80b3ef096751728cf3f43bb0111ec906e44f5d8587e02c10643b9a2cd" -"checksum arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d73f9beda665eaa98ab9e4f7442bd4e7de6652587de55b2525e52e29c1b0ba" +"checksum anyhow 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "9267dff192e68f3399525901e709a48c1d3982c9c072fa32f2127a0cb0babf14" +"checksum arc-swap 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d7b8a9123b8027467bce0099fe556c628a53c8d83df0507084c31e9ba2e39aff" +"checksum async-trait 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)" = "3495c8ae99b96d8a84050dee9d7b9d6425f9bfe84c2e3f800c2c2859457f8590" "checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90" -"checksum autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "22130e92352b948e7e82a49cdb0aa94f2211761117f29e052dd397c1ac33542b" -"checksum backtrace 0.3.34 (registry+https://github.com/rust-lang/crates.io-index)" = "b5164d292487f037ece34ec0de2fcede2faa162f085dd96d2385ab81b12765ba" -"checksum backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "82a830b4ef2d1124a711c71d263c5abdc710ef8e907bd508c88be475cebc422b" +"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" +"checksum backtrace 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)" = "924c76597f0d9ca25d762c25a4d369d51267536465dc5064bdf0eb073ed477ea" +"checksum backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6575f128516de27e3ce99689419835fce9643a9b215a14d2b5b685be018491" "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 base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" +"checksum bindgen 0.51.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ebd71393f1ec0509b553aa012b9b58e81dadbdff7130bd3b8cba576e69b32f75" +"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" "checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" "checksum block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774" -"checksum block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6d4dc3af3ee2e12f3e5d224e5e1e3d73668abbeb69e566d361f7d5563a4fdf09" +"checksum block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +"checksum bumpalo 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ad807f2fc2bf185eeb98ff3a901bd46dc5ad58163d0fa4577ba0d25674d71708" "checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" "checksum byte_string 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "11aade7a05aa8c3a351cedc44c3fc45806430543382fcc4743a9b757a2a0b4ed" "checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" "checksum bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" +"checksum bytes 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1c85319f157e4e26c703678e68e26ab71a46c0199286fa670b21cc9fec13d895" "checksum bzip2 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "42b7c3cbf0fa9c1b82308d57191728ca0256cb821220f4e2fd410a72ade26e3b" "checksum bzip2-sys 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6584aa36f5ad4c9247f5323b0a42f37802b37a836f0ad87084d7a33961abe25f" -"checksum c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7d64d04786e0f528460fc884753cf8dddcc466be308f6026f8e355c41a0e4101" -"checksum cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)" = "ce400c638d48ee0e9ab75aef7997609ec57367ccfe1463f21bf53c3eca67bf46" -"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" +"checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb" +"checksum cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)" = "aa87058dce70a3ff5621797f1506cb837edd02ac4c0ae642b4542dce802908b8" +"checksum cexpr 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "fce5b5fb86b0c57c20c834c1b412fd09c77c8a59b9473f86272709e78874cd1d" +"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" "checksum checked_int_cast 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17cc5e6b5ab06331c33589842070416baa137e8b0eb912b008cfd4a78ada7919" -"checksum chrono 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "77d81f58b7301084de3b958691458a53c3f7e0b1d702f77e550b6a88e3a88abe" +"checksum chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "31850b4a4d6bae316f7a09e691c944c28299298837edc0a03f755618c23cbc01" +"checksum clang-sys 0.28.1 (registry+https://github.com/rust-lang/crates.io-index)" = "81de550971c976f176130da4b2978d3b524eaa0fd9ac31f3ceb5ae1231fb4853" "checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" "checksum cmac 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6f4a435124bcc292eba031f1f725d7abacdaf13cbf9f935450e8c45aa9e96cad" "checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" -"checksum crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b18cd2e169ad86297e6bc0ad9aa679aee9daa4f19e8163860faf7c164e4f5a71" -"checksum crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fedcd6772e37f3da2a9af9bf12ebe046c0dfe657992377b4df982a2b54cd37a9" -"checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" "checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" "checksum crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" "checksum ctr 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "022cd691704491df67d25d006fe8eca083098253c4d43516c2206479c58c6736" "checksum data-encoding 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f4f47ca1860a761136924ddd2422ba77b2ea54fe8cc75b9040804a0d9d32ad97" "checksum dbl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "28dc203b75decc900220c4d9838e738d08413e663c26826ba92b669bed1d0795" "checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" -"checksum dns-parser 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c4d33be9473d06f75f58220f71f7a9317aca647dc061dbd3c361b0bef505fbea" "checksum dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ea57b42383d091c85abcc2706240b94ab2a8fa1fc81c10ff23c4de06e2a90b5e" -"checksum enum-as-inner 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3d58266c97445680766be408285e798d3401c6d4c378ec5552e78737e681e37d" +"checksum enum-as-inner 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "900a6c7fbe523f4c2884eaf26b57b81bb69b6810a01a236390a7ac021d09492e" "checksum env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3" -"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 env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +"checksum failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9" +"checksum failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0bc225b78e0391e4b8683440bf2e63c2deeeb2ce5189eab46e2b68c6d3725d08" "checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" -"checksum filetime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "450537dc346f0c4d738dda31e790da1da5d4bd12145aad4da0d03d713cb3794f" -"checksum flate2 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)" = "e6234dd4468ae5d1e2dbb06fe2b058696fdc50a339c68a393aefbf00bc81e423" -"checksum flate2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "550934ad4808d5d39365e5d61727309bf18b3b02c6c56b729cb92e7dd84bc3d8" +"checksum filetime 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1ff6d4dab0aa0c8e6346d46052e93b13a16cf847b54ed357087c35011048cc7d" +"checksum flate2 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6bd6d6f4752952feb71363cffc9ebac9411b75b87c6ab6058c40c8900cf43c0f" "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" "checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" "checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" -"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 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "45dc39533a6cae6da2b56da48edae506bb767ec07370f86f70fc062e9d435869" +"checksum futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b6f16056ecbb57525ff698bb955162d0cd03bee84e6241c27ff75c08d8ca5987" +"checksum futures-channel 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fcae98ca17d102fd8a3603727b9259fcf7fa4239b603d2142926189bc8999b86" +"checksum futures-channel-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)" = "d5e5f4df964fa9c1c2f8bddeb5c3611631cacd93baf810fc8bb2fb4b495c263a" +"checksum futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "79564c427afefab1dfb3298535b21eda083ef7935b4f0ecbfcb121f0aec10866" +"checksum futures-core-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)" = "b35b6263fb1ef523c3056565fa67b1d16f0a8604ff12b11b08c25f28a734c60a" +"checksum futures-executor 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1e274736563f686a837a0568b478bdabfeaec2dca794b5649b04e2fe1627c231" +"checksum futures-executor-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)" = "75236e88bd9fe88e5e8bfcd175b665d0528fe03ca4c5207fabc028c8f9d93e98" +"checksum futures-io 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e676577d229e70952ab25f3945795ba5b16d63ca794ca9d2c860e5595d20b5ff" +"checksum futures-io-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)" = "f4914ae450db1921a56c91bde97a27846287d062087d4a652efc09bb3a01ebda" +"checksum futures-macro 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "52e7c56c15537adb4f76d0b7a76ad131cb4d2f4f32d3b0bcabcbe1c7c5e87764" +"checksum futures-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)" = "3b1dce2a0267ada5c6ff75a8ba864b4e679a9e2aa44262af7a3b5516d530d76e" +"checksum futures-sink 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "171be33efae63c2d59e6dbba34186fe0d6394fb378069a76dfd80fdcffd43c16" +"checksum futures-sink-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)" = "86f148ef6b69f75bb610d4f9a2336d4fc88c4b5b67129d1a340dd0fd362efeec" +"checksum futures-task 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0bae52d6b29cf440e298856fec3965ee6fa71b06aa7495178615953fd669e5f9" +"checksum futures-util 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c0d66274fb76985d3c62c886d1da7ac4c0903a8c9f754e8fe0f35a6a6cc39e76" +"checksum futures-util-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)" = "5ce968633c17e5f97936bd2797b6e38fb56cf16a7422319f7ec2e30d3c470e8d" "checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" -"checksum getrandom 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "34f33de6f0ae7c9cb5e574502a562e2b512799e32abb801cd1e79ad952b62b49" -"checksum h2 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)" = "a5b34c246847f938a410a03c5458c7fee2274436675e76d8b903c08efc29c462" +"checksum getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e7db7ca94ed4cd01190ceee0d8a8052f08a247aa1b469a7f68c6a3b71afcf407" +"checksum glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" +"checksum h2 0.2.0-alpha.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0f107db1419ef8271686187b1a5d47c6431af4a7f4d98b495e7b7fc249bb0a78" +"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" +"checksum hermit-abi 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "307c3c9f937f38e3534b1d6447ecf090cafcc9744e4a6360e8b037b2cf5af120" +"checksum hkdf 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3fa08a006102488bd9cd5b8013aabe84955cf5ae22e304c2caf655b633aefae3" +"checksum hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695" "checksum hostname 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "21ceb46a83a85e824ef93669c8b390009623863b5c195d1ba747292c0c72f94e" -"checksum http 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "372bcb56f939e449117fb0869c2e8fd8753a8223d92a172c6e808cf123a5b6e4" -"checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114" -"checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" +"checksum http 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)" = "2790658cddc82e82b08e25176c431d7015a0adeb1718498715cbd20138a0bf68" +"checksum humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" "checksum idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" -"checksum indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7e81a7c05f79578dbc15793d8b619db9ba32b4577003ef3af1a91c416798c58d" -"checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" +"checksum indexmap 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712d7b3ea5827fcb9d4fda14bf4da5f136f0db2ae9c8f4bd4e2d1c6fde4e6db2" +"checksum iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" "checksum ipconfig 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aa79fa216fbe60834a9c0737d7fcd30425b32d1c58854663e24d4c4b328ed83f" "checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" +"checksum js-sys 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)" = "1c840fdb2167497b0bd0db43d6dfe61e91637fa72f9d061f8bd17ddc44ba6414" "checksum json5 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "85fb48cdfbe18a1ef5ce0a0edc30b8b8f61422f7073f709dd09311c2b3d2bba6" "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.60 (registry+https://github.com/rust-lang/crates.io-index)" = "d44e80633f007889c7eff624b709ab43c92d708caad982295768a7b13ca3b5eb" -"checksum libsodium-ffi 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "90066fb2807840eb1d43973563deb842cf9e8a1a6e7593d98170d25327749e15" +"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +"checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" +"checksum libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753" +"checksum libsodium-ffi 0.2.0 (git+https://github.com/zonyitoo/libsodium-ffi)" = "" "checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" -"checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" +"checksum lock_api 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e57b3997725d2b60dbec1297f6c2e2957cc383db1cebd6be812163f969c7d586" "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" "checksum lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" -"checksum maplit 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08cbb6b4fef96b6d77bfc40ec491b1690c779e77b05cd9f07f787ed376fd4c43" +"checksum maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" +"checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" "checksum md-5 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a18af3dcaf2b0219366cdb4e2af65a6101457b415c3d1a5c71dd9c2b7c77b9c8" "checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" -"checksum memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce6075db033bbbb7ee5a0bbd3a3186bbae616f57fb001c485c7ff77955f8177f" -"checksum mime 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)" = "3e27ca21f40a310bd06d9031785f4801710d566c184a6e15bad4f1d9b65f9425" -"checksum miniz-sys 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9e3ae51cea1576ceba0dde3d484d30e6e5b86dee0b2d412fe3a16a15c98202" -"checksum miniz_oxide 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c061edee74a88eb35d876ce88b94d77a0448a201de111c244b70d047f5820516" -"checksum miniz_oxide_c_api 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6c675792957b0d19933816c4e1d56663c341dd9bfa31cb2140ff2267c1d8ecf4" -"checksum mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)" = "83f51996a3ed004ef184e16818edc51fadffe8e7ca68be67f9dee67d84d0ff23" +"checksum mime 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "dd1d63acd1b78403cc0c325605908475dd9b9a3acbf65ed8bcab97e27014afcf" +"checksum miniz_oxide 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6f3f74f726ae935c3f514300cc6773a0c9492abc5e972d42ba0c0ebb88757625" +"checksum mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)" = "302dec22bcf6bae6dfb69c647187f4b4d0fb6f535521f7bc022430ce8e12008f" "checksum mio-named-pipes 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f5e374eff525ce1c5b7687c4cef63943e7686524a387933ad27ca7ec43779cb3" "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 miow 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "396aa0f2003d7df8395cb93e09871561ccc3e785f0acb369170e8cc74ddf9226" "checksum miscreant 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7e37d77fff73f19e198036d3ca2bdbf8d4bca650daeda979bae0fdd31fce11d9" -"checksum msdos_time 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "aad9dfe950c057b1bfe9c1f2aa51583a8468ef2a5baba2ebbe06d775efeb7729" "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 nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" "checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09" -"checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" -"checksum num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcef43580c035376c0705c42792c294b66974abbfd2789b511784023f71f3273" +"checksum num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c81ffc11c212fa327657cb19dd85eb7419e163b5b076bede2bdb5c974c07e4" +"checksum num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "76dac5ed2a876980778b8b85f75a71b6cbf0db0b1232ee12f826bccb00d09d72" "checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" -"checksum openssl 0.10.24 (registry+https://github.com/rust-lang/crates.io-index)" = "8152bb5a9b5b721538462336e3bef9a539f892715e5037fda0f984577311af15" -"checksum openssl-sys 0.9.48 (registry+https://github.com/rust-lang/crates.io-index)" = "b5ba300217253bcc5dc68bed23d782affa45000193866e025329aa8a7a9f05b8" -"checksum owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49a4b8ea2179e6a2e27411d3bca09ca6dd630821cf6894c6c7c8467a8ee7ef13" -"checksum parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab41b4aed082705d1056416ae4468b6ea99d52599ecf3169b00088d43113e337" -"checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9" -"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" +"checksum openssl 0.10.26 (registry+https://github.com/rust-lang/crates.io-index)" = "3a3cc5799d98e1088141b8e01ff760112bbd9f19d850c124500566ca6901a585" +"checksum openssl-sys 0.9.53 (registry+https://github.com/rust-lang/crates.io-index)" = "465d16ae7fc0e313318f7de5cecf57b2fbe7511fd213978b457e1c96ff46736f" +"checksum parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" +"checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" +"checksum peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" "checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" -"checksum pest 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "933085deae3f32071f135d799d75667b63c8dc1f4537159756e3d4ceab41868c" +"checksum pest 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7e4fb201c5c22a55d8b24fef95f78be52738e5e1361129be1b5e862ecdb6894a" "checksum pest_derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" -"checksum pest_generator 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "63120576c4efd69615b5537d3d052257328a4ca82876771d6944424ccfd9f646" -"checksum pest_meta 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f249ea6de7c7b7aba92b4ff4376a994c6dbd98fd2166c89d5c4947397ecb574d" -"checksum pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c1d2cfa5a714db3b5f24f0915e74fcdf91d09d496ba61329705dda7774d2af" +"checksum pest_generator 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7b9fcf299b5712d06ee128a556c94709aaa04512c4dffb8ead07c5c998447fc0" +"checksum pest_meta 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "df43fd99896fd72c485fe47542c7b500e4ac1e8700bf995544d1317a60ded547" +"checksum pin-project 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "94b90146c7216e4cb534069fb91366de4ea0ea353105ee45ed297e2d1619e469" +"checksum pin-project-internal 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "44ca92f893f0656d3cba8158dd0f2b99b94de256a4a54e870bd6922fcc6c8355" +"checksum pin-project-lite 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f0af6cbca0e6e3ce8692ee19fb8d734b641899e07b68eb73e9bbbd32f1703991" +"checksum pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587" +"checksum pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" "checksum pmac 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bd76d63e86aa67a4bd063079f0d93bb8730b036eea5697345f651e6874ea61e5" "checksum podio 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "780fb4b6698bbf9cf2444ea5d22411cef2953f0824b98f33cf454ec5615645bd" -"checksum ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e3cbf9f658cdb5000fcf6f362b8ea2ba154b9f146a61c7a20d647034c6b6561b" -"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" -"checksum qrcode 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "db39274031636ba52d4758837447a5c8d895cc729f27a8ec183e50a477b625de" +"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" +"checksum proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)" = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5" +"checksum proc-macro-nested 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "369a6ed065f249a159e06c45752c780bda2fb53c995718f9e484d08daa9eb42e" +"checksum proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27" +"checksum qrcode 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a0015bbb688c03b1ccc0b236fe1a1696d6481a309ca70941efb5e9417ae33de7" "checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" -"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" -"checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" -"checksum rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d47eab0e83d9693d40f825f86948aa16eff6750ead4bdffc4ab95b8b3a7f052c" -"checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" +"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" +"checksum rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412" "checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" -"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_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "615e683324e75af5d43d8f7a39ffe3ee4a9dc42c5c701167a71dc59c3a493aca" -"checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" +"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" "checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -"checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" -"checksum rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" -"checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" -"checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" -"checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" -"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 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 regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc220bd33bdce8f093101afe22a037b8eb0e5af33592e6a9caafff0d4cb81cbd" +"checksum regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716" "checksum resolv-conf 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b263b4aa1b5de9ffc0054a2386f96992058bb6870aab516f8cdeb8a667d56dcb" -"checksum ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)" = "426bc186e3e95cac1e4a4be125a4aca7e84c2d616ffc02244eef36e2a60a093c" -"checksum rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7f4dccf6f4891ebcc0c39f9b6eb1a83b9bf5d747cb439ec6fba4f3b977038af" +"checksum ring 0.16.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6747f8da1f2b1fabbee1aaa4eb8a11abf9adef0bf58a41cee45db5d59cecdfac" +"checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" +"checksum rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7540fc8b0c49f096ee9c961cda096467dce8084bec6bdca2fc83895fd9b28cb8" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -"checksum rustls 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f271e3552cd835fa28c541c34a7e8fdd8cdff09d77fe4eb8f6c42e87a11b096e" -"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" +"checksum rustls 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b25a18b1bf7387f0145e7f8324e700805aade3842dd3db2e74e4cdeb4677c09e" "checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" -"checksum sct 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f5adf8fbd58e1b1b52699dc8bed2630faecb6d8c7bee77d009d6bbe4af569b9" +"checksum sct 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)" = "7fe5626ac617da2f2d9c48af5515a21d5a480dbd151e01bb1c355e26a3e68113" -"checksum serde_derive 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)" = "01e69e1b8a631f245467ee275b8c757b818653c6d704cdbcaeb56b56767b529c" -"checksum serde_urlencoded 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "642dd69105886af2efd227f75a520ec9b44a820d65bc133a9131f7d229fd165a" +"checksum serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)" = "1217f97ab8e8904b57dd22eb61cde455fa7446a9c1cf43966066da047c1f3702" +"checksum serde_derive 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)" = "a8c6faef9a2e64b0064f48570289b4bf8823b7581f1d6157c1b52152306651d0" +"checksum serde_urlencoded 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97" "checksum sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "23962131a91661d643c98940b20fcaffe62d776a823247be80a48fcb8b6fce68" -"checksum signal-hook 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4f61c4d59f3aaa9f61bba6450a9b80ba48362fd7d651689e7a10c453b1f6dc68" -"checksum signal-hook-registry 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1797d48f38f91643908bb14e35e79928f9f4b3cefb2420a564dde0991b4358dc" +"checksum shlex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" +"checksum signal-hook-registry 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94f478ede9f64724c5d173d7bb56099ec3e2d9fc2774aac65d34b8b890405f41" "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" -"checksum smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7" -"checksum socket2 0.3.10 (registry+https://github.com/rust-lang/crates.io-index)" = "df028e0e632c2a1823d920ad74895e7f9128e6438cbc4bc6fd1f180e644767b9" -"checksum spin 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44363f6f51401c34e7be73db0db371c04705d35efbe9f7d6082e03a921a32c55" -"checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" -"checksum stream-cipher 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8861bc80f649f5b4c9bd38b696ae9af74499d479dbfb327f0607de6b326a36bc" +"checksum smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6" +"checksum smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ecf3b85f68e8abaa7555aa5abdb1153079387e60b718283d732f03897fcfc86" +"checksum socket2 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "e8b74de517221a2cb01a53349cf54182acdc31a074727d3079068448c0676d85" +"checksum sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4bf77cb82ba8453b42b6ae1d692e4cdc92f9a47beaf89a847c8be83f4e328ad3" +"checksum spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +"checksum stream-cipher 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8131256a5896cabcf5eb04f4d6dacbe1aefda854b0d9896e09cb58829ec5638c" "checksum string 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d24114bfcceb867ca7f71a0d3fe45d45619ec47a6fbfa98cb14e14250bfa5d6d" "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" "checksum subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" -"checksum subtle 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "01f40907d9ffc762709e4ff3eb4a6f6b41b650375a3f09ac92b641942b7fb082" -"checksum syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)" = "eadc09306ca51a40555dd6fc2b415538e9e18bc9f870e47b1a524a79fe2dcf5e" -"checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f" +"checksum subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c65d530b10ccaeac294f349038a597e435b18fb456aadd0840a623f83b9e941" +"checksum syn 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f89693ae015201f8de93fd96bde2d065f8bfc3f97ce006d5bc9f900b97c0c7c0" +"checksum synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" "checksum tar 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)" = "b3196bfbffbba3e57481b6ea32249fbaf590396a52505a2615adbb79d9d826d3" "checksum termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "96d6098003bde162e4277c70665bd87c326f5a0c3f3fbfb285787fa482d54e6e" "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" "checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" -"checksum tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)" = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6" -"checksum tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5c501eceaf96f0e1793cf26beb63da3d11c738c4a943fdf3746d81d64684c39f" -"checksum tokio-current-thread 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "d16217cad7f1b840c5a97dfb3c43b0c871fef423a6e8d2118c604e843662a443" -"checksum tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "0f27ee0e6db01c5f0b2973824547ce7e637b2ed79b891a9677b0de9bd532b6ac" -"checksum tokio-fs 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "3fe6dc22b08d6993916647d108a1a7d15b9cd29c4f4496c62b92c45b5041b7af" -"checksum tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "5090db468dad16e1a7a54c8c67280c5e4b544f3d3e018f0b913b400261f85926" -"checksum tokio-process 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afbd6ef1b8cc2bd2c2b580d882774d443ebb1c6ceefe35ba9ea4ab586c89dbe8" -"checksum tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6af16bfac7e112bea8b0442542161bfc41cbfa4466b580bdda7d18cb88b911ce" -"checksum tokio-rustls 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1600e90b2602df28ff54ae842519b408fbb25378c3c5aee1b795593e9263dc80" -"checksum tokio-signal 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "dd6dc5276ea05ce379a16de90083ec80836440d5ef8a6a39545a3207373b8296" -"checksum tokio-sync 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2162248ff317e2bc713b261f242b69dbb838b85248ed20bb21df56d60ea4cae7" -"checksum tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1d14b10654be682ac43efee27401d792507e30fd8d26389e1da3b185de2e4119" -"checksum tokio-threadpool 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "90ca01319dea1e376a001e8dc192d42ebde6dd532532a5bad988ac37db365b19" -"checksum tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "f2106812d500ed25a4f38235b9cae8f78a09edf43203e16e59c3b769a342a60e" -"checksum tokio-udp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "66268575b80f4a4a710ef83d087fdfeeabdce9b74c797535fbac18a2cb906e92" -"checksum tokio-uds 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "037ffc3ba0e12a0ab4aca92e5234e0dedeb48fddf6ccd260f1f150a36a9f2445" -"checksum trust-dns-https 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "616f94fe821de0b070e6f9952d0cfcf2fb85f337c8c974ad1f0dd567bb0bfa92" -"checksum trust-dns-proto 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5559ebdf6c2368ddd11e20b11d6bbaf9e46deb803acd7815e93f5a7b4a6d2901" -"checksum trust-dns-resolver 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6c9992e58dba365798803c0b91018ff6c8d3fc77e06977c4539af2a6bfe0a039" -"checksum trust-dns-rustls 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "83e0a8eb8c31a747f5307c0f604c956f1e915c599058370667af68c18f17706d" +"checksum tokio 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2e765bf9f550bd9b8a970633ca3b56b8120c4b6c5dcbe26a93744cb02fee4b17" +"checksum tokio-codec 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9f5d22fd1e84bd4045d28813491cb7d7caae34d45c80517c2213f09a85e8787a" +"checksum tokio-executor 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9ee9ceecf69145923834ea73f32ba40c790fd877b74a7817dd0b089f1eb9c7c8" +"checksum tokio-io 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)" = "112784d5543df30660b04a72ca423bfbd90e8bb32f94dcf610f15401218b22c5" +"checksum tokio-macros 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d5795a71419535c6dcecc9b6ca95bdd3c2d6142f7e8343d7beb9923f129aa87e" +"checksum tokio-net 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a441682cd32f3559383112c4a7f372f5c9fa1950c5cf8c8dd05274a2ce8c2654" +"checksum tokio-rustls 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "420448fb2a5fb489169ee30d1667d7182d7edc516a66a09f04cd2a6abf62134a" +"checksum tokio-sync 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4f1aaeb685540f7407ea0e27f1c9757d258c7c6bf4e3eb19da6fc59b747239d2" +"checksum tokio-timer 0.3.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b97c1587fe71018eb245a4a9daa13a5a3b681bbc1f7fdadfe24720e141472c13" +"checksum tracing 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ff4e4f59e752cb3beb5b61c6d5e11191c7946231ba84faec2902c9efdd8691c5" +"checksum tracing-attributes 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a4263b12c3d3c403274493eb805966093b53214124796552d674ca1dd5d27c2b" +"checksum tracing-core 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "bc913647c520c959b6d21e35ed8fa6984971deca9f0a2fcb8c51207e0c56af1d" +"checksum trust-dns-https 0.18.0-alpha.1 (git+https://github.com/bluejekyll/trust-dns)" = "" +"checksum trust-dns-proto 0.18.0-alpha.1 (git+https://github.com/bluejekyll/trust-dns)" = "" +"checksum trust-dns-resolver 0.18.0-alpha.1 (git+https://github.com/bluejekyll/trust-dns)" = "" +"checksum trust-dns-rustls 0.18.0-alpha.1 (git+https://github.com/bluejekyll/trust-dns)" = "" "checksum typed-headers 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bd6f5af532d859106afe9077c8f95bcaa09af272d5d9b338ec1ff05830b5803c" -"checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" +"checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" "checksum ucd-trie 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8f00ed7be0c1ff1e24f46c3d2af4859f7e863672ba3a6e92e7cff702bf9f06c2" -"checksum unicase 2.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a84e5511b2a947f3ae965dcb29b13b7b1691b6e7332cf5dbc1744138d5acb7f6" "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" -"checksum unicode-normalization 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "141339a08b982d942be2ca06ff8b076563cbe223d1befd5450716790d44e2426" -"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" -"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" -"checksum untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "55cd1f4b4e96b46aeb8d4855db4a7a9bd96eeeb5c6a1ab54593328761642ce2f" +"checksum unicode-normalization 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b561e267b2326bb4cebfc0ef9e68355c7abe6c6f522aeac2f5bf95d56c59bdcf" +"checksum unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" +"checksum unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7007dbd421b92cc6e28410fe7362e2e0a2503394908f417b68ec8d1c364c4e20" +"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +"checksum untrusted 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60369ef7a31de49bcb3f6ca728d4ba7300d9a1658f94c727d4cab8c8d9f4aece" "checksum unwrap 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e33648dd74328e622c7be51f3b40a303c63f93e6fa5f08778b6203a4c25c20f" -"checksum url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" "checksum url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "75b414f6c464c879d7f9babf951f23bc3743fb7313c081b2e6ca719067ea9d61" "checksum vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "33dd455d0f96e90a75803cfeb7f948768c08d70a6de9a8d2362461935698bf95" "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" "checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" -"checksum webpki 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4f7e1cd7900a3a6b65a3e8780c51a3e6b59c0e2c55c6dc69578c288d69f7d082" -"checksum webpki-roots 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c10fa4212003ba19a564f25cd8ab572c6791f99a03cc219c13ed35ccab00de0e" +"checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d" +"checksum wasm-bindgen 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)" = "29ae32af33bacd663a9a28241abecf01f2be64e6a185c6139b04f18b6385c5f2" +"checksum wasm-bindgen-backend 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)" = "1845584bd3593442dc0de6e6d9f84454a59a057722f36f005e44665d6ab19d85" +"checksum wasm-bindgen-macro 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)" = "87fcc747e6b73c93d22c947a6334644d22cfec5abd8b66238484dc2b0aeb9fe4" +"checksum wasm-bindgen-macro-support 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)" = "3dc4b3f2c4078c8c4a5f363b92fcf62604c5913cbd16c6ff5aaf0f74ec03f570" +"checksum wasm-bindgen-shared 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)" = "ca0b78d6d3be8589b95d1d49cdc0794728ca734adf36d7c9f07e6459508bb53d" +"checksum wasm-bindgen-webidl 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)" = "3126356474ceb717c8fb5549ae387c9fbf4872818454f4d87708bee997214bb5" +"checksum web-sys 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)" = "98405c0a2e722ed3db341b4c5b70eb9fe0021621f7350bab76df93b09b649bbf" +"checksum webpki 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d7e664e770ac0110e2384769bcc59ed19e329d81f555916a6e072714957b81b4" +"checksum webpki-roots 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)" = "91cd5736df7f12a964a5067a12c62fa38e1bd8080aff1f80bc29be7c80d19ab4" +"checksum weedle 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3bb43f70885151e629e2a19ce9e50bd730fd436cfd4b666894c9ce4de9141164" +"checksum which 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5475d47078209a02e60614f7ba5e645ef3ed60f771920ac1906d7c1cc65024c8" "checksum widestring 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "effc0e4ff8085673ea7b9b2e3c73f6bd4d118810c9009ed8f1e16bd96c331db6" "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 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" "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-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -"checksum wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "561ed901ae465d6185fa7864d63fbd5720d0ef718366c9a4dc83cf6170d7e9ba" +"checksum wincolor 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "96f5016b18804d24db43cebf3c77269e7569b8954a8464501c216cc5e070eaa9" "checksum winreg 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9" "checksum winutil 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7daf138b6b14196e3830a588acf1e86966c694d3e8fb026fb105b8b5dca07e6e" "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" "checksum xattr 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c" "checksum zeroize 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8ddfeb6eee2fb3b262ef6e0898a52b7563bb8e0d5955a313b3cf2f808246ea14" -"checksum zip 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "e7341988e4535c60882d5e5f0b7ad0a9a56b080ade8bdb5527cb512f7b2180e0" +"checksum zip 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3c21bb410afa2bd823a047f5bda3adb62f51074ac7e06263b2c97ecdd47e9fc6" diff --git a/Cargo.toml b/Cargo.toml index c35ed6e1..c69d53a7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "shadowsocks-rust" -version = "1.7.2" +version = "1.8.0" authors = ["Y. T. CHUNG "] description = "shadowsocks is a fast tunnel proxy that helps you bypass firewalls." repository = "https://github.com/zonyitoo/shadowsocks-rust" @@ -21,12 +21,12 @@ name = "ssserver" path = "src/bin/server.rs" [[bin]] -name = "ssurl" -path = "src/bin/ssurl.rs" +name = "sstunnel" +path = "src/bin/tunnel.rs" [[bin]] -name = "ssdns" -path = "src/bin/ssdns.rs" +name = "ssurl" +path = "src/bin/ssurl.rs" [profile.release] lto = true @@ -39,41 +39,36 @@ aes-cfb = ["openssl"] aes-ctr = ["openssl"] camellia-cfb = ["openssl"] single-threaded = [] +trust-dns = ["trust-dns-resolver"] [dependencies] log = "0.4" -byteorder = "1.3" rand = "0.7" time = "0.1" clap = "2.33" -env_logger = "0.6" +env_logger = "0.7" openssl = { version = "0.10", optional = true } libc = "0.2" -futures = "0.1" -tokio-io = "0.1" -tokio = "0.1" +tokio = { version = "0.2", features = ["full"] } +futures = "0.3" json5 = "0.2" -base64 = "0.10" -bytes = "0.4" -ring = "0.14" +base64 = "0.11" +bytes = "0.5" +byteorder = "1" +ring = "0.16" md-5 = "0.8" digest = "0.8" -typenum = "1.10" -qrcode = { version = "0.10", default-features = false } -serde_urlencoded = "0.5" +typenum = "1.11" +qrcode = { version = "0.11", default-features = false } +serde_urlencoded = "0.6" serde = { version = "1.0", features = ["derive"] } -url = "2.0" +url = "2.1" byte_string = "1.0" -libsodium-ffi = { version = "0.1", optional = true } +# libsodium-ffi = { version = "0.1", optional = true } +libsodium-ffi = { git = "https://github.com/zonyitoo/libsodium-ffi", optional = true } miscreant = { version = "0.4", optional = true } -lru-cache = "0.1" -dns-parser = "0.8" -trust-dns-resolver = { version = "0.11", features = ["dns-over-rustls", "dns-over-https-rustls"] } -spin = { version = "0.5", default-features = false } -tokio-process = "0.2" - -[target.'cfg(unix)'.dependencies] -tokio-signal = "0.2" - -[target.'cfg(windows)'.dependencies] -tokio-signal = "0.2" +# trust-dns-resolver = { version = "0.11", features = ["dns-over-rustls", "dns-over-https-rustls"] } +trust-dns-resolver = { git = "https://github.com/bluejekyll/trust-dns", features = ["dns-over-rustls", "dns-over-https-rustls"], optional = true } +hkdf = "0.8" +hmac = "0.7" +sha-1 = "0.8" \ No newline at end of file diff --git a/src/bin/local.rs b/src/bin/local.rs index 80bbf3dc..3a2d519d 100644 --- a/src/bin/local.rs +++ b/src/bin/local.rs @@ -4,22 +4,23 @@ //! or you could specify a configuration file. The format of configuration file is defined //! in mod `config`. -use std::{io::Result as IoResult, net::SocketAddr, process}; - use clap::{App, Arg}; -use futures::{future::Either, Future}; +use futures::{ + future::{self, Either}, + FutureExt, +}; use log::{debug, error, info}; -#[cfg(feature = "single-threaded")] -use tokio::runtime::current_thread::Runtime; -#[cfg(not(feature = "single-threaded"))] -use tokio::runtime::Runtime; +use std::net::SocketAddr; +use tokio; use shadowsocks::{plugin::PluginConfig, run_local, Config, ConfigType, Mode, ServerAddr, ServerConfig}; mod logging; mod monitor; -fn main() { +#[cfg_attr(feature = "single-threaded", tokio::main(basic_scheduler))] +#[cfg_attr(not(feature = "single-threaded"), tokio::main)] +async fn main() { let matches = App::new("shadowsocks") .version(shadowsocks::VERSION) .about("A fast tunnel proxy that helps you bypass firewalls.") @@ -207,47 +208,11 @@ fn main() { debug!("Config: {:?}", config); - match launch_server(config) { - Ok(()) => {} - Err(err) => { - error!("Server exited unexpectly with error: {}", err); - process::exit(1); - } - } -} - -#[cfg(feature = "single-threaded")] -fn launch_server(config: Config) -> IoResult<()> { - let mut runtime = Runtime::new().expect("Creating runtime"); - let abort_signal = monitor::create_signal_monitor(); - let result = runtime.block_on(run_local(config).select2(abort_signal)); - - match result { + match future::select(run_local(config).boxed(), abort_signal.boxed()).await { // Server future resolved without an error. This should never happen. - Ok(Either::A(_)) => panic!("Server exited unexpectly"), - // Server future resolved with an error. - Err(Either::A((err, _))) => Err(err), + Either::Left(_) => panic!("Server exited unexpectly"), // The abort signal future resolved. Means we should just exit. - Ok(Either::B(..)) | Err(Either::B(..)) => Ok(()), - } -} - -#[cfg(not(feature = "single-threaded"))] -fn launch_server(config: Config) -> IoResult<()> { - let mut runtime = Runtime::new().expect("Creating runtime"); - - let abort_signal = monitor::create_signal_monitor(); - let result = runtime.block_on(run_local(config).select2(abort_signal)); - - runtime.shutdown_now().wait().unwrap(); - - match result { - // Server future resolved without an error. This should never happen. - Ok(Either::A(_)) => panic!("Server exited unexpectly"), - // Server future resolved with an error. - Err(Either::A((err, _))) => Err(err), - // The abort signal future resolved. Means we should just exit. - Ok(Either::B(..)) | Err(Either::B(..)) => Ok(()), + Either::Right(_) => (), } } diff --git a/src/bin/logging/mod.rs b/src/bin/logging/mod.rs index be411d33..f91315e1 100644 --- a/src/bin/logging/mod.rs +++ b/src/bin/logging/mod.rs @@ -1,9 +1,8 @@ +use std::env; +use std::io::{self, Write}; + use env_logger::{fmt::Formatter, Builder}; use log::{LevelFilter, Record}; -use std::{ - env, - io::{self, Write}, -}; pub fn init(without_time: bool, debug_level: u64, bin_name: &str) { let mut log_builder = Builder::new(); diff --git a/src/bin/monitor/other.rs b/src/bin/monitor/other.rs index 158065df..4e1d332b 100644 --- a/src/bin/monitor/other.rs +++ b/src/bin/monitor/other.rs @@ -1,8 +1,8 @@ -use futures::{self, Future}; +use futures; use std::io; -pub fn create_signal_monitor() -> impl Future + Send { +pub async fn create_signal_monitor() -> io::Result<()> { // FIXME: What can I do ... // Blocks forever - futures::empty::<(), io::Error>() + futures::empty::<(), io::Error>().await } diff --git a/src/bin/monitor/unix.rs b/src/bin/monitor/unix.rs index 51f1a256..e5e3d3de 100644 --- a/src/bin/monitor/unix.rs +++ b/src/bin/monitor/unix.rs @@ -1,34 +1,19 @@ -use futures::{Future, Stream}; -use libc; -use log::{error, info}; +use futures::future::{self, Either, FutureExt}; +use log::info; use std::io; -use tokio_signal::unix::Signal; +use tokio::signal::unix::{signal, SignalKind}; -pub fn create_signal_monitor() -> impl Future + Send { +pub async fn create_signal_monitor() -> io::Result<()> { // Future resolving to two signal streams. Can fail if setting up signal monitoring fails - let signals_future = Signal::new(libc::SIGTERM).join(Signal::new(libc::SIGINT)); + let mut sigterm = signal(SignalKind::terminate())?; + let mut sigint = signal(SignalKind::interrupt())?; - signals_future - // The future resolved into our two streams of signals - .and_then(|(sigterms, sigints)| { - // Stream of all signals we care about - let signals = sigterms.select(sigints); - // Take only the first signal in the stream and log that it was triggered - signals.take(1).for_each(|signal| { - let signal_name = match signal { - libc::SIGTERM => "SIGTERM", - libc::SIGINT => "SIGINT", - _ => unreachable!(), - }; - info!("Received {}, exiting", signal_name); - Ok(()) - }).map_err(|error| { - error!("Error while listening on process signals: {:?}", error); - error - }) - }) - .map_err(|error| { - error!("Failed to set up process signal monitoring: {:?}", error); - error - }) + let signal_name = match future::select(sigterm.recv().boxed(), sigint.recv().boxed()).await { + Either::Left(..) => "SIGTERM", + Either::Right(..) => "SIGINT", + }; + + info!("Received {}, exiting", signal_name); + + Ok(()) } diff --git a/src/bin/monitor/windows.rs b/src/bin/monitor/windows.rs index c9d4ea4c..d36e2e0d 100644 --- a/src/bin/monitor/windows.rs +++ b/src/bin/monitor/windows.rs @@ -1,35 +1,19 @@ -use futures::{self, Future, Stream}; -use log::{error, info}; +use futures::future::{self, Either, FutureExt}; +use futures::StreamExt; +use log::info; use std::io; -use tokio_signal::windows::Event; +use tokio::signal::{ctrl_c, windows::ctrl_break}; -pub fn create_signal_monitor() -> impl Future + Send { - // Must be wrapped with a future, because when `Event` is being created, it will spawn - // a task into the current executor. So if there is no current running executor, thread - // will panic. - let events = futures::lazy(|| { - Event::ctrl_c() - .join(Event::ctrl_break()) - .and_then(|(c_signals, break_signals)| { - // Stream of all signals we care about - let signals = c_signals.select(break_signals); - // Take only the first signal in the stream and log that it was triggered - signals - .take(1) - .for_each(|()| { - info!("Received shutdown signal, exiting"); - Ok(()) - }) - .map_err(|error| { - error!("Error while listening on process signals: {:?}", error); - error - }) - }) - .map_err(|error| { - error!("Failed to set up process signal monitoring: {:?}", error); - error - }) - }); +pub async fn create_signal_monitor() -> io::Result<()> { + let cc = ctrl_c(); + let cb = ctrl_break()?; - events + let signal_name = match future::select(cc.boxed(), cb.into_future().boxed()).await { + Either::Left(..) => "CTRL-C", + Either::Right(..) => "CTRL-BREAK", + }; + + info!("Received {}, exiting", signal_name); + + Ok(()) } diff --git a/src/bin/server.rs b/src/bin/server.rs index a6876059..108c970f 100644 --- a/src/bin/server.rs +++ b/src/bin/server.rs @@ -7,22 +7,22 @@ //! *It should be notice that the extented configuration file is not suitable for the server //! side.* -use std::{io::Result as IoResult, process}; - use clap::{App, Arg}; -use futures::{future::Either, Future}; +use futures::{ + future::{self, Either}, + FutureExt, +}; use log::{debug, error, info}; -#[cfg(feature = "single-threaded")] -use tokio::runtime::current_thread::Runtime; -#[cfg(not(feature = "single-threaded"))] -use tokio::runtime::Runtime; +use tokio; use shadowsocks::{plugin::PluginConfig, run_server, Config, ConfigType, Mode, ServerAddr, ServerConfig}; mod logging; mod monitor; -fn main() { +#[cfg_attr(feature = "single-threaded", tokio::main(basic_scheduler))] +#[cfg_attr(not(feature = "single-threaded"), tokio::main)] +async fn main() { let matches = App::new("shadowsocks") .version(shadowsocks::VERSION) .about("A fast tunnel proxy that helps you bypass firewalls.") @@ -191,47 +191,11 @@ fn main() { debug!("Config: {:?}", config); - match launch_server(config) { - Ok(()) => {} - Err(err) => { - error!("Server exited unexpectly with error: {}", err); - process::exit(1); - } - } -} - -#[cfg(not(feature = "single-threaded"))] -fn launch_server(config: Config) -> IoResult<()> { - let mut runtime = Runtime::new().expect("Creating runtime"); - let abort_signal = monitor::create_signal_monitor(); - let result = runtime.block_on(run_server(config).select2(abort_signal)); - - runtime.shutdown_now().wait().unwrap(); - - match result { + match future::select(run_server(config).boxed(), abort_signal.boxed()).await { // Server future resolved without an error. This should never happen. - Ok(Either::A(_)) => panic!("Server exited unexpectly"), - // Server future resolved with an error. - Err(Either::A((err, _))) => Err(err), + Either::Left(_) => panic!("Server exited unexpectly"), // The abort signal future resolved. Means we should just exit. - Ok(Either::B(..)) | Err(Either::B(..)) => Ok(()), - } -} - -#[cfg(feature = "single-threaded")] -fn launch_server(config: Config) -> IoResult<()> { - let mut runtime = Runtime::new().expect("Creating runtime"); - - let abort_signal = monitor::create_signal_monitor(); - let result = runtime.block_on(run_server(config).select2(abort_signal)); - - match result { - // Server future resolved without an error. This should never happen. - Ok(Either::A(_)) => panic!("Server exited unexpectly"), - // Server future resolved with an error. - Err(Either::A((err, _))) => Err(err), - // The abort signal future resolved. Means we should just exit. - Ok(Either::B(..)) | Err(Either::B(..)) => Ok(()), + Either::Right(_) => (), } } diff --git a/src/bin/ssdns.rs b/src/bin/tunnel.rs similarity index 57% rename from src/bin/ssdns.rs rename to src/bin/tunnel.rs index a6f2fce0..a0733744 100644 --- a/src/bin/ssdns.rs +++ b/src/bin/tunnel.rs @@ -1,26 +1,39 @@ -//! DNS over shadowsocks - -use std::net::SocketAddr; +//! This is a binary running in the local environment +//! +//! You have to provide all needed configuration attributes via command line parameters, +//! or you could specify a configuration file. The format of configuration file is defined +//! in mod `config`. use clap::{App, Arg}; -use futures::Future; +use futures::{ + future::{self, Either}, + FutureExt, +}; use log::{debug, error, info}; -use tokio::runtime::Runtime; +use std::net::SocketAddr; +use tokio; -use shadowsocks::{run_dns, Config, ConfigType, ServerAddr, ServerConfig}; +use shadowsocks::plugin::PluginConfig; +use shadowsocks::relay::socks5::Address; +use shadowsocks::{run_local, Config, ConfigType, Mode, ServerAddr, ServerConfig}; mod logging; +mod monitor; -fn main() { - let matches = App::new("ssdns") +#[cfg_attr(feature = "single-threaded", tokio::main(basic_scheduler))] +#[cfg_attr(not(feature = "single-threaded"), tokio::main)] +async fn main() { + let matches = App::new("shadowsocks") .version(shadowsocks::VERSION) - .about("A DNS proxy that helps you bypass firewalls.") + .about("A fast tunnel proxy that helps you bypass firewalls.") .arg( Arg::with_name("VERBOSE") .short("v") .multiple(true) .help("Set the level of debug"), ) + .arg(Arg::with_name("UDP_ONLY").short("u").help("Server mode UDP_ONLY")) + .arg(Arg::with_name("TCP_AND_UDP").short("U").help("Server mode TCP_AND_UDP")) .arg( Arg::with_name("CONFIG") .short("c") @@ -42,6 +55,13 @@ fn main() { .takes_value(true) .help("Local address, listen only to this address if specified"), ) + .arg( + Arg::with_name("FORWARD_ADDR") + .short("f") + .long("foward-addr") + .takes_value(true) + .help("Forward address, forward to this address"), + ) .arg( Arg::with_name("PASSWORD") .short("k") @@ -56,6 +76,18 @@ fn main() { .takes_value(true) .help("Encryption method"), ) + .arg( + Arg::with_name("PLUGIN") + .long("plugin") + .takes_value(true) + .help("Enable SIP003 plugin"), + ) + .arg( + Arg::with_name("PLUGIN_OPT") + .long("plugin-opts") + .takes_value(true) + .help("Set SIP003 plugin options"), + ) .arg( Arg::with_name("LOG_WITHOUT_TIME") .long("log-without-time") @@ -68,17 +100,17 @@ fn main() { .help("Server address in SIP002 URL"), ) .arg( - Arg::with_name("REMOTE_DNS") - .long("remote-dns") - .takes_value(true) - .help("Remote DNS server, default is 8.8.8.8:53"), + Arg::with_name("NO_DELAY") + .long("no-delay") + .takes_value(false) + .help("Set no-delay option for socket"), ) .get_matches(); let without_time = matches.is_present("LOG_WITHOUT_TIME"); let debug_level = matches.occurrences_of("VERBOSE"); - logging::init(without_time, debug_level, "ssdns"); + logging::init(without_time, debug_level, "sslocal"); let mut has_provided_config = false; @@ -153,22 +185,49 @@ fn main() { return; } - if let Some(dns) = matches.value_of("REMOTE_DNS") { - let dns_addr = dns - .parse::() - .expect("`remote-dns` is not a valid SocketAddr, must be IP:Port"); - config.remote_dns = Some(dns_addr); + if let Some(url) = matches.value_of("FORWARD_ADDR") { + let forward_addr = url.parse::
().expect("Failed to parse `url`"); + + config.forward = Some(forward_addr); } - info!("ShadowSocks DNS {}", shadowsocks::VERSION); + if matches.is_present("UDP_ONLY") { + if config.mode.enable_tcp() { + config.mode = Mode::TcpAndUdp; + } else { + config.mode = Mode::UdpOnly; + } + } + + if matches.is_present("TCP_AND_UDP") { + config.mode = Mode::TcpAndUdp; + } + + if matches.is_present("NO_DELAY") { + config.no_delay = true; + } + + if let Some(p) = matches.value_of("PLUGIN") { + let plugin = PluginConfig { + plugin: p.to_owned(), + plugin_opt: matches.value_of("PLUGIN_OPT").map(ToOwned::to_owned), + }; + + // Overrides config in file + for svr in config.server.iter_mut() { + svr.set_plugin(plugin.clone()); + } + }; + + info!("ShadowSocks {}", shadowsocks::VERSION); debug!("Config: {:?}", config); - let mut runtime = Runtime::new().expect("Creating runtime"); - - let result = runtime.block_on(run_dns(config)); - - runtime.shutdown_now().wait().unwrap(); - - panic!("Server exited unexpectly with result: {:?}", result); + let abort_signal = monitor::create_signal_monitor(); + match future::select(run_local(config).boxed(), abort_signal.boxed()).await { + // Server future resolved without an error. This should never happen. + Either::Left(_) => panic!("Server exited unexpectly"), + // The abort signal future resolved. Means we should just exit. + Either::Right(_) => (), + } } diff --git a/src/config.rs b/src/config.rs index 905dd01b..f78cdaa9 100644 --- a/src/config.rs +++ b/src/config.rs @@ -41,32 +41,33 @@ //! //! These defined server will be used with a load balancing algorithm. -use std::{ - collections::HashSet, - convert::From, - default::Default, - error, - fmt::{self, Debug, Display, Formatter}, - fs::OpenOptions, - io::Read, - net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}, - option::Option, - path::Path, - str::FromStr, - string::ToString, - time::Duration, -}; +use std::collections::HashSet; +use std::convert::From; +use std::default::Default; +use std::error; +use std::fmt::{self, Debug, Display, Formatter}; +use std::fs::OpenOptions; +use std::io::Read; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; +use std::option::Option; +use std::path::Path; +use std::str::FromStr; +use std::string::ToString; +use std::time::Duration; use base64::{decode_config, encode_config, URL_SAFE_NO_PAD}; use bytes::Bytes; use json5; -use log::{error, trace}; +use log::error; use serde::{Deserialize, Serialize}; use serde_urlencoded; +#[cfg(feature = "trust-dns")] use trust_dns_resolver::config::{NameServerConfigGroup, ResolverConfig}; use url::{self, Url}; -use crate::{crypto::cipher::CipherType, plugin::PluginConfig}; +use crate::crypto::cipher::CipherType; +use crate::plugin::PluginConfig; +use crate::relay::socks5::Address; #[derive(Serialize, Deserialize, Debug, Default)] struct SSConfig { @@ -97,8 +98,6 @@ struct SSConfig { #[serde(skip_serializing_if = "Option::is_none")] dns: Option, #[serde(skip_serializing_if = "Option::is_none")] - remote_dns: Option, - #[serde(skip_serializing_if = "Option::is_none")] mode: Option, #[serde(skip_serializing_if = "Option::is_none")] no_delay: Option, @@ -220,7 +219,6 @@ impl ServerConfig { plugin: Option, ) -> ServerConfig { let enc_key = method.bytes_to_key(pwd.as_bytes()); - trace!("Initialize config with pwd: {:?}, key: {:?}", pwd, enc_key); ServerConfig { addr, password: pwd, @@ -461,9 +459,9 @@ impl error::Error for UrlParseError { } } - fn cause(&self) -> Option<&error::Error> { + fn cause(&self) -> Option<&dyn error::Error> { match *self { - UrlParseError::ParseError(ref err) => Some(err as &error::Error), + UrlParseError::ParseError(ref err) => Some(err as &dyn error::Error), UrlParseError::InvalidScheme => None, UrlParseError::InvalidUserInfo => None, UrlParseError::MissingHost => None, @@ -540,9 +538,9 @@ impl FromStr for Mode { pub struct Config { pub server: Vec, pub local: Option, + pub forward: Option
, pub forbidden_ip: HashSet, pub dns: Option, - pub remote_dns: Option, pub mode: Mode, pub no_delay: bool, pub manager_address: Option, @@ -600,9 +598,9 @@ impl Config { Config { server: Vec::new(), local: None, + forward: None, forbidden_ip: HashSet::new(), dns: None, - remote_dns: None, mode: Mode::TcpOnly, no_delay: false, manager_address: None, @@ -761,20 +759,6 @@ impl Config { // DNS nconfig.dns = config.dns; - if let Some(rdns) = config.remote_dns { - match rdns.parse::() { - Ok(r) => nconfig.remote_dns = Some(r), - Err(..) => { - let e = Error::new( - ErrorKind::Malformed, - "malformed `remote_dns`, which must be a valid SocketAddr", - None, - ); - return Err(e); - } - } - } - // Mode if let Some(m) = config.mode { match m.parse::() { @@ -810,6 +794,7 @@ impl Config { Config::load_from_str(&content[..], config_type) } + #[cfg(feature = "trust-dns")] pub fn get_dns_config(&self) -> Option { self.dns.as_ref().and_then(|ds| { match &ds[..] { @@ -843,11 +828,14 @@ impl Config { }) } - pub fn get_remote_dns(&self) -> SocketAddr { - match self.remote_dns { - None => SocketAddr::from(SocketAddrV4::new(Ipv4Addr::new(8, 8, 8, 8), 53)), - Some(ip) => ip, + /// Check if there are any plugin are enabled with servers + pub fn has_server_plugins(&self) -> bool { + for server in &self.server { + if server.plugin().is_some() { + return true; + } } + false } } @@ -922,10 +910,6 @@ impl fmt::Display for Config { jconf.dns = Some(dns.to_string()); } - if let Some(ref remote_dns) = self.remote_dns { - jconf.remote_dns = Some(remote_dns.to_string()); - } - write!(f, "{}", json5::to_string(&jconf).unwrap()) } } diff --git a/src/context.rs b/src/context.rs index 3e4fdded..69c6f6d9 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,59 +1,93 @@ //! Shadowsocks Server Context -use std::{ - net::SocketAddr, - sync::{Arc, Mutex, MutexGuard}, - time::Instant, -}; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; -use lru_cache::LruCache; +#[cfg(futures = "trust-dns")] use trust_dns_resolver::AsyncResolver; -use crate::{config::Config, relay::dns_resolver::create_resolver}; +use crate::config::Config; -type DnsQueryCache = LruCache; +#[cfg(futures = "trust-dns")] +use crate::relay::dns_resolver::create_resolver; #[derive(Clone)] -pub struct Context { - config: Config, +pub struct SharedServerState { + #[cfg(futures = "trust-dns")] dns_resolver: Arc, - dns_query_cache: Option>>, + server_running: Arc, } +impl SharedServerState { + #[allow(unused_variables)] + pub fn new(config: &Config) -> SharedServerState { + SharedServerState { + #[cfg(futures = "trust-dns")] + dns_resolver: Arc::new(create_resolver(config.get_dns_config())), + server_running: Arc::new(AtomicBool::new(true)), + } + } + + /// Check if the server is still in running state + pub fn server_running(&self) -> bool { + self.server_running.load(Ordering::Acquire) + } + + /// Stops the server, kills all detached running tasks + pub fn server_stopped(&self) { + self.server_running.store(false, Ordering::Release) + } + + /// Get the global shared resolver + #[cfg(futures = "trust-dns")] + pub fn dns_resolver(&self) -> &AsyncResolver { + &*self.dns_resolver + } +} + +/// Shared basic configuration for the whole server +pub struct Context { + config: Config, + server_state: SharedServerState, +} + +/// Unique context thw whole server pub type SharedContext = Arc; impl Context { - pub fn new(config: Config) -> Context { - let resolver = create_resolver(config.get_dns_config()); - Context { - config, - dns_resolver: Arc::new(resolver), - dns_query_cache: None, - } + pub fn new(config: Config, server_state: SharedServerState) -> Context { + Context { config, server_state } } - pub fn new_dns(config: Config) -> Context { - let resolver = create_resolver(config.get_dns_config()); - Context { - config, - dns_resolver: Arc::new(resolver), - dns_query_cache: Some(Arc::new(Mutex::new(LruCache::new(1024)))), - } + pub fn new_shared(config: Config, server_state: SharedServerState) -> SharedContext { + SharedContext::new(Context::new(config, server_state)) } + /// Config for TCP server pub fn config(&self) -> &Config { &self.config } + /// Mutable Config for TCP server + /// + /// NOTE: Only for launching plugins pub fn config_mut(&mut self) -> &mut Config { &mut self.config } + /// Get the global shared resolver + #[cfg(futures = "trust-dns")] pub fn dns_resolver(&self) -> &AsyncResolver { - &*self.dns_resolver + self.server_state.dns_resolver() } - pub fn dns_query_cache(&self) -> MutexGuard { - self.dns_query_cache.as_ref().unwrap().lock().unwrap() + /// Check if the server is still in running state + pub fn server_running(&self) -> bool { + self.server_state.server_running() + } + + /// Stops the server, kills all detached running tasks + pub fn server_stopped(&self) { + self.server_state.server_stopped() } } diff --git a/src/crypto/aead.rs b/src/crypto/aead.rs index f3c6c60b..6d68efa2 100644 --- a/src/crypto/aead.rs +++ b/src/crypto/aead.rs @@ -9,7 +9,8 @@ use crate::crypto::siv::MiscreantCipher; use crate::crypto::sodium::SodiumAeadCipher; use bytes::{Bytes, BytesMut}; -use ring::{digest::SHA1, hkdf, hmac::SigningKey}; +use hkdf::Hkdf; +use sha1::Sha1; /// Encryptor API for AEAD ciphers pub trait AeadEncryptor { @@ -110,14 +111,14 @@ const SUBKEY_INFO: &[u8] = b"ss-subkey"; pub fn make_skey(t: CipherType, key: &[u8], salt: &[u8]) -> Bytes { assert!(t.category() == CipherCategory::Aead); - let salt = SigningKey::new(&SHA1, salt); + let hkdf = Hkdf::::new(Some(salt), key); let mut skey = BytesMut::with_capacity(key.len()); unsafe { skey.set_len(key.len()); } - hkdf::extract_and_expand(&salt, key, SUBKEY_INFO, &mut skey); + hkdf.expand(SUBKEY_INFO, &mut skey).unwrap(); skey.freeze() } diff --git a/src/crypto/digest.rs b/src/crypto/digest.rs index 18fd27e1..d981af1a 100644 --- a/src/crypto/digest.rs +++ b/src/crypto/digest.rs @@ -1,7 +1,7 @@ //! Message digest algorithm use md5::Md5; -use ring::digest::{Context, SHA1, SHA1_OUTPUT_LEN}; +use sha1::Sha1; use bytes::BufMut; @@ -32,14 +32,14 @@ pub enum DigestType { pub fn with_type(t: DigestType) -> DigestVariant { match t { DigestType::Md5 => DigestVariant::Md5(Md5::default()), - DigestType::Sha1 | DigestType::Sha => DigestVariant::Sha1(Context::new(&SHA1)), + DigestType::Sha1 | DigestType::Sha => DigestVariant::Sha1(Sha1::default()), } } /// Variant of supported digest pub enum DigestVariant { Md5(Md5), - Sha1(Context), + Sha1(Sha1), } impl Digest for DigestVariant { @@ -48,7 +48,7 @@ impl Digest for DigestVariant { match *self { DigestVariant::Md5(ref mut d) => d.input(data), - DigestVariant::Sha1(ref mut d) => d.update(data), + DigestVariant::Sha1(ref mut d) => d.input(data), } } @@ -57,7 +57,7 @@ impl Digest for DigestVariant { match *self { DigestVariant::Md5(ref d) => buf.put(&*d.clone().result()), - DigestVariant::Sha1(ref d) => buf.put(d.clone().finish().as_ref()), + DigestVariant::Sha1(ref d) => buf.put(&*d.clone().result()), } } @@ -67,14 +67,14 @@ impl Digest for DigestVariant { match *self { DigestVariant::Md5(_) => ::OutputSize::to_usize(), - DigestVariant::Sha1(_) => SHA1_OUTPUT_LEN, + DigestVariant::Sha1(_) => ::OutputSize::to_usize(), } } fn reset(&mut self) { match *self { DigestVariant::Md5(ref mut d) => d.clone_from(&Md5::default()), - DigestVariant::Sha1(ref mut d) => d.clone_from(&Context::new(&SHA1)), + DigestVariant::Sha1(ref mut d) => d.clone_from(&Sha1::default()), } } } diff --git a/src/crypto/dummy.rs b/src/crypto/dummy.rs index a8687a8e..e380cb65 100644 --- a/src/crypto/dummy.rs +++ b/src/crypto/dummy.rs @@ -10,12 +10,12 @@ use bytes::BufMut; pub struct DummyCipher; impl StreamCipher for DummyCipher { - fn update(&mut self, data: &[u8], out: &mut BufMut) -> CipherResult<()> { + fn update(&mut self, data: &[u8], out: &mut dyn BufMut) -> CipherResult<()> { out.put_slice(data); Ok(()) } - fn finalize(&mut self, _: &mut BufMut) -> CipherResult<()> { + fn finalize(&mut self, _: &mut dyn BufMut) -> CipherResult<()> { Ok(()) } diff --git a/src/crypto/openssl.rs b/src/crypto/openssl.rs index 0b3897cd..eb908392 100644 --- a/src/crypto/openssl.rs +++ b/src/crypto/openssl.rs @@ -109,7 +109,7 @@ impl OpenSSLCrypto { } /// Update data - pub fn update(&mut self, data: &[u8], out: &mut BufMut) -> CipherResult<()> { + pub fn update(&mut self, data: &[u8], out: &mut dyn BufMut) -> CipherResult<()> { let least_reserved = data.len() + self.cipher.block_size(); let mut buf = BytesMut::with_capacity(least_reserved); // NOTE: len() is 0 now! unsafe { @@ -122,7 +122,7 @@ impl OpenSSLCrypto { } /// Generate the final block - pub fn finalize(&mut self, out: &mut BufMut) -> CipherResult<()> { + pub fn finalize(&mut self, out: &mut dyn BufMut) -> CipherResult<()> { let least_reserved = self.cipher.block_size(); let mut buf = BytesMut::with_capacity(least_reserved); // NOTE: len() is 0 now! unsafe { @@ -185,11 +185,11 @@ impl OpenSSLCipher { unsafe impl Send for OpenSSLCipher {} impl StreamCipher for OpenSSLCipher { - fn update(&mut self, data: &[u8], out: &mut BufMut) -> CipherResult<()> { + fn update(&mut self, data: &[u8], out: &mut dyn BufMut) -> CipherResult<()> { self.worker.update(data, out) } - fn finalize(&mut self, out: &mut BufMut) -> CipherResult<()> { + fn finalize(&mut self, out: &mut dyn BufMut) -> CipherResult<()> { self.worker.finalize(out) } diff --git a/src/crypto/rc4_md5.rs b/src/crypto/rc4_md5.rs index 7367fd8f..b5833cef 100644 --- a/src/crypto/rc4_md5.rs +++ b/src/crypto/rc4_md5.rs @@ -28,11 +28,11 @@ impl Rc4Md5Cipher { } impl StreamCipher for Rc4Md5Cipher { - fn update(&mut self, data: &[u8], out: &mut BufMut) -> CipherResult<()> { + fn update(&mut self, data: &[u8], out: &mut dyn BufMut) -> CipherResult<()> { self.crypto.update(data, out) } - fn finalize(&mut self, out: &mut BufMut) -> CipherResult<()> { + fn finalize(&mut self, out: &mut dyn BufMut) -> CipherResult<()> { self.crypto.finalize(out) } diff --git a/src/crypto/ring.rs b/src/crypto/ring.rs index db4a0734..a3671b28 100644 --- a/src/crypto/ring.rs +++ b/src/crypto/ring.rs @@ -1,9 +1,11 @@ //! Cipher defined with Ring -use std::{mem, ptr}; - -use ring::aead::{ - open_in_place, seal_in_place, Aad, Nonce, OpeningKey, SealingKey, AES_128_GCM, AES_256_GCM, CHACHA20_POLY1305, +use ring::{ + aead::{ + Aad, Algorithm, Nonce, NonceSequence, OpeningKey, SealingKey, UnboundKey, AES_128_GCM, AES_256_GCM, + CHACHA20_POLY1305, NONCE_LEN, + }, + error::Unspecified, }; use crate::crypto::{ @@ -13,13 +15,33 @@ use crate::crypto::{ }; use byte_string::ByteStr; -use bytes::{BufMut, Bytes, BytesMut}; +use bytes::{BufMut, BytesMut}; use log::error; /// AEAD ciphers provided by Ring pub enum RingAeadCryptoVariant { - Seal(SealingKey, Bytes), - Open(OpeningKey, Bytes), + Seal(SealingKey), + Open(OpeningKey), +} + +pub struct RingAeadNonceSequence { + nonce: [u8; NONCE_LEN], +} + +impl RingAeadNonceSequence { + fn new() -> RingAeadNonceSequence { + RingAeadNonceSequence { + nonce: [0u8; NONCE_LEN], + } + } +} + +impl NonceSequence for RingAeadNonceSequence { + fn advance(&mut self) -> Result { + let nonce = Nonce::assume_unique_for_key(self.nonce); + increase_nonce(&mut self.nonce); + Ok(nonce) + } } /// AEAD Cipher context @@ -28,8 +50,6 @@ pub enum RingAeadCryptoVariant { pub struct RingAeadCipher { cipher: RingAeadCryptoVariant, cipher_type: CipherType, - key: Bytes, - nonce: BytesMut, } impl RingAeadCipher { @@ -37,57 +57,34 @@ impl RingAeadCipher { pub fn new(t: CipherType, key: &[u8], salt: &[u8], is_seal: bool) -> RingAeadCipher { // TODO: Check if salt is duplicated - let nonce_size = t.iv_size(); - let mut nonce = BytesMut::with_capacity(nonce_size); - unsafe { - nonce.set_len(nonce_size); - ptr::write_bytes(nonce.as_mut_ptr(), 0, nonce_size); - } + // Nonce is 12 bytes + assert_eq!(t.iv_size(), NONCE_LEN); let skey = make_skey(t, key, salt); - let cipher = RingAeadCipher::new_variant(t, &skey, &nonce, is_seal); - RingAeadCipher { - cipher, - cipher_type: t, - key: skey, - nonce, - } + let cipher = RingAeadCipher::new_variant(t, &skey, is_seal); + RingAeadCipher { cipher, cipher_type: t } } - fn new_variant(t: CipherType, key: &[u8], nonce: &[u8], is_seal: bool) -> RingAeadCryptoVariant { - macro_rules! seal_or_open { - ($item:ident, $key:ident, $crypt:ident) => { - RingAeadCryptoVariant::$item($key::new(&$crypt, key).unwrap(), Bytes::from(nonce)) - }; - ($crypt:ident) => { - if is_seal { - seal_or_open!(Seal, SealingKey, $crypt) - } else { - seal_or_open!(Open, OpeningKey, $crypt) - } - }; - } - + fn new_variant(t: CipherType, key: &[u8], is_seal: bool) -> RingAeadCryptoVariant { match t { - CipherType::Aes128Gcm => seal_or_open!(AES_128_GCM), - CipherType::Aes256Gcm => seal_or_open!(AES_256_GCM), - CipherType::ChaCha20IetfPoly1305 => seal_or_open!(CHACHA20_POLY1305), + CipherType::Aes128Gcm => RingAeadCipher::new_crypt(&AES_128_GCM, key, is_seal), + CipherType::Aes256Gcm => RingAeadCipher::new_crypt(&AES_256_GCM, key, is_seal), + CipherType::ChaCha20IetfPoly1305 => RingAeadCipher::new_crypt(&CHACHA20_POLY1305, key, is_seal), _ => panic!("unsupported cipher in ring {:?}", t), } } - fn increase_nonce(&mut self) { - increase_nonce(&mut self.nonce); - } + #[inline] + fn new_crypt(algorithm: &'static Algorithm, key: &[u8], is_seal: bool) -> RingAeadCryptoVariant { + use ring::aead::BoundKey; - fn reset(&mut self) { - self.increase_nonce(); - let is_seal = match self.cipher { - RingAeadCryptoVariant::Seal(..) => true, - RingAeadCryptoVariant::Open(..) => false, - }; - let var = RingAeadCipher::new_variant(self.cipher_type, &self.key, &self.nonce, is_seal); - mem::replace(&mut self.cipher, var); + let unbound_key = UnboundKey::new(algorithm, key).unwrap(); + + if is_seal { + RingAeadCryptoVariant::Seal(SealingKey::new(unbound_key, RingAeadNonceSequence::new())) + } else { + RingAeadCryptoVariant::Open(OpeningKey::new(unbound_key, RingAeadNonceSequence::new())) + } } } @@ -99,26 +96,14 @@ impl AeadEncryptor for RingAeadCipher { let mut buf = BytesMut::with_capacity(output.len()); buf.put_slice(input); - unsafe { - buf.set_len(output.len()); - } - if let RingAeadCryptoVariant::Seal(ref key, ref nonce) = self.cipher { - seal_in_place( - key, - Nonce::try_assume_unique_for_key(nonce).expect("AEAD cipher nonce length not match"), - Aad::empty(), - &mut buf, - tag_len, - ) - .unwrap(); + if let RingAeadCryptoVariant::Seal(ref mut key) = self.cipher { + key.seal_in_place_append_tag(Aad::empty(), &mut buf).unwrap(); } else { unreachable!("encrypt is called on a non-seal cipher"); } - output.copy_from_slice(&buf); - - self.reset(); + output.copy_from_slice(&buf[..buf_len]); } } @@ -130,36 +115,25 @@ impl AeadDecryptor for RingAeadCipher { let mut buf = BytesMut::with_capacity(input.len()); buf.put_slice(input); - let r = if let RingAeadCryptoVariant::Open(ref key, ref nonce) = self.cipher { - match open_in_place( - key, - Nonce::try_assume_unique_for_key(nonce).expect("AEAD cipher nonce length not match"), - Aad::empty(), - 0, - &mut buf, - ) { + if let RingAeadCryptoVariant::Open(ref mut key) = self.cipher { + match key.open_in_place(Aad::empty(), &mut buf) { Ok(obuf) => { output.copy_from_slice(obuf); Ok(()) } - Err(err) => { + Err(..) => { error!( - "AEAD decrypt failed, nonce={:?}, input={:?}, tag={:?}, err: {:?}", - ByteStr::new(nonce), - ByteStr::new(&input[..tag_len]), - ByteStr::new(&input[tag_len..]), - err + "AEAD decrypt failed, input={:?}, tag={:?}, opening: {:?}", + ByteStr::new(&input[..output.len()]), + ByteStr::new(&input[output.len()..]), + key, ); Err(Error::AeadDecryptFailed) } } } else { unreachable!("decrypt is called on a non-open cipher"); - }; - - self.reset(); - - r + } } } diff --git a/src/crypto/sodium.rs b/src/crypto/sodium.rs index 79bebb7c..2f32e8af 100644 --- a/src/crypto/sodium.rs +++ b/src/crypto/sodium.rs @@ -1,9 +1,6 @@ //! Cipher defined with libsodium -use std::{ - ptr, - sync::{Once, ONCE_INIT}, -}; +use std::{ptr, sync::Once}; use bytes::{BufMut, Bytes, BytesMut}; @@ -20,7 +17,7 @@ use crate::crypto::{ AeadDecryptor, AeadEncryptor, CipherResult, CipherType, StreamCipher, }; -static SODIUM_INIT_FLAG: Once = ONCE_INIT; +static SODIUM_INIT_FLAG: Once = Once::new(); /// Cipher provided by `libsodium` pub struct SodiumStreamCipher { @@ -56,7 +53,7 @@ impl SodiumStreamCipher { self.counter % SODIUM_BLOCK_SIZE } - fn process(&mut self, data: &[u8], out: &mut BufMut) -> CipherResult<()> { + fn process(&mut self, data: &[u8], out: &mut dyn BufMut) -> CipherResult<()> { let padding = self.padding_len(); let mut plain_text = vec![0u8; data.len() + padding]; @@ -88,12 +85,12 @@ fn crypto_stream_xor_ic( data: &[u8], out: &mut B, ) -> CipherResult<()> { - assert!(data.len() <= unsafe { out.bytes_mut().len() }); + assert!(data.len() <= out.bytes_mut().len()); let ret = unsafe { match t { CipherType::ChaCha20 => crypto_stream_chacha20_xor_ic( - out.bytes_mut().as_mut_ptr(), + out.bytes_mut().as_mut_ptr() as *mut u8, data.as_ptr(), data.len() as c_ulonglong, iv.as_ptr(), @@ -101,7 +98,7 @@ fn crypto_stream_xor_ic( key.as_ptr(), ), CipherType::ChaCha20Ietf => crypto_stream_chacha20_ietf_xor_ic( - out.bytes_mut().as_mut_ptr(), + out.bytes_mut().as_mut_ptr() as *mut u8, data.as_ptr(), data.len() as c_ulonglong, iv.as_ptr(), @@ -109,7 +106,7 @@ fn crypto_stream_xor_ic( key.as_ptr(), ), CipherType::Salsa20 => crypto_stream_salsa20_xor_ic( - out.bytes_mut().as_mut_ptr(), + out.bytes_mut().as_mut_ptr() as *mut u8, data.as_ptr(), data.len() as c_ulonglong, iv.as_ptr(), @@ -117,7 +114,7 @@ fn crypto_stream_xor_ic( key.as_ptr(), ), CipherType::XSalsa20 => crypto_stream_xsalsa20_xor_ic( - out.bytes_mut().as_mut_ptr(), + out.bytes_mut().as_mut_ptr() as *mut u8, data.as_ptr(), data.len() as c_ulonglong, iv.as_ptr(), @@ -140,11 +137,11 @@ fn crypto_stream_xor_ic( } impl StreamCipher for SodiumStreamCipher { - fn update(&mut self, data: &[u8], out: &mut BufMut) -> CipherResult<()> { + fn update(&mut self, data: &[u8], out: &mut dyn BufMut) -> CipherResult<()> { self.process(data, out) } - fn finalize(&mut self, _: &mut BufMut) -> CipherResult<()> { + fn finalize(&mut self, _: &mut dyn BufMut) -> CipherResult<()> { Ok(()) } diff --git a/src/crypto/stream.rs b/src/crypto/stream.rs index ebf6b8fc..2ef979bb 100644 --- a/src/crypto/stream.rs +++ b/src/crypto/stream.rs @@ -18,8 +18,8 @@ use bytes::BufMut; /// The `update` method could be called multiple times, and the `finalize` method will /// encrypt the last block pub trait StreamCipher { - fn update(&mut self, data: &[u8], out: &mut BufMut) -> CipherResult<()>; - fn finalize(&mut self, out: &mut BufMut) -> CipherResult<()>; + fn update(&mut self, data: &[u8], out: &mut dyn BufMut) -> CipherResult<()>; + fn finalize(&mut self, out: &mut dyn BufMut) -> CipherResult<()>; fn buffer_size(&self, data: &[u8]) -> usize; } diff --git a/src/crypto/table.rs b/src/crypto/table.rs index eae0ce68..ff182eab 100644 --- a/src/crypto/table.rs +++ b/src/crypto/table.rs @@ -56,7 +56,7 @@ impl TableCipher { } } - fn process(&mut self, data: &[u8], out: &mut BufMut) -> CipherResult<()> { + fn process(&mut self, data: &[u8], out: &mut dyn BufMut) -> CipherResult<()> { let mut buf = BytesMut::with_capacity(self.buffer_size(data)); unsafe { buf.set_len(self.buffer_size(data)); // Set length @@ -70,11 +70,11 @@ impl TableCipher { } impl StreamCipher for TableCipher { - fn update(&mut self, data: &[u8], out: &mut BufMut) -> CipherResult<()> { + fn update(&mut self, data: &[u8], out: &mut dyn BufMut) -> CipherResult<()> { self.process(data, out) } - fn finalize(&mut self, _: &mut BufMut) -> CipherResult<()> { + fn finalize(&mut self, _: &mut dyn BufMut) -> CipherResult<()> { Ok(()) } diff --git a/src/lib.rs b/src/lib.rs index d4500541..c35f5534 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -75,9 +75,11 @@ pub const VERSION: &str = env!("CARGO_PKG_VERSION"); pub use self::{ config::{ClientConfig, Config, ConfigType, Mode, ServerAddr, ServerConfig}, - relay::{dns::run as run_dns, local::run as run_local, server::run as run_server, tcprelay::client::Socks5Client}, + relay::{local::run as run_local, server::run as run_server, tcprelay::client::Socks5Client}, }; +// relay::{dns::run as run_dns}, + pub mod config; mod context; pub mod crypto; diff --git a/src/plugin/mod.rs b/src/plugin/mod.rs index db96b6f1..7ecd36b5 100644 --- a/src/plugin/mod.rs +++ b/src/plugin/mod.rs @@ -13,13 +13,13 @@ //! ``` use crate::config::{Config, ServerAddr}; -use futures::{stream::futures_unordered, Future, Stream}; +use futures::stream::{FuturesUnordered, StreamExt}; use log::{error, info}; use std::{ io, net::{IpAddr, Ipv4Addr, SocketAddr, TcpListener}, }; -use tokio_process::{Child, CommandExt}; +use tokio::process::Child; mod obfs_proxy; mod ss_plugin; @@ -38,71 +38,78 @@ pub enum PluginMode { Client, } -/// Launch plugins in config. Returns a future that completes when any plugin terminates -/// or there were an error in watching the subprocess. Returns `None` if no plugins -/// were launched. -pub fn launch_plugins( - config: &mut Config, - mode: PluginMode, -) -> io::Result>> { - let mut plugins = Vec::new(); +/// Started plugins' subprocesses carrier +pub struct Plugins { + plugins: FuturesUnordered, +} - for svr in &mut config.server { - let mut svr_addr_opt = None; +impl Plugins { + /// Launch plugins in configuration. + /// + /// Will modify servers' listen addresses to plugins' listen addresses. + pub fn launch_plugins(config: &mut Config, mode: PluginMode) -> io::Result { + let plugins = FuturesUnordered::new(); - if let Some(c) = svr.plugin() { - let loop_ip = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)); - let local_addr = SocketAddr::new(loop_ip, get_local_port()?); + for svr in &mut config.server { + let mut svr_addr_opt = None; - let svr_addr = match start_plugin(c, svr.addr(), &local_addr, mode) { - Err(err) => { - error!("Failed to start plugin \"{}\", err: {}", c.plugin, err); - return Err(err); + if let Some(c) = svr.plugin() { + let loop_ip = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)); + let local_addr = SocketAddr::new(loop_ip, get_local_port()?); + + let svr_addr = match start_plugin(c, svr.addr(), &local_addr, mode) { + Err(err) => { + error!("Failed to start plugin \"{}\", err: {}", c.plugin, err); + return Err(err); + } + Ok(process) => { + let svr_addr = ServerAddr::SocketAddr(local_addr); + plugins.push(process); + + // Replace addr with plugin + svr_addr + } + }; + + match mode { + PluginMode::Client => info!("Started plugin \"{}\" on {} <-> {}", c.plugin, local_addr, svr.addr()), + PluginMode::Server => info!("Started plugin \"{}\" on {} <-> {}", c.plugin, svr.addr(), local_addr), } - Ok(process) => { - let svr_addr = ServerAddr::SocketAddr(local_addr); - plugins.push(process); - // Replace addr with plugin - svr_addr - } - }; - - match mode { - PluginMode::Client => info!("Started plugin \"{}\" on {} <-> {}", c.plugin, local_addr, svr.addr()), - PluginMode::Server => info!("Started plugin \"{}\" on {} <-> {}", c.plugin, svr.addr(), local_addr), + svr_addr_opt = Some(svr_addr); // Fuck borrow checker } - svr_addr_opt = Some(svr_addr); // Fuck borrow checker + if let Some(svr_addr) = svr_addr_opt { + svr.set_plugin_addr(svr_addr); + } } - if let Some(svr_addr) = svr_addr_opt { - svr.set_plugin_addr(svr_addr); + if plugins.is_empty() { + panic!("Didn't find any plugins to start"); } + + Ok(Plugins { plugins }) } - if plugins.is_empty() { - Ok(None) - } else { + /// Returns a future that completes when any plugin terminates or there were an error in watching the subprocess. + pub async fn into_future(self) -> io::Result<()> { // Turn the vector of `Child` futures into a single future that // completes with an error if any of them exits or waiting for it // fails. When this future completes, the remaining `Child`ren will be // dropped and as a result the rest of the plugins will be killed // automatically. - let plugins_future = - futures_unordered(plugins) - .into_future() - .then(|first_plugin_result| match first_plugin_result { - Ok((first_plugin_exit_status, _)) => { - let msg = format!("Plugin exited unexpectedly with {}", first_plugin_exit_status.unwrap()); - Err(io::Error::new(io::ErrorKind::Other, msg)) - } - Err((first_plugin_error, _)) => { - error!("Error while waiting for plugin subprocess: {}", first_plugin_error); - Err(first_plugin_error) - } - }); - Ok(Some(plugins_future)) + + match self.plugins.into_future().await { + (Some(Ok(first_plugin_exit_status)), _) => { + let msg = format!("Plugin exited unexpectedly with {}", first_plugin_exit_status); + Err(io::Error::new(io::ErrorKind::Other, msg)) + } + (Some(Err(first_plugin_error)), _) => { + error!("Error while waiting for plugin subprocess: {}", first_plugin_error); + Err(first_plugin_error) + } + _ => unreachable!(), + } } } @@ -112,7 +119,7 @@ fn start_plugin(plugin: &PluginConfig, remote: &ServerAddr, local: &SocketAddr, } else { ss_plugin::plugin_cmd(plugin, remote, local, mode) }; - cmd.spawn_async() + cmd.spawn() } fn get_local_port() -> io::Result { diff --git a/src/plugin/obfs_proxy.rs b/src/plugin/obfs_proxy.rs index bf984b80..098d230a 100644 --- a/src/plugin/obfs_proxy.rs +++ b/src/plugin/obfs_proxy.rs @@ -1,9 +1,7 @@ use super::{PluginConfig, PluginMode}; use crate::config::ServerAddr; -use std::{ - net::SocketAddr, - process::{Command, Stdio}, -}; +use std::{net::SocketAddr, process::Stdio}; +use tokio::process::Command; /// For obfsproxy, we use standalone mode for now. /// Managed mode needs to use SOCKS5 proxy as forwarder, which is not supported diff --git a/src/plugin/ss_plugin.rs b/src/plugin/ss_plugin.rs index 00d7c9ea..156a2337 100644 --- a/src/plugin/ss_plugin.rs +++ b/src/plugin/ss_plugin.rs @@ -1,10 +1,8 @@ use super::{PluginConfig, PluginMode}; use crate::config::ServerAddr; use log::trace; -use std::{ - net::SocketAddr, - process::{Command, Stdio}, -}; +use std::{net::SocketAddr, process::Stdio}; +use tokio::process::Command; pub fn plugin_cmd(plugin: &PluginConfig, remote: &ServerAddr, local: &SocketAddr, _mode: PluginMode) -> Command { trace!("Start plugin \"{:?}\" remote: {}, local: {}", plugin, remote, local); diff --git a/src/relay/dns.rs b/src/relay/dns.rs deleted file mode 100644 index f7bb3ab2..00000000 --- a/src/relay/dns.rs +++ /dev/null @@ -1,16 +0,0 @@ -//! DNS relay - -use std::io; - -use futures::{self, Future}; - -use crate::{ - config::Config, - context::{Context, SharedContext}, - relay::udprelay::dns::run as run_udp, -}; - -/// DNS Relay server running under local environment. -pub fn run(config: Config) -> impl Future + Send { - futures::lazy(move || run_udp(SharedContext::new(Context::new_dns(config)))) -} diff --git a/src/relay/dns_resolver/mod.rs b/src/relay/dns_resolver/mod.rs new file mode 100644 index 00000000..cd57e76a --- /dev/null +++ b/src/relay/dns_resolver/mod.rs @@ -0,0 +1,6 @@ +//! Asynchronous DNS resolver + +mod trust_dns_resolver; + +pub use self::trust_dns_resolver::create_resolver; +pub use self::trust_dns_resolver::resolve; diff --git a/src/relay/dns_resolver/tokio_dns_resolver.rs b/src/relay/dns_resolver/tokio_dns_resolver.rs new file mode 100644 index 00000000..7ffeb197 --- /dev/null +++ b/src/relay/dns_resolver/tokio_dns_resolver.rs @@ -0,0 +1,77 @@ +use std::io::{self, ErrorKind}; +use std::net::SocketAddr; +use std::net::ToSocketAddrs; + +use log::{debug, error}; + +use crate::context::SharedContext; + +/// Resolve address to IP +#[cfg(not(feature = "single-threaded"))] +pub async fn resolve( + context: SharedContext, + addr: &str, + port: u16, + check_forbidden: bool, +) -> io::Result> { + use tokio::task::spawn_blocking; + + let addr = addr.to_owned(); + spawn_blocking(move || { + let mut vaddr = Vec::new(); + for addr in (addr.as_str(), port).to_socket_addrs()? { + let ip = addr.ip(); + + if check_forbidden { + let forbidden_ip = &context.config().forbidden_ip; + if forbidden_ip.contains(&ip) { + debug!("Resolved {} => {}, which is skipped by forbidden_ip", addr, ip); + continue; + } + } + vaddr.push(addr); + } + + if vaddr.is_empty() { + error!("Failed to resolve {}:{}, all IPs are filtered", addr, port); + let err = io::Error::new(ErrorKind::Other, "resolved to empty address, all IPs are filtered"); + Err(err) + } else { + debug!("Resolved {}:{} => {:?}", addr, port, vaddr); + Ok(vaddr) + } + }) + .await? +} + +/// Resolve address to IP +#[cfg(feature = "single-threaded")] +pub async fn resolve( + context: SharedContext, + addr: &str, + port: u16, + check_forbidden: bool, +) -> io::Result> { + let mut vaddr = Vec::new(); + for addr in (addr, port).to_socket_addrs()? { + let ip = addr.ip(); + + if check_forbidden { + let forbidden_ip = &context.config().forbidden_ip; + if forbidden_ip.contains(&ip) { + debug!("Resolved {} => {}, which is skipped by forbidden_ip", addr, ip); + continue; + } + } + vaddr.push(addr); + } + + if vaddr.is_empty() { + error!("Failed to resolve {}:{}, all IPs are filtered", addr, port); + let err = io::Error::new(ErrorKind::Other, "resolved to empty address, all IPs are filtered"); + Err(err) + } else { + debug!("Resolved {}:{} => {:?}", addr, port, vaddr); + Ok(vaddr) + } +} diff --git a/src/relay/dns_resolver.rs b/src/relay/dns_resolver/trust_dns_resolver.rs similarity index 62% rename from src/relay/dns_resolver.rs rename to src/relay/dns_resolver/trust_dns_resolver.rs index 3e7380ee..2742eadb 100644 --- a/src/relay/dns_resolver.rs +++ b/src/relay/dns_resolver/trust_dns_resolver.rs @@ -5,7 +5,7 @@ use std::{ net::SocketAddr, }; -use futures::Future; +use log::{debug, error, trace}; use tokio; use trust_dns_resolver::{config::ResolverConfig, AsyncResolver}; @@ -18,11 +18,22 @@ pub fn create_resolver(dns: Option) -> AsyncResolver { { if let Some(conf) = dns { use trust_dns_resolver::config::ResolverOpts; + trace!( + "Initializing DNS resolver with config {:?} opts {:?}", + conf, + ResolverOpts::default() + ); AsyncResolver::new(conf, ResolverOpts::default()) } else { use trust_dns_resolver::system_conf::read_system_conf; // use the system resolver configuration let (config, opts) = read_system_conf().expect("Failed to read global dns sysconf"); + trace!( + "Initializing DNS resolver with system-config {:?} opts {:?}", + config, + opts + ); + AsyncResolver::new(config, opts) } } @@ -34,9 +45,19 @@ pub fn create_resolver(dns: Option) -> AsyncResolver { use trust_dns_resolver::config::{ResolverConfig, ResolverOpts}; if let Some(conf) = dns { + trace!( + "Initializing DNS resolver with config {:?} opts {:?}", + conf, + ResolverOpts::default() + ); AsyncResolver::new(conf, ResolverOpts::default()) } else { // Get a new resolver with the google nameservers as the upstream recursive resolvers + trace!( + "Initializing DNS resolver with google-config {:?} opts {:?}", + ResolverConfig::google(), + ResolverOpts::default() + ); AsyncResolver::new(ResolverConfig::google(), ResolverOpts::default()) } } @@ -48,17 +69,15 @@ pub fn create_resolver(dns: Option) -> AsyncResolver { resolver } -fn inner_resolve( +async fn inner_resolve( context: SharedContext, addr: &str, port: u16, check_forbidden: bool, -) -> impl Future, Error = io::Error> + Send { - // let owned_addr = addr.to_owned(); - let cloned_context = context.clone(); - context.dns_resolver().lookup_ip(addr).then(move |r| match r { +) -> io::Result> { + match context.dns_resolver().lookup_ip(addr).await { Err(err) => { - // error!("Failed to resolve {}, err: {}", owned_addr, err); + error!("Failed to resolve {}:{}, err: {}", addr, port, err); Err(io::Error::new( io::ErrorKind::Other, format!("dns resolve error: {}", err), @@ -68,9 +87,9 @@ fn inner_resolve( let mut vaddr = Vec::new(); for ip in lookup_result.iter() { if check_forbidden { - let forbidden_ip = &cloned_context.config().forbidden_ip; + let forbidden_ip = &context.config().forbidden_ip; if forbidden_ip.contains(&ip) { - // debug!("Resolved {} => {}, which is skipped by forbidden_ip", owned_addr, ip); + debug!("Resolved {} => {}, which is skipped by forbidden_ip", addr, ip); continue; } } @@ -78,26 +97,23 @@ fn inner_resolve( } if vaddr.is_empty() { - let err = io::Error::new( - ErrorKind::Other, - // format!("resolved {} to empty address, all IPs are filtered", owned_addr), - "resolved to empty address, all IPs are filtered", - ); + error!("Failed to resolve {}:{}, all IPs are filtered", addr, port); + let err = io::Error::new(ErrorKind::Other, "resolved to empty address, all IPs are filtered"); Err(err) } else { - // debug!("Resolved {} => {:?}", owned_addr, vaddr); + debug!("Resolved {}:{} => {:?}", addr, port, vaddr); Ok(vaddr) } } - }) + } } /// Resolve address to IP -pub fn resolve( +pub async fn resolve( context: SharedContext, addr: &str, port: u16, check_forbidden: bool, -) -> impl Future, Error = io::Error> + Send { - inner_resolve(context, addr, port, check_forbidden) +) -> io::Result> { + inner_resolve(context, addr, port, check_forbidden).await } diff --git a/src/relay/loadbalancing/server/ping.rs b/src/relay/loadbalancing/server/ping.rs index b8d6064d..2b178cfe 100644 --- a/src/relay/loadbalancing/server/ping.rs +++ b/src/relay/loadbalancing/server/ping.rs @@ -5,22 +5,19 @@ use std::{ atomic::{AtomicU64, AtomicUsize, Ordering}, Arc, Mutex, }, - time::{Duration, Instant}, }; use crate::{ config::ServerConfig, context::SharedContext, - relay::tcprelay::EncryptedWrite, relay::{loadbalancing::server::LoadBalancer, socks5::Address, tcprelay::client::ServerClient}, }; -use futures::{Future, Stream}; use log::{debug, error, info}; use tokio::{ self, - io::read_exact, - timer::{Interval, Timeout}, + io::{AsyncReadExt, AsyncWriteExt}, + time::{self, Duration, Instant}, }; struct Server { @@ -137,26 +134,22 @@ impl Inner { tokio::spawn( // Check every DEFAULT_CHECK_INTERVAL_SEC seconds - Interval::new( - Instant::now() + Duration::from_secs(DEFAULT_CHECK_INTERVAL_SEC), - Duration::from_secs(DEFAULT_CHECK_INTERVAL_SEC), - ) - .for_each(move |_| { - let sc = sc.clone(); - let lat = latency.clone(); + async move { + // Wait until the server is ready (plugins are started) + time::delay_for(Duration::from_secs(DEFAULT_CHECK_INTERVAL_SEC)).await; - Inner::check_delay(sc.clone(), context.clone()).then(move |res| { - let score = match res { - Ok(d) => lat.push(d), - Err(..) => lat.push(DEFAULT_CHECK_TIMEOUT_SEC * 2 * 1000), // Penalty + let mut interval = time::interval(Duration::from_secs(DEFAULT_CHECK_INTERVAL_SEC)); + + while context.server_running() { + interval.tick().await; + let score = match Inner::check_delay(sc.clone(), context.clone()).await { + Ok(d) => latency.push(d), + Err(..) => latency.push(DEFAULT_CHECK_TIMEOUT_SEC * 2 * 1000), // Penalty }; debug!("updated remote server {} (score: {})", sc.config.addr(), score); sc.score.store(score, Ordering::Release); - - Ok(()) - }) - }) - .map_err(|_| ()), + } + }, ); } @@ -166,52 +159,54 @@ impl Inner { } } - fn check_delay(sc: Arc, context: SharedContext) -> impl Future { - static GET_BODY: &[u8] = b"GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: close\r\nAccept: */*\r\n\r\n"; + async fn check_request(sc: Arc, context: SharedContext) -> io::Result<()> { + static GET_BODY: &[u8] = + b"GET /generate_204 HTTP/1.1\r\nHost: dl.google.com\r\nConnection: close\r\nAccept: */*\r\n\r\n"; - let addr = Address::DomainNameAddress("www.example.com".to_owned(), 80); + let addr = Address::DomainNameAddress("dl.google.com".to_owned(), 80); + let ServerClient { mut stream } = ServerClient::connect(context, &addr, sc).await?; + stream.write_all(GET_BODY).await?; + stream.flush().await?; + let mut buf = [0u8; 1]; + stream.read_exact(&mut buf).await?; + + Ok(()) + } + + async fn check_delay(sc: Arc, context: SharedContext) -> io::Result { let start = Instant::now(); // Send HTTP GET and read the first byte - let fut = ServerClient::connect(context, addr, sc.config.clone()).and_then(|ServerClient { r, w }| { - w.write_all(GET_BODY) - .and_then(|(mut w, _)| w.flush()) - .and_then(move |_| read_exact(r, [0u8, 1])) - }); + let timeout = Duration::from_secs(DEFAULT_CHECK_TIMEOUT_SEC); + let res = time::timeout(timeout, Inner::check_request(sc.config.clone(), context.clone())).await; - Timeout::new(fut, Duration::from_secs(DEFAULT_CHECK_TIMEOUT_SEC)).then(move |res| { - let elapsed = Instant::now() - start; - let elapsed = elapsed.as_secs() * 1000 + u64::from(elapsed.subsec_millis()); // Converted to ms - match res { - Ok(..) => { - // Got the result ... record its time - debug!("checked remote server {} latency with {} ms", sc.config.addr(), elapsed); - Ok(elapsed) - } - Err(err) => { - match err.into_inner() { - Some(err) => { - error!("failed to check server {}, error: {}", sc.config.addr(), err); - - // NOTE: connection / handshake error, server is down - Err(err) - } - None => { - // Timeout - debug!( - "checked remote server {} latency timeout, elapsed {} ms", - sc.config.addr(), - elapsed - ); - - // NOTE: timeout is still available, but server is too slow - Ok(elapsed) - } - } - } + let elapsed = Instant::now() - start; + let elapsed = elapsed.as_secs() * 1000 + u64::from(elapsed.subsec_millis()); // Converted to ms + match res { + Ok(Ok(..)) => { + // Got the result ... record its time + debug!("checked remote server {} latency with {} ms", sc.config.addr(), elapsed); + Ok(elapsed) } - }) + Ok(Err(err)) => { + error!("failed to check server {}, error: {}", sc.config.addr(), err); + + // NOTE: connection / handshake error, server is down + Err(err) + } + Err(..) => { + // Timeout + debug!( + "checked remote server {} latency timeout, elapsed {} ms", + sc.config.addr(), + elapsed + ); + + // NOTE: timeout is still available, but server is too slow + Ok(elapsed) + } + } } fn best_idx(&self) -> usize { @@ -234,54 +229,54 @@ pub struct PingBalancer { impl PingBalancer { pub fn new(context: SharedContext) -> PingBalancer { - let inner = Arc::new(Inner::new(context)); + let inner = Arc::new(Inner::new(context.clone())); - let inner_cloned = inner.clone(); - tokio::spawn( - Interval::new( - Instant::now() + Duration::from_secs(2), - Duration::from_secs(DEFAULT_CHECK_INTERVAL_SEC), - ) - .for_each(move |_| { - let inner = inner_cloned.clone(); + if inner.servers.len() > 1 { + let cloned_inner = inner.clone(); + tokio::spawn(async move { + let inner = cloned_inner; - if inner.servers.is_empty() { - panic!("No server"); - } + // Wait until the server is ready (plugins are started) + time::delay_for(Duration::from_secs(2)).await; - // Choose the best one - let mut svr_idx = 0; + let mut interval = time::interval(Duration::from_secs(DEFAULT_CHECK_INTERVAL_SEC)); - for (idx, svr) in inner.servers.iter().enumerate() { - let choosen_svr = &inner.servers[svr_idx]; - if svr.score() < choosen_svr.score() { - svr_idx = idx; + while context.server_running() { + interval.tick().await; + + assert!(!inner.servers.is_empty()); + + // Choose the best one + let mut svr_idx = 0; + + for (idx, svr) in inner.servers.iter().enumerate() { + let choosen_svr = &inner.servers[svr_idx]; + if svr.score() < choosen_svr.score() { + svr_idx = idx; + } } - } - let choosen_svr = &inner.servers[svr_idx]; - debug!( - "chosen the best server {} (score: {})", - choosen_svr.config.addr(), - choosen_svr.score() - ); - - if inner.best_idx() != svr_idx { - info!( - "switched server from {} (score: {}) to {} (score: {})", - inner.best_server().config.addr(), - inner.best_server().score(), + let choosen_svr = &inner.servers[svr_idx]; + debug!( + "chosen the best server {} (score: {})", choosen_svr.config.addr(), - choosen_svr.score(), + choosen_svr.score() ); + + if inner.best_idx() != svr_idx { + info!( + "switched server from {} (score: {}) to {} (score: {})", + inner.best_server().config.addr(), + inner.best_server().score(), + choosen_svr.config.addr(), + choosen_svr.score(), + ); + } + + inner.set_best_idx(svr_idx); } - - inner.set_best_idx(svr_idx); - - Ok(()) - }) - .map_err(|_| ()), - ); + }); + } PingBalancer { inner } } diff --git a/src/relay/local.rs b/src/relay/local.rs index 66785d13..a38be0b3 100644 --- a/src/relay/local.rs +++ b/src/relay/local.rs @@ -2,67 +2,49 @@ use std::io; -use futures::{stream::futures_unordered, Future, Stream}; +use futures::{future::select_all, FutureExt}; + +use log::error; use crate::{ config::Config, - context::{Context, SharedContext}, - plugin::{launch_plugins, PluginMode}, - relay::{boxed_future, tcprelay::local::run as run_tcp, udprelay::local::run as run_udp}, + context::{Context, SharedServerState}, + plugin::{PluginMode, Plugins}, + relay::{tcprelay::local::run as run_tcp, udprelay::local::run as run_udp}, }; /// Relay server running under local environment. -/// -/// ```no_run -/// use shadowsocks::{ -/// config::{Config, ConfigType, ServerConfig}, -/// crypto::CipherType, -/// relay::local::run, -/// }; -/// -/// use tokio::prelude::*; -/// -/// let mut config = Config::new(ConfigType::Local); -/// config.local = Some("127.0.0.1:1080".parse().unwrap()); -/// config.server = vec![ServerConfig::basic( -/// "127.0.0.1:8388".parse().unwrap(), -/// "server-password".to_string(), -/// CipherType::Aes256Cfb, -/// )]; -/// let fut = run(config); -/// tokio::run(fut.map_err(|err| panic!("Server run failed with error {}", err))); -/// ``` -pub fn run(config: Config) -> impl Future + Send { - futures::lazy(move || { - let mut context = Context::new(config); +pub async fn run(mut config: Config) -> io::Result<()> { + // Create a context containing a DNS resolver and server running state flag. + let state = SharedServerState::new(&config); - let mut vf = Vec::new(); + let mut vf = Vec::new(); - if context.config().mode.enable_udp() { - // Clone config here, because the config for TCP relay will be modified - // after plugins started - let udp_context = SharedContext::new(context.clone()); + if config.mode.enable_udp() { + // Clone config here, because the config for TCP relay will be modified + // after plugins started. + // But DNS resolver and running state flag is still shared. + let udp_context = Context::new_shared(config.clone(), state.clone()); - // Run UDP relay before starting plugins - // Because plugins doesn't support UDP relay - let udp_fut = run_udp(udp_context); - vf.push(boxed_future(udp_fut)); - } + // Run UDP relay before starting plugins + // Because plugins doesn't support UDP relay + let udp_fut = run_udp(udp_context); + vf.push(udp_fut.boxed()); + } - if let Some(plugins) = - launch_plugins(context.config_mut(), PluginMode::Client).expect("Failed to launch plugins") - { - vf.push(boxed_future(plugins)); - } + if config.has_server_plugins() { + let plugins = Plugins::launch_plugins(&mut config, PluginMode::Client)?; + vf.push(plugins.into_future().boxed()); + } - let tcp_fut = run_tcp(SharedContext::new(context)); + let tcp_fut = run_tcp(Context::new_shared(config, state.clone())); + vf.push(tcp_fut.boxed()); - vf.push(boxed_future(tcp_fut)); - futures_unordered(vf).into_future().then(|res| -> io::Result<()> { - match res { - Ok(..) => Ok(()), - Err((err, ..)) => Err(err), - } - }) - }) + let (res, ..) = select_all(vf.into_iter()).await; + error!("One of TCP servers exited unexpectly, result: {:?}", res); + + // Tells all detached tasks to exit + state.server_stopped(); + + Err(io::Error::new(io::ErrorKind::Other, "server exited unexpectly")) } diff --git a/src/relay/mod.rs b/src/relay/mod.rs index 9901b34a..aefdbdf0 100644 --- a/src/relay/mod.rs +++ b/src/relay/mod.rs @@ -1,20 +1,11 @@ //! Relay server in local and server side implementations. -use futures::Future; - -pub mod dns; +#[cfg(feature = "trust-dns")] pub(crate) mod dns_resolver; -mod loadbalancing; +pub(crate) mod loadbalancing; pub mod local; pub mod server; pub mod socks5; pub mod tcprelay; pub mod udprelay; -mod utils; - -pub fn boxed_future(f: F) -> Box + Send + 'static> -where - F: Future + Send + 'static, -{ - Box::new(f) -} +pub(crate) mod utils; diff --git a/src/relay/server.rs b/src/relay/server.rs index 4e118fb1..1fbcb5bb 100644 --- a/src/relay/server.rs +++ b/src/relay/server.rs @@ -2,69 +2,50 @@ use std::io; -use futures::{stream::futures_unordered, Future, Stream}; +use futures::future::{select_all, FutureExt}; + +use log::error; use crate::{ config::Config, - context::{Context, SharedContext}, - plugin::{launch_plugins, PluginMode}, - relay::{boxed_future, tcprelay::server::run as run_tcp, udprelay::server::run as run_udp}, + context::{Context, SharedServerState}, + plugin::{PluginMode, Plugins}, + relay::{tcprelay::server::run as run_tcp, udprelay::server::run as run_udp}, }; /// Relay server running on server side. -/// -/// ```no_run -/// use shadowsocks::{ -/// config::{Config, ConfigType, ServerConfig}, -/// crypto::CipherType, -/// relay::server::run, -/// }; -/// -/// use tokio::prelude::*; -/// -/// let mut config = Config::new(ConfigType::Server); -/// config.server = vec![ServerConfig::basic( -/// "127.0.0.1:8388".parse().unwrap(), -/// "server-password".to_string(), -/// CipherType::Aes256Cfb, -/// )]; -/// -/// let fut = run(config); -/// tokio::run(fut.map_err(|err| panic!("Server run failed with error {}", err))); -/// ``` -pub fn run(config: Config) -> impl Future + Send { - futures::lazy(move || { - let mut context = Context::new(config); +pub async fn run(mut config: Config) -> io::Result<()> { + // Create a context containing a DNS resolver and server running state flag. + let state = SharedServerState::new(&config); - let mut vf = Vec::new(); + let mut vf = Vec::new(); - if context.config().mode.enable_udp() { - // Clone config here, because the config for TCP relay will be modified - // after plugins started - let udp_context = SharedContext::new(context.clone()); + if config.mode.enable_udp() { + // Clone config here, because the config for TCP relay will be modified + // after plugins started + let udp_context = Context::new_shared(config.clone(), state.clone()); - // Run UDP relay before starting plugins - // Because plugins doesn't support UDP relay - let udp_fut = run_udp(udp_context); - vf.push(boxed_future(udp_fut)); + // Run UDP relay before starting plugins + // Because plugins doesn't support UDP relay + let udp_fut = run_udp(udp_context); + vf.push(udp_fut.boxed()); + } + + if config.mode.enable_tcp() { + if config.has_server_plugins() { + let plugins = Plugins::launch_plugins(&mut config, PluginMode::Client)?; + vf.push(plugins.into_future().boxed()); } - if context.config().mode.enable_tcp() { - if let Some(plugins) = - launch_plugins(context.config_mut(), PluginMode::Server).expect("Failed to launch plugins") - { - vf.push(boxed_future(plugins)); - } + let tcp_fut = run_tcp(Context::new_shared(config, state.clone())); + vf.push(tcp_fut.boxed()); + } - let tcp_fut = run_tcp(SharedContext::new(context)); - vf.push(boxed_future(tcp_fut)); - } + let (res, ..) = select_all(vf.into_iter()).await; + error!("One of TCP servers exited unexpectly, result: {:?}", res); - futures_unordered(vf).into_future().then(|res| -> io::Result<()> { - match res { - Ok(..) => Ok(()), - Err((err, ..)) => Err(err), - } - }) - }) + // Tells all detached tasks to exit + state.server_stopped(); + + Err(io::Error::new(io::ErrorKind::Other, "server exited unexpectly")) } diff --git a/src/relay/socks5.rs b/src/relay/socks5.rs index e7153d76..590e623d 100644 --- a/src/relay/socks5.rs +++ b/src/relay/socks5.rs @@ -2,27 +2,24 @@ //! //! Implements [SOCKS Protocol Version 5](https://www.ietf.org/rfc/rfc1928.txt) proxy protocol -use std::{ - convert::From, - error, - fmt::{self, Debug, Formatter}, - io::{self, Cursor, Read, Write}, - net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs}, - u8, vec, -}; +use std::convert::From; +use std::error; +use std::fmt::{self, Debug, Formatter}; +use std::io::{self, Cursor}; +use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs}; +use std::str::FromStr; +use std::u8; +use std::vec; -use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; -use bytes::{BufMut, Bytes, BytesMut, IntoBuf}; -use futures::{try_ready, Async, Future, Poll}; +use bytes::buf::BufExt; +use bytes::{Buf, BufMut, BytesMut}; use log::error; -use tokio_io::{io::read_exact, try_nb, AsyncRead, AsyncWrite}; +use tokio::prelude::*; pub use self::consts::{ SOCKS5_AUTH_METHOD_GSSAPI, SOCKS5_AUTH_METHOD_NONE, SOCKS5_AUTH_METHOD_NOT_ACCEPTABLE, SOCKS5_AUTH_METHOD_PASSWORD, }; -use super::utils::{write_bytes, WriteBytes}; - #[rustfmt::skip] mod consts { pub const SOCKS5_VERSION: u8 = 0x05; @@ -192,7 +189,7 @@ impl error::Error for Error { &self.message[..] } - fn cause(&self) -> Option<&error::Error> { + fn cause(&self) -> Option<&dyn error::Error> { None } } @@ -219,17 +216,84 @@ pub enum Address { } impl Address { - #[inline] - pub fn read_from(stream: R) -> ReadAddress { - ReadAddress::new(stream) + pub async fn read_from(stream: &mut R) -> Result + where + R: AsyncRead + Unpin, + { + let mut addr_type_buf = [0u8; 1]; + let _ = stream.read_exact(&mut addr_type_buf).await?; + + let addr_type = addr_type_buf[0]; + match addr_type { + consts::SOCKS5_ADDR_TYPE_IPV4 => { + let mut buf = BytesMut::with_capacity(6); + buf.resize(6, 0); + let _ = stream.read_exact(&mut buf).await?; + + let mut cursor = buf.to_bytes(); + let v4addr = Ipv4Addr::new(cursor.get_u8(), cursor.get_u8(), cursor.get_u8(), cursor.get_u8()); + let port = cursor.get_u16(); + Ok(Address::SocketAddress(SocketAddr::V4(SocketAddrV4::new(v4addr, port)))) + } + consts::SOCKS5_ADDR_TYPE_IPV6 => { + let mut buf = [0u8; 18]; + let _ = stream.read_exact(&mut buf).await?; + + let mut cursor = Cursor::new(&buf); + let v6addr = Ipv6Addr::new( + cursor.get_u16(), + cursor.get_u16(), + cursor.get_u16(), + cursor.get_u16(), + cursor.get_u16(), + cursor.get_u16(), + cursor.get_u16(), + cursor.get_u16(), + ); + let port = cursor.get_u16(); + + Ok(Address::SocketAddress(SocketAddr::V6(SocketAddrV6::new( + v6addr, port, 0, 0, + )))) + } + consts::SOCKS5_ADDR_TYPE_DOMAIN_NAME => { + let mut length_buf = [0u8; 1]; + let _ = stream.read_exact(&mut length_buf).await?; + let length = length_buf[0] as usize; + + // Len(Domain) + Len(Port) + let buf_length = length + 2; + let mut buf = BytesMut::with_capacity(buf_length); + buf.resize(buf_length, 0); + let _ = stream.read_exact(&mut buf).await?; + + let mut cursor = buf.to_bytes(); + let mut raw_addr = Vec::with_capacity(length); + raw_addr.put(&mut BufExt::take(&mut cursor, length)); + let addr = match String::from_utf8(raw_addr) { + Ok(addr) => addr, + Err(..) => return Err(Error::new(Reply::GeneralFailure, "Invalid address encoding")), + }; + let port = cursor.get_u16(); + + Ok(Address::DomainNameAddress(addr, port)) + } + _ => { + error!("Invalid address type {}", addr_type); + Err(Error::new(Reply::AddressTypeNotSupported, "Not supported address type")) + } + } } /// Writes to writer #[inline] - pub fn write_to(self, writer: W) -> WriteBytes { + pub async fn write_to(&self, writer: &mut W) -> io::Result<()> + where + W: AsyncWrite + Unpin, + { let mut buf = BytesMut::with_capacity(self.serialized_len()); self.write_to_buf(&mut buf); - write_bytes(writer, buf.freeze()) + writer.write_all(&buf).await } /// Writes to buffer @@ -287,215 +351,42 @@ impl From<(String, u16)> for Address { } } -pub struct ReadAddress -where - R: AsyncRead, -{ - reader: Option, - state: ReadAddressState, - buf: Option, - already_read: usize, -} +/// Parse `Address` error +#[derive(Debug)] +pub struct AddressError; -enum ReadAddressState { - Type, - Ipv4, - Ipv6, - DomainNameLength, - DomainName, -} +impl FromStr for Address { + type Err = AddressError; -impl Future for ReadAddress -where - R: AsyncRead, -{ - type Error = Error; - type Item = (R, Address); - - fn poll(&mut self) -> Poll { - debug_assert!(self.reader.is_some()); - - loop { - match self.state { - ReadAddressState::Type => { - try_ready!(self.read_addr_type()); + fn from_str(s: &str) -> Result { + match s.parse::() { + Ok(addr) => Ok(Address::SocketAddress(addr)), + Err(..) => { + let mut sp = s.split(':'); + match (sp.next(), sp.next()) { + (Some(dn), Some(port)) => match port.parse::() { + Ok(port) => Ok(Address::DomainNameAddress(dn.to_owned(), port)), + Err(..) => Err(AddressError), + }, + _ => Err(AddressError), } - ReadAddressState::Ipv4 => { - let addr = try_ready!(self.read_ipv4()); - let reader = self.reader.take().unwrap(); - return Ok((reader, addr).into()); - } - ReadAddressState::Ipv6 => { - let addr = try_ready!(self.read_ipv6()); - let reader = self.reader.take().unwrap(); - return Ok((reader, addr).into()); - } - ReadAddressState::DomainNameLength => { - try_ready!(self.read_domain_name_length()); - } - ReadAddressState::DomainName => { - let addr = try_ready!(self.read_domain_name()); - let reader = self.reader.take().unwrap(); - return Ok((reader, addr).into()); - } - }; - } - } -} - -impl ReadAddress -where - R: AsyncRead, -{ - fn new(r: R) -> ReadAddress { - ReadAddress { - reader: Some(r), - state: ReadAddressState::Type, - buf: None, - already_read: 0, - } - } - - fn read_addr_type(&mut self) -> Poll<(), Error> { - let addr_type = try_nb!(self.reader.as_mut().unwrap().read_u8()); - match addr_type { - consts::SOCKS5_ADDR_TYPE_IPV4 => { - self.state = ReadAddressState::Ipv4; - self.alloc_buf(6); - } - consts::SOCKS5_ADDR_TYPE_IPV6 => { - self.state = ReadAddressState::Ipv6; - self.alloc_buf(18); - } - consts::SOCKS5_ADDR_TYPE_DOMAIN_NAME => { - self.state = ReadAddressState::DomainNameLength; - } - _ => { - error!("Invalid address type {}", addr_type); - return Err(Error::new(Reply::AddressTypeNotSupported, "Not supported address type")); - } - }; - - Ok(Async::Ready(())) - } - - fn read_ipv4(&mut self) -> Poll { - try_ready!(self.read_data()); - let mut stream: Cursor = self.freeze_buf().into_buf(); - let v4addr = Ipv4Addr::new( - stream.read_u8()?, - stream.read_u8()?, - stream.read_u8()?, - stream.read_u8()?, - ); - let port = stream.read_u16::()?; - let addr = Address::SocketAddress(SocketAddr::V4(SocketAddrV4::new(v4addr, port))); - Ok(Async::Ready(addr)) - } - - fn read_ipv6(&mut self) -> Poll { - try_ready!(self.read_data()); - let mut stream: Cursor = self.freeze_buf().into_buf(); - let v6addr = Ipv6Addr::new( - stream.read_u16::()?, - stream.read_u16::()?, - stream.read_u16::()?, - stream.read_u16::()?, - stream.read_u16::()?, - stream.read_u16::()?, - stream.read_u16::()?, - stream.read_u16::()?, - ); - let port = stream.read_u16::()?; - - let addr = Address::SocketAddress(SocketAddr::V6(SocketAddrV6::new(v6addr, port, 0, 0))); - Ok(Async::Ready(addr)) - } - - fn read_domain_name_length(&mut self) -> Poll<(), Error> { - let length = try_nb!(self.reader.as_mut().unwrap().read_u8()); - self.state = ReadAddressState::DomainName; - self.alloc_buf(length as usize + 2); - Ok(Async::Ready(())) - } - - fn read_domain_name(&mut self) -> Poll { - try_ready!(self.read_data()); - let buf = self.freeze_buf(); - let addr_len = buf.len() - 2; - let mut stream: Cursor = buf.into_buf(); - - let mut raw_addr = Vec::with_capacity(addr_len); - unsafe { - raw_addr.set_len(addr_len); - } - stream.read_exact(&mut raw_addr)?; - - let addr = match String::from_utf8(raw_addr) { - Ok(addr) => addr, - Err(..) => return Err(Error::new(Reply::GeneralFailure, "Invalid address encoding")), - }; - let port = stream.read_u16::()?; - - let addr = Address::DomainNameAddress(addr, port); - Ok(Async::Ready(addr)) - } - - fn alloc_buf(&mut self, size: usize) { - let mut buf = BytesMut::with_capacity(size); - unsafe { - buf.set_len(size); - } - self.buf = Some(buf); - } - - fn read_data(&mut self) -> Poll<(), io::Error> { - let buf = self.buf.as_mut().unwrap(); - - while self.already_read < buf.len() { - match self.reader.as_mut().unwrap().read(&mut buf[self.already_read..]) { - Ok(0) => { - let err = io::Error::new(io::ErrorKind::Other, "Unexpected EOF"); - return Err(err); - } - Ok(n) => self.already_read += n, - Err(err) => return Err(err), } } - - Ok(Async::Ready(())) - } - - fn freeze_buf(&mut self) -> Bytes { - let buf = self.buf.take().unwrap(); - buf.freeze() } } fn write_ipv4_address(addr: &SocketAddrV4, buf: &mut B) { - let mut dbuf = [0u8; 1 + 4 + 2]; - { - let mut cur = Cursor::new(&mut dbuf[..]); - let _ = cur.write_u8(consts::SOCKS5_ADDR_TYPE_IPV4); // Address type - let _ = cur.write_all(&addr.ip().octets()); // Ipv4 bytes - let _ = cur.write_u16::(addr.port()); - } - buf.put_slice(&dbuf[..]); + buf.put_u8(consts::SOCKS5_ADDR_TYPE_IPV4); // Address type + buf.put_slice(&addr.ip().octets()); // Ipv4 bytes + buf.put_u16(addr.port()); // Port } fn write_ipv6_address(addr: &SocketAddrV6, buf: &mut B) { - let mut dbuf = [0u8; 1 + 16 + 2]; - - { - let mut cur = Cursor::new(&mut dbuf[..]); - let _ = cur.write_u8(consts::SOCKS5_ADDR_TYPE_IPV6); - for seg in &addr.ip().segments() { - let _ = cur.write_u16::(*seg); - } - let _ = cur.write_u16::(addr.port()); + buf.put_u8(consts::SOCKS5_ADDR_TYPE_IPV6); // Address type + for seg in &addr.ip().segments() { + buf.put_u16(*seg); // Ipv6 bytes } - - buf.put_slice(&dbuf[..]); + buf.put_u16(addr.port()); // Port } fn write_domain_name_address(dnaddr: &str, port: u16, buf: &mut B) { @@ -504,7 +395,7 @@ fn write_domain_name_address(dnaddr: &str, port: u16, buf: &mut B) { buf.put_u8(consts::SOCKS5_ADDR_TYPE_DOMAIN_NAME); buf.put_u8(dnaddr.len() as u8); buf.put_slice(dnaddr[..].as_bytes()); - buf.put_u16_be(port); + buf.put_u16(port); } fn write_socket_address(addr: &SocketAddr, buf: &mut B) { @@ -557,42 +448,38 @@ impl TcpRequestHeader { } /// Read from a reader - pub fn read_from(r: R) -> impl Future + Send + pub async fn read_from(r: &mut R) -> Result where - R: AsyncRead + Send + 'static, + R: AsyncRead + Unpin, { - read_exact(r, [0u8; 3]) - .map_err(From::from) - .and_then(|(r, buf)| { - let ver = buf[0]; - if ver != consts::SOCKS5_VERSION { - return Err(Error::new(Reply::ConnectionRefused, "Unsupported Socks version")); - } + let mut buf = [0u8; 3]; + let _ = r.read_exact(&mut buf).await?; - let cmd = buf[1]; - let command = match Command::from_u8(cmd) { - Some(c) => c, - None => { - return Err(Error::new(Reply::CommandNotSupported, "Unsupported command")); - } - }; + let ver = buf[0]; + if ver != consts::SOCKS5_VERSION { + return Err(Error::new(Reply::ConnectionRefused, "Unsupported Socks version")); + } - Ok((r, command)) - }) - .and_then(|(r, command)| { - Address::read_from(r).map(move |(conn, address)| { - let header = TcpRequestHeader { command, address }; + let cmd = buf[1]; + let command = match Command::from_u8(cmd) { + Some(c) => c, + None => { + return Err(Error::new(Reply::CommandNotSupported, "Unsupported command")); + } + }; - (conn, header) - }) - }) + let address = Address::read_from(r).await?; + Ok(TcpRequestHeader { command, address }) } /// Write data into a writer - pub fn write_to(self, w: W) -> WriteBytes { + pub async fn write_to(&self, w: &mut W) -> io::Result<()> + where + W: AsyncWrite + Unpin, + { let mut buf = BytesMut::with_capacity(self.serialized_len()); self.write_to_buf(&mut buf); - write_bytes(w, buf.freeze()) + w.write_all(&buf).await } /// Writes to buffer @@ -637,39 +524,36 @@ impl TcpResponseHeader { } /// Read from a reader - pub fn read_from(r: R) -> impl Future + Send + pub async fn read_from(r: &mut R) -> Result where - R: AsyncRead + Send + 'static, + R: AsyncRead + Unpin, { - read_exact(r, [0u8; 3]) - .map_err(From::from) - .and_then(|(r, buf)| { - let ver = buf[0]; - let reply_code = buf[1]; + let mut buf = [0u8; 3]; + let _ = r.read_exact(&mut buf).await?; - if ver != consts::SOCKS5_VERSION { - return Err(Error::new(Reply::ConnectionRefused, "Unsupported Socks version")); - } + let ver = buf[0]; + let reply_code = buf[1]; - Ok((r, reply_code)) - }) - .and_then(|(r, reply_code)| { - Address::read_from(r).map(move |(r, address)| { - let rep = TcpResponseHeader { - reply: Reply::from_u8(reply_code), - address, - }; + if ver != consts::SOCKS5_VERSION { + return Err(Error::new(Reply::ConnectionRefused, "Unsupported Socks version")); + } - (r, rep) - }) - }) + let address = Address::read_from(r).await?; + + Ok(TcpResponseHeader { + reply: Reply::from_u8(reply_code), + address, + }) } /// Write to a writer - pub fn write_to(self, w: W) -> WriteBytes { + pub async fn write_to(&self, w: &mut W) -> io::Result<()> + where + W: AsyncWrite + Unpin, + { let mut buf = BytesMut::with_capacity(self.serialized_len()); self.write_to_buf(&mut buf); - write_bytes(w, buf.freeze()) + w.write_all(&buf).await } /// Writes to buffer @@ -707,30 +591,34 @@ impl HandshakeRequest { } /// Read from a reader - pub fn read_from(r: R) -> impl Future + pub async fn read_from(r: &mut R) -> io::Result where - R: AsyncRead + Send + 'static, + R: AsyncRead + Unpin, { - read_exact(r, [0u8, 0u8]) - .and_then(|(r, buf)| { - let ver = buf[0]; - let nmet = buf[1]; + let mut buf = [0u8; 2]; + let _ = r.read_exact(&mut buf).await?; - if ver != consts::SOCKS5_VERSION { - return Err(io::Error::new(io::ErrorKind::Other, "Invalid Socks5 version")); - } + let ver = buf[0]; + let nmet = buf[1]; - Ok((r, nmet)) - }) - .and_then(|(r, nmet)| read_exact(r, vec![0u8; nmet as usize])) - .and_then(|(r, methods)| Ok((r, HandshakeRequest { methods }))) + if ver != consts::SOCKS5_VERSION { + return Err(io::Error::new(io::ErrorKind::Other, "Invalid Socks5 version")); + } + + let mut methods = vec![0u8; nmet as usize]; + let _ = r.read_exact(&mut methods).await?; + + Ok(HandshakeRequest { methods }) } /// Write to a writer - pub fn write_to(self, w: W) -> WriteBytes { + pub async fn write_to(&self, w: &mut W) -> io::Result<()> + where + W: AsyncWrite + Unpin, + { let mut buf = BytesMut::with_capacity(self.serialized_len()); self.write_to_buf(&mut buf); - write_bytes(w, buf.freeze()) + w.write_all(&buf).await } /// Write to buffer @@ -767,36 +655,40 @@ impl HandshakeResponse { } /// Read from a reader - pub fn read_from(r: R) -> impl Future + pub async fn read_from(r: &mut R) -> io::Result where - R: AsyncRead + Send + 'static, + R: AsyncRead + Unpin, { - read_exact(r, [0u8, 0u8]).and_then(|(r, buf)| { - let ver = buf[0]; - let met = buf[1]; + let mut buf = [0u8; 2]; + let _ = r.read_exact(&mut buf).await?; - if ver != consts::SOCKS5_VERSION { - Err(io::Error::new(io::ErrorKind::Other, "Invalid Socks5 version")) - } else { - Ok((r, HandshakeResponse { chosen_method: met })) - } - }) + let ver = buf[0]; + let met = buf[1]; + + if ver != consts::SOCKS5_VERSION { + Err(io::Error::new(io::ErrorKind::Other, "Invalid Socks5 version")) + } else { + Ok(HandshakeResponse { chosen_method: met }) + } } /// Write to a writer - pub fn write_to(self, w: W) -> WriteBytes { + pub async fn write_to(self, w: &mut W) -> io::Result<()> + where + W: AsyncWrite + Unpin, + { let mut buf = BytesMut::with_capacity(self.serialized_len()); self.write_to_buf(&mut buf); - write_bytes(w, buf.freeze()) + w.write_all(&buf).await } /// Write to buffer - pub fn write_to_buf(&self, buf: &mut B) { + pub fn write_to_buf(self, buf: &mut B) { buf.put_slice(&[consts::SOCKS5_VERSION, self.chosen_method]); } /// Length in bytes - pub fn serialized_len(&self) -> usize { + pub fn serialized_len(self) -> usize { 2 } } @@ -827,24 +719,26 @@ impl UdpAssociateHeader { } /// Read from a reader - pub fn read_from(r: R) -> impl Future + Send + pub async fn read_from(r: &mut R) -> Result where - R: AsyncRead + Send + 'static, + R: AsyncRead + Unpin, { - read_exact(r, [0u8; 3]).map_err(From::from).and_then(|(r, buf)| { - let frag = buf[2]; - Address::read_from(r).map(move |(r, address)| { - let h = UdpAssociateHeader::new(frag, address); - (r, h) - }) - }) + let mut buf = [0u8; 3]; + let _ = r.read_exact(&mut buf).await?; + + let frag = buf[2]; + let address = Address::read_from(r).await?; + Ok(UdpAssociateHeader::new(frag, address)) } /// Write to a writer - pub fn write_to(self, w: W) -> WriteBytes { + pub async fn write_to(&self, w: &mut W) -> io::Result<()> + where + W: AsyncWrite + Unpin, + { let mut buf = BytesMut::with_capacity(self.serialized_len()); self.write_to_buf(&mut buf); - write_bytes(w, buf.freeze()) + w.write_all(&buf).await } /// Write to buffer diff --git a/src/relay/tcprelay/aead.rs b/src/relay/tcprelay/aead.rs index d936ff8f..3105b2ff 100644 --- a/src/relay/tcprelay/aead.rs +++ b/src/relay/tcprelay/aead.rs @@ -33,295 +33,257 @@ //! ``` use std::{ - cmp, - io::{self, BufRead, Read}, + cmp, io, + marker::Unpin, + pin::Pin, + slice, + task::{Context, Poll}, u16, }; use byteorder::{BigEndian, ByteOrder}; use bytes::{BufMut, BytesMut}; -use log::error; -use tokio_io::{AsyncRead, AsyncWrite}; +use futures::ready; +use tokio::prelude::*; use crate::crypto::{self, BoxAeadDecryptor, BoxAeadEncryptor, CipherType}; -use super::{DecryptedRead, EncryptedWrite, BUFFER_SIZE}; +use super::BUFFER_SIZE; /// AEAD packet payload must be smaller than 0x3FFF const MAX_PACKET_SIZE: usize = 0x3FFF; -enum ReadingStep { +#[derive(Debug)] +enum DecryptReadStep { Length, - DataAndTag(usize), - Done, + Data(usize), } /// Reader wrapper that will decrypt data automatically -pub struct DecryptedReader -where - R: AsyncRead, -{ - reader: R, +pub struct DecryptedReader { buffer: BytesMut, data: BytesMut, cipher: BoxAeadDecryptor, pos: usize, - sent_final: bool, tag_size: usize, - read_step: ReadingStep, + steps: DecryptReadStep, + got_final: bool, } -impl DecryptedReader -where - R: AsyncRead, -{ - pub fn new(r: R, t: CipherType, key: &[u8], nounce: &[u8]) -> DecryptedReader { +impl DecryptedReader { + pub fn new(t: CipherType, key: &[u8], nonce: &[u8]) -> DecryptedReader { DecryptedReader { - reader: r, buffer: BytesMut::with_capacity(BUFFER_SIZE), data: BytesMut::with_capacity(BUFFER_SIZE), - cipher: crypto::new_aead_decryptor(t, key, nounce), + cipher: crypto::new_aead_decryptor(t, key, nonce), pos: 0, - sent_final: false, tag_size: t.tag_size(), - read_step: ReadingStep::Length, + steps: DecryptReadStep::Length, + got_final: false, } } - pub fn get_ref(&self) -> &R { - &self.reader - } - - /// Gets a mutable reference to the underlying reader. - /// - /// # Warning - /// - /// It is inadvisable to read directly from or write directly to the - /// underlying reader. - pub fn get_mut(&mut self) -> &mut R { - &mut self.reader - } - - /// Unwraps this `DecryptedReader`, returning the underlying reader. - /// - /// The internal buffer is flushed before returning the reader. Any leftover - /// data in the read buffer is lost. - pub fn into_inner(self) -> R { - self.reader - } - - fn read_exact(&mut self, expect_length: usize, ignore_final: bool) -> io::Result<()> { - let mut incoming = [0u8; BUFFER_SIZE]; - self.buffer.reserve(expect_length); - - while self.buffer.len() < expect_length { - let remain = expect_length - self.buffer.len(); - let rlen = cmp::min(remain, incoming.len()); - match self.reader.read(&mut incoming[..rlen]) { - Ok(0) => { - if ignore_final && self.buffer.is_empty() { - self.sent_final = true; - return Ok(()); - } - - return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "unexpected eof")); - } - Ok(n) => self.buffer.put_slice(&incoming[..n]), - Err(e) => return Err(e), - } - } - Ok(()) - } - - fn read_length(&mut self) -> io::Result<()> { - let expect_length = 2 + self.tag_size; - self.read_exact(expect_length, true)?; - - if !self.sent_final { - // Ok, read length finished - { - let len = { - let mut len_buf = [0u8; 2]; - self.cipher.decrypt(&self.buffer[..], &mut len_buf)?; - BigEndian::read_u16(&len_buf) as usize - }; - - if len > MAX_PACKET_SIZE { - use std::io::{Error, ErrorKind}; - - error!( - "AEAD packet size must be <= {}, but received length {}", - MAX_PACKET_SIZE, len - ); - - let err = Error::new( - ErrorKind::InvalidData, - format!( - "AEAD packet size must be <= {}, but received length {}", - MAX_PACKET_SIZE, len - ), - ); - return Err(err); - } - - self.read_step = ReadingStep::DataAndTag(len); - } - self.buffer.clear(); - } - - Ok(()) - } - - fn read_data(&mut self, dlen: usize) -> io::Result<()> { - let expect_length = dlen + self.tag_size; - self.read_exact(expect_length, false)?; - - if !self.sent_final { - { - // Ok, got data - self.data.clear(); - self.data.reserve(dlen); - unsafe { - self.data.set_len(dlen); // Decrypted data has exactly the same length - } - self.cipher.decrypt(&self.buffer[..], &mut *self.data)?; - } - - self.read_step = ReadingStep::Done; - self.buffer.clear(); - } - - Ok(()) - } - - fn read_some(&mut self) -> io::Result<()> { - while !self.sent_final { - match self.read_step { - ReadingStep::Length => self.read_length()?, - ReadingStep::DataAndTag(dlen) => { - self.read_data(dlen)?; - break; // Read finished! Break out - } - ReadingStep::Done => { - self.read_step = ReadingStep::Length; - self.data.clear(); - } - } - } - Ok(()) - } -} - -impl BufRead for DecryptedReader -where - R: AsyncRead, -{ - fn fill_buf(&mut self) -> io::Result<&[u8]> { + pub fn poll_read_decrypted( + &mut self, + ctx: &mut Context<'_>, + r: &mut R, + dst: &mut [u8], + ) -> Poll> + where + R: AsyncRead + Unpin, + { while self.pos >= self.data.len() { - if self.sent_final { - return Ok(&[]); + // Already received EOF + if self.got_final { + return Poll::Ready(Ok(0)); } - self.read_some()?; - if let ReadingStep::Done = self.read_step { - self.pos = 0; + // Refill buffer + match self.steps { + DecryptReadStep::Length => ready!(self.poll_read_decrypted_length(ctx, r))?, + DecryptReadStep::Data(len) => ready!(self.poll_read_decrypted_data(ctx, r, len))?, } } - Ok(&self.data[self.pos..]) + let remaining_len = self.data.len() - self.pos; + let n = cmp::min(dst.len(), remaining_len); + (&mut dst[..n]).copy_from_slice(&self.data[self.pos..self.pos + n]); + self.pos += n; + Poll::Ready(Ok(n)) } - fn consume(&mut self, amt: usize) { - self.pos = cmp::min(self.pos + amt, self.data.len()); - } -} + fn poll_read_decrypted_length(&mut self, ctx: &mut Context<'_>, r: &mut R) -> Poll> + where + R: AsyncRead + Unpin, + { + let buf_len = 2 + self.tag_size; + ready!(self.poll_read_exact(ctx, r, buf_len, true))?; + if self.got_final { + return Poll::Ready(Ok(())); + } -impl Read for DecryptedReader -where - R: AsyncRead, -{ - fn read(&mut self, buf: &mut [u8]) -> io::Result { - let nread = { - let mut available = self.fill_buf()?; - available.read(buf)? + // Done reading, decrypt it + let len = { + let mut len_buf = [0u8; 2]; + self.cipher.decrypt(&self.buffer[..], &mut len_buf)?; + BigEndian::read_u16(&len_buf) as usize }; - self.consume(nread); - Ok(nread) + + // Clear buffer before overwriting it + self.buffer.clear(); + self.data.clear(); + self.pos = 0; + + // Next step, read data + self.steps = DecryptReadStep::Data(len); + self.buffer.reserve(len + self.tag_size); + self.data.reserve(len); + + Poll::Ready(Ok(())) + } + + fn poll_read_decrypted_data(&mut self, ctx: &mut Context<'_>, r: &mut R, size: usize) -> Poll> + where + R: AsyncRead + Unpin, + { + let buf_len = size + self.tag_size; + ready!(self.poll_read_exact(ctx, r, buf_len, false))?; + + // Done reading data, decrypt it + unsafe { + // It has enough space, I am sure about that + let buffer = slice::from_raw_parts_mut(self.data.bytes_mut().as_mut_ptr() as *mut u8, size); + self.cipher.decrypt(&self.buffer[..], buffer)?; + + // Move forward the pointer + self.data.advance_mut(size); + } + + // Clear buffer before overwriting it + self.buffer.clear(); + + // Reset read position + self.pos = 0; + + // Next step, read length + self.steps = DecryptReadStep::Length; + self.buffer.reserve(2 + self.tag_size); + + Poll::Ready(Ok(())) + } + + fn poll_read_exact( + &mut self, + ctx: &mut Context<'_>, + r: &mut R, + size: usize, + allow_eof: bool, + ) -> Poll> + where + R: AsyncRead + Unpin, + { + while self.buffer.len() < size { + let remaining = size - self.buffer.len(); + unsafe { + // It has enough space, I am sure about that + let buffer = slice::from_raw_parts_mut(self.buffer.bytes_mut().as_mut_ptr() as *mut u8, remaining); + let n = ready!(Pin::new(&mut *r).poll_read(ctx, buffer))?; + if n == 0 { + if self.buffer.is_empty() && allow_eof && !self.got_final { + // Read nothing + self.got_final = true; + return Poll::Ready(Ok(())); + } else { + use std::io::ErrorKind; + return Poll::Ready(Err(ErrorKind::UnexpectedEof.into())); + } + } + self.buffer.advance_mut(n); + } + } + + Poll::Ready(Ok(())) } } -impl DecryptedRead for DecryptedReader -where - R: AsyncRead, -{ - fn buffer_size(&self, data: &[u8]) -> usize { - 2 + self.tag_size // len and len_tag - + data.len() + self.tag_size // data and data_tag - } +enum EncryptWriteStep { + Nothing, + Writing(BytesMut, usize), } -impl AsyncRead for DecryptedReader where R: AsyncRead {} - /// Writer wrapper that will encrypt data automatically -pub struct EncryptedWriter -where - W: AsyncWrite, -{ - writer: W, +pub struct EncryptedWriter { cipher: BoxAeadEncryptor, tag_size: usize, + steps: EncryptWriteStep, } -impl EncryptedWriter -where - W: AsyncWrite, -{ +impl EncryptedWriter { /// Creates a new EncryptedWriter - pub fn new(w: W, t: CipherType, key: &[u8], nonce: &[u8]) -> EncryptedWriter { + pub fn new(t: CipherType, key: &[u8], nonce: &[u8]) -> EncryptedWriter { EncryptedWriter { - writer: w, cipher: crypto::new_aead_encryptor(t, key, nonce), tag_size: t.tag_size(), + steps: EncryptWriteStep::Nothing, } } -} -impl EncryptedWrite for EncryptedWriter -where - W: AsyncWrite, -{ - fn write_raw(&mut self, data: &[u8]) -> io::Result { - self.writer.write(data) + pub fn poll_write_encrypted(&mut self, ctx: &mut Context<'_>, w: &mut W, data: &[u8]) -> Poll> + where + W: AsyncWrite + Unpin, + { + ready!(self.poll_write_all_encrypted(ctx, w, data))?; + Poll::Ready(Ok(data.len())) } - fn flush(&mut self) -> io::Result<()> { - self.writer.flush() - } - - fn encrypt(&mut self, data: &[u8], buf: &mut B) -> io::Result<()> { + pub fn poll_write_all_encrypted(&mut self, ctx: &mut Context<'_>, w: &mut W, data: &[u8]) -> Poll> + where + W: AsyncWrite + Unpin, + { // Data.Len is a 16-bit big-endian integer indicating the length of Data. It should be smaller than 0x3FFF. assert!( data.len() <= MAX_PACKET_SIZE, "Buffer size too large, AEAD encryption protocol requires buffer to be smaller than 0x3FFF" ); - let output_length = self.buffer_size(data); - let data_length = data.len() as u16; + loop { + match self.steps { + EncryptWriteStep::Nothing => { + let output_length = self.buffer_size(data); + let data_length = data.len() as u16; - let mut data_len_buf = [0u8; 2]; - BigEndian::write_u16(&mut data_len_buf, data_length); + let mut buf = BytesMut::with_capacity(output_length); - let output_length_size = 2 + self.tag_size; - self.cipher - .encrypt(&data_len_buf, unsafe { &mut buf.bytes_mut()[..output_length_size] }); - self.cipher - .encrypt(data, unsafe { &mut buf.bytes_mut()[output_length_size..output_length] }); + let mut data_len_buf = [0u8; 2]; + BigEndian::write_u16(&mut data_len_buf, data_length); - unsafe { - buf.advance_mut(output_length); + unsafe { + let b = slice::from_raw_parts_mut(buf.bytes_mut().as_mut_ptr() as *mut u8, output_length); + + let output_length_size = 2 + self.tag_size; + self.cipher.encrypt(&data_len_buf, &mut b[..output_length_size]); + self.cipher.encrypt(data, &mut b[output_length_size..output_length]); + + buf.advance_mut(output_length); + } + + self.steps = EncryptWriteStep::Writing(buf, 0); + } + EncryptWriteStep::Writing(ref mut buf, ref mut pos) => { + while *pos < buf.len() { + let n = ready!(Pin::new(&mut *w).poll_write(ctx, &buf[*pos..]))?; + if n == 0 { + use std::io::ErrorKind; + return Poll::Ready(Err(ErrorKind::UnexpectedEof.into())); + } + *pos += n; + } + + self.steps = EncryptWriteStep::Nothing; + return Poll::Ready(Ok(())); + } + } } - - Ok(()) } fn buffer_size(&self, data: &[u8]) -> usize { diff --git a/src/relay/tcprelay/client.rs b/src/relay/tcprelay/client.rs index c226dc1e..ab421e56 100644 --- a/src/relay/tcprelay/client.rs +++ b/src/relay/tcprelay/client.rs @@ -1,20 +1,21 @@ //! TCP relay client implementation use std::{ - io::{self, Read, Write}, + io, net::SocketAddr, + pin::Pin, sync::Arc, + task::{Context, Poll}, }; -use futures::{self, Async, Future, Poll}; use log::trace; -use tokio::net::TcpStream; -use tokio_io::{io::flush, AsyncRead, AsyncWrite}; +use tokio::{net::TcpStream, prelude::*}; use crate::relay::socks5::{ self, Address, Command, HandshakeRequest, HandshakeResponse, Reply, TcpRequestHeader, TcpResponseHeader, }; +use super::{CryptoStream, STcpStream}; use crate::{config::ServerConfig, context::SharedContext}; /// Socks5 proxy client @@ -24,131 +25,117 @@ pub struct Socks5Client { impl Socks5Client { /// Connects to `addr` via `proxy` - pub fn connect(addr: A, proxy: SocketAddr) -> impl Future + Send + pub async fn connect(addr: A, proxy: &SocketAddr) -> io::Result where Address: From, - A: Send + 'static, { - futures::lazy(move || TcpStream::connect(&proxy)) - .and_then(move |s| { - // 1. Handshake - let hs = HandshakeRequest::new(vec![socks5::SOCKS5_AUTH_METHOD_NONE]); - trace!("Client connected, going to send handshake: {:?}", hs); + let mut s = TcpStream::connect(proxy).await?; - hs.write_to(s) - .and_then(flush) - .and_then(HandshakeResponse::read_from) - .and_then(|(s, rsp)| { - trace!("Got handshake response: {:?}", rsp); - assert_eq!(rsp.chosen_method, socks5::SOCKS5_AUTH_METHOD_NONE); - Ok(s) - }) - }) - .and_then(move |s| { - // 2. Send request header - let h = TcpRequestHeader::new(Command::TcpConnect, From::from(addr)); - trace!("Going to connect, req: {:?}", h); - h.write_to(s) - .and_then(flush) - .and_then(|s| TcpResponseHeader::read_from(s).map_err(From::from)) - .and_then(|(s, rsp)| { - trace!("Got response: {:?}", rsp); - match rsp.reply { - Reply::Succeeded => Ok(s), - r => { - let err = io::Error::new(io::ErrorKind::Other, format!("{}", r)); - Err(err) - } - } - }) - }) - .map(|s| Socks5Client { stream: s }) + // 1. Handshake + let hs = HandshakeRequest::new(vec![socks5::SOCKS5_AUTH_METHOD_NONE]); + trace!("Client connected, going to send handshake: {:?}", hs); + + hs.write_to(&mut s).await?; + s.flush().await?; + + let hsp = HandshakeResponse::read_from(&mut s).await?; + + trace!("Got handshake response: {:?}", hsp); + assert_eq!(hsp.chosen_method, socks5::SOCKS5_AUTH_METHOD_NONE); + + // 2. Send request header + let h = TcpRequestHeader::new(Command::TcpConnect, From::from(addr)); + trace!("Going to connect, req: {:?}", h); + + h.write_to(&mut s).await?; + s.flush().await?; + let hp = TcpResponseHeader::read_from(&mut s).await?; + + trace!("Got response: {:?}", hp); + match hp.reply { + Reply::Succeeded => (), + r => { + let err = io::Error::new(io::ErrorKind::Other, format!("{}", r)); + return Err(err); + } + } + + Ok(Socks5Client { stream: s }) } /// UDP Associate `addr` via `proxy` - pub fn udp_associate( - addr: A, - proxy: SocketAddr, - ) -> impl Future + Send + pub async fn udp_associate(addr: A, proxy: &SocketAddr) -> io::Result<(Socks5Client, Address)> where Address: From, - A: Send + 'static, { - futures::lazy(move || TcpStream::connect(&proxy)) - .and_then(move |s| { - // 1. Handshake - let hs = HandshakeRequest::new(vec![socks5::SOCKS5_AUTH_METHOD_NONE]); - trace!("Client connected, going to send handshake: {:?}", hs); + let mut s = TcpStream::connect(proxy).await?; - hs.write_to(s) - .and_then(flush) - .and_then(HandshakeResponse::read_from) - .and_then(|(s, rsp)| { - trace!("Got handshake response: {:?}", rsp); - assert_eq!(rsp.chosen_method, socks5::SOCKS5_AUTH_METHOD_NONE); - Ok(s) - }) - }) - .and_then(move |s| { - // 2. Send request header - let h = TcpRequestHeader::new(Command::UdpAssociate, From::from(addr)); - trace!("Going to connect, req: {:?}", h); - h.write_to(s) - .and_then(flush) - .and_then(|s| TcpResponseHeader::read_from(s).map_err(From::from)) - .and_then(|(s, rsp)| { - trace!("Got response: {:?}", rsp); - match rsp.reply { - Reply::Succeeded => Ok((s, rsp.address)), - r => { - let err = io::Error::new(io::ErrorKind::Other, format!("{}", r)); - Err(err) - } - } - }) - }) - .map(|(s, a)| (Socks5Client { stream: s }, a)) + // 1. Handshake + let hs = HandshakeRequest::new(vec![socks5::SOCKS5_AUTH_METHOD_NONE]); + trace!("Client connected, going to send handshake: {:?}", hs); + + hs.write_to(&mut s).await?; + s.flush().await?; + + let hsp = HandshakeResponse::read_from(&mut s).await?; + + trace!("Got handshake response: {:?}", hsp); + assert_eq!(hsp.chosen_method, socks5::SOCKS5_AUTH_METHOD_NONE); + + // 2. Send request header + let h = TcpRequestHeader::new(Command::UdpAssociate, From::from(addr)); + trace!("Going to connect, req: {:?}", h); + + h.write_to(&mut s).await?; + s.flush().await?; + let hp = TcpResponseHeader::read_from(&mut s).await?; + + trace!("Got response: {:?}", hp); + match hp.reply { + Reply::Succeeded => (), + r => { + let err = io::Error::new(io::ErrorKind::Other, format!("{}", r)); + return Err(err); + } + } + + Ok((Socks5Client { stream: s }, hp.address)) } } -impl Read for Socks5Client { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.stream.read(buf) +impl AsyncRead for Socks5Client { + fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context, buf: &mut [u8]) -> Poll> { + Pin::new(&mut self.stream).poll_read(cx, buf) } } -impl Write for Socks5Client { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.stream.write(buf) - } - - fn flush(&mut self) -> io::Result<()> { - self.stream.flush() - } -} - -impl AsyncRead for Socks5Client {} impl AsyncWrite for Socks5Client { - fn shutdown(&mut self) -> Poll<(), io::Error> { - // FIXME: Finalize the internal cipher - Ok(Async::Ready(())) + fn poll_write(mut self: Pin<&mut Self>, cx: &mut Context, buf: &[u8]) -> Poll> { + Pin::new(&mut self.stream).poll_write(cx, buf) + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + Pin::new(&mut self.stream).poll_flush(cx) + } + + fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + Pin::new(&mut self.stream).poll_shutdown(cx) } } pub(crate) struct ServerClient { - pub r: super::DecryptedHalf, - pub w: super::EncryptedHalf, + pub stream: CryptoStream, } impl ServerClient { - pub(crate) fn connect( + pub(crate) async fn connect( context: SharedContext, - addr: Address, + addr: &Address, svr_cfg: Arc, - ) -> impl Future { - super::connect_proxy_server(context, svr_cfg.clone()) - .and_then(|svr_s| super::proxy_server_handshake(svr_s, svr_cfg, addr)) - .and_then(|(svr_r, svr_w)| svr_r.join(svr_w)) - .map(|(r, w)| ServerClient { r, w }) + ) -> io::Result { + let stream = super::connect_proxy_server(context, svr_cfg.clone()).await?; + Ok(ServerClient { + stream: super::proxy_server_handshake(stream, svr_cfg, addr).await?, + }) } } diff --git a/src/relay/tcprelay/context.rs b/src/relay/tcprelay/context.rs index d3b99850..ad79ba83 100644 --- a/src/relay/tcprelay/context.rs +++ b/src/relay/tcprelay/context.rs @@ -4,7 +4,7 @@ use std::{ io, net::SocketAddr, sync::{ - atomic::{AtomicBool, AtomicUsize, Ordering}, + atomic::{AtomicUsize, Ordering}, Arc, }, time::Duration, @@ -13,19 +13,16 @@ use std::{ use crate::{ config::{ServerAddr, ServerConfig}, context::SharedContext, - relay::{boxed_future, dns_resolver::resolve}, }; -use futures::{future, Future, Stream}; use log::debug; -use tokio::{self, net::UdpSocket, timer::Interval}; +use tokio::{self, net::UdpSocket, time}; /// TCP Relay Server Context pub struct TcpServerContext { tx: AtomicUsize, rx: AtomicUsize, context: SharedContext, - stop_flag: Arc, svr_cfg: Arc, } @@ -36,34 +33,28 @@ pub type SharedTcpServerContext = Arc; impl TcpServerContext { /// Create a new server context pub fn new(context: SharedContext, svr_cfg: Arc) -> SharedTcpServerContext { - let stop_flag = Arc::new(AtomicBool::new(false)); - let ctx = TcpServerContext { tx: AtomicUsize::new(0), rx: AtomicUsize::new(0), context, - stop_flag: stop_flag.clone(), svr_cfg, }; let ctx = Arc::new(ctx); if ctx.context.config().manager_address.is_some() { - let ctx2 = ctx.clone(); + let ctx = ctx.clone(); + tokio::spawn(async move { + let mut interval = time::interval(UPDATE_INTERVAL); + while ctx.context.server_running() { + interval.tick().await; - let fut = Interval::new_interval(UPDATE_INTERVAL) - .map_err(|e| io::Error::new(io::ErrorKind::Other, format!("timer error: {}", e))) - .for_each(move |_| { - let ctx = ctx2.clone(); - if stop_flag.load(Ordering::Acquire) { - // Finished - Err(io::Error::new(io::ErrorKind::Other, "server terminated")) - } else { - tokio::spawn(ctx.stat_interval().map_err(|_| ())); - Ok(()) - } - }); - tokio::spawn(fut.map_err(|_| ())); + let ctx = ctx.clone(); + tokio::spawn(async move { + let _ = ctx.stat_interval().await; + }); + } + }); } ctx @@ -85,32 +76,41 @@ impl TcpServerContext { self.rx.fetch_add(x, Ordering::Release); } - fn stat_interval(&self) -> impl Future + Send { - let addr_fut = match self.context.config().manager_address { - Some(ServerAddr::SocketAddr(ref addr)) => boxed_future(future::ok(*addr)), - Some(ServerAddr::DomainName(ref domain, ref port)) => { - let fut = resolve(self.context.clone(), &domain[..], *port, false).map(|vec_ipaddr| vec_ipaddr[0]); - boxed_future(fut) - } - None => unreachable!(), - }; - + async fn stat_interval(&self) -> io::Result<()> { let svr_cfg = self.svr_cfg.clone(); let transmission = self.tx.load(Ordering::Acquire) + self.rx.load(Ordering::Acquire); - addr_fut - .and_then(move |addr| { - let any_addr = "0.0.0.0:0".parse::().unwrap(); - UdpSocket::bind(&any_addr).map(move |socket| (socket, addr)) - }) - .and_then(move |(socket, addr)| { - let payload = format!("stat: {{\"{}\": {}}}", svr_cfg.addr().port(), transmission); - debug!("Sending Tcp Relay to {}, payload: {}", addr, payload); - socket.send_dgram(payload, &addr).map(|_| ()) - }) - } + let any_addr = "0.0.0.0:0".parse::().unwrap(); + let mut socket = UdpSocket::bind(&any_addr).await?; - pub fn close(&self) { - self.stop_flag.store(true, Ordering::Release) + let payload = format!("stat: {{\"{}\": {}}}", svr_cfg.addr().port(), transmission); + + let maddr = self + .context + .config() + .manager_address + .as_ref() + .expect("manager_address must not be None"); + + debug!("Sending Tcp Relay to {}, payload: {}", maddr, payload); + + match maddr { + ServerAddr::SocketAddr(ref addr) => { + socket.send_to(payload.as_ref(), addr).await?; + } + #[cfg(feature = "trust-dns")] + ServerAddr::DomainName(ref domain, ref port) => { + use crate::relay::dns_resolver::resolve; + + let addrs = resolve(self.context.clone(), &domain[..], *port, false).await?; + socket.send_to(payload.as_ref(), addrs[0]).await?; + } + #[cfg(not(feature = "trust-dns"))] + ServerAddr::DomainName(ref domain, ref port) => { + socket.send_to(payload.as_ref(), (domain.as_str(), *port)).await?; + } + } + + Ok(()) } } diff --git a/src/relay/tcprelay/crypto_io.rs b/src/relay/tcprelay/crypto_io.rs index f9c18bf4..999a3966 100644 --- a/src/relay/tcprelay/crypto_io.rs +++ b/src/relay/tcprelay/crypto_io.rs @@ -1,450 +1,254 @@ //! IO facilities for TCP relay use std::{ - io::{self, BufRead, Read}, - mem, - time::{Duration, Instant}, + io, + marker::{PhantomData, Unpin}, + pin::Pin, + sync::Arc, + task::{Context, Poll}, }; -use bytes::{BufMut, BytesMut}; -use futures::{Async, Future, Poll}; -use tokio::timer::Delay; -use tokio_io::{ - io::{copy, Copy}, - try_nb, AsyncRead, AsyncWrite, -}; +use byte_string::ByteStr; +use bytes::Bytes; +use futures::ready; +use log::trace; +use tokio::prelude::*; use super::{ - utils::{copy_timeout, copy_timeout_opt, CopyTimeout, CopyTimeoutOpt}, - BUFFER_SIZE, + aead::{DecryptedReader as AeadDecryptedReader, EncryptedWriter as AeadEncryptedWriter}, + stream::{DecryptedReader as StreamDecryptedReader, EncryptedWriter as StreamEncryptedWriter}, }; -static DUMMY_BUFFER: [u8; BUFFER_SIZE] = [0u8; BUFFER_SIZE]; +use crate::{config::ServerConfig, crypto::CipherCategory}; -/// Reader to read data from ShadowSocks protocol -/// -/// This trait requires `BufRead`, because obviously this reader has to contain a buffer inside, -/// which stores the decrypted data. -pub trait DecryptedRead: BufRead + AsyncRead { - /// Decrypt buffer size - fn buffer_size(&self, data: &[u8]) -> usize; - - /// Copies all data to `w` - fn copy(self, w: W) -> Copy - where - Self: Sized, - W: AsyncWrite, - { - copy(self, w) - } - - /// Copies all data to `w`, return `TimedOut` if timeout reaches - fn copy_timeout(self, w: W, timeout: Duration) -> CopyTimeout - where - Self: Sized, - W: AsyncWrite, - { - copy_timeout(self, w, timeout) - } - - /// The same as `copy_timeout`, but has optional `timeout` - fn copy_timeout_opt(self, w: W, timeout: Option) -> CopyTimeoutOpt - where - Self: Sized, - W: AsyncWrite, - { - copy_timeout_opt(self, w, timeout) - } +enum DecryptedReader { + Aead(AeadDecryptedReader), + Stream(StreamDecryptedReader), } -/// Writer that encrypt data and write it as ShadowSocks protocol -/// -/// The writer cannot implement `io::Write`, because you cannot ensure that you can write all the data in a batch -/// in non-blocking I/O environment. -pub trait EncryptedWrite { - /// Writes raw bytes directly to the writer - fn write_raw(&mut self, data: &[u8]) -> io::Result; - /// Flush the writer - fn flush(&mut self) -> io::Result<()>; - /// Encrypt data into buffer for writing - fn encrypt(&mut self, data: &[u8], buf: &mut B) -> io::Result<()>; - /// Encrypt buffer size - fn buffer_size(&self, data: &[u8]) -> usize; - - /// Encrypt data in `buf` and write all to the writer - fn write_all>(self, buf: B) -> EncryptedWriteAll - where - Self: Sized, - { - EncryptedWriteAll::new(self, buf) - } - - /// Copies all data from `r` - fn copy(self, r: R) -> EncryptedCopy - where - Self: Sized, - { - EncryptedCopy::new(r, self) - } - - /// Copies all data from `r` with timeout - fn copy_timeout(self, r: R, timeout: Duration) -> EncryptedCopyTimeout - where - Self: Sized, - { - EncryptedCopyTimeout::new(r, self, timeout) - } - - /// Copies all data from `r` with optional timeout - fn copy_timeout_opt(self, r: R, timeout: Option) -> EncryptedCopyOpt - where - Self: Sized, - { - match timeout { - Some(t) => EncryptedCopyOpt::CopyTimeout(self.copy_timeout(r, t)), - None => EncryptedCopyOpt::Copy(self.copy(r)), - } - } +enum EncryptedWriter { + Aead(AeadEncryptedWriter), + Stream(StreamEncryptedWriter), } -/// Write all data encrypted -pub enum EncryptedWriteAll -where - W: EncryptedWrite, - B: AsRef<[u8]>, -{ - Writing { - writer: W, - buf: B, - pos: usize, - enc_buf: BytesMut, - encrypted: bool, - }, - Empty, +enum ReadStatus { + WaitIv(Vec, usize), + Established, } -impl EncryptedWriteAll -where - W: EncryptedWrite, - B: AsRef<[u8]>, -{ - fn new(w: W, buf: B) -> EncryptedWriteAll { - let buffer_size = w.buffer_size(&DUMMY_BUFFER); - EncryptedWriteAll::Writing { - writer: w, - buf, - pos: 0, - enc_buf: BytesMut::with_capacity(buffer_size), - encrypted: false, - } - } +enum WriteStatus { + SendIv(Bytes, usize), + Established, } -impl Future for EncryptedWriteAll -where - W: EncryptedWrite, - B: AsRef<[u8]>, -{ - type Error = io::Error; - type Item = (W, B); +pub struct CryptoStream { + stream: S, + dec: Option, + enc: Option, + svr_cfg: Arc, + read_status: ReadStatus, + write_status: WriteStatus, +} - fn poll(&mut self) -> Poll { - match *self { - EncryptedWriteAll::Empty => panic!("poll after EncryptedWriteAll finished"), - EncryptedWriteAll::Writing { - ref mut writer, - ref buf, - ref mut pos, - ref mut enc_buf, - ref mut encrypted, - } => { - if !*encrypted { - *encrypted = true; +impl Unpin for CryptoStream {} - // Ensure buffer has enough space - let buffer_len = writer.buffer_size(buf.as_ref()); - enc_buf.reserve(buffer_len); +impl CryptoStream { + pub fn new(stream: S, svr_cfg: Arc) -> CryptoStream { + let method = svr_cfg.method(); + let prev_len = match method.category() { + CipherCategory::Stream => method.iv_size(), + CipherCategory::Aead => method.salt_size(), + }; - writer.encrypt(buf.as_ref(), enc_buf)?; - } - - while *pos < enc_buf.len() { - let n = try_nb!(writer.write_raw(&enc_buf[*pos..])); - *pos += n; - if n == 0 { - let err = io::Error::new(io::ErrorKind::Other, "zero-length write"); - return Err(err); - } - } + let local_iv = match method.category() { + CipherCategory::Stream => { + let local_iv = method.gen_init_vec(); + trace!("Generated Stream cipher IV {:?}", local_iv); + local_iv } - } + CipherCategory::Aead => { + let local_salt = method.gen_salt(); + trace!("Generated AEAD cipher salt {:?}", local_salt); + local_salt + } + }; - match mem::replace(self, EncryptedWriteAll::Empty) { - EncryptedWriteAll::Writing { writer, buf, .. } => Ok((writer, buf).into()), - EncryptedWriteAll::Empty => unreachable!(), + CryptoStream { + stream, + dec: None, + enc: None, + svr_cfg, + read_status: ReadStatus::WaitIv(vec![0u8; prev_len], 0usize), + write_status: WriteStatus::SendIv(local_iv, 0usize), } } } -/// Encrypted copy -pub struct EncryptedCopy +impl CryptoStream where - R: Read, - W: EncryptedWrite, + S: AsyncRead + Unpin, { - reader: Option, - writer: Option, - read_done: bool, - amt: u64, - pos: usize, - cap: usize, - buf: BytesMut, -} - -impl EncryptedCopy -where - R: Read, - W: EncryptedWrite, -{ - fn new(r: R, w: W) -> EncryptedCopy { - let buffer_size = w.buffer_size(&DUMMY_BUFFER); - EncryptedCopy { - reader: Some(r), - writer: Some(w), - read_done: false, - amt: 0, - pos: 0, - cap: 0, - buf: BytesMut::with_capacity(buffer_size), - } - } -} - -impl Future for EncryptedCopy -where - R: Read, - W: EncryptedWrite, -{ - type Error = io::Error; - type Item = (u64, R, W); - - fn poll(&mut self) -> Poll { - let mut local_buf = [0u8; BUFFER_SIZE]; - loop { - // If our buffer is empty, then we need to read some data to - // continue. - if self.pos == self.cap && !self.read_done { - let n = try_nb!(self.reader.as_mut().unwrap().read(&mut local_buf[..])); - self.buf.clear(); + fn poll_read_handshake(&mut self, cx: &mut Context<'_>) -> Poll> { + if let ReadStatus::WaitIv(ref mut buf, ref mut pos) = self.read_status { + while *pos < buf.len() { + let n = ready!(Pin::new(&mut self.stream).poll_read(cx, &mut buf[*pos..]))?; if n == 0 { - self.read_done = true; - } else { - let data = &local_buf[..n]; - // Ensure we have enough space - let buffer_len = self.writer.as_mut().unwrap().buffer_size(data); - self.buf.reserve(buffer_len); - - self.writer.as_mut().unwrap().encrypt(data, &mut self.buf)?; + use std::io::ErrorKind; + return Poll::Ready(Err(ErrorKind::UnexpectedEof.into())); } - self.pos = 0; - self.cap = self.buf.len(); + *pos += n; } - // If our buffer has some data, let's write it out! - while self.pos < self.cap { - let i = try_nb!(self.writer.as_mut().unwrap().write_raw(&self.buf[self.pos..self.cap])); - self.pos += i; - self.amt += i as u64; - } + let method = self.svr_cfg.method(); + let dec = match method.category() { + CipherCategory::Stream => { + trace!("Got Stream cipher IV {:?}", ByteStr::new(&buf)); + DecryptedReader::Stream(StreamDecryptedReader::new(method, self.svr_cfg.key(), &buf)) + } + CipherCategory::Aead => { + trace!("Got AEAD cipher salt {:?}", ByteStr::new(&buf)); + DecryptedReader::Aead(AeadDecryptedReader::new(method, self.svr_cfg.key(), &buf)) + } + }; - // If we've written al the data and we've seen EOF, flush out the - // data and finish the transfer. - // done with the entire transfer. - if self.pos == self.cap && self.read_done { - try_nb!(self.writer.as_mut().unwrap().flush()); - return Ok((self.amt, self.reader.take().unwrap(), self.writer.take().unwrap()).into()); - } + self.dec = Some(dec); + self.read_status = ReadStatus::Established; + } + + Poll::Ready(Ok(())) + } + + fn priv_poll_read(mut self: Pin<&mut Self>, ctx: &mut Context<'_>, buf: &mut [u8]) -> Poll> { + ready!(self.poll_read_handshake(ctx))?; + + let stream = unsafe { &mut *(&mut self.stream as *mut _) }; + match *self.dec.as_mut().unwrap() { + DecryptedReader::Aead(ref mut r) => r.poll_read_decrypted(ctx, stream, buf), + DecryptedReader::Stream(ref mut r) => r.poll_read_decrypted(ctx, stream, buf), } } } -/// Encrypted copy -pub struct EncryptedCopyTimeout +impl CryptoStream where - R: Read, - W: EncryptedWrite, + S: AsyncWrite + Unpin, { - reader: Option, - writer: Option, - read_done: bool, - amt: u64, - pos: usize, - cap: usize, - timeout: Duration, - timer: Option, - read_buf: [u8; BUFFER_SIZE], - write_buf: BytesMut, -} - -impl EncryptedCopyTimeout -where - R: Read, - W: EncryptedWrite, -{ - fn new(r: R, w: W, dur: Duration) -> EncryptedCopyTimeout { - let buffer_size = w.buffer_size(&DUMMY_BUFFER); - EncryptedCopyTimeout { - reader: Some(r), - writer: Some(w), - read_done: false, - amt: 0, - pos: 0, - cap: 0, - timeout: dur, - timer: None, - read_buf: [0u8; BUFFER_SIZE], - write_buf: BytesMut::with_capacity(buffer_size), - } - } - - fn try_poll_timeout(&mut self) -> io::Result<()> { - match self.timer.as_mut() { - None => Ok(()), - Some(t) => match t.poll() { - Err(err) => panic!("Failed to poll on timer, err: {}", err), - Ok(Async::Ready(..)) => Err(io::Error::new(io::ErrorKind::TimedOut, "connection timed out")), - Ok(Async::NotReady) => Ok(()), - }, - } - } - - fn clear_timer(&mut self) { - let _ = self.timer.take(); - } - - fn read_or_set_timeout(&mut self) -> io::Result { - // First, return if timeout - self.try_poll_timeout()?; - - // Then, unset the previous timeout - self.clear_timer(); - - self.write_buf.clear(); - match self.reader.as_mut().unwrap().read(&mut self.read_buf) { - Ok(0) => { - self.cap = 0; - self.pos = 0; - Ok(0) - } - Ok(n) => { - let data = &self.read_buf[..n]; - // Ensoure we have enough space - let buffer_len = self.writer.as_mut().unwrap().buffer_size(data); - self.write_buf.reserve(buffer_len); - - self.writer.as_mut().unwrap().encrypt(data, &mut self.write_buf)?; - self.cap = self.write_buf.len(); - self.pos = 0; - Ok(n) - } - Err(e) => { - if e.kind() == io::ErrorKind::WouldBlock { - self.timer = Some(Delay::new(Instant::now() + self.timeout)); - } - Err(e) - } - } - } - - fn write_or_set_timeout(&mut self) -> io::Result { - // First, return if timeout - self.try_poll_timeout()?; - - // Then, unset the previous timeout - self.clear_timer(); - - match self - .writer - .as_mut() - .unwrap() - .write_raw(&self.write_buf[self.pos..self.cap]) - { - Ok(n) => { - self.pos += n; - Ok(n) - } - Err(e) => { - if e.kind() == io::ErrorKind::WouldBlock { - self.timer = Some(Delay::new(Instant::now() + self.timeout)); - } - Err(e) - } - } - } -} - -impl Future for EncryptedCopyTimeout -where - R: Read, - W: EncryptedWrite, -{ - type Error = io::Error; - type Item = (u64, R, W); - - fn poll(&mut self) -> Poll { - loop { - // If our buffer is empty, then we need to read some data to - // continue. - if self.pos == self.cap && !self.read_done { - let n = try_nb!(self.read_or_set_timeout()); + fn poll_write_handshake(&mut self, cx: &mut Context<'_>) -> Poll> { + if let WriteStatus::SendIv(ref iv, ref mut pos) = self.write_status { + while *pos < iv.len() { + let n = ready!(Pin::new(&mut self.stream).poll_write(cx, &iv[*pos..]))?; if n == 0 { - self.read_done = true; + use std::io::ErrorKind; + return Poll::Ready(Err(ErrorKind::UnexpectedEof.into())); } + *pos += n; } - // If our buffer has some data, let's write it out! - while self.pos < self.cap { - let i = try_nb!(self.write_or_set_timeout()); - if i == 0 { - let err = io::Error::new(io::ErrorKind::WriteZero, "copied zero bytes into writer"); - return Err(err); + let method = self.svr_cfg.method(); + let enc = match method.category() { + CipherCategory::Stream => { + trace!("Sent Stream cipher IV {:?}", ByteStr::new(&iv)); + EncryptedWriter::Stream(StreamEncryptedWriter::new(method, self.svr_cfg.key(), &iv)) } - self.amt += i as u64; - } + CipherCategory::Aead => { + trace!("Sent AEAD cipher salt {:?}", ByteStr::new(&iv)); + EncryptedWriter::Aead(AeadEncryptedWriter::new(method, self.svr_cfg.key(), &iv)) + } + }; - // If we've written al the data and we've seen EOF, flush out the - // data and finish the transfer. - // done with the entire transfer. - if self.pos == self.cap && self.read_done { - try_nb!(self.writer.as_mut().unwrap().flush()); - return Ok((self.amt, self.reader.take().unwrap(), self.writer.take().unwrap()).into()); - } + self.enc = Some(enc); + self.write_status = WriteStatus::Established; } + + Poll::Ready(Ok(())) + } + + fn priv_poll_write(mut self: Pin<&mut Self>, ctx: &mut Context<'_>, buf: &[u8]) -> Poll> { + ready!(self.poll_write_handshake(ctx))?; + + let stream = unsafe { &mut *(&mut self.stream as *mut _) }; + match *self.enc.as_mut().unwrap() { + EncryptedWriter::Aead(ref mut w) => w.poll_write_encrypted(ctx, stream, buf), + EncryptedWriter::Stream(ref mut w) => w.poll_write_encrypted(ctx, stream, buf), + } + } + + fn priv_poll_flush(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll> { + AsyncWrite::poll_flush(Pin::new(&mut self.stream), ctx) + } + + fn priv_poll_shutdown(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll> { + AsyncWrite::poll_shutdown(Pin::new(&mut self.stream), ctx) } } -/// Work for both timeout or no timeout -pub enum EncryptedCopyOpt +impl CryptoStream where - R: Read, - W: EncryptedWrite, + S: AsyncRead + AsyncWrite + Unpin, { - Copy(EncryptedCopy), - CopyTimeout(EncryptedCopyTimeout), + pub fn split(&mut self) -> (CryptoStreamReadHalf<'_, S>, CryptoStreamWriteHalf<'_, S>) { + let p = self as *mut _; + ( + CryptoStreamReadHalf(p, PhantomData), + CryptoStreamWriteHalf(p, PhantomData), + ) + } } -impl Future for EncryptedCopyOpt +impl AsyncRead for CryptoStream where - R: Read, - W: EncryptedWrite, + S: AsyncRead + Unpin, { - type Error = io::Error; - type Item = (u64, R, W); - - fn poll(&mut self) -> Poll { - match *self { - EncryptedCopyOpt::Copy(ref mut c) => c.poll(), - EncryptedCopyOpt::CopyTimeout(ref mut c) => c.poll(), - } + fn poll_read(self: Pin<&mut Self>, ctx: &mut Context<'_>, buf: &mut [u8]) -> Poll> { + self.priv_poll_read(ctx, buf) + } +} + +impl AsyncWrite for CryptoStream +where + S: AsyncWrite + Unpin, +{ + fn poll_write(self: Pin<&mut Self>, ctx: &mut Context<'_>, buf: &[u8]) -> Poll> { + self.priv_poll_write(ctx, buf) + } + + fn poll_flush(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll> { + self.priv_poll_flush(ctx) + } + + fn poll_shutdown(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll> { + self.priv_poll_shutdown(ctx) + } +} + +pub struct CryptoStreamReadHalf<'a, S: 'a>(*mut CryptoStream, PhantomData<&'a S>); + +unsafe impl<'a, S: Send + 'a> Send for CryptoStreamReadHalf<'a, S> {} + +impl<'a, S: AsyncRead + Unpin + 'a> AsyncRead for CryptoStreamReadHalf<'a, S> { + fn poll_read(mut self: Pin<&mut Self>, ctx: &mut Context<'_>, buf: &mut [u8]) -> Poll> { + let stream = unsafe { &mut *self.0 }; + Pin::new(stream).priv_poll_read(ctx, buf) + } +} + +pub struct CryptoStreamWriteHalf<'a, S: 'a>(*mut CryptoStream, PhantomData<&'a S>); + +unsafe impl<'a, S: Send + 'a> Send for CryptoStreamWriteHalf<'a, S> {} + +impl<'a, S: AsyncWrite + Unpin + 'a> AsyncWrite for CryptoStreamWriteHalf<'a, S> { + fn poll_write(mut self: Pin<&mut Self>, ctx: &mut Context<'_>, buf: &[u8]) -> Poll> { + let stream = unsafe { &mut *self.0 }; + Pin::new(stream).priv_poll_write(ctx, buf) + } + + fn poll_flush(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll> { + let stream = unsafe { &mut *self.0 }; + Pin::new(stream).priv_poll_flush(ctx) + } + + fn poll_shutdown(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll> { + let stream = unsafe { &mut *self.0 }; + Pin::new(stream).priv_poll_shutdown(ctx) } } diff --git a/src/relay/tcprelay/local.rs b/src/relay/tcprelay/local.rs index 2c07d247..72606326 100644 --- a/src/relay/tcprelay/local.rs +++ b/src/relay/tcprelay/local.rs @@ -2,12 +2,13 @@ use std::io; -use futures::Future; - -use super::socks5_local; +use super::{socks5_local, tunnel_local}; use crate::context::SharedContext; /// Starts a TCP local server -pub fn run(context: SharedContext) -> impl Future + Send { - socks5_local::run(context) +pub async fn run(context: SharedContext) -> io::Result<()> { + match context.config().forward { + Some(..) => tunnel_local::run(context).await, + None => socks5_local::run(context).await, + } } diff --git a/src/relay/tcprelay/mod.rs b/src/relay/tcprelay/mod.rs index c29ccc93..05b85ea0 100644 --- a/src/relay/tcprelay/mod.rs +++ b/src/relay/tcprelay/mod.rs @@ -1,40 +1,30 @@ //! Relay for TCP implementation -use std::{ - io::{self, BufRead, Read, Write}, - iter::{IntoIterator, Iterator}, - mem, - net::SocketAddr, - sync::Arc, - time::Duration, -}; +// Allow for futures +// Maybe removed in the future +#![allow(clippy::unnecessary_mut_passed)] -use crate::{ - config::{ConfigType, ServerAddr, ServerConfig}, - context::SharedContext, - crypto::CipherCategory, - relay::{boxed_future, dns_resolver::resolve, socks5::Address}, -}; +use std::io; +use std::marker::Unpin; +use std::ops::{Deref, DerefMut}; +use std::pin::Pin; +use std::sync::Arc; +use std::task::{Context, Poll}; +use std::time::Duration; -use byte_string::ByteStr; -use bytes::{BufMut, BytesMut}; -use futures::{self, Async, Future, Poll}; -use log::{error, trace}; -use tokio::{ - net::{tcp::ConnectFuture, TcpStream}, - timer::Timeout, -}; -use tokio_io::{ - io::{read_exact, write_all, ReadHalf, WriteHalf}, - try_nb, AsyncRead, AsyncWrite, -}; +use crate::config::{ConfigType, ServerAddr, ServerConfig}; +use crate::context::SharedContext; +use crate::relay::socks5::Address; +use crate::relay::utils::try_timeout; -pub use self::crypto_io::{DecryptedRead, EncryptedWrite}; - -use self::{ - aead::{DecryptedReader as AeadDecryptedReader, EncryptedWriter as AeadEncryptedWriter}, - stream::{DecryptedReader as StreamDecryptedReader, EncryptedWriter as StreamEncryptedWriter}, -}; +use bytes::BytesMut; +use futures::future::FusedFuture; +use futures::{ready, select, Future}; +use log::trace; +use tokio::io::{ReadHalf, WriteHalf}; +use tokio::net::TcpStream; +use tokio::prelude::*; +use tokio::time::{self, Delay}; mod aead; pub mod client; @@ -45,224 +35,144 @@ mod monitor; pub mod server; mod socks5_local; mod stream; +mod tunnel_local; mod utils; +pub use self::crypto_io::CryptoStream; + const BUFFER_SIZE: usize = 8 * 1024; // 8K buffer -// /// Directions in the tunnel -// #[derive(Debug, Copy, Clone)] -// pub enum TunnelDirection { -// /// Client -> Server -// Client2Server, -// /// Client <- Server -// Server2Client, -// } - -type TcpReadHalf = ReadHalf; -type TcpWriteHalf = WriteHalf; - -/// `ReadHalf `of `TcpStream` with decryption -pub enum DecryptedHalf -where - S: Read + Write + AsyncRead + AsyncWrite + Send, -{ - Stream(StreamDecryptedReader>), - Aead(AeadDecryptedReader>), +/// Shadowsocks' Connection +/// +/// The only feature: Supports timeout +pub struct Connection { + // Actual connection socket + stream: S, + // Timer instance + // Read and Write operations shares the same timer + timer: Option, + // User defined server timeout + timeout: Option, } -macro_rules! ref_half_do { - ($self:expr,$t:ident,$m:ident$(,$p:expr)*) => { - match *$self { - $t::Stream(ref d) => d.$m($($p),*), - $t::Aead(ref d) => d.$m($($p),*), +impl Connection { + pub fn new(stream: S, timeout: Option) -> Connection { + Connection { + stream, + timer: None, + timeout, } } -} -macro_rules! mut_half_do { - ($self:expr,$t:ident,$m:ident$(,$p:expr)*) => { - match *$self { - $t::Stream(ref mut d) => d.$m($($p),*), - $t::Aead(ref mut d) => d.$m($($p),*), - } - } -} - -impl Read for DecryptedHalf -where - S: Read + Write + AsyncRead + AsyncWrite + Send, -{ - fn read(&mut self, buf: &mut [u8]) -> io::Result { - mut_half_do!(self, DecryptedHalf, read, buf) - } -} - -impl DecryptedRead for DecryptedHalf -where - S: Read + Write + AsyncRead + AsyncWrite + Send, -{ - fn buffer_size(&self, data: &[u8]) -> usize { - ref_half_do!(self, DecryptedHalf, buffer_size, data) - } -} - -impl AsyncRead for DecryptedHalf where S: Read + Write + AsyncRead + AsyncWrite + Send {} - -impl BufRead for DecryptedHalf -where - S: Read + Write + AsyncRead + AsyncWrite + Send, -{ - fn fill_buf(&mut self) -> io::Result<&[u8]> { - mut_half_do!(self, DecryptedHalf, fill_buf) - } - - fn consume(&mut self, amt: usize) { - mut_half_do!(self, DecryptedHalf, consume, amt) - } -} - -impl From>> for DecryptedHalf -where - S: Read + Write + AsyncRead + AsyncWrite + Send, -{ - fn from(r: StreamDecryptedReader>) -> DecryptedHalf { - DecryptedHalf::Stream(r) - } -} - -impl From>> for DecryptedHalf -where - S: Read + Write + AsyncRead + AsyncWrite + Send, -{ - fn from(r: AeadDecryptedReader>) -> DecryptedHalf { - DecryptedHalf::Aead(r) - } -} - -/// `WriteHalf` of `TcpStream` with encryption -pub enum EncryptedHalf -where - S: Read + Write + AsyncRead + AsyncWrite + Send, -{ - Stream(StreamEncryptedWriter>), - Aead(AeadEncryptedWriter>), -} - -impl EncryptedWrite for EncryptedHalf -where - S: Read + Write + AsyncRead + AsyncWrite + Send, -{ - fn write_raw(&mut self, data: &[u8]) -> io::Result { - mut_half_do!(self, EncryptedHalf, write_raw, data) - } - - fn flush(&mut self) -> io::Result<()> { - mut_half_do!(self, EncryptedHalf, flush) - } - - fn encrypt(&mut self, data: &[u8], buf: &mut B) -> io::Result<()> { - mut_half_do!(self, EncryptedHalf, encrypt, data, buf) - } - - fn buffer_size(&self, data: &[u8]) -> usize { - ref_half_do!(self, EncryptedHalf, buffer_size, data) - } -} - -impl From>> for EncryptedHalf -where - S: Read + Write + AsyncRead + AsyncWrite + Send, -{ - fn from(d: StreamEncryptedWriter>) -> EncryptedHalf { - EncryptedHalf::Stream(d) - } -} - -impl From>> for EncryptedHalf -where - S: Read + Write + AsyncRead + AsyncWrite + Send, -{ - fn from(d: AeadEncryptedWriter>) -> EncryptedHalf { - EncryptedHalf::Aead(d) - } -} - -/// Try to connect every IPs one by one -enum TcpStreamConnect> { - Empty, - Connect { - last_err: Option, - addr_iter: I, - opt_stream_new: Option<(ConnectFuture, SocketAddr)>, - }, -} - -impl> TcpStreamConnect { - fn new(iter: I) -> TcpStreamConnect { - TcpStreamConnect::Connect { - last_err: None, - addr_iter: iter, - opt_stream_new: None, - } - } -} - -impl> Future for TcpStreamConnect { - type Error = io::Error; - type Item = TcpStream; - - fn poll(&mut self) -> Poll { + fn make_timeout_error() -> io::Error { use std::io::ErrorKind; + ErrorKind::TimedOut.into() + } - match *self { - TcpStreamConnect::Empty => unreachable!(), - TcpStreamConnect::Connect { - ref mut last_err, - ref mut addr_iter, - ref mut opt_stream_new, - } => { - loop { - // 1. Poll before doing anything else - if let Some((ref mut stream_new, ref addr)) = *opt_stream_new { - match stream_new.poll() { - Ok(Async::Ready(stream)) => return Ok(Async::Ready(stream)), - Ok(Async::NotReady) => return Ok(Async::NotReady), - Err(ref err) if err.kind() == ErrorKind::WouldBlock => return Ok(Async::NotReady), - Err(err) => { - error!("Failed to connect {}: {}", addr, err); - - *last_err = Some(err); - } - } - } - - match addr_iter.next() { - None => break, - Some(addr) => { - *opt_stream_new = Some((TcpStream::connect(&addr), addr)); - } - } + fn poll_timeout(&mut self, cx: &mut Context<'_>) -> Poll> { + loop { + if let Some(ref mut timer) = self.timer { + ready!(Pin::new(timer).poll(cx)); + // FIXME: Clear self.timer or not? + return Poll::Ready(Err(Connection::::make_timeout_error())); + } else { + match self.timeout { + Some(timeout) => self.timer = Some(time::delay_for(timeout)), + None => break, } } } + Poll::Ready(Ok(())) + } - match mem::replace(self, TcpStreamConnect::Empty) { - TcpStreamConnect::Empty => unreachable!(), - TcpStreamConnect::Connect { last_err, .. } => match last_err { - None => { - let err = io::Error::new(ErrorKind::Other, "connect TCP without any addresses"); - Err(err) - } - Some(err) => Err(err), - }, + fn cancel_timeout(&mut self) { + let _ = self.timer.take(); + } +} + +impl Connection +where + S: AsyncRead + AsyncWrite + Unpin, +{ + pub fn split(self) -> (ReadHalf>, WriteHalf>) { + use tokio::io::split; + split(self) + } +} + +impl Deref for Connection { + type Target = S; + + fn deref(&self) -> &Self::Target { + &self.stream + } +} + +impl DerefMut for Connection { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.stream + } +} + +impl Unpin for Connection {} + +impl AsyncRead for Connection +where + S: AsyncRead + Unpin, +{ + fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll> { + match Pin::new(&mut self.stream).poll_read(cx, buf) { + Poll::Ready(r) => { + self.cancel_timeout(); + Poll::Ready(r) + } + Poll::Pending => { + ready!(self.poll_timeout(cx))?; + Poll::Pending + } } } } -fn connect_proxy_server( - context: SharedContext, - svr_cfg: Arc, -) -> impl Future + Send { +impl AsyncWrite for Connection +where + S: AsyncWrite + Unpin, +{ + fn poll_write(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll> { + match Pin::new(&mut self.stream).poll_write(cx, buf) { + Poll::Ready(r) => { + self.cancel_timeout(); + Poll::Ready(r) + } + Poll::Pending => { + ready!(self.poll_timeout(cx))?; + Poll::Pending + } + } + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + match Pin::new(&mut self.stream).poll_flush(cx) { + Poll::Ready(r) => { + self.cancel_timeout(); + Poll::Ready(r) + } + Poll::Pending => { + ready!(self.poll_timeout(cx))?; + Poll::Pending + } + } + } + + fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.stream).poll_shutdown(cx) + } +} + +pub type STcpStream = Connection; + +/// Connect to proxy server with `ServerConfig` +async fn connect_proxy_server(context: SharedContext, svr_cfg: Arc) -> io::Result { let timeout = svr_cfg.timeout(); let svr_addr = match context.config().config_type { @@ -273,286 +183,90 @@ fn connect_proxy_server( trace!("Connecting to proxy {:?}, timeout: {:?}", svr_addr, timeout); match svr_addr { ServerAddr::SocketAddr(ref addr) => { - let fut = try_timeout(TcpStream::connect(addr), timeout); - boxed_future(fut) + let stream = try_timeout(TcpStream::connect(addr), timeout).await?; + Ok(STcpStream::new(stream, timeout)) } + #[cfg(feature = "trust-dns")] ServerAddr::DomainName(ref domain, port) => { - let fut = { - try_timeout(resolve(context, &domain[..], *port, false), timeout).and_then(move |vec_ipaddr| { - let fut = TcpStreamConnect::new(vec_ipaddr.into_iter()); - try_timeout(fut, timeout) - }) - }; - boxed_future(fut) + use crate::relay::dns_resolver::resolve; + use log::error; + + let vec_ipaddr = try_timeout(resolve(context, &domain[..], *port, false), timeout).await?; + + assert!(!vec_ipaddr.is_empty()); + + let mut last_err: Option = None; + for addr in &vec_ipaddr { + match try_timeout(TcpStream::connect(addr), timeout).await { + Ok(s) => return Ok(STcpStream::new(s, timeout)), + Err(e) => { + error!( + "Failed to connect {}:{}, resolved address {}, try another (err: {})", + domain, port, addr, e + ); + last_err = Some(e); + } + } + } + + let err = last_err.unwrap(); + error!( + "Failed to connect {}:{}, tried all addresses but still failed (last err: {})", + domain, port, err + ); + Err(err) + } + #[cfg(not(feature = "trust-dns"))] + ServerAddr::DomainName(ref domain, port) => { + let stream = try_timeout(TcpStream::connect((domain.as_str(), *port)), timeout).await?; + Ok(STcpStream::new(stream, timeout)) } } } /// Handshake logic for ShadowSocks Client -pub fn proxy_server_handshake( - remote_stream: S, +pub async fn proxy_server_handshake( + remote_stream: STcpStream, svr_cfg: Arc, - relay_addr: Address, -) -> impl Future< - Item = ( - impl Future, Error = io::Error> + Send, - impl Future, Error = io::Error> + Send, - ), - Error = io::Error, -> + Send -where - S: Read + Write + AsyncRead + AsyncWrite + Send + 'static, -{ - let timeout = svr_cfg.timeout(); - proxy_handshake(remote_stream, svr_cfg).and_then(move |(r_fut, w_fut)| { - let w_fut = w_fut.and_then(move |enc_w| { - // Send relay address to remote - let mut buf = BytesMut::with_capacity(relay_addr.serialized_len()); - relay_addr.write_to_buf(&mut buf); + relay_addr: &Address, +) -> io::Result> { + let mut stream = CryptoStream::new(remote_stream, svr_cfg.clone()); - trace!( - "Got encrypt stream and going to send addr: {:?}, buf: {:?}", - relay_addr, - buf - ); + trace!("Got encrypt stream and going to send addr: {:?}", relay_addr); - try_timeout(enc_w.write_all(buf), timeout).map(|(w, _)| w) - }); + // Send relay address to remote + let mut addr_buf = BytesMut::with_capacity(relay_addr.serialized_len()); + relay_addr.write_to_buf(&mut addr_buf); + stream.write_all(&addr_buf).await?; - Ok((r_fut, w_fut)) - }) -} - -/// ShadowSocks Client-Server handshake protocol -/// Exchange cipher IV and creates stream wrapper -pub fn proxy_handshake( - remote_stream: S, - svr_cfg: Arc, -) -> impl Future< - Item = ( - impl Future, Error = io::Error> + Send, - impl Future, Error = io::Error> + Send, - ), - Error = io::Error, -> + Send -where - S: Read + Write + AsyncRead + AsyncWrite + Send + 'static, -{ - futures::lazy(|| Ok(remote_stream.split())).and_then(move |(r, w)| { - let timeout = svr_cfg.timeout(); - - let svr_cfg_cloned = svr_cfg.clone(); - - let enc = { - // Encrypt data to remote server - - // Send initialize vector to remote and create encryptor - - let method = svr_cfg.method(); - let prev_buf = match method.category() { - CipherCategory::Stream => { - let local_iv = method.gen_init_vec(); - trace!("Going to send initialize vector: {:?}", local_iv); - local_iv - } - CipherCategory::Aead => { - let local_salt = method.gen_salt(); - trace!("Going to send salt: {:?}", local_salt); - local_salt - } - }; - - try_timeout(write_all(w, prev_buf), timeout).and_then(move |(w, prev_buf)| { - match svr_cfg.method().category() { - CipherCategory::Stream => { - let local_iv = prev_buf; - let wtr = StreamEncryptedWriter::new(w, svr_cfg.method(), svr_cfg.key(), &local_iv); - Ok(From::from(wtr)) - } - CipherCategory::Aead => { - let local_salt = prev_buf; - let wtr = AeadEncryptedWriter::new(w, svr_cfg.method(), svr_cfg.key(), &local_salt[..]); - Ok(From::from(wtr)) - } - } - }) - }; - - let dec = { - let svr_cfg = svr_cfg_cloned; - - // Decrypt data from remote server - - let method = svr_cfg.method(); - let prev_len = match method.category() { - CipherCategory::Stream => method.iv_size(), - CipherCategory::Aead => method.salt_size(), - }; - - try_timeout(read_exact(r, vec![0u8; prev_len]), timeout).and_then(move |(r, remote_iv)| { - match svr_cfg.method().category() { - CipherCategory::Stream => { - trace!("Got initialize vector {:?}", ByteStr::new(&remote_iv)); - let decrypt_stream = StreamDecryptedReader::new(r, svr_cfg.method(), svr_cfg.key(), &remote_iv); - Ok(From::from(decrypt_stream)) - } - CipherCategory::Aead => { - trace!("Got salt {:?}", ByteStr::new(&remote_iv)); - let dr = AeadDecryptedReader::new(r, svr_cfg.method(), svr_cfg.key(), &remote_iv); - Ok(From::from(dr)) - } - } - }) - }; - - Ok((dec, enc)) - }) + Ok(stream) } /// Establish tunnel between server and client // pub fn tunnel(addr: Address, c2s: CF, s2c: SF) -> impl Future + Send -pub fn tunnel(c2s: CF, s2c: SF) -> impl Future + Send +pub async fn tunnel(mut c2s: CF, mut s2c: SF) -> io::Result<()> where - CF: Future + Send + 'static, - SF: Future + Send + 'static, + CF: Future> + Unpin + FusedFuture, + SF: Future> + Unpin + FusedFuture, { - c2s.map(|_| ()).select(s2c.map(|_| ())).then(|r| match r { - Ok(..) => Ok(()), - Err((err, _)) => Err(err), - }) - - // let addr = Arc::new(addr); - - // let cloned_addr = addr.clone(); - // let c2s = c2s.then(move |res| { - // match res { - // Ok(..) => { - // // Continue reading response from remote server - // trace!("Relay {} client -> server is finished", cloned_addr); - - // Ok(TunnelDirection::Client2Server) - // } - // Err(err) => { - // error!("Relay {} client -> server aborted: {}", cloned_addr, err); - // Err(err) - // } - // } - // }); - - // let cloned_addr = addr.clone(); - // let s2c = s2c.then(move |res| match res { - // Ok(..) => { - // trace!("Relay {} client <- server is finished", cloned_addr); - - // Ok(TunnelDirection::Server2Client) - // } - // Err(err) => { - // error!("Relay {} client <- server aborted: {}", cloned_addr, err); - // Err(err) - // } - // }); - - // c2s.select(s2c) - // .and_then(move |(dir, _)| { - // match dir { - // TunnelDirection::Server2Client => trace!("Relay {} client <- server is closed, abort connection", addr), - // TunnelDirection::Client2Server => trace!("Relay {} server -> client is closed, abort connection", addr), - // } - - // Ok(()) - // }) - // .map_err(|(err, _)| err) -} - -/// Read until EOF, and ignore -pub enum IgnoreUntilEnd { - Pending { r: R, amt: u64 }, - Empty, -} - -impl Future for IgnoreUntilEnd { - type Error = io::Error; - type Item = u64; - - fn poll(&mut self) -> Poll { - match *self { - IgnoreUntilEnd::Empty => panic!("poll IgnoreUntilEnd after it is finished"), - IgnoreUntilEnd::Pending { ref mut r, ref mut amt } => { - let mut buf = [0u8; 4096]; - loop { - let n = try_nb!(r.read(&mut buf)); - *amt += n as u64; - - if n == 0 { - break; - } - } - } - } - - match mem::replace(self, IgnoreUntilEnd::Empty) { - IgnoreUntilEnd::Pending { amt, .. } => Ok(amt.into()), - IgnoreUntilEnd::Empty => unreachable!(), - } + select! { + r1 = c2s => r1.map(|_| ()), + r2 = s2c => r2.map(|_| ()), } } -/// Ignore all data from the reader -pub fn ignore_until_end(r: R) -> IgnoreUntilEnd { - IgnoreUntilEnd::Pending { r, amt: 0 } -} - -pub enum TimeoutFuture +pub async fn ignore_until_end(r: &mut R) -> io::Result where - T: 'static, - F: Future + Send + 'static, + R: AsyncRead + Unpin, { - Direct(F), - Wait(Timeout), -} - -impl Future for TimeoutFuture -where - T: 'static, - F: Future + Send + 'static, -{ - type Error = io::Error; - type Item = T; - - fn poll(&mut self) -> Poll { - match *self { - TimeoutFuture::Direct(ref mut f) => f.poll(), - TimeoutFuture::Wait(ref mut f) => match f.poll() { - Ok(Async::NotReady) => Ok(Async::NotReady), - Ok(Async::Ready(x)) => Ok(Async::Ready(x)), - Err(err) => match err.into_inner() { - Some(e) => Err(e), - None => Err(io::Error::new(io::ErrorKind::TimedOut, "connection timed out")), - }, - }, + let mut buf = [0u8; BUFFER_SIZE]; + let mut amt = 0u64; + loop { + let n = r.read(&mut buf).await?; + if n == 0 { + break; } + amt += n as u64; } + Ok(amt) } - -fn try_timeout(fut: F, dur: Option) -> impl Future + Send -where - F: Future + Send + 'static, - T: 'static, -{ - use tokio::prelude::*; - - match dur { - Some(dur) => TimeoutFuture::Wait(fut.timeout(dur)), - None => TimeoutFuture::Direct(fut), - } -} - -// fn io_timeout(fut: F, dur: Duration) -> impl Future + Send -// where -// F: Future + Send + 'static, -// T: 'static, -// { -// use tokio::prelude::*; - -// TimeoutFuture::Wait(fut.timeout(dur)) -// } diff --git a/src/relay/tcprelay/monitor.rs b/src/relay/tcprelay/monitor.rs index 73072fce..182a51ad 100644 --- a/src/relay/tcprelay/monitor.rs +++ b/src/relay/tcprelay/monitor.rs @@ -1,67 +1,74 @@ //! Server traffic monitor use std::{ - io::{self, Read, Write}, + io, + marker::Unpin, ops::{Deref, DerefMut}, + pin::Pin, + task::{Context, Poll}, }; -use tokio::{ - io::{AsyncRead, AsyncWrite}, - net::TcpStream, - prelude::Async, -}; +use tokio::io::{AsyncRead, AsyncWrite}; use super::context::SharedTcpServerContext; -pub struct TcpMonStream { - stream: TcpStream, +pub struct TcpMonStream { + stream: S, context: SharedTcpServerContext, } -impl TcpMonStream { - pub fn new(c: SharedTcpServerContext, s: TcpStream) -> TcpMonStream { +impl TcpMonStream { + pub fn new(c: SharedTcpServerContext, s: S) -> TcpMonStream { TcpMonStream { stream: s, context: c } } } -impl Read for TcpMonStream { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - let n = self.stream.read(buf)?; +impl AsyncRead for TcpMonStream +where + S: AsyncRead + Unpin, +{ + fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll> { + let n = match Pin::new(&mut self.stream).poll_read(cx, buf)? { + Poll::Ready(n) => n, + Poll::Pending => return Poll::Pending, + }; self.context.incr_rx(n); - Ok(n) + Poll::Ready(Ok(n)) } } -impl Write for TcpMonStream { - fn write(&mut self, buf: &[u8]) -> io::Result { - let n = self.stream.write(buf)?; +impl AsyncWrite for TcpMonStream +where + S: AsyncWrite + Unpin, +{ + fn poll_write(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll> { + let n = match Pin::new(&mut self.stream).poll_write(cx, buf)? { + Poll::Ready(n) => n, + Poll::Pending => return Poll::Pending, + }; self.context.incr_tx(n); - Ok(n) + Poll::Ready(Ok(n)) } - fn flush(&mut self) -> io::Result<()> { - self.stream.flush() + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.stream).poll_flush(cx) + } + + fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.stream).poll_shutdown(cx) } } -impl AsyncRead for TcpMonStream {} +impl Deref for TcpMonStream { + type Target = S; -impl AsyncWrite for TcpMonStream { - fn shutdown(&mut self) -> Result, io::Error> { - AsyncWrite::shutdown(&mut self.stream) - } -} - -impl Deref for TcpMonStream { - type Target = TcpStream; - - fn deref(&self) -> &TcpStream { + fn deref(&self) -> &Self::Target { &self.stream } } -impl DerefMut for TcpMonStream { - fn deref_mut(&mut self) -> &mut TcpStream { +impl DerefMut for TcpMonStream { + fn deref_mut(&mut self) -> &mut Self::Target { &mut self.stream } } diff --git a/src/relay/tcprelay/server.rs b/src/relay/tcprelay/server.rs index 5176ce4d..4b8fe611 100644 --- a/src/relay/tcprelay/server.rs +++ b/src/relay/tcprelay/server.rs @@ -1,197 +1,33 @@ //! Relay for TCP server that running on the server side -use std::{ - io::{self, ErrorKind}, - net::SocketAddr, - sync::Arc, - time::Duration, -}; +use std::{io, net::SocketAddr, sync::Arc}; -use crate::relay::{ - boxed_future, - dns_resolver::resolve, - socks5::Address, - tcprelay::crypto_io::{DecryptedRead, EncryptedWrite}, -}; +use crate::relay::socks5::Address; use crate::context::SharedContext; use futures::{ - self, - stream::{futures_unordered, Stream}, - Future, + future::{self, Either}, + stream::{FuturesUnordered, StreamExt}, }; use log::{debug, error, info, trace}; use tokio::{ self, net::{TcpListener, TcpStream}, }; -use tokio_io::{ - io::{ReadHalf, WriteHalf}, - AsyncRead, -}; use super::{ context::{SharedTcpServerContext, TcpServerContext}, monitor::TcpMonStream, - proxy_handshake, try_timeout, tunnel, DecryptedHalf, EncryptedHalf, TcpStreamConnect, + CryptoStream, STcpStream, }; -/// Context for doing handshake with client -pub struct TcpRelayClientHandshake { - s: TcpMonStream, +#[allow(clippy::cognitive_complexity)] +async fn handle_client( svr_context: SharedTcpServerContext, -} - -impl TcpRelayClientHandshake { - #[inline] - fn error_handshake(peer_addr: SocketAddr) -> io::Error { - io::Error::new( - ErrorKind::Other, - format!( - "failed to decode Address, may be wrong method or key, peer: {}", - peer_addr - ), - ) - } - - /// Doing handshake with client - pub fn handshake( - self, - ) -> impl Future< - Item = TcpRelayClientPending< - impl Future, Error = io::Error> + Send + 'static, - >, - Error = io::Error, - > + Send { - let TcpRelayClientHandshake { s, svr_context } = self; - - futures::lazy(move || s.peer_addr().map(|p| (s, p))).and_then(|(s, peer_addr)| { - debug!("Handshaking with peer {}", peer_addr); - - let timeout = svr_context.svr_cfg().timeout(); - proxy_handshake(s, svr_context.svr_cfg().clone()).and_then(move |(r_fut, w_fut)| { - r_fut - .and_then(move |r| { - let fut = - Address::read_from(r).map_err(move |_| TcpRelayClientHandshake::error_handshake(peer_addr)); - try_timeout(fut, timeout) - }) - .map(move |(r, addr)| TcpRelayClientPending { - r, - addr, - w: w_fut, - timeout, - svr_context, - }) - }) - }) - } -} - -/// Context for connecting remote -pub struct TcpRelayClientPending -where - E: Future, Error = io::Error> + Send + 'static, -{ - r: DecryptedHalf, - addr: Address, - w: E, - timeout: Option, - svr_context: SharedTcpServerContext, -} - -/// Connect to the remote server -#[inline] -fn connect_remote( - context: SharedContext, - addr: Address, - timeout: Option, -) -> impl Future + Send { - debug!("Connecting to remote {}", addr); - - match addr { - Address::SocketAddress(saddr) => { - if context.config().forbidden_ip.contains(&saddr.ip()) { - let err = io::Error::new( - ErrorKind::Other, - format!("{} is forbidden, failed to connect {}", saddr.ip(), saddr), - ); - return boxed_future(futures::done(Err(err))); - } - - let conn = TcpStream::connect(&saddr); - let fut = try_timeout(conn, timeout); - boxed_future(fut) - } - Address::DomainNameAddress(dname, port) => { - let fut = { - try_timeout(resolve(context, dname.as_str(), port, true), timeout).and_then(move |addrs| { - let conn = TcpStreamConnect::new(addrs.into_iter()); - try_timeout(conn, timeout) - }) - }; - boxed_future(fut) - } - } -} - -impl TcpRelayClientPending -where - E: Future, Error = io::Error> + Send + 'static, -{ - /// Connect to the remote server - pub fn connect( - self, - ) -> impl Future< - Item = TcpRelayClientConnected< - impl Future, Error = io::Error> + Send + 'static, - >, - Error = io::Error, - > + Send { - let client_pair = (self.r, self.w); - let timeout = self.timeout; - connect_remote(self.svr_context.context().clone(), self.addr, self.timeout).map(move |stream| { - TcpRelayClientConnected { - server: stream.split(), - client: client_pair, - timeout, - } - }) - } -} - -/// Context for extablishing tunnel -pub struct TcpRelayClientConnected -where - E: Future, Error = io::Error> + Send + 'static, -{ - server: (ReadHalf, WriteHalf), - client: (DecryptedHalf, E), - timeout: Option, -} - -impl TcpRelayClientConnected -where - E: Future, Error = io::Error> + Send + 'static, -{ - /// Establish tunnel - #[inline] - pub fn tunnel(self) -> impl Future + Send { - let (svr_r, svr_w) = self.server; - let (r, w_fut) = self.client; - let timeout = self.timeout; - - tunnel( - r.copy_timeout_opt(svr_w, self.timeout), - w_fut.and_then(move |w| w.copy_timeout_opt(svr_r, timeout)), - ) - } -} - -fn handle_client(svr_context: SharedTcpServerContext, socket: TcpStream) -> impl Future + Send { - let socket = TcpMonStream::new(svr_context.clone(), socket); - + socket: TcpStream, + peer_addr: SocketAddr, +) -> io::Result<()> { if let Err(err) = socket.set_keepalive(svr_context.svr_cfg().timeout()) { error!("Failed to set keep alive: {:?}", err); } @@ -202,39 +38,167 @@ fn handle_client(svr_context: SharedTcpServerContext, socket: TcpStream) -> impl } } - futures::lazy(move || match socket.peer_addr() { - Ok(addr) => Ok((socket, addr)), + trace!( + "Got connection addr: {} with proxy server: {:?}", + peer_addr, + svr_context.svr_cfg() + ); + + let stream = TcpMonStream::new( + svr_context.clone(), + STcpStream::new(socket, svr_context.svr_cfg().timeout()), + ); + + // Do server-client handshake + // Perform encryption IV exchange + let mut stream = CryptoStream::new(stream, svr_context.svr_cfg().clone()); + + // Read remote Address + let remote_addr = match Address::read_from(&mut stream).await { + Ok(o) => o, Err(err) => { - error!("Failed to get peer_addr after accept: {}", err); - Err(()) + error!( + "Failed to decode Address, may be wrong method or key, peer {}", + peer_addr + ); + return Err(From::from(err)); } - }) - .and_then(move |(socket, addr)| { - trace!("Got connection, addr: {}", addr); - trace!("Picked proxy server: {:?}", svr_context.svr_cfg()); + }; - let client = TcpRelayClientHandshake { s: socket, svr_context }; + debug!("Relay {} <-> {} establishing", peer_addr, remote_addr); - client - .handshake() - .and_then(TcpRelayClientPending::connect) - .and_then(TcpRelayClientConnected::tunnel) - .map_err(move |err| { - error!("Failed to handle client ({}): {}", addr, err); - }) - }) + let context = svr_context.context(); + + let mut remote_stream = match remote_addr { + Address::SocketAddress(ref saddr) => { + if context.config().forbidden_ip.contains(&saddr.ip()) { + error!("{} is forbidden, failed to connect {}", saddr.ip(), saddr); + let err = io::Error::new( + io::ErrorKind::Other, + format!("{} is forbidden, failed to connect {}", saddr.ip(), saddr), + ); + return Err(err); + } + + match TcpStream::connect(saddr).await { + Ok(s) => { + debug!("Connected to remote {}", saddr); + s + } + Err(err) => { + error!("Failed to connect remote {}, {}", saddr, err); + return Err(err); + } + } + } + #[cfg(feature = "trust-dns")] + Address::DomainNameAddress(ref dname, port) => { + use crate::relay::dns_resolver::resolve; + + let addrs = match resolve(context.clone(), dname.as_str(), port, true).await { + Ok(r) => r, + Err(err) => { + error!("Failed to resolve {}, {}", dname, err); + return Err(err); + } + }; + + let mut last_err: Option = None; + let mut stream_opt = None; + for addr in &addrs { + match TcpStream::connect(addr).await { + Ok(s) => stream_opt = Some(s), + Err(err) => { + error!( + "Failed to connect remote {}:{} (resolved: {}), {}, try others", + dname, port, addr, err + ); + last_err = Some(err); + } + } + } + + match stream_opt { + Some(s) => { + debug!("Connected to remote {}:{}", dname, port); + s + } + None => { + let err = last_err.unwrap(); + error!("Failed to connect remote {}:{}, {}", dname, port, err); + return Err(io::Error::new(io::ErrorKind::Other, err)); + } + } + } + #[cfg(not(feature = "trust-dns"))] + Address::DomainNameAddress(ref dname, port) => { + let s = match TcpStream::connect((dname.as_str(), port)).await { + Ok(s) => { + debug!("Connected to remote {}:{}", dname, port); + s + } + Err(err) => { + error!("Failed to connect remote {}:{}, {}", dname, port, err); + return Err(err); + } + }; + + // Still need to check forbidden IPs + let forbidden_ip = &context.config().forbidden_ip; + if !forbidden_ip.is_empty() { + let peer_addr = s.peer_addr()?; + + if forbidden_ip.contains(&peer_addr.ip()) { + error!("{} is forbidden, failed to connect {}", peer_addr.ip(), peer_addr); + let err = io::Error::new( + io::ErrorKind::Other, + format!("{} is forbidden, failed to connect {}", peer_addr.ip(), peer_addr), + ); + return Err(err); + } + } + + s + } + }; + + debug!("Relay {} <-> {} established", peer_addr, remote_addr); + + let (mut cr, mut cw) = stream.split(); + let (mut sr, mut sw) = remote_stream.split(); + + use tokio::io::copy; + + // CLIENT -> SERVER + let rhalf = copy(&mut cr, &mut sw); + + // CLIENT <- SERVER + let whalf = copy(&mut sr, &mut cw); + + match future::select(rhalf, whalf).await { + Either::Left((Ok(_), _)) => trace!("Relay {} -> {} closed", peer_addr, remote_addr), + Either::Left((Err(err), _)) => trace!("Relay {} -> {} closed with error {:?}", peer_addr, remote_addr, err), + Either::Right((Ok(_), _)) => trace!("Relay {} <- {} closed", peer_addr, remote_addr), + Either::Right((Err(err), _)) => trace!("Relay {} <- {} closed with error {:?}", peer_addr, remote_addr, err), + } + + debug!("Relay {} <-> {} closing", peer_addr, remote_addr); + + Ok(()) } /// Runs the server -pub fn run(context: SharedContext) -> impl Future + Send { - let mut vec_fut = Vec::with_capacity(context.config().server.len()); +pub async fn run(context: SharedContext) -> io::Result<()> { + let vec_fut = FuturesUnordered::new(); for svr_cfg in &context.config().server { - let listener = { + let mut listener = { let addr = svr_cfg.plugin_addr().as_ref().unwrap_or_else(|| svr_cfg.addr()); let addr = addr.listen_addr(); - let listener = TcpListener::bind(&addr).unwrap_or_else(|err| panic!("Failed to listen, {}", err)); + let listener = TcpListener::bind(&addr) + .await + .unwrap_or_else(|err| panic!("Failed to listen, {}", err)); info!("ShadowSocks TCP Listening on {}", addr); listener @@ -244,44 +208,30 @@ pub fn run(context: SharedContext) -> impl Future let context = context.clone(); let svr_context = TcpServerContext::new(context.clone(), svr_cfg.clone()); - struct CloseGuard(SharedTcpServerContext); - impl Drop for CloseGuard { - fn drop(&mut self) { - self.0.close(); + vec_fut.push(async move { + loop { + match listener.accept().await { + Ok((socket, peer_addr)) => { + let svr_context = svr_context.clone(); + tokio::spawn(async move { + let _ = handle_client(svr_context, socket, peer_addr).await; + }); + } + Err(err) => { + error!("Server run failed: {}", err); + break; + } + } } - } - - let close_guard = CloseGuard(svr_context.clone()); - - let listening = listener - .incoming() - .for_each(move |socket| { - let svr_context = svr_context.clone(); - tokio::spawn(handle_client(svr_context, socket)); - Ok(()) - }) - .map_err(|err| { - error!("Server run failed: {}", err); - err - }) - .then(move |r| { - // Close the context to ensure reporting Future is terminated - drop(close_guard); - r - }); - - vec_fut.push(boxed_future(listening)); + }); } - futures_unordered(vec_fut).into_future().then(|res| match res { - Ok(..) => { - error!("One of TCP servers exited unexpectly without error"); + match vec_fut.into_future().await.0 { + Some(()) => { + error!("One of TCP servers exited unexpectly"); let err = io::Error::new(io::ErrorKind::Other, "server exited unexpectly"); Err(err) } - Err((err, ..)) => { - error!("One of TCP servers exited unexpectly with error {}", err); - Err(err) - } - }) + None => unreachable!(), + } } diff --git a/src/relay/tcprelay/socks5_local.rs b/src/relay/tcprelay/socks5_local.rs index 8e76115d..7600a64b 100644 --- a/src/relay/tcprelay/socks5_local.rs +++ b/src/relay/tcprelay/socks5_local.rs @@ -2,28 +2,25 @@ use std::{io, net::SocketAddr, sync::Arc}; -use futures::{self, stream::Stream, Future}; - +use futures::future::{self, Either}; use log::{debug, error, info, trace, warn}; use tokio::{ self, - net::{TcpListener, TcpStream}, -}; -use tokio_io::{ - io::{flush, ReadHalf, WriteHalf}, - AsyncRead, + net::{ + tcp::{ReadHalf, WriteHalf}, + TcpListener, TcpStream, + }, + prelude::*, }; use crate::{config::ServerConfig, context::SharedContext}; use crate::relay::{ - boxed_future, loadbalancing::server::{LoadBalancer, PingBalancer}, socks5::{self, Address, HandshakeRequest, HandshakeResponse, TcpRequestHeader, TcpResponseHeader}, - tcprelay::crypto_io::{DecryptedRead, EncryptedWrite}, }; -use super::{ignore_until_end, try_timeout, tunnel}; +use super::ignore_until_end; #[derive(Debug, Clone)] struct UdpConfig { @@ -31,62 +28,95 @@ struct UdpConfig { client_addr: SocketAddr, } -fn handle_socks5_connect( +async fn handle_socks5_connect<'a>( context: SharedContext, - (r, w): (ReadHalf, WriteHalf), + (mut r, mut w): (ReadHalf<'a>, WriteHalf<'a>), client_addr: SocketAddr, - addr: Address, + local_addr: SocketAddr, + addr: &Address, svr_cfg: Arc, -) -> impl Future + Send { - let timeout = svr_cfg.timeout(); - super::connect_proxy_server(context, svr_cfg.clone()) - .then(move |res| { - let (header, r) = match res { - Ok(svr_s) => { - trace!("Proxy server connected"); +) -> io::Result<()> { + let svr_s = match super::connect_proxy_server(context, svr_cfg.clone()).await { + Ok(svr_s) => { + trace!("Proxy server connected, {:?}", svr_cfg); - // Tell the client that we are ready - let header = TcpResponseHeader::new(socks5::Reply::Succeeded, Address::SocketAddress(client_addr)); - - (header, Ok(svr_s)) - } - Err(err) => { - use crate::relay::socks5::Reply; - use std::io::ErrorKind; - - error!("Failed to connect remote server, err: {}", err); - - let reply = match err.kind() { - ErrorKind::ConnectionRefused => Reply::ConnectionRefused, - ErrorKind::ConnectionAborted => Reply::HostUnreachable, - _ => Reply::NetworkUnreachable, - }; - - let header = TcpResponseHeader::new(reply, Address::SocketAddress(client_addr)); - - (header, Err(err)) - } - }; + // Tell the client that we are ready + let header = TcpResponseHeader::new(socks5::Reply::Succeeded, Address::SocketAddress(local_addr)); + header.write_to(&mut w).await?; + w.flush().await?; trace!("Sent header: {:?}", header); - try_timeout(header.write_to(w).and_then(flush), timeout).and_then(|w| match r { - Ok(svr_s) => Ok((svr_s, w)), - Err(err) => Err(err), - }) - }) - .and_then(move |(svr_s, w)| { - let timeout = svr_cfg.timeout(); - super::proxy_server_handshake(svr_s, svr_cfg, addr).and_then(move |(svr_r, svr_w)| { - let rhalf = svr_r.and_then(move |svr_r| svr_r.copy_timeout_opt(w, timeout)); - let whalf = svr_w.and_then(move |svr_w| svr_w.copy_timeout_opt(r, timeout)); - tunnel(whalf, rhalf) - }) - }) + + svr_s + } + Err(err) => { + use crate::relay::socks5::Reply; + use std::io::ErrorKind; + + error!("Failed to connect remote server, err: {}", err); + + let reply = match err.kind() { + ErrorKind::ConnectionRefused => Reply::ConnectionRefused, + ErrorKind::ConnectionAborted => Reply::HostUnreachable, + _ => Reply::NetworkUnreachable, + }; + + let header = TcpResponseHeader::new(reply, Address::SocketAddress(local_addr)); + header.write_to(&mut w).await?; + w.flush().await?; + + return Err(err); + } + }; + + let mut svr_s = super::proxy_server_handshake(svr_s, svr_cfg.clone(), addr).await?; + let (mut svr_r, mut svr_w) = svr_s.split(); + + use tokio::io::copy; + + let rhalf = copy(&mut r, &mut svr_w); + let whalf = copy(&mut svr_r, &mut w); + + debug!( + "CONNECT relay established {} <-> {} ({})", + client_addr, + svr_cfg.addr(), + addr + ); + + match future::select(rhalf, whalf).await { + Either::Left((Ok(..), _)) => trace!("CONNECT relay {} -> {} ({}) closed", client_addr, svr_cfg.addr(), addr), + Either::Left((Err(err), _)) => trace!( + "CONNECT relay {} -> {} ({}) closed with error {:?}", + client_addr, + svr_cfg.addr(), + err, + addr, + ), + Either::Right((Ok(..), _)) => trace!("CONNECT relay {} <- {} ({}) closed", client_addr, svr_cfg.addr(), addr), + Either::Right((Err(err), _)) => trace!( + "CONNECT relay {} <- {} ({}) closed with error {:?}", + client_addr, + svr_cfg.addr(), + err, + addr, + ), + } + + debug!( + "CONNECT relay {} <-> {} ({}) closing", + client_addr, + svr_cfg.addr(), + addr + ); + + Ok(()) } -fn handle_socks5_client( +#[allow(clippy::cognitive_complexity)] +async fn handle_socks5_client( context: SharedContext, - s: TcpStream, + mut s: TcpStream, conf: Arc, udp_conf: UdpConfig, ) -> io::Result<()> { @@ -101,120 +131,108 @@ fn handle_socks5_client( } let client_addr = s.peer_addr()?; - let cloned_client_addr = client_addr; - let fut = futures::lazy(|| Ok(s.split())) - .and_then(|(r, w)| { - // Socks5 handshakes - HandshakeRequest::read_from(r).and_then(move |(r, req)| { - trace!("Socks5 {:?}", req); + let local_addr = s.local_addr()?; - let (resp, res) = if !req.methods.contains(&socks5::SOCKS5_AUTH_METHOD_NONE) { - let resp = HandshakeResponse::new(socks5::SOCKS5_AUTH_METHOD_NOT_ACCEPTABLE); - warn!("Currently shadowsocks-rust does not support authentication"); - ( - resp, - Err(io::Error::new( - io::ErrorKind::Other, - "Currently shadowsocks-rust does not support authentication", - )), - ) - } else { - // Reply to client - let resp = HandshakeResponse::new(socks5::SOCKS5_AUTH_METHOD_NONE); - trace!("Reply handshake {:?}", resp); - (resp, Ok(())) - }; + let (mut r, mut w) = s.split(); - resp.write_to(w).and_then(flush).and_then(move |w| match res { - Ok(..) => Ok((r, w)), - Err(err) => Err(err), - }) - }) - }) - .and_then(move |(r, w)| { - // Fetch headers - TcpRequestHeader::read_from(r).then(move |res| match res { - Ok((r, h)) => boxed_future(futures::finished((r, w, h))), - Err(err) => { - error!("Failed to get TcpRequestHeader: {}", err); - let fut = TcpResponseHeader::new(err.reply, Address::SocketAddress(client_addr)) - .write_to(w) - .then(|_| Err(From::from(err))); - boxed_future(fut) - } - }) - }) - .and_then(move |(r, w, header)| { - trace!("Socks5 {:?}", header); + let handshake_req = HandshakeRequest::read_from(&mut r).await?; - let addr = header.address; - match header.command { - socks5::Command::TcpConnect => { - let enable_tcp = context.config().mode.enable_tcp(); - if enable_tcp { - debug!("CONNECT {}", addr); - let fut = handle_socks5_connect(context, (r, w), cloned_client_addr, addr.clone(), conf) - .map_err(move |err| { - io::Error::new(err.kind(), format!("CONNECT {} failed with error \"{}\"", addr, err)) - }); - boxed_future(fut) - } else { - warn!("CONNECT is not enabled"); - let fut = TcpResponseHeader::new(socks5::Reply::CommandNotSupported, addr) - .write_to(w) - .map(|_| ()); - boxed_future(fut) - } - } - socks5::Command::TcpBind => { - warn!("BIND is not supported"); - let fut = TcpResponseHeader::new(socks5::Reply::CommandNotSupported, addr) - .write_to(w) - .map(|_| ()); - boxed_future(fut) - } - socks5::Command::UdpAssociate => { - if udp_conf.enable_udp { - debug!("UDP ASSOCIATE {}", addr); - let fut = TcpResponseHeader::new(socks5::Reply::Succeeded, From::from(udp_conf.client_addr)) - .write_to(w) - .and_then(flush) - .and_then(|_| { - // Hold the connection until it ends by its own - ignore_until_end(r).map(|_| ()) - }); + // Socks5 handshakes + trace!("Socks5 {:?}", handshake_req); - boxed_future(fut) - } else { - warn!("UDP ASSOCIATE is not enabled"); - let fut = TcpResponseHeader::new(socks5::Reply::CommandNotSupported, addr) - .write_to(w) - .map(|_| ()); - boxed_future(fut) - } - } - } - }); + let (handshake_resp, res) = if !handshake_req.methods.contains(&socks5::SOCKS5_AUTH_METHOD_NONE) { + let resp = HandshakeResponse::new(socks5::SOCKS5_AUTH_METHOD_NOT_ACCEPTABLE); + warn!("Currently shadowsocks-rust does not support authentication"); + ( + resp, + Err(io::Error::new( + io::ErrorKind::Other, + "Currently shadowsocks-rust does not support authentication", + )), + ) + } else { + // Reply to client + let resp = HandshakeResponse::new(socks5::SOCKS5_AUTH_METHOD_NONE); + trace!("Reply handshake {:?}", resp); + (resp, Ok(())) + }; - tokio::spawn(fut.then(|res| match res { - Ok(..) => Ok(()), + handshake_resp.write_to(&mut w).await?; + w.flush().await?; + + res?; + + // Fetch headers + let header = match TcpRequestHeader::read_from(&mut r).await { + Ok(h) => h, Err(err) => { - if err.kind() != io::ErrorKind::BrokenPipe { - error!("Socks5 client: {}", err); - } - Err(()) + error!("Failed to get TcpRequestHeader: {}", err); + let rh = TcpResponseHeader::new(err.reply, Address::SocketAddress(client_addr)); + rh.write_to(&mut w).await?; + return Err(From::from(err)); } - })); + }; - Ok(()) + trace!("Socks5 {:?}", header); + + let addr = header.address; + match header.command { + socks5::Command::TcpConnect => { + let enable_tcp = context.config().mode.enable_tcp(); + if enable_tcp { + debug!("CONNECT {}", addr); + + match handle_socks5_connect(context, (r, w), client_addr, local_addr, &addr, conf).await { + Ok(..) => Ok(()), + Err(err) => Err(io::Error::new( + err.kind(), + format!("CONNECT {} failed with error \"{}\"", addr, err), + )), + } + } else { + warn!("CONNECT is not enabled"); + let rh = TcpResponseHeader::new(socks5::Reply::CommandNotSupported, addr); + rh.write_to(&mut w).await?; + + Ok(()) + } + } + socks5::Command::TcpBind => { + warn!("BIND is not supported"); + let rh = TcpResponseHeader::new(socks5::Reply::CommandNotSupported, addr); + rh.write_to(&mut w).await?; + + Ok(()) + } + socks5::Command::UdpAssociate => { + if udp_conf.enable_udp { + debug!("UDP ASSOCIATE {}", addr); + let rh = TcpResponseHeader::new(socks5::Reply::Succeeded, From::from(udp_conf.client_addr)); + rh.write_to(&mut w).await?; + w.flush().await?; + + // Hold the connection until it ends by its own + ignore_until_end(&mut r).await?; + + Ok(()) + } else { + warn!("UDP ASSOCIATE is not enabled"); + let rh = TcpResponseHeader::new(socks5::Reply::CommandNotSupported, addr); + rh.write_to(&mut w).await?; + + Ok(()) + } + } + } } /// Starts a TCP local server with Socks5 proxy protocol -pub fn run(context: SharedContext) -> impl Future + Send { +pub async fn run(context: SharedContext) -> io::Result<()> { let local_addr = *context.config().local.as_ref().expect("Missing local config"); - let listener = - TcpListener::bind(&local_addr).unwrap_or_else(|err| panic!("Failed to listen on {}, {}", local_addr, err)); + let mut listener = TcpListener::bind(&local_addr) + .await + .unwrap_or_else(|err| panic!("Failed to listen on {}, {}", local_addr, err)); let actual_local_addr = listener.local_addr().expect("Could not determine port bound to"); @@ -226,12 +244,20 @@ pub fn run(context: SharedContext) -> impl Future }; let mut servers = PingBalancer::new(context.clone()); - listener.incoming().for_each(move |socket| { + + loop { + let (socket, peer_addr) = listener.accept().await?; let server_cfg = servers.pick_server(); - trace!("Got connection, addr: {}", socket.peer_addr()?); + trace!("Got connection, addr: {}", peer_addr); trace!("Picked proxy server: {:?}", server_cfg); - handle_socks5_client(context.clone(), socket, server_cfg, udp_conf.clone()) - }) + let context = context.clone(); + let udp_conf = udp_conf.clone(); + tokio::spawn(async move { + if let Err(err) = handle_socks5_client(context, socket, server_cfg, udp_conf).await { + error!("Socks5 client {}", err); + } + }); + } } diff --git a/src/relay/tcprelay/stream.rs b/src/relay/tcprelay/stream.rs index 999ea405..ac35fa59 100644 --- a/src/relay/tcprelay/stream.rs +++ b/src/relay/tcprelay/stream.rs @@ -1,159 +1,145 @@ //! Stream protocol implementation use std::{ - cmp, - io::{self, BufRead, Read}, + cmp, io, + marker::Unpin, + pin::Pin, + task::{Context, Poll}, }; use crate::crypto::{new_stream, BoxStreamCipher, CipherType, CryptoMode}; use bytes::{BufMut, BytesMut}; -use tokio_io::{AsyncRead, AsyncWrite}; +use futures::ready; +use tokio::prelude::*; -use super::{DecryptedRead, EncryptedWrite, BUFFER_SIZE}; +use super::BUFFER_SIZE; const DUMMY_BUFFER: [u8; BUFFER_SIZE] = [0u8; BUFFER_SIZE]; /// Reader wrapper that will decrypt data automatically -pub struct DecryptedReader -where - R: AsyncRead, -{ - reader: R, +pub struct DecryptedReader { buffer: BytesMut, cipher: BoxStreamCipher, pos: usize, - sent_final: bool, + got_final: bool, + incoming_buffer: Vec, } -impl DecryptedReader -where - R: AsyncRead, -{ - pub fn new(r: R, t: CipherType, key: &[u8], iv: &[u8]) -> DecryptedReader { +impl DecryptedReader { + pub fn new(t: CipherType, key: &[u8], iv: &[u8]) -> DecryptedReader { let cipher = new_stream(t, key, iv, CryptoMode::Decrypt); let buffer_size = cipher.buffer_size(&DUMMY_BUFFER); DecryptedReader { - reader: r, buffer: BytesMut::with_capacity(buffer_size), cipher, pos: 0, - sent_final: false, + got_final: false, + incoming_buffer: vec![0u8; BUFFER_SIZE], } } - pub fn get_ref(&self) -> &R { - &self.reader - } - - /// Gets a mutable reference to the underlying reader. - /// - /// # Warning - /// - /// It is inadvisable to read directly from or write directly to the - /// underlying reader. - pub fn get_mut(&mut self) -> &mut R { - &mut self.reader - } - - /// Unwraps this `DecryptedReader`, returning the underlying reader. - /// - /// The internal buffer is flushed before returning the reader. Any leftover - /// data in the read buffer is lost. - pub fn into_inner(self) -> R { - self.reader - } -} - -impl BufRead for DecryptedReader -where - R: AsyncRead, -{ - fn fill_buf(&mut self) -> io::Result<&[u8]> { + pub fn poll_read_decrypted( + &mut self, + ctx: &mut Context<'_>, + r: &mut R, + dst: &mut [u8], + ) -> Poll> + where + R: AsyncRead + Unpin, + { while self.pos >= self.buffer.len() { - if self.sent_final { - return Ok(&[]); + if self.got_final { + return Poll::Ready(Ok(0)); } - let mut incoming = [0u8; BUFFER_SIZE]; + let n = ready!(Pin::new(&mut *r).poll_read(ctx, &mut self.incoming_buffer))?; + + // Reset pointers self.buffer.clear(); - match self.reader.read(&mut incoming) { - Ok(0) => { - // Ensure we have enough space - let buffer_len = self.buffer_size(&[]); - self.buffer.reserve(buffer_len); - - // EOF - self.cipher.finalize(&mut self.buffer)?; - self.sent_final = true; - } - Ok(l) => { - let data = &incoming[..l]; - - // Ensure we have enough space - let buffer_len = self.buffer_size(data); - self.buffer.reserve(buffer_len); - - self.cipher.update(data, &mut self.buffer)?; - } - Err(err) => { - return Err(err); - } - } - self.pos = 0; + + if n == 0 { + // Finialize block + self.buffer.reserve(self.buffer_size(&[])); + self.cipher.finalize(&mut self.buffer)?; + self.got_final = true; + } else { + let data = &self.incoming_buffer[..n]; + // Ensure we have enough space + let buffer_len = self.buffer_size(data); + self.buffer.reserve(buffer_len); + self.cipher.update(data, &mut self.buffer)?; + } } - Ok(&self.buffer[self.pos..]) + let remaining_len = self.buffer.len() - self.pos; + let n = cmp::min(dst.len(), remaining_len); + (&mut dst[..n]).copy_from_slice(&self.buffer[self.pos..self.pos + n]); + self.pos += n; + Poll::Ready(Ok(n)) } - fn consume(&mut self, amt: usize) { - self.pos = cmp::min(self.pos + amt, self.buffer.len()); - } -} - -impl Read for DecryptedReader -where - R: AsyncRead, -{ - fn read(&mut self, buf: &mut [u8]) -> io::Result { - let nread = { - let mut available = self.fill_buf()?; - available.read(buf)? - }; - self.consume(nread); - Ok(nread) - } -} - -impl DecryptedRead for DecryptedReader -where - R: AsyncRead, -{ fn buffer_size(&self, data: &[u8]) -> usize { self.cipher.buffer_size(data) } } -impl AsyncRead for DecryptedReader where R: AsyncRead {} - -/// Writer wrapper that will encrypt data automatically -pub struct EncryptedWriter -where - W: AsyncWrite, -{ - writer: W, - cipher: BoxStreamCipher, +enum EncryptWriteStep { + Nothing, + Writing(BytesMut, usize), } -impl EncryptedWriter -where - W: AsyncWrite, -{ +/// Writer wrapper that will encrypt data automatically +pub struct EncryptedWriter { + cipher: BoxStreamCipher, + steps: EncryptWriteStep, +} + +impl EncryptedWriter { /// Creates a new EncryptedWriter - pub fn new(w: W, t: CipherType, key: &[u8], iv: &[u8]) -> EncryptedWriter { + pub fn new(t: CipherType, key: &[u8], iv: &[u8]) -> EncryptedWriter { EncryptedWriter { - writer: w, cipher: new_stream(t, key, iv, CryptoMode::Encrypt), + steps: EncryptWriteStep::Nothing, + } + } + + pub fn poll_write_encrypted(&mut self, ctx: &mut Context<'_>, w: &mut W, data: &[u8]) -> Poll> + where + W: AsyncWrite + Unpin, + { + ready!(self.poll_write_all_encrypted(ctx, w, data))?; + Poll::Ready(Ok(data.len())) + } + + pub fn poll_write_all_encrypted(&mut self, ctx: &mut Context<'_>, w: &mut W, data: &[u8]) -> Poll> + where + W: AsyncWrite + Unpin, + { + // FIXME: How about finalize? + + loop { + match self.steps { + EncryptWriteStep::Nothing => { + let mut buf = BytesMut::with_capacity(self.buffer_size(data)); + self.cipher_update(data, &mut buf)?; + + self.steps = EncryptWriteStep::Writing(buf, 0); + } + EncryptWriteStep::Writing(ref mut buf, ref mut pos) => { + while *pos < buf.len() { + let n = ready!(Pin::new(&mut *w).poll_write(ctx, &buf[*pos..]))?; + if n == 0 { + use std::io::ErrorKind; + return Poll::Ready(Err(ErrorKind::UnexpectedEof.into())); + } + *pos += n; + } + + self.steps = EncryptWriteStep::Nothing; + return Poll::Ready(Ok(())); + } + } } } @@ -161,40 +147,10 @@ where self.cipher.update(data, buf).map_err(From::from) } + #[allow(dead_code)] fn cipher_finalize(&mut self, buf: &mut B) -> io::Result<()> { self.cipher.finalize(buf).map_err(From::from) } -} - -impl Drop for EncryptedWriter -where - W: AsyncWrite, -{ - fn drop(&mut self) { - let mut buf = Vec::new(); - if let Ok(..) = self.cipher_finalize(&mut buf) { - if !buf.is_empty() { - let _ = self.write_raw(&buf); - } - } - } -} - -impl EncryptedWrite for EncryptedWriter -where - W: AsyncWrite, -{ - fn write_raw(&mut self, data: &[u8]) -> io::Result { - self.writer.write(data) - } - - fn flush(&mut self) -> io::Result<()> { - self.writer.flush() - } - - fn encrypt(&mut self, data: &[u8], buf: &mut B) -> io::Result<()> { - self.cipher_update(data, buf) - } fn buffer_size(&self, data: &[u8]) -> usize { self.cipher.buffer_size(data) diff --git a/src/relay/tcprelay/tunnel_local.rs b/src/relay/tcprelay/tunnel_local.rs new file mode 100644 index 00000000..9c3b2b54 --- /dev/null +++ b/src/relay/tcprelay/tunnel_local.rs @@ -0,0 +1,129 @@ +//! Local server that establish a TCP tunnel with server + +use std::io; +use std::net::SocketAddr; +use std::sync::Arc; + +use futures::future::{self, Either}; +use log::{debug, error, info, trace}; +use tokio::net::{TcpListener, TcpStream}; + +use crate::config::ServerConfig; +use crate::context::SharedContext; +use crate::relay::loadbalancing::server::{LoadBalancer, PingBalancer}; +use crate::relay::socks5::Address; + +/// Established Client Tunnel +/// +/// This method must be called after handshaking with client (for example, socks5 handshaking) +async fn establish_client_tcp_tunnel<'a>( + context: SharedContext, + mut s: TcpStream, + client_addr: SocketAddr, + addr: &Address, + svr_cfg: Arc, +) -> io::Result<()> { + let svr_s = match super::connect_proxy_server(context, svr_cfg.clone()).await { + Ok(svr_s) => { + trace!("Proxy server connected, {:?}", svr_cfg); + svr_s + } + Err(err) => { + // Just close the connection. + return Err(err); + } + }; + + let mut svr_s = super::proxy_server_handshake(svr_s, svr_cfg.clone(), addr).await?; + let (mut svr_r, mut svr_w) = svr_s.split(); + + let (mut r, mut w) = s.split(); + + use tokio::io::copy; + + let rhalf = copy(&mut r, &mut svr_w); + let whalf = copy(&mut svr_r, &mut w); + + debug!( + "TUNNEL relay established {} <-> {} ({})", + client_addr, + svr_cfg.addr(), + addr + ); + + match future::select(rhalf, whalf).await { + Either::Left((Ok(..), _)) => trace!("TUNNEL relay {} -> {} ({}) closed", client_addr, svr_cfg.addr(), addr), + Either::Left((Err(err), _)) => trace!( + "TUNNEL relay {} -> {} ({}) closed with error {:?}", + client_addr, + svr_cfg.addr(), + err, + addr, + ), + Either::Right((Ok(..), _)) => trace!("TUNNEL relay {} <- {} ({}) closed", client_addr, svr_cfg.addr(), addr), + Either::Right((Err(err), _)) => trace!( + "TUNNEL relay {} <- {} ({}) closed with error {:?}", + client_addr, + svr_cfg.addr(), + err, + addr + ), + } + + debug!("TUNNEL relay {} <-> {} ({}) closing", client_addr, svr_cfg.addr(), addr); + + Ok(()) +} + +async fn handle_tunnel_client(context: SharedContext, s: TcpStream, conf: Arc) -> io::Result<()> { + if let Err(err) = s.set_keepalive(conf.timeout()) { + error!("Failed to set keep alive: {:?}", err); + } + + if context.config().no_delay { + if let Err(err) = s.set_nodelay(true) { + error!("Failed to set no delay: {:?}", err); + } + } + + let client_addr = s.peer_addr()?; + + // forward must not be None, it is already checked in local.rs + let target_addr = context.config().forward.as_ref().unwrap(); + + establish_client_tcp_tunnel(context.clone(), s, client_addr, target_addr, conf).await +} + +pub async fn run(context: SharedContext) -> io::Result<()> { + assert!( + context.config().mode.enable_tcp(), + "You must enable TCP relay for tunneling" + ); + + let local_addr = *context.config().local.as_ref().expect("Missing local config"); + + let mut listener = TcpListener::bind(&local_addr) + .await + .unwrap_or_else(|err| panic!("Failed to listen on {}, {}", local_addr, err)); + + let actual_local_addr = listener.local_addr().expect("Could not determine port bound to"); + + info!("ShadowSocks TCP Tunnel Listening on {}", actual_local_addr); + + let mut servers = PingBalancer::new(context.clone()); + + loop { + let (socket, peer_addr) = listener.accept().await?; + let server_cfg = servers.pick_server(); + + trace!("Got connection, addr: {}", peer_addr); + trace!("Picked proxy server: {:?}", server_cfg); + + let context = context.clone(); + tokio::spawn(async move { + if let Err(err) = handle_tunnel_client(context, socket, server_cfg).await { + error!("Socks5 client {}", err); + } + }); + } +} diff --git a/src/relay/tcprelay/utils.rs b/src/relay/tcprelay/utils.rs index bf6f89e9..ab0c6515 100644 --- a/src/relay/tcprelay/utils.rs +++ b/src/relay/tcprelay/utils.rs @@ -1,182 +1 @@ //! Utility functions - -use std::{ - io, - time::{Duration, Instant}, -}; - -use futures::{Async, Future, Poll}; -use tokio::timer::Delay; -use tokio_io::{ - io::{copy, Copy}, - try_nb, AsyncRead, AsyncWrite, -}; - -use super::BUFFER_SIZE; - -/// Copies all data from `r` to `w`, abort if timeout reaches -pub fn copy_timeout(r: R, w: W, dur: Duration) -> CopyTimeout -where - R: AsyncRead, - W: AsyncWrite, -{ - CopyTimeout::new(r, w, dur) -} - -/// Copies all data from `r` to `w`, abort if timeout reaches -pub struct CopyTimeout -where - R: AsyncRead, - W: AsyncWrite, -{ - r: Option, - w: Option, - timeout: Duration, - amt: u64, - timer: Option, - buf: [u8; BUFFER_SIZE], - pos: usize, - cap: usize, -} - -impl CopyTimeout -where - R: AsyncRead, - W: AsyncWrite, -{ - fn new(r: R, w: W, timeout: Duration) -> CopyTimeout { - CopyTimeout { - r: Some(r), - w: Some(w), - timeout, - amt: 0, - timer: None, - buf: [0u8; BUFFER_SIZE], - pos: 0, - cap: 0, - } - } - - fn try_poll_timeout(&mut self) -> io::Result<()> { - match self.timer.as_mut() { - None => Ok(()), - Some(t) => match t.poll() { - Err(err) => panic!("Failed to poll on timer, err: {}", err), - Ok(Async::Ready(..)) => Err(io::Error::new(io::ErrorKind::TimedOut, "connection timed out")), - Ok(Async::NotReady) => Ok(()), - }, - } - } - - fn clear_timer(&mut self) { - let _ = self.timer.take(); - } - - fn read_or_set_timeout(&mut self) -> io::Result { - // First, return if timeout - self.try_poll_timeout()?; - - // Then, unset the previous timeout - self.clear_timer(); - - match self.r.as_mut().unwrap().read(&mut self.buf) { - Ok(n) => Ok(n), - Err(e) => { - if e.kind() == io::ErrorKind::WouldBlock { - self.timer = Some(Delay::new(Instant::now() + self.timeout)); - } - Err(e) - } - } - } - - fn write_or_set_timeout(&mut self, beg: usize, end: usize) -> io::Result { - // First, return if timeout - self.try_poll_timeout()?; - - // Then, unset the previous timeout - self.clear_timer(); - - match self.w.as_mut().unwrap().write(&self.buf[beg..end]) { - Ok(n) => Ok(n), - Err(e) => { - if e.kind() == io::ErrorKind::WouldBlock { - self.timer = Some(Delay::new(Instant::now() + self.timeout)); - } - Err(e) - } - } - } -} - -impl Future for CopyTimeout -where - R: AsyncRead, - W: AsyncWrite, -{ - type Error = io::Error; - type Item = (u64, R, W); - - fn poll(&mut self) -> Poll { - loop { - if self.pos == self.cap { - let n = try_nb!(self.read_or_set_timeout()); - - if n == 0 { - // If we've written all the data and we've seen EOF, flush out the - // data and finish the transfer. - // done with the entire transfer. - try_nb!(self.w.as_mut().unwrap().flush()); - return Ok((self.amt, self.r.take().unwrap(), self.w.take().unwrap()).into()); - } - - self.pos = 0; - self.cap = n; - - // Clear it before write - self.clear_timer(); - } - - // If our buffer has some data, let's write it out! - while self.pos < self.cap { - let (pos, cap) = (self.pos, self.cap); - let i = try_nb!(self.write_or_set_timeout(pos, cap)); - self.pos += i; - self.amt += i as u64; - } - - // Clear it before read - self.clear_timer(); - } - } -} - -/// Copies all data from `r` to `w` with optional timeout param -pub fn copy_timeout_opt(r: R, w: W, dur: Option) -> CopyTimeoutOpt -where - R: AsyncRead, - W: AsyncWrite, -{ - match dur { - Some(d) => CopyTimeoutOpt::CopyTimeout(copy_timeout(r, w, d)), - None => CopyTimeoutOpt::Copy(copy(r, w)), - } -} - -/// Copies all data from `R` to `W` -pub enum CopyTimeoutOpt { - Copy(Copy), - CopyTimeout(CopyTimeout), -} - -impl Future for CopyTimeoutOpt { - type Error = io::Error; - type Item = (u64, R, W); - - fn poll(&mut self) -> Poll { - match *self { - CopyTimeoutOpt::CopyTimeout(ref mut c) => c.poll(), - CopyTimeoutOpt::Copy(ref mut c) => c.poll(), - } - } -} diff --git a/src/relay/udprelay/crypto_io.rs b/src/relay/udprelay/crypto_io.rs index fc58d711..97007f6f 100644 --- a/src/relay/udprelay/crypto_io.rs +++ b/src/relay/udprelay/crypto_io.rs @@ -21,81 +21,105 @@ //! ``` use std::io; +use std::slice; + +use byte_string::ByteStr; +use bytes::{BufMut, BytesMut}; +use log::trace; use crate::crypto::{self, CipherCategory, CipherType, CryptoMode}; /// Encrypt payload into ShadowSocks UDP encrypted packet -pub fn encrypt_payload(t: CipherType, key: &[u8], payload: &[u8]) -> io::Result> { +pub fn encrypt_payload(t: CipherType, key: &[u8], payload: &[u8], dst: &mut BytesMut) -> io::Result<()> { match t.category() { - CipherCategory::Stream => encrypt_payload_stream(t, key, payload), - CipherCategory::Aead => encrypt_payload_aead(t, key, payload), + CipherCategory::Stream => encrypt_payload_stream(t, key, payload, dst), + CipherCategory::Aead => encrypt_payload_aead(t, key, payload, dst), } } -fn encrypt_payload_stream(t: CipherType, key: &[u8], payload: &[u8]) -> io::Result> { +fn encrypt_payload_stream(t: CipherType, key: &[u8], payload: &[u8], dst: &mut BytesMut) -> io::Result<()> { let iv = t.gen_init_vec(); let mut cipher = crypto::new_stream(t, key, &iv, CryptoMode::Encrypt); - let mut send_payload = Vec::with_capacity(iv.len() + payload.len()); - send_payload.extend_from_slice(&iv); - cipher.update(&payload[..], &mut send_payload)?; - cipher.finalize(&mut send_payload)?; - Ok(send_payload) + trace!("UDP packet generated stream iv {:?}", ByteStr::new(&iv)); + + dst.reserve(iv.len() + payload.len()); + + // First of all, IV + dst.put_slice(&iv); + + // Encrypted data + cipher.update(&payload[..], dst)?; + cipher.finalize(dst)?; + + Ok(()) } -fn encrypt_payload_aead(t: CipherType, key: &[u8], payload: &[u8]) -> io::Result> { +fn encrypt_payload_aead(t: CipherType, key: &[u8], payload: &[u8], dst: &mut BytesMut) -> io::Result<()> { let salt = t.gen_salt(); let tag_size = t.tag_size(); let mut cipher = crypto::new_aead_encryptor(t, key, &salt); - let mut send_payload = Vec::with_capacity(salt.len() + payload.len() + tag_size); - send_payload.extend_from_slice(&salt); - let start_pos = send_payload.len(); - send_payload.resize(start_pos + payload.len() + tag_size, 0); + trace!("UDP packet generated AEAD salt {:?}", ByteStr::new(&salt)); - cipher.encrypt(payload, &mut send_payload[start_pos..]); + dst.reserve(salt.len() + payload.len() + tag_size); - Ok(send_payload) + // First of all, salt + dst.put_slice(&salt); + + // Encrypted data + unsafe { + let remaining = dst.bytes_mut(); + let b = slice::from_raw_parts_mut(remaining.as_mut_ptr() as *mut u8, remaining.len()); + + cipher.encrypt(payload, b); + dst.advance_mut(payload.len() + tag_size); + } + + Ok(()) } /// Decrypt payload from ShadowSocks UDP encrypted packet -pub fn decrypt_payload(t: CipherType, key: &[u8], payload: &[u8]) -> io::Result> { +pub fn decrypt_payload(t: CipherType, key: &[u8], payload: &[u8]) -> io::Result>> { match t.category() { CipherCategory::Stream => decrypt_payload_stream(t, key, payload), CipherCategory::Aead => decrypt_payload_aead(t, key, payload), } } -fn decrypt_payload_stream(t: CipherType, key: &[u8], payload: &[u8]) -> io::Result> { +fn decrypt_payload_stream(t: CipherType, key: &[u8], payload: &[u8]) -> io::Result>> { let iv_size = t.iv_size(); if payload.len() < iv_size { - let err = io::Error::new(io::ErrorKind::Other, "udp packet too short"); - return Err(err); + return Ok(None); } let iv = &payload[..iv_size]; let data = &payload[iv_size..]; + trace!("UDP packet got stream IV {:?}", ByteStr::new(iv)); + let mut cipher = crypto::new_stream(t, key, iv, CryptoMode::Decrypt); let mut recv_payload = Vec::with_capacity(data.len()); cipher.update(data, &mut recv_payload)?; cipher.finalize(&mut recv_payload)?; - Ok(recv_payload) + Ok(Some(recv_payload)) } -fn decrypt_payload_aead(t: CipherType, key: &[u8], payload: &[u8]) -> io::Result> { +fn decrypt_payload_aead(t: CipherType, key: &[u8], payload: &[u8]) -> io::Result>> { let tag_size = t.tag_size(); let salt_size = t.salt_size(); if payload.len() < tag_size + salt_size { - let err = io::Error::new(io::ErrorKind::Other, "udp packet too short"); - return Err(err); + return Ok(None); } let salt = &payload[..salt_size]; let data = &payload[salt_size..]; + + trace!("UDP packet got AEAD salt {:?}", ByteStr::new(salt)); + let data_length = payload.len() - tag_size - salt_size; let mut cipher = crypto::new_aead_decryptor(t, key, salt); @@ -103,5 +127,5 @@ fn decrypt_payload_aead(t: CipherType, key: &[u8], payload: &[u8]) -> io::Result let mut recv_payload = vec![0u8; data_length]; cipher.decrypt(data, &mut recv_payload)?; - Ok(recv_payload) + Ok(Some(recv_payload)) } diff --git a/src/relay/udprelay/dns.rs b/src/relay/udprelay/dns.rs deleted file mode 100644 index 63a0b3f9..00000000 --- a/src/relay/udprelay/dns.rs +++ /dev/null @@ -1,205 +0,0 @@ -//! UDP DNS relay - -use std::{ - io::{self, Cursor}, - net::{IpAddr, Ipv4Addr, SocketAddr}, - sync::{Arc, Mutex}, - time::Instant, -}; - -use dns_parser::Packet; -use futures::{self, future::join_all, stream::futures_unordered, Future, Stream}; -use log::{error, info, trace}; -use tokio::{self, net::UdpSocket}; - -use super::{ - crypto_io::{decrypt_payload, encrypt_payload}, - PacketStream, SendDgramRc, SharedUdpSocket, -}; -use crate::{ - config::{ServerAddr, ServerConfig}, - context::SharedContext, - relay::{boxed_future, dns_resolver::resolve, socks5::Address}, -}; - -/// Starts a UDP DNS server -pub fn run(context: SharedContext) -> impl Future + Send { - let local_addr = *context.config().local.as_ref().unwrap(); - - futures::lazy(move || { - info!("ShadowSocks UDP DNS Listening on {}", local_addr); - - UdpSocket::bind(&local_addr) - }) - .and_then(move |l| listen(context, l)) -} - -fn listen(context: SharedContext, l: UdpSocket) -> impl Future + Send { - assert!(!context.config().server.is_empty()); - - let mut svr_fut = Vec::with_capacity(context.config().server.len()); - for svr in &context.config().server { - match *svr.addr() { - ServerAddr::SocketAddr(ref addr) => { - svr_fut.push(boxed_future(futures::finished::<_, io::Error>(vec![*addr]))); - } - ServerAddr::DomainName(ref dom, ref port) => { - svr_fut.push(boxed_future(resolve(context.clone(), &*dom, *port, false))); - } - } - } - - let cloned_context = context.clone(); - join_all(svr_fut) - .and_then(move |svr_addrs| { - let mut u = Vec::with_capacity(svr_addrs.len()); - for (idx, svr_addr) in svr_addrs.into_iter().enumerate() { - let local_addr = SocketAddr::new(IpAddr::from(Ipv4Addr::new(0, 0, 0, 0)), 0); - let s = UdpSocket::bind(&local_addr)?; - let svr_cfg = Arc::new(cloned_context.config().server[idx].clone()); - u.push((Arc::new(Mutex::new(s)), svr_cfg, svr_addr[0])); - } - Ok(u) - }) - .and_then(move |vec_servers| { - let mut f = Vec::with_capacity(1 + vec_servers.len()); - let l = Arc::new(Mutex::new(l)); - f.push(boxed_future(handle_l2r( - context.clone(), - l.clone(), - vec_servers.clone(), - ))); - for (svr, cfg, _) in vec_servers { - f.push(boxed_future(handle_r2l(context.clone(), l.clone(), svr, cfg))) - } - futures_unordered(f).into_future().then(|res| match res { - Ok(..) => { - error!("One of DNS servers exited unexpectly without error"); - let err = io::Error::new(io::ErrorKind::Other, "server exited unexpectly"); - Err(err) - } - Err((err, ..)) => { - error!("One of DNS servers exited unexpectly with error {}", err); - Err(err) - } - }) - }) -} - -fn handle_l2r( - context: SharedContext, - l: SharedUdpSocket, - server: Vec<(SharedUdpSocket, Arc, SocketAddr)>, -) -> impl Future + Send { - assert!(!server.is_empty()); - - let mut server_idx: usize = 0; - let server = Arc::new(server); - - PacketStream::new(l).for_each(move |(payload, src)| { - let server = server.clone(); - let context = context.clone(); - let context2 = context.clone(); - let pkt_fut = futures::lazy(move || { - let pkt = Packet::parse(&payload[..]).map_err(|err| { - error!("Failed to parse DNS payload, err: {}", err); - io::Error::new(io::ErrorKind::Other, "parse DNS packet failed") - })?; - - let (ref socket, ref svr_cfg, svr_addr) = server[server_idx % server.len()]; - let (ni, _) = server_idx.overflowing_add(1); - server_idx = ni; - - trace!("DNS {} -> {} packet: {:?}", src, svr_cfg.addr(), pkt); - - let mut buf = Vec::new(); - Address::SocketAddress(context.config().get_remote_dns()).write_to_buf(&mut buf); - - buf.extend_from_slice(&payload); - - let socket = socket.clone(); - let id = pkt.header.id; - encrypt_payload(svr_cfg.method(), svr_cfg.key(), &buf) - .map(move |send_payload| (socket, svr_addr, send_payload, id)) - }) - .and_then(move |(socket, svr_addr, send_payload, id)| { - SendDgramRc::new(socket, send_payload, svr_addr).map(move |_| { - context2.dns_query_cache().insert(id, (src, Instant::now())); - }) - }); - tokio::spawn(pkt_fut.then(|res| match res { - Ok(..) => Ok(()), - Err(err) => { - error!("Failed to handle local -> remote packet, err: {}", err); - Err(()) - } - })); - - Ok(()) - }) -} - -fn handle_r2l( - context: SharedContext, - l: SharedUdpSocket, - r: SharedUdpSocket, - svr_cfg: Arc, -) -> impl Future + Send { - PacketStream::new(r).for_each(move |(payload, src)| { - let l = l.clone(); - let svr_cfg = svr_cfg.clone(); - let context = context.clone(); - let pkt_fut = futures::lazy(move || decrypt_payload(svr_cfg.method(), svr_cfg.key(), &payload)) - .and_then(move |payload| Address::read_from(Cursor::new(payload)).map_err(From::from)) - .and_then(move |(cur, ..)| { - let pos = cur.position() as usize; - let payload = cur.into_inner(); - - let pkt = Packet::parse(&payload[pos..]).map_err(|err| { - error!("Failed to parse DNS payload, err: {}", err); - io::Error::new(io::ErrorKind::Other, "parse DNS packet failed") - })?; - - let payload = payload[pos..].to_vec(); - - let mut cache = context.dns_query_cache(); - match cache.remove(&pkt.header.id) { - Some((cli_addr, start_time)) => { - trace!( - "DNS {} <- {} elapsed: {:?} packet: {:?}", - cli_addr, - src, - Instant::now() - start_time, - pkt, - ); - - Ok(Some((cli_addr, payload))) - } - None => { - error!( - "DNS received packet id={} opcode={:?} but found no local endpoint", - pkt.header.id, pkt.header.opcode - ); - Ok(None) - } - } - }) - .and_then(move |opt| match opt { - Some((cli_addr, payload)) => { - let f = SendDgramRc::new(l, payload, cli_addr).map(|_| ()); - boxed_future(f) - } - None => boxed_future(futures::finished(())), - }); - - tokio::spawn(pkt_fut.then(|res| match res { - Ok(..) => Ok(()), - Err(err) => { - error!("Failed to handle local <- remote packet, err: {}", err); - Err(()) - } - })); - - Ok(()) - }) -} diff --git a/src/relay/udprelay/local.rs b/src/relay/udprelay/local.rs index af18cc43..11274297 100644 --- a/src/relay/udprelay/local.rs +++ b/src/relay/udprelay/local.rs @@ -3,178 +3,193 @@ use std::{ io::{self, Cursor, ErrorKind, Read}, net::{IpAddr, Ipv4Addr, SocketAddr}, - sync::{Arc, Mutex}, + sync::Arc, time::Duration, }; -use futures::{self, Future, Stream}; -use log::{debug, error, info}; -use tokio::{self, net::UdpSocket, util::FutureExt}; +use bytes::BytesMut; +use log::{debug, error, info, warn}; +use tokio::{self, net::UdpSocket, sync::mpsc}; use crate::{ config::{ServerAddr, ServerConfig}, context::SharedContext, relay::{ - boxed_future, - dns_resolver::resolve, loadbalancing::server::{LoadBalancer, RoundRobin}, socks5::{Address, UdpAssociateHeader}, + utils::try_timeout, }, }; use super::{ crypto_io::{decrypt_payload, encrypt_payload}, - PacketStream, SendDgramRc, MAXIMUM_UDP_PAYLOAD_SIZE, + MAXIMUM_UDP_PAYLOAD_SIZE, }; -/// Resolves server address to SocketAddr -fn resolve_server_addr( +#[allow(unused_variables)] // `context` is only used if trust-dns is enabled +async fn udp_associate( context: SharedContext, svr_cfg: Arc, -) -> impl Future + Send { - match *svr_cfg.addr() { - // Return directly if it is a SocketAddr - ServerAddr::SocketAddr(ref addr) => boxed_future(futures::finished(*addr)), - // Resolve domain name to SocketAddr + pkt: Vec, + src: SocketAddr, +) -> io::Result> { + const DEFAULT_TIMEOUT: Duration = Duration::from_secs(5); + + // PKT = UdpAssociateHeader + PAYLOAD + let mut cur = Cursor::new(pkt); + + let header = UdpAssociateHeader::read_from(&mut cur).await?; + + if header.frag != 0 { + error!("Received UDP associate with frag != 0, which is not supported by ShadowSocks"); + let err = io::Error::new(ErrorKind::Other, "unsupported UDP fragmentation"); + return Err(err); + } + + let addr = header.address; + + // The remaining is PAYLOAD + let mut payload = Vec::new(); + Read::read_to_end(&mut cur, &mut payload)?; + + // Binds to 0.0.0.0:0 (let system choose a random port) + // FIXME: Create a UdpSocket for every UDP associate requests + let local_addr = SocketAddr::new(IpAddr::from(Ipv4Addr::new(0, 0, 0, 0)), 0); + let mut remote_udp = UdpSocket::bind(&local_addr).await?; + + // CLIENT -> SERVER protocol: ADDRESS + PAYLOAD + let mut send_buf = Vec::new(); + addr.write_to_buf(&mut send_buf); + send_buf.extend_from_slice(&payload); + + debug!( + "UDP ASSOCIATE {} -> {}, payload length {} bytes", + src, + addr, + payload.len() + ); + + let timeout = svr_cfg.udp_timeout().unwrap_or(DEFAULT_TIMEOUT); + + // Write to remote socket CLIENT -> SERVER + let mut encrypt_buf = BytesMut::new(); + encrypt_payload(svr_cfg.method(), svr_cfg.key(), &send_buf, &mut encrypt_buf)?; + + let send_n = match *svr_cfg.addr() { + ServerAddr::SocketAddr(ref addr) => try_timeout(remote_udp.send_to(&encrypt_buf, addr), Some(timeout)).await?, + #[cfg(feature = "trust-dns")] ServerAddr::DomainName(ref dname, port) => { - let fut = resolve(context, dname, port, false).map(move |vec_ipaddr| { - assert!(!vec_ipaddr.is_empty()); - vec_ipaddr[0] - }); - boxed_future(fut) + use crate::relay::dns_resolver::resolve; + + let vec_ipaddr = resolve(context, dname, port, false).await?; + assert!(!vec_ipaddr.is_empty()); + + try_timeout(remote_udp.send_to(&encrypt_buf, &vec_ipaddr[0]), Some(timeout)).await? } + #[cfg(not(feature = "trust-dns"))] + ServerAddr::DomainName(ref dname, port) => { + try_timeout(remote_udp.send_to(&encrypt_buf, (dname.as_str(), port)), Some(timeout)).await? + } + }; + + if send_n != encrypt_buf.len() { + warn!( + "Sent packet length {}, but expected length {}", + send_n, + encrypt_buf.len() + ); + } + + // Waiting for response from server SERVER -> CLIENT + // Packet length is limited by MAXIMUM_UDP_PAYLOAD_SIZE, excess bytes will be discarded. + let mut recv_buf = [0u8; MAXIMUM_UDP_PAYLOAD_SIZE]; + let recv_n = try_timeout(remote_udp.recv(&mut recv_buf), Some(timeout)).await?; + + let decrypt_buf = match decrypt_payload(svr_cfg.method(), svr_cfg.key(), &recv_buf[..recv_n])? { + None => { + error!("UDP packet too short, received length {}", recv_n); + let err = io::Error::new(io::ErrorKind::InvalidData, "packet too short"); + return Err(err); + } + Some(b) => b, + }; + + // SERVER -> CLIENT protocol: ADDRESS + PAYLOAD + let mut cur = Cursor::new(decrypt_buf); + + // FIXME: Address is ignored. Maybe useful in the future if we uses one common UdpSocket for communicate with remote server + let _ = Address::read_from(&mut cur).await?; + + let payload_len = cur.get_ref().len() - cur.position() as usize; + debug!( + "UDP ASSOCIATE {} <- {}, payload length {} bytes", + src, addr, payload_len + ); + + let mut data = Vec::new(); + UdpAssociateHeader::new(0, Address::SocketAddress(src)).write_to_buf(&mut data); + + // Copy payload directly + Read::read_to_end(&mut cur, &mut data)?; + + Ok(data) +} + +async fn listen(context: SharedContext, l: UdpSocket) -> io::Result<()> { + let mut balancer = RoundRobin::new(context.config()); + + let (mut r, mut w) = l.split(); + + let mut pkt_buf = [0u8; MAXIMUM_UDP_PAYLOAD_SIZE]; + + // FIXME: Channel size 1024? + let (tx, mut rx) = mpsc::channel::<(SocketAddr, Vec)>(1024); + tokio::spawn(async move { + while let Some((src, pkt)) = rx.recv().await { + if let Err(err) = w.send_to(&pkt, &src).await { + error!("UDP packet send failed, err: {:?}", err); + break; + } + } + + // FIXME: How to stop the outer listener Future? + }); + + loop { + let (recv_len, src) = r.recv_from(&mut pkt_buf).await?; + + // Packet length is limited by MAXIMUM_UDP_PAYLOAD_SIZE, excess bytes will be discarded. + // Copy bytes, because udp_associate runs in another tokio Task + let pkt = pkt_buf[..recv_len].to_vec(); + + let svr_cfg = balancer.pick_server(); + + let context = context.clone(); + let svr_cfg = svr_cfg.clone(); + let mut tx = tx.clone(); + let assoc = async move { + match udp_associate(context, svr_cfg, pkt, src).await { + Ok(pkt) => { + if let Err(..) = tx.send((src, pkt)).await { + error!("UDP packet channel closed"); + } + } + Err(err) => { + error!("Error occurs in UDP relay: {}", err); + } + } + }; + + tokio::spawn(assoc); } } -fn listen(context: SharedContext, l: UdpSocket) -> impl Future + Send { - let socket = Arc::new(Mutex::new(l)); - let mut balancer = RoundRobin::new(context.config()); - - PacketStream::new(socket.clone()).for_each(move |(pkt, src)| { - let svr_cfg = balancer.pick_server(); - let svr_cfg_cloned = svr_cfg.clone(); - let svr_cfg_cloned_cloned = svr_cfg.clone(); - let socket = socket.clone(); - let context = context.clone(); - let timeout = *svr_cfg.udp_timeout(); - - const DEFAULT_TIMEOUT: Duration = Duration::from_secs(5); - - let rel = futures::lazy(|| UdpAssociateHeader::read_from(Cursor::new(pkt))) - .map_err(From::from) - .and_then(|(cur, header)| { - if header.frag != 0 { - error!("Received UDP associate with frag != 0, which is not supported by ShadowSocks"); - let err = io::Error::new(ErrorKind::Other, "unsupported UDP fragmentation"); - Err(err) - } else { - Ok((cur, header.address)) - } - }) - .and_then(|(mut cur, addr)| { - let svr_cfg = svr_cfg_cloned_cloned; - - let mut payload = Vec::new(); - cur.read_to_end(&mut payload).unwrap(); - - resolve_server_addr(context, svr_cfg) - .and_then(|remote_addr| { - let local_addr = SocketAddr::new(IpAddr::from(Ipv4Addr::new(0, 0, 0, 0)), 0); - UdpSocket::bind(&local_addr).map(|remote_udp| (remote_udp, remote_addr)) - }) - .map(|(remote_udp, remote_addr)| (remote_udp, remote_addr, payload, addr)) - }) - .and_then(move |(remote_udp, remote_addr, payload, addr)| { - let mut buf = Vec::new(); - addr.write_to_buf(&mut buf); - buf.extend_from_slice(&payload); - encrypt_payload(svr_cfg.method(), svr_cfg.key(), &buf) - .map(|payload| (remote_udp, remote_addr, payload, addr)) - }) - .and_then(move |(remote_udp, remote_addr, payload, addr)| { - debug!( - "UDP ASSOCIATE {} -> {}, payload length {} bytes", - src, - addr, - payload.len() - ); - let to = timeout.unwrap_or(DEFAULT_TIMEOUT); - let caddr = addr.clone(); - remote_udp - .send_dgram(payload, &remote_addr) - .timeout(to) - .map_err(move |err| match err.into_inner() { - Some(e) => e, - None => { - error!( - "Udp associate sending datagram {} -> {} timed out in {:?}", - src, caddr, to - ); - io::Error::new(io::ErrorKind::TimedOut, "udp send timed out") - } - }) - .map(|(remote_udp, _)| (remote_udp, addr)) - }) - .and_then(move |(remote_udp, addr)| { - let buf = vec![0u8; MAXIMUM_UDP_PAYLOAD_SIZE]; - let to = timeout.unwrap_or(DEFAULT_TIMEOUT); - let caddr = addr.clone(); - remote_udp - .recv_dgram(buf) - .timeout(to) - .map_err(move |err| match err.into_inner() { - Some(e) => e, - None => { - error!( - "Udp associate waiting datagram {} <- {} timed out in {:?}", - src, caddr, to - ); - io::Error::new(io::ErrorKind::TimedOut, "udp recv timed out") - } - }) - .and_then(move |(_remote_udp, buf, n, _from)| { - let svr_cfg = svr_cfg_cloned; - decrypt_payload(svr_cfg.method(), svr_cfg.key(), &buf[..n]) - }) - .map(|payload| (payload, addr)) - }) - .and_then(move |(payload, addr)| { - Address::read_from(Cursor::new(payload)) - .map_err(From::from) - .map(|(cur, ..)| (cur, addr)) - }) - .and_then(move |(mut cur, addr)| { - let payload_len = cur.get_ref().len() - cur.position() as usize; - debug!( - "UDP ASSOCIATE {} <- {}, payload length {} bytes", - src, addr, payload_len - ); - - let mut data = Vec::new(); - UdpAssociateHeader::new(0, Address::SocketAddress(src)).write_to_buf(&mut data); - - cur.read_to_end(&mut data).unwrap(); - - SendDgramRc::new(socket, data, src) - }) - .map(|_| ()); - - tokio::spawn(rel.map_err(|err| { - error!("Error occurs in UDP relay: {}", err); - })); - - Ok(()) - }) -} - /// Starts a UDP local server -pub fn run(context: SharedContext) -> impl Future + Send { +pub async fn run(context: SharedContext) -> io::Result<()> { let local_addr = *context.config().local.as_ref().unwrap(); - futures::lazy(move || { - info!("ShadowSocks UDP Listening on {}", local_addr); + let listener = UdpSocket::bind(&local_addr).await?; + info!("ShadowSocks UDP listening on {}", local_addr); - UdpSocket::bind(&local_addr) - }) - .and_then(move |l| listen(context, l)) + listen(context, listener).await } diff --git a/src/relay/udprelay/mod.rs b/src/relay/udprelay/mod.rs index 04f6a20f..1b7ab8db 100644 --- a/src/relay/udprelay/mod.rs +++ b/src/relay/udprelay/mod.rs @@ -37,17 +37,6 @@ //! | Fixed | Variable | //! +-------+--------------+ -use std::{ - io, mem, - net::SocketAddr, - sync::{Arc, Mutex}, -}; - -use tokio::net::UdpSocket; - -use futures::{try_ready, Async, Future, Poll, Stream}; - -pub mod dns; pub mod local; pub mod server; @@ -59,74 +48,3 @@ mod crypto_io; /// Socks5 UDP ASSOCIATE packet size. The only thing I can find is /// [here](http://support.microsoft.com/kb/822061/)* pub const MAXIMUM_UDP_PAYLOAD_SIZE: usize = 65536; - -type SharedUdpSocket = Arc>; - -/// UDP `recv_from` stream -pub struct PacketStream { - udp: SharedUdpSocket, - buf: [u8; MAXIMUM_UDP_PAYLOAD_SIZE], -} - -impl PacketStream { - /// Creates a new `PacketStream` - pub fn new(udp: SharedUdpSocket) -> PacketStream { - PacketStream { - udp, - buf: [0u8; MAXIMUM_UDP_PAYLOAD_SIZE], - } - } -} - -impl Stream for PacketStream { - type Error = io::Error; - type Item = (Vec, SocketAddr); - - fn poll(&mut self) -> Poll, Self::Error> { - let (n, addr) = try_ready!(self.udp.lock().unwrap().poll_recv_from(&mut self.buf)); - Ok(Async::Ready(Some((self.buf[..n].to_vec(), addr)))) - } -} - -enum SendDgramStat> { - Pending { - udp: SharedUdpSocket, - buf: B, - addr: SocketAddr, - }, - Empty, -} - -/// Send datagram with `Rc` -pub struct SendDgramRc> { - stat: SendDgramStat, -} - -impl> SendDgramRc { - pub fn new(udp: SharedUdpSocket, buf: B, addr: SocketAddr) -> SendDgramRc { - SendDgramRc { - stat: SendDgramStat::Pending { udp, buf, addr }, - } - } -} - -impl> Future for SendDgramRc { - type Error = io::Error; - type Item = (SharedUdpSocket, usize, B); - - fn poll(&mut self) -> Poll { - let n = match self.stat { - SendDgramStat::Pending { - ref udp, - ref buf, - ref addr, - } => try_ready!(udp.lock().unwrap().poll_send_to(buf.as_ref(), addr)), - SendDgramStat::Empty => unreachable!(), - }; - - match mem::replace(&mut self.stat, SendDgramStat::Empty) { - SendDgramStat::Pending { udp, buf, .. } => Ok(Async::Ready((udp, n, buf))), - SendDgramStat::Empty => unreachable!(), - } - } -} diff --git a/src/relay/udprelay/server.rs b/src/relay/udprelay/server.rs index ad7ec4ce..a38a80ce 100644 --- a/src/relay/udprelay/server.rs +++ b/src/relay/udprelay/server.rs @@ -1,163 +1,183 @@ //! UDP relay proxy server use std::{ - io::{self, Cursor, ErrorKind}, + io::{self, Cursor}, net::{IpAddr, Ipv4Addr, SocketAddr}, - sync::{Arc, Mutex}, + sync::Arc, time::Duration, }; -use futures::{self, stream::futures_unordered, Future, Stream}; +use bytes::BytesMut; +use futures::{self, stream::FuturesUnordered, StreamExt}; use log::{debug, error, info}; -use tokio::{self, net::UdpSocket, util::FutureExt}; +use tokio::{self, net::UdpSocket, sync::mpsc}; use crate::{ config::ServerConfig, context::SharedContext, - relay::{boxed_future, dns_resolver::resolve, socks5::Address}, + relay::{socks5::Address, utils::try_timeout}, }; use super::{ crypto_io::{decrypt_payload, encrypt_payload}, - PacketStream, SendDgramRc, MAXIMUM_UDP_PAYLOAD_SIZE, + MAXIMUM_UDP_PAYLOAD_SIZE, }; -fn resolve_remote_addr( +#[allow(unused_variables)] // `context` is only used if trust-dns is enabled +async fn udp_associate( context: SharedContext, - addr: Address, -) -> impl Future + Send { - match addr { - Address::SocketAddress(s) => { - if context.config().forbidden_ip.contains(&s.ip()) { - let err = io::Error::new( - ErrorKind::Other, - format!("{} is forbidden, failed to connect {}", s.ip(), s), - ); - return boxed_future(futures::done(Err(err))); + svr_cfg: Arc, + decrypted_pkt: Vec, + src: SocketAddr, +) -> io::Result> { + const DEFAULT_TIMEOUT: Duration = Duration::from_secs(5); + + // CLIENT -> SERVER protocol: ADDRESS + PAYLOAD + let mut cur = Cursor::new(decrypted_pkt); + + let addr = Address::read_from(&mut cur).await?; + + // Take out internal buffer for optimizing one byte copy + let header_len = cur.position() as usize; + let decrypted_pkt = cur.into_inner(); + let body = &decrypted_pkt[header_len..]; + + debug!("UDP ASSOCIATE {} -> {}, payload length {} bytes", src, addr, body.len()); + + // FIXME: Create one UdpSocket for one associate + let local_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0); + let mut remote_udp = UdpSocket::bind(&local_addr).await?; + + let timeout = svr_cfg.udp_timeout().unwrap_or(DEFAULT_TIMEOUT); + + // Writes body to remote + let send_len = match addr { + Address::SocketAddress(ref remote_addr) => { + try_timeout(remote_udp.send_to(&body, remote_addr), Some(timeout)).await? + } + #[cfg(feature = "trust-dns")] + Address::DomainNameAddress(ref dname, port) => { + use crate::relay::dns_resolver::resolve; + + let vec_ipaddr = resolve(context, dname, port, false).await?; + assert!(!vec_ipaddr.is_empty()); + + try_timeout(remote_udp.send_to(&body, &vec_ipaddr[0]), Some(timeout)).await? + } + #[cfg(not(feature = "trust-dns"))] + Address::DomainNameAddress(ref dname, port) => { + try_timeout(remote_udp.send_to(&body, (dname.as_str(), port)), Some(timeout)).await? + } + }; + assert_eq!(body.len(), send_len); + + // Waiting for response from server SERVER -> CLIENT + // Packet length is limited by MAXIMUM_UDP_PAYLOAD_SIZE, excess bytes will be discarded. + let mut remote_buf = [0u8; MAXIMUM_UDP_PAYLOAD_SIZE]; + let remote_recv_len = try_timeout(remote_udp.recv(&mut remote_buf), Some(timeout)).await?; + + // Making response packet, SERVER -> CLIENT: ADDRESS + PAYLOAD + let mut send_buf = Vec::new(); + addr.write_to_buf(&mut send_buf); + send_buf.extend_from_slice(&remote_buf[..remote_recv_len]); + + debug!( + "UDP ASSOCIATE {} <- {}, payload length {} bytes", + src, + addr, + send_buf.len() + ); + + Ok(send_buf) +} + +async fn listen(context: SharedContext, svr_cfg: Arc) -> io::Result<()> { + let listen_addr = *svr_cfg.addr().listen_addr(); + info!("ShadowSocks UDP listening on {}", listen_addr); + + let listener = UdpSocket::bind(&listen_addr).await?; + let (mut r, mut w) = listener.split(); + + let svr_cfg_cloned = svr_cfg.clone(); + + // FIXME: Channel size 1024? + let (tx, mut rx) = mpsc::channel::<(SocketAddr, Vec)>(1024); + tokio::spawn(async move { + let svr_cfg = svr_cfg_cloned; + + while let Some((src, pkt)) = rx.recv().await { + // Encrypts + let mut response_pkt = BytesMut::new(); + if let Err(err) = encrypt_payload(svr_cfg.method(), svr_cfg.key(), &pkt, &mut response_pkt) { + error!("UDP packet encrypt failed, err: {:?}", err); + continue; } - boxed_future(futures::finished(s)) - } - Address::DomainNameAddress(dname, port) => { - let fut = resolve(context, &dname, port, true).map(move |vec_ipaddr| { - assert!(!vec_ipaddr.is_empty()); - vec_ipaddr[0] - }); - boxed_future(fut) + if let Err(err) = w.send_to(&response_pkt, &src).await { + error!("UDP packet send failed, err: {:?}", err); + break; + } } + + // FIXME: How to stop the outer listener Future? + }); + + let mut pkt_buf = [0u8; MAXIMUM_UDP_PAYLOAD_SIZE]; + + loop { + let (recv_len, src) = r.recv_from(&mut pkt_buf).await?; + + // Packet length is limited by MAXIMUM_UDP_PAYLOAD_SIZE, excess bytes will be discarded. + let pkt = &pkt_buf[..recv_len]; + + // First of all, decrypt payload CLIENT -> SERVER + let decrypted_pkt = match decrypt_payload(svr_cfg.method(), svr_cfg.key(), pkt) { + Ok(Some(pkt)) => pkt, + Ok(None) => { + error!("Failed to decrypt pkt in UDP relay, packet too short"); + continue; + } + Err(err) => { + error!("Failed to decrypt pkt in UDP relay: {}", err); + continue; + } + }; + + let context = context.clone(); + let svr_cfg = svr_cfg.clone(); + let mut tx = tx.clone(); + tokio::spawn(async move { + match udp_associate(context, svr_cfg, decrypted_pkt, src).await { + Ok(pkt) => { + if let Err(..) = tx.send((src, pkt)).await { + error!("UDP packet channel closed"); + } + } + Err(err) => { + error!("Error occurs in UDP relay: {}", err); + } + } + }); } } -fn listen(context: SharedContext, svr_cfg: Arc) -> impl Future + Send { - let listen_addr = *svr_cfg.addr().listen_addr(); - info!("ShadowSocks UDP listening on {}", listen_addr); - futures::lazy(move || UdpSocket::bind(&listen_addr)).and_then(move |socket| { - let socket = Arc::new(Mutex::new(socket)); - PacketStream::new(socket.clone()).for_each(move |(pkt, src)| { - let svr_cfg = svr_cfg.clone(); - let svr_cfg_cloned = svr_cfg.clone(); - let socket = socket.clone(); - let context = context.clone(); - let timeout = svr_cfg.timeout(); - let rel = futures::lazy(move || decrypt_payload(svr_cfg.method(), svr_cfg.key(), &pkt)) - .and_then(move |payload| { - // Read Address in the front (ShadowSocks protocol) - Address::read_from(Cursor::new(payload)) - .map_err(From::from) - .and_then(move |(r, addr)| { - let header_len = r.position() as usize; - let mut payload = r.into_inner(); - payload.drain(..header_len); - let body = payload; - - debug!("UDP ASSOCIATE {} -> {}, payload length {} bytes", src, addr, body.len()); - Ok((addr, body)) - }) - .and_then(|(addr, body)| { - let local_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0); - UdpSocket::bind(&local_addr).map(|remote_udp| (remote_udp, addr, body)) - }) - .and_then(|(remote_udp, addr, body)| { - resolve_remote_addr(context, addr.clone()) - .and_then(|addr| remote_udp.send_dgram(body, &addr)) - .map(|(remote_udp, _)| (remote_udp, addr)) - }) - }) - .and_then(move |(remote_udp, addr)| { - let buf = vec![0u8; MAXIMUM_UDP_PAYLOAD_SIZE]; - let to = timeout.unwrap_or(Duration::from_secs(5)); - let caddr = addr.clone(); - remote_udp - .recv_dgram(buf) - .timeout(to) - .map_err(move |err| match err.into_inner() { - Some(e) => e, - None => { - error!( - "Udp associate waiting datagram {} -> {} timed out in {:?}", - src, caddr, to - ); - io::Error::new(io::ErrorKind::TimedOut, "udp recv timed out") - } - }) - .and_then(|(_remote_udp, buf, n, _from)| { - let svr_cfg = svr_cfg_cloned; - - let mut send_buf = Vec::new(); - addr.write_to_buf(&mut send_buf); - send_buf.extend_from_slice(&buf[..n]); - encrypt_payload(svr_cfg.method(), svr_cfg.key(), &send_buf).map(|buf| (buf, addr)) - }) - }) - .and_then(move |(buf, addr)| { - debug!("UDP ASSOCIATE {} <- {}, payload length {} bytes", src, addr, buf.len()); - - let to = timeout.unwrap_or(Duration::from_secs(5)); - let caddr = addr.clone(); - SendDgramRc::new(socket, buf, src) - .timeout(to) - .map_err(move |err| match err.into_inner() { - Some(e) => e, - None => { - error!( - "Udp associate sending datagram {} <- {} timed out in {:?}", - src, caddr, to - ); - io::Error::new(io::ErrorKind::TimedOut, "udp send timed out") - } - }) - }) - .map(|_| ()); - - tokio::spawn(rel.map_err(|err| { - error!("Udp relay error: {}", err); - })); - - Ok(()) - }) - }) -} - /// Starts a UDP relay server -pub fn run(context: SharedContext) -> impl Future + Send { - let mut vec_fut = Vec::new(); +pub async fn run(context: SharedContext) -> io::Result<()> { + let vec_fut = FuturesUnordered::new(); for svr in &context.config().server { let svr_cfg = Arc::new(svr.clone()); let svr_fut = listen(context.clone(), svr_cfg); - vec_fut.push(boxed_future(svr_fut)); + vec_fut.push(svr_fut); } - futures_unordered(vec_fut).into_future().then(|res| match res { - Ok(..) => { - error!("One of UDP servers exited unexpectly without error"); + match vec_fut.into_future().await.0 { + Some(res) => { + error!("One of TCP servers exited unexpectly, result: {:?}", res); let err = io::Error::new(io::ErrorKind::Other, "server exited unexpectly"); Err(err) } - Err((err, ..)) => { - error!("One of UDP servers exited unexpectly with error {}", err); - Err(err) - } - }) + None => unreachable!(), + } } diff --git a/src/relay/utils.rs b/src/relay/utils.rs index 175bc857..61782773 100644 --- a/src/relay/utils.rs +++ b/src/relay/utils.rs @@ -1,47 +1,16 @@ -use std::io; +use std::future::Future; +use std::{io, time::Duration}; -use futures::{try_ready, Async, Future, Poll}; -use tokio_io::{ - io::{write_all, WriteAll}, - AsyncWrite, -}; +use tokio::time; -/// Write all bytes without returning the internal bytes buffer -pub struct WriteBytes +pub async fn try_timeout(fut: F, timeout: Option) -> io::Result where - W: AsyncWrite, - B: AsRef<[u8]>, + F: Future>, + io::Error: From, { - inner: WriteAll, -} - -impl WriteBytes -where - W: AsyncWrite, - B: AsRef<[u8]>, -{ - fn new(writer: W, bytes: B) -> WriteBytes { - WriteBytes { - inner: write_all(writer, bytes), - } + match timeout { + Some(t) => time::timeout(t, fut).await?, + None => fut.await, } -} - -impl Future for WriteBytes -where - W: AsyncWrite, - B: AsRef<[u8]>, -{ - type Error = io::Error; - type Item = W; - - fn poll(&mut self) -> Poll { - let (w, _) = try_ready!(self.inner.poll()); - Ok(Async::Ready(w)) - } -} - -/// Write all bytes without returning the internal bytes buffer -pub fn write_bytes>(w: W, b: B) -> WriteBytes { - WriteBytes::new(w, b) + .map_err(From::from) } diff --git a/tests/dns.rs b/tests/dns.rs deleted file mode 100644 index f1454030..00000000 --- a/tests/dns.rs +++ /dev/null @@ -1,84 +0,0 @@ -use std::{ - collections::HashSet, - net::{SocketAddr, UdpSocket}, - thread, - time::Duration, -}; - -use dns_parser::{Builder, Packet, QueryClass, QueryType}; -use log::trace; -use shadowsocks::{ - config::{Config, ConfigType}, - run_dns, run_server, -}; -use tokio::runtime::current_thread::Runtime; - -const CONFIG: &str = r#"{ - "server": "127.0.0.1", - "server_port": 8988, - "local_port": 5030, - "local_address": "127.0.0.1", - "password": "abc", - "timeout": 20, - "method": "aes-256-gcm", - "mode": "tcp_and_udp", - "remote_dns": "114.114.114.114:53" - }"#; - -#[test] -fn dns_relay() { - let _ = env_logger::try_init(); - - let server_cfg = Config::load_from_str(CONFIG, ConfigType::Server).unwrap(); - let dns_cfg = Config::load_from_str(CONFIG, ConfigType::Local).unwrap(); - - trace!("Server: {:?}", server_cfg); - trace!("Client: {:?}", dns_cfg); - - thread::spawn(move || { - let mut runtime = Runtime::new().expect("Failed to create Runtime"); - runtime.block_on(run_server(server_cfg)).unwrap(); - }); - - thread::spawn(move || { - let mut runtime = Runtime::new().expect("Failed to create Runtime"); - runtime.block_on(run_dns(dns_cfg)).unwrap(); - }); - - thread::sleep(Duration::from_secs(1)); - - let dns_addr = "127.0.0.1:5030".parse::().unwrap(); - let local_addr = "0.0.0.0:0".parse::().unwrap(); - let local = UdpSocket::bind(&local_addr).unwrap(); - local.set_read_timeout(Some(Duration::from_secs(5))).unwrap(); - local.set_write_timeout(Some(Duration::from_secs(5))).unwrap(); - - const LOOP_ROUND: usize = 10; - - let mut sent_id = HashSet::::with_capacity(LOOP_ROUND); - for _ in 0..LOOP_ROUND { - let mut id = rand::random::(); - while let Some(..) = sent_id.get(&id) { - id = rand::random::(); - } - - sent_id.insert(id); - - let mut builder = Builder::new_query(id, false); - builder.add_question("www.example.com", false, QueryType::A, QueryClass::IN); - - let payload = builder.build().unwrap(); - - local.send_to(&payload, &dns_addr).unwrap(); - - trace!("DNS SENT {:?}", payload); - - let mut buf = [0u8; 65535]; - let (len, _) = local.recv_from(&mut buf).unwrap(); - - let packet = Packet::parse(&buf[..len]).unwrap(); - - trace!("DNS GOT {:?}", packet); - assert_eq!(packet.header.id, id); - } -} diff --git a/tests/socks5.rs b/tests/socks5.rs index d54665db..fb566797 100644 --- a/tests/socks5.rs +++ b/tests/socks5.rs @@ -1,12 +1,7 @@ -use std::{ - net::{SocketAddr, ToSocketAddrs}, - thread, - time::Duration, -}; +use std::net::{SocketAddr, ToSocketAddrs}; -use futures::Future; -use tokio::runtime::current_thread::Runtime; -use tokio_io::io::{flush, read_to_end, write_all}; +use tokio::prelude::*; +use tokio::time::{self, Duration}; use shadowsocks::{ config::{Config, ConfigType, Mode, ServerConfig}, @@ -52,27 +47,19 @@ impl Socks5TestServer { &self.local_addr } - pub fn run(&self) { + pub async fn run(&self) { let svr_cfg = self.svr_config.clone(); - thread::spawn(move || { - let mut runtime = Runtime::new().expect("Failed to create Runtime"); - let fut = run_server(svr_cfg); - runtime.block_on(fut).expect("Failed to run Server"); - }); + tokio::spawn(run_server(svr_cfg)); let client_cfg = self.cli_config.clone(); - thread::spawn(move || { - let mut runtime = Runtime::new().expect("Failed to create Runtime"); - let fut = run_local(client_cfg); - runtime.block_on(fut).expect("Failed to run Local"); - }); + tokio::spawn(run_local(client_cfg)); - thread::sleep(Duration::from_secs(1)); + time::delay_for(Duration::from_secs(1)).await; } } -#[test] -fn socks5_relay_stream() { +#[tokio::test] +async fn socks5_relay_stream() { let _ = env_logger::try_init(); const SERVER_ADDR: &str = "127.0.0.1:8100"; @@ -82,29 +69,27 @@ fn socks5_relay_stream() { const METHOD: CipherType = CipherType::Aes256Cfb; let svr = Socks5TestServer::new(SERVER_ADDR, LOCAL_ADDR, PASSWORD, METHOD, false); - svr.run(); + svr.run().await; - let c = Socks5Client::connect( + let mut c = Socks5Client::connect( Address::DomainNameAddress("www.example.com".to_owned(), 80), - *svr.client_addr(), - ); + svr.client_addr(), + ) + .await + .unwrap(); - let fut = c.and_then(|c| { - let req = b"GET / HTTP/1.0\r\nHost: www.example.com\r\nAccept: */*\r\n\r\n"; - write_all(c, req.to_vec()) - .and_then(|(c, _)| flush(c)) - .and_then(|c| read_to_end(c, Vec::new())) - .map(|(_, buf)| { - println!("Got reply from server: {}", String::from_utf8(buf).unwrap()); - }) - }); + let req = b"GET / HTTP/1.0\r\nHost: www.example.com\r\nAccept: */*\r\n\r\n"; + c.write_all(req).await.unwrap(); + c.flush().await.unwrap(); - let mut runtime = Runtime::new().expect("Failed to create Runtime"); - runtime.block_on(fut).unwrap(); + let mut buf = Vec::new(); + c.read_to_end(&mut buf).await.unwrap(); + + println!("Got reply from server: {}", String::from_utf8(buf).unwrap()); } -#[test] -fn socks5_relay_aead() { +#[tokio::test] +async fn socks5_relay_aead() { let _ = env_logger::try_init(); const SERVER_ADDR: &str = "127.0.0.1:8110"; @@ -114,22 +99,21 @@ fn socks5_relay_aead() { const METHOD: CipherType = CipherType::Aes256Gcm; let svr = Socks5TestServer::new(SERVER_ADDR, LOCAL_ADDR, PASSWORD, METHOD, false); - svr.run(); + svr.run().await; - let c = Socks5Client::connect( + let mut c = Socks5Client::connect( Address::DomainNameAddress("www.example.com".to_owned(), 80), - *svr.client_addr(), - ); - let fut = c.and_then(|c| { - let req = b"GET / HTTP/1.0\r\nHost: www.example.com\r\nAccept: */*\r\n\r\n"; - write_all(c, req.to_vec()) - .and_then(|(c, _)| flush(c)) - .and_then(|c| read_to_end(c, Vec::new())) - .map(|(_, buf)| { - println!("Got reply from server: {}", String::from_utf8(buf).unwrap()); - }) - }); + svr.client_addr(), + ) + .await + .unwrap(); - let mut runtime = Runtime::new().expect("Failed to create Runtime"); - runtime.block_on(fut).unwrap(); + let req = b"GET / HTTP/1.0\r\nHost: www.example.com\r\nAccept: */*\r\n\r\n"; + c.write_all(req).await.unwrap(); + c.flush().await.unwrap(); + + let mut buf = Vec::new(); + c.read_to_end(&mut buf).await.unwrap(); + + println!("Got reply from server: {}", String::from_utf8(buf).unwrap()); } diff --git a/tests/tunnel.rs b/tests/tunnel.rs new file mode 100644 index 00000000..3564f211 --- /dev/null +++ b/tests/tunnel.rs @@ -0,0 +1,57 @@ +use env_logger; +use tokio; +use tokio::net::TcpStream; +use tokio::prelude::*; +use tokio::time::{self, Duration}; + +use shadowsocks::config::{Config, ConfigType}; +use shadowsocks::relay::socks5::Address; +use shadowsocks::{run_local, run_server}; + +#[tokio::test] +async fn tcp_tunnel() { + let _ = env_logger::try_init(); + + let mut local_config = Config::load_from_str( + r#"{ + "local_port": 9110, + "local_address": "127.0.0.1", + "server": "127.0.0.1", + "server_port": 9120, + "password": "password", + "method": "aes-256-gcm" + }"#, + ConfigType::Local, + ) + .unwrap(); + + local_config.forward = Some("www.example.com:80".parse::
().unwrap()); + + let server_config = Config::load_from_str( + r#"{ + "server": "127.0.0.1", + "server_port": 9120, + "password": "password", + "method": "aes-256-gcm" + }"#, + ConfigType::Server, + ) + .unwrap(); + + tokio::spawn(run_local(local_config)); + tokio::spawn(run_server(server_config)); + + time::delay_for(Duration::from_secs(1)).await; + + // Connect it directly, because it is now established a TCP tunnel with www.example.com + let mut stream = TcpStream::connect("127.0.0.1:9110").await.unwrap(); + + let req = b"GET / HTTP/1.0\r\nHost: www.example.com\r\nAccept: */*\r\n\r\n"; + stream.write_all(req).await.unwrap(); + stream.flush().await.unwrap(); + + let mut buf = Vec::new(); + stream.read_to_end(&mut buf).await.unwrap(); + + println!("Got reply from server: {}", String::from_utf8(buf).unwrap()); +} diff --git a/tests/udp.rs b/tests/udp.rs index 5d821727..7b026bf8 100644 --- a/tests/udp.rs +++ b/tests/udp.rs @@ -1,17 +1,14 @@ #![cfg_attr(clippy, allow(blacklisted_name))] use std::{ - io::Cursor, + io::{self, Cursor}, net::SocketAddr, - sync::{Arc, Barrier}, - thread, - time::Duration, }; use bytes::{BufMut, BytesMut}; -use futures::Future; -use tokio::runtime::current_thread::Runtime; -use tokio_io::io::read_to_end; +use log::debug; +use tokio::prelude::*; +use tokio::time::{self, Duration}; use shadowsocks::{ config::{Config, ConfigType, Mode, ServerConfig}, @@ -59,88 +56,67 @@ fn get_client_addr() -> SocketAddr { LOCAL_ADDR.parse().unwrap() } -fn start_server(bar: Arc) { - thread::spawn(move || { - let mut runtime = Runtime::new().expect("Failed to create Runtime"); +fn start_server() { + tokio::spawn(run_server(get_svr_config())); +} - let fut = run_server(get_svr_config()); - bar.wait(); - runtime.block_on(fut).expect("Failed to run Server"); +fn start_local() { + tokio::spawn(run_local(get_cli_config())); +} + +fn start_udp_echo_server() { + use tokio::net::UdpSocket; + + tokio::spawn(async { + let mut l = UdpSocket::bind(UDP_ECHO_SERVER_ADDR).await.unwrap(); + + debug!("UDP echo server started {}", UDP_ECHO_SERVER_ADDR); + + let mut buf = vec![0u8; 65536]; + let (amt, src) = l.recv_from(&mut buf).await.unwrap(); + + debug!("UDP echo received {} bytes from {}", amt, src); + + l.send_to(&buf[..amt], &src).await.unwrap(); + + debug!("UDP echo sent {} bytes to {}", amt, src); }); } -fn start_local(bar: Arc) { - thread::spawn(move || { - let mut runtime = Runtime::new().expect("Failed to create Runtime"); +fn start_udp_request_holder(addr: Address) { + tokio::spawn(async move { + let (mut c, addr) = Socks5Client::udp_associate(addr, &get_client_addr()).await?; + assert_eq!(addr, Address::SocketAddress(LOCAL_ADDR.parse().unwrap())); - let fut = run_local(get_cli_config()); - bar.wait(); - runtime.block_on(fut).expect("Failed to run Local"); + debug!("TCP sent UDP associate {} request", addr); + + // Holds it forever + let mut buf = Vec::new(); + c.read_to_end(&mut buf).await?; + + io::Result::Ok(()) }); } -fn start_udp_echo_server(bar: Arc) { - use std::net::UdpSocket; - - thread::spawn(move || { - let l = UdpSocket::bind(UDP_ECHO_SERVER_ADDR).unwrap(); - - bar.wait(); - - let mut buf = [0u8; 65536]; - let (amt, src) = l.recv_from(&mut buf).unwrap(); - - l.send_to(&buf[..amt], &src).unwrap(); - }); -} - -fn start_udp_request_holder(bar: Arc, addr: Address) { - thread::spawn(move || { - let mut runtime = Runtime::new().expect("Failed to create Runtime"); - - let c = Socks5Client::udp_associate(addr, get_client_addr()); - let fut = c.and_then(|(c, addr)| { - assert_eq!(addr, Address::SocketAddress(LOCAL_ADDR.parse().unwrap())); - - // Holds it forever - read_to_end(c, Vec::new()).map(|_| ()) - }); - - bar.wait(); - - runtime.block_on(fut).expect("Failed to run UDP socks5 client"); - }); -} - -#[test] -fn udp_relay() { - use std::net::UdpSocket; +#[tokio::test] +async fn udp_relay() { + use tokio::net::UdpSocket; let _ = env_logger::try_init(); let remote_addr = Address::SocketAddress(UDP_ECHO_SERVER_ADDR.parse().unwrap()); - let bar = Arc::new(Barrier::new(4)); + start_server(); + start_local(); - start_server(bar.clone()); - start_local(bar.clone()); - - start_udp_echo_server(bar.clone()); - - bar.wait(); + start_udp_echo_server(); // Wait until all server starts - thread::sleep(Duration::from_secs(1)); + time::delay_for(Duration::from_secs(1)).await; - let bar = Arc::new(Barrier::new(2)); + start_udp_request_holder(remote_addr.clone()); - start_udp_request_holder(bar.clone(), remote_addr.clone()); - - bar.wait(); - - let l = UdpSocket::bind(UDP_LOCAL_ADDR).unwrap(); - l.set_read_timeout(Some(Duration::from_secs(5))).unwrap(); - l.set_write_timeout(Some(Duration::from_secs(5))).unwrap(); + let mut l = UdpSocket::bind(UDP_LOCAL_ADDR).await.unwrap(); let header = UdpAssociateHeader::new(0, remote_addr); let mut buf = BytesMut::with_capacity(header.serialized_len()); @@ -152,14 +128,17 @@ fn udp_relay() { buf.put_slice(payload); let local_addr = LOCAL_ADDR.parse::().unwrap(); - l.send_to(&buf[..], &local_addr).unwrap(); + l.send_to(&buf[..], &local_addr).await.unwrap(); - let mut buf = [0u8; 65536]; - let (amt, _) = l.recv_from(&mut buf).unwrap(); + let mut buf = vec![0u8; 65536]; + let (amt, _) = time::timeout(Duration::from_secs(5), l.recv_from(&mut buf)) + .await + .unwrap() + .unwrap(); println!("Received buf size={} {:?}", amt, &buf[..amt]); - let cur = Cursor::new(buf[..amt].to_vec()); - let (cur, header) = UdpAssociateHeader::read_from(cur).wait().expect("Invalid UDP header"); + let mut cur = Cursor::new(buf[..amt].to_vec()); + let header = UdpAssociateHeader::read_from(&mut cur).await.unwrap(); println!("{:?}", header); let header_len = cur.position() as usize; let buf = cur.into_inner();