Repository: kpcyrd/authoscope (language: rust)

Issue:
pwntools-like socket library
It should be possible to naively implement arbitrary (text) protocols using badtouch, using functions similar to the socket functions provided by pwntools.

This allows more creative solutions for #58, eg by talking to an smtpd.

- [ ] `sock_connect(host, port, {fam='ipv4', timeout=3, ssl=true, ssl_verify=false, newline="\r\n"})`, create a connection
- [x] `sock_send(sock, data)`, send data
- [x] `sock_recv(sock)`, receive up to 4096 bytes
- [x] `sock_sendline(sock, line)`, send a line, automatically adds a newline
- [x] `sock_recvline(sock)`, receive a line, strips newline
- [x] `sock_recvall(sock)`, receive until EOF
- [ ] `sock_recvline_contains(sock, needle)`, receive lines until one contains at least one needle, then return that line (supports string and list of strings)
- [x] `sock_recvline_regex(sock, regex)`, receive lines until one matches the regex, then return that line
- [x] `sock_recvn(sock, n)`, receive exactly n bytes
- [x] `sock_recvuntil(sock, delim)`, receive until a sequence of bytes is found, then return all data including the sequence (supports string and list of strings)
- [x] `sock_sendafter(sock, delim, data)`, shorthand for `sock_recvuntil(sock, delim)` and `sock_send(sock, data)`
- [ ] `sock_unrecv(sock, data)`, put data back into the buffered reader
- [x] `sock_newline(sock, "\r\n")`, update the newline


Discussion / hints:

Fix patch:
diff --git a/.travis.yml b/.travis.yml
index 4a54117..604181d 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,5 +1,15 @@
 language: rust
+
+matrix:
+  include:
+  - os: linux
+    rust: stable
+    env: BUILD_MODE=build
+  - os: linux
+    rust: stable
+    env: BUILD_MODE=integration
+
+before_install:
+- sudo ci/dependencies.sh
 script:
-- cargo build --verbose --all
-- cargo build --verbose --examples
-- cargo test --verbose --all
+- ci/run.sh
diff --git a/Cargo.lock b/Cargo.lock
index 839587b..4cf541a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -67,7 +67,7 @@ name = "backtrace-sys"
 version = "0.1.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "cc 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cc 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
@@ -78,7 +78,8 @@ dependencies = [
  "atty 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
  "base64 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "bcrypt 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "colored 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bufstream 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "colored 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "digest 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "env_logger 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
  "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -95,9 +96,10 @@ dependencies = [
  "nix 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "pbr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "rand 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "reqwest 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde 1.0.69 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde_derive 1.0.69 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_derive 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_json 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)",
  "sha-1 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "sha2 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -107,7 +109,7 @@ dependencies = [
  "threadpool 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
  "toml 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "twox-hash 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "twox-hash 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -204,7 +206,7 @@ dependencies = [
 
 [[package]]
 name = "cc"
-version = "1.0.17"
+version = "1.0.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
@@ -246,10 +248,10 @@ dependencies = [
 
 [[package]]
 name = "colored"
-version = "1.6.0"
+version = "1.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -328,7 +330,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "cssparser-macros 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "dtoa-short 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "itoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)",
  "proc-macro2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -360,7 +362,7 @@ dependencies = [
 
 [[package]]
 name = "dtoa"
-version = "0.4.2"
+version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
@@ -368,7 +370,7 @@ name = "dtoa-short"
 version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -456,7 +458,7 @@ dependencies = [
 
 [[package]]
 name = "futures"
-version = "0.1.21"
+version = "0.1.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
@@ -464,7 +466,7 @@ name = "futures-cpupool"
 version = "0.1.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
  "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
@@ -541,7 +543,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "base64 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
  "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "httparse 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)",
@@ -565,7 +567,7 @@ name = "hyper-tls"
 version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
  "hyper 0.11.27 (registry+https://github.com/rust-lang/crates.io-index)",
  "native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -576,7 +578,7 @@ dependencies = [
 
 [[package]]
 name = "idna"
-version = "0.1.4"
+version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -595,7 +597,7 @@ dependencies = [
 
 [[package]]
 name = "itoa"
-version = "0.4.1"
+version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
@@ -660,7 +662,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
  "lber 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -673,7 +675,7 @@ dependencies = [
  "tokio-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-uds 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-uds-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -729,8 +731,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "phf 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf_codegen 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde 1.0.69 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde_derive 1.0.69 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_derive 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_json 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)",
  "string_cache 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "string_cache_codegen 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -789,7 +791,7 @@ name = "miniz-sys"
 version = "0.1.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "cc 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cc 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
@@ -848,11 +850,11 @@ dependencies = [
  "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
  "nix 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "regex 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde 1.0.69 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_json 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)",
  "smallvec 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "twox-hash 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "twox-hash 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
@@ -871,13 +873,13 @@ dependencies = [
  "num-bigint 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "rand 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "regex 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde 1.0.69 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_json 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)",
  "sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "sha2 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "smallvec 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
- "twox-hash 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "twox-hash 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "uuid 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
@@ -928,7 +930,7 @@ version = "0.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "cc 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cc 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
  "cfg-if 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
  "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -996,7 +998,7 @@ name = "openssl-sys"
 version = "0.9.33"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "cc 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cc 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
  "pkg-config 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)",
  "vcpkg 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1181,7 +1183,7 @@ name = "relay"
 version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -1199,20 +1201,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "encoding_rs 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
  "hyper 0.11.27 (registry+https://github.com/rust-lang/crates.io-index)",
  "hyper-tls 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "libflate 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "mime_guess 2.0.0-alpha.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde 1.0.69 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_json 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_urlencoded 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "uuid 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
@@ -1284,12 +1286,12 @@ dependencies = [
 
 [[package]]
 name = "serde"
-version = "1.0.69"
+version = "1.0.70"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "serde_derive"
-version = "1.0.69"
+version = "1.0.70"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "proc-macro2 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1302,9 +1304,9 @@ name = "serde_json"
 version = "1.0.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde 1.0.69 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "itoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -1312,10 +1314,10 @@ name = "serde_urlencoded"
 version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde 1.0.69 (registry+https://github.com/rust-lang/crates.io-index)",
- "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "itoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)",
+ "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -1404,7 +1406,7 @@ dependencies = [
  "new_debug_unreachable 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf_shared 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)",
  "precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde 1.0.69 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)",
  "string_cache_codegen 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
@@ -1568,14 +1570,14 @@ name = "tokio"
 version = "0.1.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
  "mio 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-fs 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-reactor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-tcp 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-threadpool 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-threadpool 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-timer 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-udp 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
@@ -1586,7 +1588,7 @@ version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
@@ -1596,7 +1598,7 @@ version = "0.1.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
  "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "mio 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1613,7 +1615,7 @@ name = "tokio-executor"
 version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -1621,9 +1623,9 @@ name = "tokio-fs"
 version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-threadpool 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-threadpool 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -1632,7 +1634,7 @@ version = "0.1.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
@@ -1641,7 +1643,7 @@ name = "tokio-proto"
 version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
  "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
  "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1658,7 +1660,7 @@ name = "tokio-reactor"
 version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "mio 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)",
  "slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1671,7 +1673,7 @@ name = "tokio-service"
 version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -1680,7 +1682,7 @@ version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.22 (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.15 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1689,11 +1691,11 @@ dependencies = [
 
 [[package]]
 name = "tokio-threadpool"
-version = "0.1.4"
+version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "crossbeam-deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1705,7 +1707,7 @@ name = "tokio-timer"
 version = "0.2.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
@@ -1714,7 +1716,7 @@ name = "tokio-tls"
 version = "0.1.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
  "native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1726,7 +1728,7 @@ version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "mio 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-codec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1740,7 +1742,7 @@ version = "0.1.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.22 (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.42 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1755,7 +1757,7 @@ name = "tokio-uds-proto"
 version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1767,7 +1769,7 @@ name = "toml"
 version = "0.4.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "serde 1.0.69 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -1777,10 +1779,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "twox-hash"
-version = "1.1.0"
+version = "1.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -1798,7 +1800,7 @@ name = "unicase"
 version = "1.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "version_check 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -1806,7 +1808,7 @@ name = "unicase"
 version = "2.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "version_check 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -1842,10 +1844,10 @@ dependencies = [
 
 [[package]]
 name = "url"
-version = "1.7.0"
+version = "1.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
@@ -1881,7 +1883,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "version_check"
-version = "0.1.3"
+version = "0.1.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
@@ -1894,7 +1896,7 @@ name = "want"
 version = "0.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "try-lock 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
@@ -1968,12 +1970,12 @@ dependencies = [
 "checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40"
 "checksum byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "74c0b906e9446b0a2e4f760cdb3fa4b2c48cdc6db8766a845c54b6ff063fd2e9"
 "checksum bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7dd32989a66957d3f0cba6588f15d4281a733f4e9ffc43fcd2385f57d3bf99ff"
-"checksum cc 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "49ec142f5768efb5b7622aebc3fdbdbb8950a4b9ba996393cb76ef7466e8747d"
+"checksum cc 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)" = "2119ea4867bd2b8ed3aecab467709720b2d55b1bcfe09f772fd68066eaf15275"
 "checksum cfg-if 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "efe5c877e17a9c717a0bf3613b2709f723202c4e4675cc8f12926ded29bcb17e"
 "checksum chrono 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6962c635d530328acc53ac6a955e83093fedc91c5809dfac1fa60fa470830a37"
 "checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e"
 "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
-"checksum colored 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b0aa3473e85a3161b59845d6096b289bb577874cafeaf75ea1b1beaa6572c7fc"
+"checksum colored 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc0a60679001b62fb628c4da80e574b9645ab4646056d7c9018885efffe45533"
 "checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e"
 "checksum core-foundation 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "25bfd746d203017f7d5cbd31ee5d8e17f94b6521c7af77ece6c9e4b2d4b16c67"
 "checksum core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "065a5d7ffdcbc8fa145d6f0746f3555025b9097a9e9cda59f7467abae670c78d"
@@ -1985,7 +1987,7 @@ dependencies = [
 "checksum cssparser 0.23.10 (registry+https://github.com/rust-lang/crates.io-index)" = "8f1c74d99b0f489cc546336b911452562ebfd4aec034b0c526cb77a3a02d3790"
 "checksum cssparser-macros 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "72b64d0d64184a082f38028396875c08c8ee083481f6f16e6e0cf52c338bd785"
 "checksum digest 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3cae2388d706b52f2f2f9afe280f9d768be36544bd71d1b8120cb34ea6450b55"
-"checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab"
+"checksum dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6d301140eb411af13d3115f9a562c85cc6b541ade9dfa314132244aaee7489dd"
 "checksum dtoa-short 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "59020b8513b76630c49d918c33db9f4c91638e7d3404a28084083b87e33f76f2"
 "checksum encoding_rs 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "98fd0f24d1fb71a4a6b9330c8ca04cbd4e7cc5d846b54ca74ff376bc7c9f798d"
 "checksum env_logger 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "0e6e40ebb0e66918a37b38c7acab4e10d299e0463fe2af5d29b9cc86710cfd2a"
@@ -1998,7 +2000,7 @@ dependencies = [
 "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 futf 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7c9c1ce3fa9336301af935ab852c437817d14cd33690446569392e65170aac3b"
-"checksum futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)" = "1a70b146671de62ec8c8ed572219ca5d594d9b06c0b364d5e67b722fc559b48c"
+"checksum futures 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)" = "80599c995ed197a276e27c27f94a6346446538adde3b87c1ab384f6f8cabfed4"
 "checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4"
 "checksum gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb"
 "checksum generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d"
@@ -2010,9 +2012,9 @@ dependencies = [
 "checksum humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0484fda3e7007f2a4a0d9c3a703ca38c71c54c55602ce4660c419fd32e188c9e"
 "checksum hyper 0.11.27 (registry+https://github.com/rust-lang/crates.io-index)" = "34a590ca09d341e94cddf8e5af0bbccde205d5fbc2fa3c09dd67c7f85cea59d7"
 "checksum hyper-tls 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a5aa51f6ae9842239b0fac14af5f22123b8432b4cc774a44ff059fcba0f675ca"
-"checksum idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "014b298351066f1512874135335d62a789ffe78a9974f94b43ed5621951eaf7d"
+"checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e"
 "checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08"
-"checksum itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c069bbec61e1ca5a596166e55dfe4773ff745c3d16b700013bcaff9a6df2c682"
+"checksum itoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5adb58558dcd1d786b5f0bd15f3226ee23486e24b7b58304b60f64dc68e62606"
 "checksum keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7"
 "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
 "checksum kuchiki 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "527efdb1790299c18df1240b780a348b0adaa4c66dd06ab9f25a4200a8bab9ec"
@@ -2088,8 +2090,8 @@ dependencies = [
 "checksum security-framework 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "dfa44ee9c54ce5eecc9de7d5acbad112ee58755239381f687e564004ba4a2332"
 "checksum security-framework-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "5421621e836278a0b139268f36eee0dc7e389b784dc3f79d8f11aabadf41bead"
 "checksum selectors 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4c81ca1c531d2774be3fe90ed5dab4b2f88ca99b7bdf6a511d212fa44adf9dde"
-"checksum serde 1.0.69 (registry+https://github.com/rust-lang/crates.io-index)" = "210e5a3b159c566d7527e9b22e44be73f2e0fcc330bb78fef4dbccb56d2e74c8"
-"checksum serde_derive 1.0.69 (registry+https://github.com/rust-lang/crates.io-index)" = "dd724d68017ae3a7e63600ee4b2fdb3cad2158ffd1821d44aff4580f63e2b593"
+"checksum serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)" = "0c3adf19c07af6d186d91dae8927b83b0553d07ca56cbf7f2f32560455c91920"
+"checksum serde_derive 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)" = "3525a779832b08693031b8ecfb0de81cd71cfd3812088fafe9a7496789572124"
 "checksum serde_json 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)" = "84b8035cabe9b35878adec8ac5fe03d5f6bc97ff6edd7ccb96b44c1276ba390e"
 "checksum serde_urlencoded 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e703cef904312097cfceab9ce131ff6bbe09e8c964a0703345a5f49238757bc1"
 "checksum servo_arc 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432"
@@ -2132,7 +2134,7 @@ dependencies = [
 "checksum tokio-reactor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e00ec63bbec2c97ce1178cb0587b2c438b2f6b09d3ee54a33c45a9cf0d530810"
 "checksum tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24da22d077e0f15f55162bdbdc661228c1581892f52074fb242678d015b45162"
 "checksum tokio-tcp 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ec9b094851aadd2caf83ba3ad8e8c4ce65a42104f7b94d9e6550023f0407853f"
-"checksum tokio-threadpool 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c3873a6d8d0b636e024e77b9a82eaab6739578a06189ecd0e731c7308fbc5d"
+"checksum tokio-threadpool 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "24ab84f574027b0e875378f31575cf175360891919e93a3490f07e76e00e4efb"
 "checksum tokio-timer 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "028b94314065b90f026a21826cffd62a4e40a92cda3e5c069cc7b02e5945f5e9"
 "checksum tokio-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "772f4b04e560117fe3b0a53e490c16ddc8ba6ec437015d91fa385564996ed913"
 "checksum tokio-udp 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "43eb534af6e8f37d43ab1b612660df14755c42bd003c5f8d2475ee78cc4600c0"
@@ -2140,7 +2142,7 @@ dependencies = [
 "checksum tokio-uds-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "93842f83f760d2a48eb54225f819d05549e69c481f56be4a1b1f51decf99da5b"
 "checksum toml 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a0263c6c02c4db6c8f7681f9fd35e90de799ebd4cfdeab77a38f4ff6b3d8c0d9"
 "checksum try-lock 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee2aa4715743892880f70885373966c83d73ef1b0838a664ef0c76fffd35e7c2"
-"checksum twox-hash 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "475352206e7a290c5fccc27624a163e8d0d115f7bb60ca18a64fc9ce056d7435"
+"checksum twox-hash 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4f85be565a110ed72ed7048cf56570db04ce0a592c98aa59b7dacde3e5718750"
 "checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169"
 "checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d"
 "checksum unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33"
@@ -2150,13 +2152,13 @@ dependencies = [
 "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 unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
-"checksum url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f808aadd8cfec6ef90e4a14eb46f24511824d1ac596b9682703c87056c8678b7"
+"checksum url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2a321979c09843d272956e73700d12c4e7d3d92b2ee112b31548aef0d4efc5a6"
 "checksum utf-8 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f1262dfab4c30d5cb7c07026be00ee343a6cf5027fdc0104a9160f354e5db75c"
 "checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122"
 "checksum uuid 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e1436e58182935dcd9ce0add9ea0b558e8a87befe01c1a301e6020aeb0876363"
 "checksum vcpkg 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cbe533e138811704c0e3cbde65a818b35d3240409b4346256c5ede403e082474"
 "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
-"checksum version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6b772017e347561807c1aa192438c5fd74242a670a6cffacc40f2defd1dc069d"
+"checksum version_check 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7716c242968ee87e5542f8021178248f267f295a5c4803beae8b8b7fd9bc6051"
 "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
 "checksum want 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a05d9d966753fa4b5c8db73fcab5eed4549cfe0e1e4e66911e5564a0085c35d1"
 "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
diff --git a/Cargo.toml b/Cargo.toml
index eca82bb..fedbc4d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -33,6 +33,8 @@ nix = "0.11"
 serde = "1.0"
 serde_json = "1.0"
 serde_derive = "1.0"
+bufstream = "0.1.3"
+regex = "1.0.1"
 
 md-5 = "0.7"
 sha-1 = "0.7"
diff --git a/ci/dependencies.sh b/ci/dependencies.sh
new file mode 100755
index 0000000..29d0b27
--- /dev/null
+++ b/ci/dependencies.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+apt-get -qq update
+
+if [ -n "$TRAVIS" ]; then
+    # update docker
+    apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce
+fi
diff --git a/ci/run.sh b/ci/run.sh
new file mode 100755
index 0000000..8831e9e
--- /dev/null
+++ b/ci/run.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+set -exu
+case "$BUILD_MODE" in
+    build)
+        cargo build --verbose --all
+        cargo build --verbose --examples
+        cargo test --verbose --all
+        ;;
+    integration)
+        echo "[*] building badtouch"
+        cargo build --verbose --all
+
+        echo "[*] testing smtp"
+        docker build -t badtouch-smtpd ci/smtp/
+        docker run --name badtouch-smtpd -d --rm -p 127.0.0.1:25:25 badtouch-smtpd
+
+        echo root@example.com:foo > /tmp/badtouch-smtp-input.txt
+        target/debug/badtouch -o badtouch-smtp-output.txt creds /tmp/badtouch-smtp-input.txt scripts/smtp.lua
+        grep root@example.com badtouch-smtp-output.txt
+
+        docker stop badtouch-smtpd
+        ;;
+esac
diff --git a/ci/smtp/Dockerfile b/ci/smtp/Dockerfile
new file mode 100644
index 0000000..fc04642
--- /dev/null
+++ b/ci/smtp/Dockerfile
@@ -0,0 +1,7 @@
+FROM debian:stretch
+RUN apt-get update -qq \
+    && apt-get install -yq opensmtpd
+RUN echo "foo" > /etc/mailname
+ADD smtpd.conf /etc/smtpd.conf
+EXPOSE 25
+CMD ["/usr/sbin/smtpd", "-d"]
diff --git a/ci/smtp/smtpd.conf b/ci/smtp/smtpd.conf
new file mode 100644
index 0000000..3819b6d
--- /dev/null
+++ b/ci/smtp/smtpd.conf
@@ -0,0 +1,14 @@
+# This is the smtpd server system-wide configuration file.
+# See smtpd.conf(5) for more information.
+
+# To accept external mail, replace with: listen on all
+listen on 0.0.0.0
+
+# If you edit the file, you have to run "smtpctl update table aliases"
+table aliases file:/etc/aliases
+
+# Uncomment the following to accept external mail for domain "example.org"
+accept from any for domain "example.com" alias <aliases> deliver to mbox
+
+#accept for local alias <aliases> deliver to mbox
+accept for any relay
diff --git a/scripts/smtp.lua b/scripts/smtp.lua
new file mode 100644
index 0000000..cfde7e5
--- /dev/null
+++ b/scripts/smtp.lua
@@ -0,0 +1,40 @@
+descr = "smtp enum"
+
+function verify(user, password)
+    -- enumeration only, password is ignored
+    sock = sock_connect("127.0.0.1", 25)
+    if last_err() then return end
+    sock_newline(sock, "\r\n")
+
+    -- get the banner
+    sock_recvline(sock)
+    if last_err() then return end
+
+    -- send ehlo
+    sock_sendline(sock, "ehlo localhost")
+    if last_err() then return end
+
+    -- read extensions
+    sock_recvline_regex(sock, "^250 ")
+    if last_err() then return end
+
+    -- start delivering an email
+    sock_sendline(sock, "MAIL FROM:<root>")
+    if last_err() then return end
+
+    -- read reply
+    sock_recvline(sock)
+    if last_err() then return end
+    -- TODO: verify starts with "250 "
+
+    -- probe for user
+    sock_sendline(sock, "RCPT TO:<" .. user .. ">")
+    if last_err() then return end
+
+    -- read reply
+    reply = sock_recvn(sock, 1)
+    if last_err() then return end
+
+    -- check it was successful
+    return reply == "2"
+end
diff --git a/src/ctx.rs b/src/ctx.rs
index 26f8601..2ebdd56 100644
--- a/src/ctx.rs
+++ b/src/ctx.rs
@@ -13,6 +13,7 @@ use http::{HttpSession,
            RequestOptions};
 use config::Config;
 use mysql;
+use sockets::Socket;
 
 
 #[derive(Debug, Clone)]
@@ -21,6 +22,7 @@ pub struct State {
     error: Arc<Mutex<Option<Error>>>,
     http_sessions: Arc<Mutex<HashMap<String, HttpSession>>>,
     mysql_sessions: Arc<Mutex<HashMap<String, Arc<Mutex<mysql::Conn>>>>>,
+    socket_sessions: Arc<Mutex<HashMap<String, Arc<Mutex<Socket>>>>>,
 }
 
 impl State {
@@ -30,6 +32,7 @@ impl State {
             error: Arc::new(Mutex::new(None)),
             http_sessions: Arc::new(Mutex::new(HashMap::new())),
             mysql_sessions: Arc::new(Mutex::new(HashMap::new())),
+            socket_sessions: Arc::new(Mutex::new(HashMap::new())),
         }
     }
 
@@ -50,6 +53,10 @@ impl State {
         cp.into()
     }
 
+    fn random_id(&self) -> String {
+        thread_rng().sample_iter(&Alphanumeric).take(16).collect()
+    }
+
     pub fn register_in_jar(&self, session: &str, cookies: Vec<(String, String)>) {
         let mut mtx = self.http_sessions.lock().unwrap();
         if let Some(session) = mtx.get_mut(session) {
@@ -73,7 +80,7 @@ impl State {
 
     pub fn mysql_register(&self, sock: mysql::Conn) -> String {
         let mut mtx = self.mysql_sessions.lock().unwrap();
-        let id: String = thread_rng().sample_iter(&Alphanumeric).take(16).collect();
+        let id = self.random_id();
 
         let sock = Arc::new(Mutex::new(sock));
         mtx.insert(id.clone(), sock);
@@ -86,6 +93,22 @@ impl State {
         let sock = mtx.get(id).expect("invalid session reference"); // TODO
         sock.clone()
     }
+
+    pub fn sock_connect(&self, host: &str, port: u16) -> Result<String> {
+        let mut mtx = self.socket_sessions.lock().unwrap();
+        let id = self.random_id();
+
+        let sock = Socket::connect(host, port)?;
+        mtx.insert(id.clone(), Arc::new(Mutex::new(sock)));
+
+        Ok(id)
+    }
+
+    pub fn get_sock(&self, id: &str)-> Arc<Mutex<Socket>> {
+        let mtx = self.socket_sessions.lock().unwrap();
+        let sock = mtx.get(id).expect("invalid session reference"); // TODO
+        sock.clone()
+    }
 }
 
 
@@ -169,6 +192,18 @@ impl Script {
         runtime::sha3_256(&mut lua, state.clone());
         runtime::sha3_512(&mut lua, state.clone());
         runtime::sleep(&mut lua, state.clone());
+        runtime::sock_connect(&mut lua, state.clone());
+        runtime::sock_send(&mut lua, state.clone());
+        runtime::sock_recv(&mut lua, state.clone());
+        runtime::sock_sendline(&mut lua, state.clone());
+        runtime::sock_recvline(&mut lua, state.clone());
+        runtime::sock_recvall(&mut lua, state.clone());
+        runtime::sock_recvline_contains(&mut lua, state.clone());
+        runtime::sock_recvline_regex(&mut lua, state.clone());
+        runtime::sock_recvn(&mut lua, state.clone());
+        runtime::sock_recvuntil(&mut lua, state.clone());
+        runtime::sock_sendafter(&mut lua, state.clone());
+        runtime::sock_newline(&mut lua, state.clone());
 
         (lua, state)
     }
diff --git a/src/lib.rs b/src/lib.rs
index 72e3fde..5a09ca2 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -12,6 +12,8 @@ extern crate kuchiki;
 extern crate toml;
 extern crate nix;
 extern crate libc;
+extern crate bufstream;
+extern crate regex;
 #[macro_use] extern crate log;
 #[macro_use] extern crate serde_derive;
 #[macro_use] extern crate error_chain;
@@ -46,6 +48,7 @@ pub mod keyboard;
 pub mod pb;
 pub mod runtime;
 pub mod scheduler;
+pub mod sockets;
 pub mod structs;
 pub mod ulimit;
 pub mod utils;
@@ -61,6 +64,7 @@ pub mod errors {
     use nix;
     use bcrypt;
     use mysql;
+    use regex;
 
     error_chain! {
         foreign_links {
@@ -68,13 +72,13 @@ pub mod errors {
             Lua(hlua::LuaError);
             Json(serde_json::Error);
             Reqwest(reqwest::Error);
-            Utf8(std::str::Utf8Error);
             BufWrite(std::io::IntoInnerError<std::io::BufWriter<std::io::Stdout>>);
             Base64Decode(base64::DecodeError);
             Toml(toml::de::Error);
             Nix(nix::Error);
             Bcrypt(bcrypt::BcryptError);
             Mysql(mysql::Error);
+            Regex(regex::Error);
         }
     }
 }
diff --git a/src/runtime.rs b/src/runtime.rs
index a59336f..ade4b17 100644
--- a/src/runtime.rs
+++ b/src/runtime.rs
@@ -511,3 +511,151 @@ pub fn sleep(lua: &mut hlua::Lua, _: State) {
         0
     }))
 }
+
+pub fn sock_connect(lua: &mut hlua::Lua, state: State) {
+    lua.set("sock_connect", hlua::function3(move |host: String, port: u16, _settings: AnyLuaValue| -> Result<String> {
+        state.sock_connect(&host, port)
+            .map_err(|err| state.set_error(err))
+    }))
+}
+
+pub fn sock_send(lua: &mut hlua::Lua, state: State) {
+    lua.set("sock_send", hlua::function2(move |sock: String, bytes: AnyLuaValue| -> Result<()> {
+        let sock = state.get_sock(&sock);
+        let mut sock = sock.lock().unwrap();
+
+        let bytes = byte_array(bytes)
+            .map_err(|err| state.set_error(err))?;
+
+        sock.send(&bytes)
+            .map_err(|err| state.set_error(err))?;
+
+        Ok(())
+    }))
+}
+
+pub fn sock_recv(lua: &mut hlua::Lua, state: State) {
+    lua.set("sock_recv", hlua::function1(move |sock: String| -> Result<AnyLuaValue> {
+        let sock = state.get_sock(&sock);
+        let mut sock = sock.lock().unwrap();
+
+        let bytes = sock.recv()
+            .map_err(|err| state.set_error(err))?;
+
+        Ok(lua_bytes(&bytes))
+    }))
+}
+
+pub fn sock_sendline(lua: &mut hlua::Lua, state: State) {
+    lua.set("sock_sendline", hlua::function2(move |sock: String, line: String| -> Result<()> {
+        let sock = state.get_sock(&sock);
+        let mut sock = sock.lock().unwrap();
+
+        sock.sendline(&line)
+            .map_err(|err| state.set_error(err))?;
+
+        Ok(())
+    }))
+}
+
+pub fn sock_recvline(lua: &mut hlua::Lua, state: State) {
+    lua.set("sock_recvline", hlua::function1(move |sock: String| -> Result<String> {
+        let sock = state.get_sock(&sock);
+        let mut sock = sock.lock().unwrap();
+
+        let line = sock.recvline()
+            .map_err(|err| state.set_error(err))?;
+
+        Ok(line)
+    }))
+}
+
+pub fn sock_recvall(lua: &mut hlua::Lua, state: State) {
+    lua.set("sock_recvall", hlua::function1(move |sock: String| -> Result<AnyLuaValue> {
+        let sock = state.get_sock(&sock);
+        let mut sock = sock.lock().unwrap();
+
+        let bytes = sock.recvall()
+            .map_err(|err| state.set_error(err))?;
+
+        Ok(lua_bytes(&bytes))
+    }))
+}
+
+pub fn sock_recvline_contains(lua: &mut hlua::Lua, state: State) {
+    lua.set("sock_recvline_contains", hlua::function2(move |sock: String, needle: String| -> Result<String> {
+        let sock = state.get_sock(&sock);
+        let mut sock = sock.lock().unwrap();
+
+        let line = sock.recvline_contains(&needle)
+            .map_err(|err| state.set_error(err))?;
+
+        Ok(line)
+    }))
+}
+
+pub fn sock_recvline_regex(lua: &mut hlua::Lua, state: State) {
+    lua.set("sock_recvline_regex", hlua::function2(move |sock: String, regex: String| -> Result<String> {
+        let sock = state.get_sock(&sock);
+        let mut sock = sock.lock().unwrap();
+
+        let line = sock.recvline_regex(&regex)
+            .map_err(|err| state.set_error(err))?;
+
+        Ok(line)
+    }))
+}
+
+pub fn sock_recvn(lua: &mut hlua::Lua, state: State) {
+    lua.set("sock_recvn", hlua::function2(move |sock: String, n: u32| -> Result<AnyLuaValue> {
+        let sock = state.get_sock(&sock);
+        let mut sock = sock.lock().unwrap();
+
+        let bytes = sock.recvn(n)
+            .map_err(|err| state.set_error(err))?;
+
+        Ok(lua_bytes(&bytes))
+    }))
+}
+
+pub fn sock_recvuntil(lua: &mut hlua::Lua, state: State) {
+    lua.set("sock_recvuntil", hlua::function2(move |sock: String, delim: AnyLuaValue| -> Result<AnyLuaValue> {
+        let sock = state.get_sock(&sock);
+        let mut sock = sock.lock().unwrap();
+
+        let delim = byte_array(delim)
+            .map_err(|err| state.set_error(err))?;
+
+        let bytes = sock.recvuntil(&delim)
+            .map_err(|err| state.set_error(err))?;
+
+        Ok(lua_bytes(&bytes))
+    }))
+}
+
+pub fn sock_sendafter(lua: &mut hlua::Lua, state: State) {
+    lua.set("sock_sendafter", hlua::function3(move |sock: String, delim: AnyLuaValue, bytes: AnyLuaValue| -> Result<()> {
+        let sock = state.get_sock(&sock);
+        let mut sock = sock.lock().unwrap();
+
+        let delim = byte_array(delim)
+            .map_err(|err| state.set_error(err))?;
+
+        let bytes = byte_array(bytes)
+            .map_err(|err| state.set_error(err))?;
+
+        sock.sendafter(&delim, &bytes)
+            .map_err(|err| state.set_error(err))?;
+
+        Ok(())
+    }))
+}
+
+pub fn sock_newline(lua: &mut hlua::Lua, state: State) {
+    lua.set("sock_newline", hlua::function2(move |sock: String, newline: String| -> () {
+        let sock = state.get_sock(&sock);
+        let mut sock = sock.lock().unwrap();
+
+        sock.newline(newline);
+    }))
+}
diff --git a/src/sockets.rs b/src/sockets.rs
new file mode 100644
index 0000000..93cd4a0
--- /dev/null
+++ b/src/sockets.rs
@@ -0,0 +1,166 @@
+use errors::{Result, ResultExt};
+
+use bufstream::BufStream;
+use regex::Regex;
+
+use std::str;
+use std::io;
+use std::io::prelude::*;
+use std::io::BufRead;
+use std::net::TcpStream;
+use std::net::ToSocketAddrs;
+
+
+#[derive(Debug)]
+pub struct Socket {
+    stream: BufStream<TcpStream>,
+    newline: String,
+}
+
+impl Socket {
+    pub fn connect(host: &str, port: u16) -> Result<Socket> {
+        let addrs = (host, port).to_socket_addrs()?;
+
+        let mut errors = Vec::new();
+
+        for addr in addrs {
+            debug!("connecting to {:?}", addr);
+            match TcpStream::connect(&addr) {
+                Ok(socket) => {
+                    debug!("successfully connected to {:?}", addr);
+                    let stream = BufStream::new(socket);
+
+                    return Ok(Socket {
+                        stream,
+                        newline: String::from("\n"),
+                    });
+                },
+                Err(err) => errors.push((addr, err)),
+            }
+        }
+
+        if errors.is_empty() {
+            bail!("no dns records found");
+        } else {
+            bail!("couldn't connect: {:?}", errors);
+        }
+    }
+
+    pub fn send(&mut self, data: &[u8]) -> Result<()> {
+        match str::from_utf8(&data) {
+            Ok(data) => debug!("send: {:?}", data),
+            Err(_) => debug!("send: {:?}", data),
+        };
+        self.stream.write_all(data)?;
+        self.stream.flush()?;
+        Ok(())
+    }
+
+    pub fn recv(&mut self) -> Result<Vec<u8>> {
+        let mut buf = [0; 4096];
+        let n = self.stream.read(&mut buf)?;
+        let data = buf[..n].to_vec();
+        match str::from_utf8(&data) {
+            Ok(data) => debug!("recv: {:?}", data),
+            Err(_) => debug!("recv: {:?}", data),
+        };
+        Ok(data)
+    }
+
+    pub fn sendline(&mut self, line: &str) -> Result<()> {
+        let line = format!("{}{}", line, self.newline);
+        self.send(line.as_bytes())
+    }
+
+    pub fn recvline(&mut self) -> Result<String> {
+        let needle = self.newline.clone();
+        let buf = self.recvuntil(needle.as_bytes())?;
+        let line = String::from_utf8(buf)
+            .chain_err(|| "failed to decode utf8")?;
+        Ok(line)
+    }
+
+    pub fn recvall(&mut self) -> Result<Vec<u8>> {
+        let mut buf = Vec::new();
+        self.stream.read_to_end(&mut buf)?;
+        match str::from_utf8(&buf) {
+            Ok(buf) => debug!("recvall: {:?}", buf),
+            Err(_) => debug!("recvall: {:?}", buf),
+        };
+        Ok(buf)
+    }
+
+    pub fn recvline_contains(&mut self, needle: &str) -> Result<String> {
+        loop {
+            let line = self.recvline()?;
+            if line.contains(needle) {
+                return Ok(line);
+            }
+        }
+    }
+
+    pub fn recvline_regex(&mut self, regex: &str) -> Result<String> {
+        let regex = Regex::new(regex)?;
+        loop {
+            let line = self.recvline()?;
+            if regex.is_match(&line) {
+                return Ok(line);
+            }
+        }
+    }
+
+    pub fn recvn(&mut self, n: u32) -> Result<Vec<u8>> {
+        let mut buf = vec![0; n as usize];
+        self.stream.read_exact(buf.as_mut_slice())?;
+        match str::from_utf8(&buf) {
+            Ok(buf) => debug!("recvn: {:?}", buf),
+            Err(_) => debug!("recvn: {:?}", buf),
+        };
+        Ok(buf.to_vec())
+    }
+
+    pub fn recvuntil(&mut self, delim: &[u8]) -> Result<Vec<u8>> {
+        let mut buf = Vec::new();
+
+        let delim_len = delim.len();
+
+        loop {
+            let (done, used) = {
+                let available = match self.stream.fill_buf() {
+                    Ok(n) => n,
+                    Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue,
+                    Err(e) => return Err(e.into())
+                };
+
+                match available.windows(delim_len).position(|window| window == delim) {
+                    Some(i) => {
+                        buf.extend_from_slice(&available[..i + delim_len]);
+                        (true, i + delim_len)
+                    }
+                    None => {
+                        buf.extend_from_slice(available);
+                        (false, available.len())
+                    }
+                }
+            };
+            self.stream.consume(used);
+
+            if done || used == 0 {
+                match str::from_utf8(&buf) {
+                    Ok(buf) => debug!("recvuntil: {:?}", buf),
+                    Err(_) => debug!("recvuntil: {:?}", buf),
+                };
+                return Ok(buf);
+            }
+        }
+    }
+
+    pub fn sendafter(&mut self, delim: &[u8], data: &[u8]) -> Result<()> {
+        self.recvuntil(delim)?;
+        self.send(data)
+    }
+
+    pub fn newline<I: Into<String>>(&mut self, delim: I) {
+        self.newline = delim.into();
+    }
+}
diff --git a/src/utils.rs b/src/utils.rs
index d51db1e..1628f60 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -1,10 +1,11 @@
+use errors::{Result, ResultExt};
+
 use std::str;
 use std::fs::{self, File};
 use std::sync::Arc;
 use std::io::{self, BufReader};
 use std::io::prelude::*;
 use config::Config;
-use errors::Result;
 
 use ctx;
 
@@ -33,7 +34,8 @@ pub fn load_creds(path: &str) -> Result<Vec<Arc<Vec<u8>>>> {
         }
 
         // ensure line is valid utf8
-        str::from_utf8(&buf)?;
+        str::from_utf8(&buf)
+            .chain_err(|| "failed to decode utf8")?;
 
         if buf.iter().any(|x| *x == b':') {
             creds.push(Arc::new(buf.clone()));


Test patch: