浏览代码

Replace Tokio with std

This commit removes a load of dependencies and replaces them with code that just uses std. dog was only dealing with one request at a time anyway, so all the async stuff was just unnecessary overhead.

• tokio for UDP and TCP transports gets replaced with std::net
• native-tls + tokio-tls get replaced with just native-tls
• hyper and hyper-tls also get replaced with native-tls and httparse, where we write the HTTP headers ourselves
• async-trait is no longer necessary since the trait is sync now

Now, all the four main transport types work the same way — create a buffer and fill it with bytes read from a stream (or datagram for UDP). This commit also removes like 40 dependencies, and just about halves compilation time.
Benjamin Sago 4 年之前
父节点
当前提交
5f69a6afb7
共有 11 个文件被更改,包括 142 次插入536 次删除
  1. 9 413
      Cargo.lock
  2. 5 11
      dns-transport/Cargo.toml
  3. 3 5
      dns-transport/src/auto.rs
  4. 59 32
      dns-transport/src/https.rs
  5. 13 11
      dns-transport/src/lib.rs
  6. 12 14
      dns-transport/src/tcp.rs
  7. 11 15
      dns-transport/src/tls.rs
  8. 10 13
      dns-transport/src/udp.rs
  9. 1 2
      src/main.rs
  10. 11 12
      src/output.rs
  11. 8 8
      xtests/badssl.toml

+ 9 - 413
Cargo.lock

@@ -21,7 +21,7 @@ version = "0.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
 dependencies = [
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -30,18 +30,7 @@ version = "0.12.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
 dependencies = [
- "winapi 0.3.9",
-]
-
-[[package]]
-name = "async-trait"
-version = "0.1.41"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b246867b8b3b6ae56035f1eb1ed557c1d8eae97f0d53696138a50fa0e3a3b8c0"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
+ "winapi",
 ]
 
 [[package]]
@@ -52,7 +41,7 @@ checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
 dependencies = [
  "hermit-abi",
  "libc",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -87,12 +76,6 @@ version = "1.3.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
 
-[[package]]
-name = "bytes"
-version = "0.5.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38"
-
 [[package]]
 name = "cc"
 version = "1.0.61"
@@ -145,7 +128,7 @@ checksum = "d0fcb4df22ae812fa2f6d5e3b577247584cc67fce06ad0779168d1dd41cbcce3"
 dependencies = [
  "libc",
  "redox_syscall",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -179,15 +162,11 @@ dependencies = [
 name = "dns-transport"
 version = "0.1.0"
 dependencies = [
- "async-trait",
  "derive_more",
  "dns",
- "hyper",
- "hyper-tls",
+ "httparse",
  "log",
  "native-tls",
- "tokio",
- "tokio-tls",
 ]
 
 [[package]]
@@ -230,12 +209,6 @@ dependencies = [
  "synstructure",
 ]
 
-[[package]]
-name = "fnv"
-version = "1.0.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
-
 [[package]]
 name = "foreign-types"
 version = "0.3.2"
@@ -251,61 +224,6 @@ version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
 
-[[package]]
-name = "fuchsia-zircon"
-version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
-dependencies = [
- "bitflags",
- "fuchsia-zircon-sys",
-]
-
-[[package]]
-name = "fuchsia-zircon-sys"
-version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
-
-[[package]]
-name = "futures-channel"
-version = "0.3.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7a4d35f7401e948629c9c3d6638fb9bf94e0b2121e96c3b428cc4e631f3eb74"
-dependencies = [
- "futures-core",
-]
-
-[[package]]
-name = "futures-core"
-version = "0.3.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d674eaa0056896d5ada519900dbf97ead2e46a7b6621e8160d79e2f2e1e2784b"
-
-[[package]]
-name = "futures-sink"
-version = "0.3.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0d8764258ed64ebc5d9ed185cf86a95db5cac810269c5d20ececb32e0088abbd"
-
-[[package]]
-name = "futures-task"
-version = "0.3.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4dd26820a9f3637f1302da8bceba3ff33adbe53464b54ca24d4e2d4f1db30f94"
-
-[[package]]
-name = "futures-util"
-version = "0.3.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a894a0acddba51a2d49a6f4263b1e64b8c579ece8af50fa86503d52cd1eea34"
-dependencies = [
- "futures-core",
- "futures-task",
- "pin-project",
- "pin-utils",
-]
-
 [[package]]
 name = "getopts"
 version = "0.2.21"
@@ -332,31 +250,6 @@ version = "0.22.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724"
 
-[[package]]
-name = "h2"
-version = "0.2.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "993f9e0baeed60001cf565546b0d3dbe6a6ad23f2bd31644a133c641eccf6d53"
-dependencies = [
- "bytes",
- "fnv",
- "futures-core",
- "futures-sink",
- "futures-util",
- "http",
- "indexmap",
- "slab",
- "tokio",
- "tokio-util",
- "tracing",
-]
-
-[[package]]
-name = "hashbrown"
-version = "0.9.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
-
 [[package]]
 name = "hermit-abi"
 version = "0.1.17"
@@ -366,95 +259,12 @@ dependencies = [
  "libc",
 ]
 
-[[package]]
-name = "http"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28d569972648b2c512421b5f2a405ad6ac9666547189d0c5477a3f200f3e02f9"
-dependencies = [
- "bytes",
- "fnv",
- "itoa",
-]
-
-[[package]]
-name = "http-body"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b"
-dependencies = [
- "bytes",
- "http",
-]
-
 [[package]]
 name = "httparse"
 version = "1.3.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9"
 
-[[package]]
-name = "httpdate"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47"
-
-[[package]]
-name = "hyper"
-version = "0.13.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2f3afcfae8af5ad0576a31e768415edb627824129e8e5a29b8bfccb2f234e835"
-dependencies = [
- "bytes",
- "futures-channel",
- "futures-core",
- "futures-util",
- "h2",
- "http",
- "http-body",
- "httparse",
- "httpdate",
- "itoa",
- "pin-project",
- "socket2",
- "tokio",
- "tower-service",
- "tracing",
- "want",
-]
-
-[[package]]
-name = "hyper-tls"
-version = "0.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d979acc56dcb5b8dddba3917601745e877576475aa046df3226eabdecef78eed"
-dependencies = [
- "bytes",
- "hyper",
- "native-tls",
- "tokio",
- "tokio-tls",
-]
-
-[[package]]
-name = "indexmap"
-version = "1.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "55e2e4c765aa53a0424761bf9f41aa7a6ac1efa87238f59560640e27fca028f2"
-dependencies = [
- "autocfg",
- "hashbrown",
-]
-
-[[package]]
-name = "iovec"
-version = "0.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e"
-dependencies = [
- "libc",
-]
-
 [[package]]
 name = "itoa"
 version = "0.4.6"
@@ -467,16 +277,6 @@ version = "0.12.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd"
 
-[[package]]
-name = "kernel32-sys"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
-dependencies = [
- "winapi 0.2.8",
- "winapi-build",
-]
-
 [[package]]
 name = "lazy_static"
 version = "1.4.0"
@@ -498,12 +298,6 @@ dependencies = [
  "cfg-if 0.1.10",
 ]
 
-[[package]]
-name = "memchr"
-version = "2.3.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
-
 [[package]]
 name = "miniz_oxide"
 version = "0.4.3"
@@ -514,37 +308,6 @@ dependencies = [
  "autocfg",
 ]
 
-[[package]]
-name = "mio"
-version = "0.6.22"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430"
-dependencies = [
- "cfg-if 0.1.10",
- "fuchsia-zircon",
- "fuchsia-zircon-sys",
- "iovec",
- "kernel32-sys",
- "libc",
- "log",
- "miow",
- "net2",
- "slab",
- "winapi 0.2.8",
-]
-
-[[package]]
-name = "miow"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
-dependencies = [
- "kernel32-sys",
- "net2",
- "winapi 0.2.8",
- "ws2_32-sys",
-]
-
 [[package]]
 name = "mutagen"
 version = "0.2.0"
@@ -596,17 +359,6 @@ dependencies = [
  "tempfile",
 ]
 
-[[package]]
-name = "net2"
-version = "0.2.35"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3ebc3ec692ed7c9a255596c67808dee269f64655d8baf7b4f0638e51ba1d6853"
-dependencies = [
- "cfg-if 0.1.10",
- "libc",
- "winapi 0.3.9",
-]
-
 [[package]]
 name = "object"
 version = "0.21.1"
@@ -652,41 +404,9 @@ version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9"
 dependencies = [
- "winapi 0.3.9",
-]
-
-[[package]]
-name = "pin-project"
-version = "0.4.27"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2ffbc8e94b38ea3d2d8ba92aea2983b503cd75d0888d75b86bb37970b5698e15"
-dependencies = [
- "pin-project-internal",
-]
-
-[[package]]
-name = "pin-project-internal"
-version = "0.4.27"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "65ad2ae56b6abe3a1ee25f15ee605bacadb9a764edaba9c2bf4103800d4a1895"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
+ "winapi",
 ]
 
-[[package]]
-name = "pin-project-lite"
-version = "0.1.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e555d9e657502182ac97b539fb3dae8b79cda19e3e4f8ffb5e8de4f18df93c95"
-
-[[package]]
-name = "pin-utils"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
-
 [[package]]
 name = "pkg-config"
 version = "0.3.19"
@@ -797,7 +517,7 @@ version = "0.5.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
 dependencies = [
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -819,7 +539,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75"
 dependencies = [
  "lazy_static",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -876,24 +596,6 @@ dependencies = [
  "serde",
 ]
 
-[[package]]
-name = "slab"
-version = "0.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
-
-[[package]]
-name = "socket2"
-version = "0.3.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1fa70dc5c8104ec096f4fe7ede7a221d35ae13dcd19ba1ad9a81d2cab9a1c44"
-dependencies = [
- "cfg-if 0.1.10",
- "libc",
- "redox_syscall",
- "winapi 0.3.9",
-]
-
 [[package]]
 name = "syn"
 version = "1.0.48"
@@ -928,83 +630,9 @@ dependencies = [
  "rand",
  "redox_syscall",
  "remove_dir_all",
- "winapi 0.3.9",
-]
-
-[[package]]
-name = "tokio"
-version = "0.2.22"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5d34ca54d84bf2b5b4d7d31e901a8464f7b60ac145a284fba25ceb801f2ddccd"
-dependencies = [
- "bytes",
- "fnv",
- "futures-core",
- "iovec",
- "lazy_static",
- "memchr",
- "mio",
- "pin-project-lite",
- "slab",
+ "winapi",
 ]
 
-[[package]]
-name = "tokio-tls"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a70f4fcd7b3b24fb194f837560168208f669ca8cb70d0c4b862944452396343"
-dependencies = [
- "native-tls",
- "tokio",
-]
-
-[[package]]
-name = "tokio-util"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499"
-dependencies = [
- "bytes",
- "futures-core",
- "futures-sink",
- "log",
- "pin-project-lite",
- "tokio",
-]
-
-[[package]]
-name = "tower-service"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860"
-
-[[package]]
-name = "tracing"
-version = "0.1.21"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b0987850db3733619253fe60e17cb59b82d37c7e6c0236bb81e4d6b87c879f27"
-dependencies = [
- "cfg-if 0.1.10",
- "log",
- "pin-project-lite",
- "tracing-core",
-]
-
-[[package]]
-name = "tracing-core"
-version = "0.1.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f50de3927f93d202783f4513cda820ab47ef17f624b03c096e86ef00c67e6b5f"
-dependencies = [
- "lazy_static",
-]
-
-[[package]]
-name = "try-lock"
-version = "0.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
-
 [[package]]
 name = "unicode-width"
 version = "0.1.8"
@@ -1023,28 +651,12 @@ version = "0.2.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c"
 
-[[package]]
-name = "want"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0"
-dependencies = [
- "log",
- "try-lock",
-]
-
 [[package]]
 name = "wasi"
 version = "0.9.0+wasi-snapshot-preview1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
 
-[[package]]
-name = "winapi"
-version = "0.2.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
-
 [[package]]
 name = "winapi"
 version = "0.3.9"
@@ -1055,12 +667,6 @@ dependencies = [
  "winapi-x86_64-pc-windows-gnu",
 ]
 
-[[package]]
-name = "winapi-build"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
-
 [[package]]
 name = "winapi-i686-pc-windows-gnu"
 version = "0.4.0"
@@ -1072,13 +678,3 @@ name = "winapi-x86_64-pc-windows-gnu"
 version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
-
-[[package]]
-name = "ws2_32-sys"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
-dependencies = [
- "winapi 0.2.8",
- "winapi-build",
-]

+ 5 - 11
dns-transport/Cargo.toml

@@ -14,19 +14,13 @@ dns = { path = "../dns" }
 # logging
 log = "0.4"
 
-# base networking
-async-trait = "0.1"
-tokio = { version = "0.2", features = ["dns", "tcp", "udp", "io-util"] }  # dns is used to resolve nameservers
-tokio-tls = { version = "0.3", optional = true }
-
-# tls
+# tls networking
 native-tls = { version = "0.2", optional = true }
 
-# https
-hyper = { version = "0.13", optional = true, default_features = false }
-hyper-tls = { version = "0.4", optional = true }
+# http response parsing
+httparse = { version = "1.3", optional = true }
 
 [features]
 default = []  # these are enabled in the main dog crate
-tls = ["native-tls", "tokio-tls"]
-https = ["hyper", "hyper-tls"]
+tls   = ["native-tls"]
+https = ["native-tls", "httparse"]

+ 3 - 5
dns-transport/src/auto.rs

@@ -1,4 +1,3 @@
-use async_trait::async_trait;
 use log::*;
 
 use dns::{Request, Response};
@@ -45,11 +44,10 @@ impl AutoTransport {
 }
 
 
-#[async_trait]
 impl Transport for AutoTransport {
-    async fn send(&self, request: &Request) -> Result<Response, Error> {
+    fn send(&self, request: &Request) -> Result<Response, Error> {
         let udp_transport = UdpTransport::new(&self.addr);
-        let udp_response = udp_transport.send(&request).await?;
+        let udp_response = udp_transport.send(&request)?;
 
         if ! udp_response.flags.truncated {
             return Ok(udp_response);
@@ -58,7 +56,7 @@ impl Transport for AutoTransport {
         debug!("Truncated flag set, so switching to TCP");
 
         let tcp_transport = TcpTransport::new(&self.addr);
-        let tcp_response = tcp_transport.send(&request).await?;
+        let tcp_response = tcp_transport.send(&request)?;
         Ok(tcp_response)
     }
 }

+ 59 - 32
dns-transport/src/https.rs

@@ -1,6 +1,8 @@
 #![cfg_attr(not(feature="https"), allow(unused))]
 
-use async_trait::async_trait;
+use std::io::{Read, Write};
+use std::net::TcpStream;
+
 use log::*;
 
 use dns::{Request, Response};
@@ -44,49 +46,74 @@ impl HttpsTransport {
     }
 }
 
-#[async_trait]
 impl Transport for HttpsTransport {
 
     #[cfg(feature="https")]
-    async fn send(&self, request: &Request) -> Result<Response, Error> {
-        use hyper::body::HttpBody as _;
-
-        let https = hyper_tls::HttpsConnector::new();
-        let client = hyper::Client::builder().build::<_, hyper::Body>(https);
-
-        let bytes = request.to_bytes().expect("failed to serialise request");
-        info!("Sending {} bytes of data to {:?}", bytes.len(), self.url);
-
-        let request = hyper::Request::builder()
-            .method("POST")
-            .uri(&self.url)
-            .header("Content-Type", "application/dns-message")
-            .header("Accept",       "application/dns-message")
-            .body(hyper::Body::from(bytes))
-            .expect("Failed to build request");  // we control the request, so this should never fail
-
-        let mut response = client.request(request).await?;
-        debug!("Response: {}", response.status());
-        debug!("Headers: {:#?}", response.headers());
-
-        if response.status() != 200 {
+    fn send(&self, request: &Request) -> Result<Response, Error> {
+        let connector = native_tls::TlsConnector::new()?;
+
+        let (domain, path) = self.split_domain().expect("Invalid HTTPS nameserver");
+
+        info!("Opening TLS socket to {:?}", domain);
+        let stream = TcpStream::connect(format!("{}:443", domain))?;
+        let mut stream = connector.connect(domain, stream)?;
+
+        let request_bytes = request.to_bytes().expect("failed to serialise request");
+        let mut bytes = format!("\
+            POST {} HTTP/1.1\r\n\
+            Host: {}\r\n\
+            Content-Type: application/dns-message\r\n\
+            Accept: application/dns-message\r\n\
+            User-Agent: {}\r\n\
+            Content-Length: {}\r\n\r\n",
+            path, domain, USER_AGENT, request_bytes.len()).into_bytes();
+        bytes.extend(request_bytes);
+
+        info!("Sending {:?} bytes of data to {}", bytes.len(), self.url);
+        stream.write_all(&bytes)?;
+        debug!("Sent");
+
+        info!("Waiting to receive...");
+        let mut buf = [0; 4096];
+        let read_len = stream.read(&mut buf)?;
+        info!("Received {} bytes of data", read_len);
+
+        let mut headers = [httparse::EMPTY_HEADER; 16];
+        let mut response = httparse::Response::new(&mut headers);
+        let index: usize = response.parse(&buf).unwrap().unwrap();
+        let body = &buf[index .. read_len];
+
+        if response.code != Some(200) {
             return Err(Error::BadRequest);
         }
 
-        debug!("Reading body...");
-        let mut buf = Vec::new();
-        while let Some(chunk) = response.body_mut().data().await {
-            buf.extend(&chunk?);
+        for header in response.headers {
+            trace!("Header {:?} -> {:?}", header.name, String::from_utf8_lossy(header.value));
         }
 
-        info!("Received {} bytes of data", buf.len());
-        let response = Response::from_bytes(&buf)?;
-
+        info!("HTTP body has {} bytes", body.len());
+        let response = Response::from_bytes(&body)?;
         Ok(response)
     }
 
     #[cfg(not(feature="https"))]
-    async fn send(&self, _request: &Request) -> Result<Response, Error> {
+    fn send(&self, request: &Request) -> Result<Response, Error> {
         unimplemented!("HTTPS feature disabled")
     }
 }
+
+impl HttpsTransport {
+    fn split_domain(&self) -> Option<(&str, &str)> {
+        if let Some(sp) = self.url.strip_prefix("https://") {
+            if let Some(colon_index) = sp.find('/') {
+                return Some((&sp[.. colon_index], &sp[colon_index ..]));
+            }
+        }
+
+        None
+    }
+}
+
+/// The User-Agent header sent with HTTPS requests.
+static USER_AGENT: &str = concat!("dog/", env!("CARGO_PKG_VERSION"));
+

+ 13 - 11
dns-transport/src/lib.rs

@@ -23,7 +23,6 @@
 #![deny(clippy::cast_sign_loss)]
 #![deny(unsafe_code)]
 
-use async_trait::async_trait;
 use derive_more::From;
 
 use dns::{Request, Response};
@@ -48,17 +47,20 @@ pub use self::tls::TlsTransport;
 mod https;
 pub use self::https::HttpsTransport;
 
-pub use tokio::runtime::Runtime;
-
-
 
 /// The trait implemented by all four transport types.
-#[async_trait]
 pub trait Transport {
 
     /// Convert the request to bytes, send it over the network, wait for a
     /// response, deserialise it from bytes, and return it, asynchronously.
-    async fn send(&self, request: &Request) -> Result<Response, Error>;
+    ///
+    /// # Errors
+    ///
+    /// Returns an [`Error`] error if there's an I/O error sending or
+    /// receiving data, or the DNS packet in the response contained invalid
+    /// bytes and failed to parse, or if there was a protocol-level error for
+    /// the TLS and HTTPS transports.
+    fn send(&self, request: &Request) -> Result<Response, Error>;
 }
 
 /// Something that can go wrong making a DNS request.
@@ -67,16 +69,16 @@ pub enum Error {
 
     /// There was a problem with the network sending the request or receiving
     /// a response asynchorously.
-    NetworkError(tokio::io::Error),
-
-    /// There was a problem making an HTTPS request.
-    #[cfg(feature="https")]
-    HttpError(hyper::Error),
+    NetworkError(std::io::Error),
 
     /// There was a problem making a TLS request.
     #[cfg(feature="tls")]
     TlsError(native_tls::Error),
 
+    /// There was a problem _establishing_ a TLS request.
+    #[cfg(feature="tls")]
+    TlsHandshakeError(native_tls::HandshakeError<std::net::TcpStream>),
+
     /// The data in the response did not parse correctly from the DNS wire
     /// protocol format.
     WireError(dns::WireError),

+ 12 - 14
dns-transport/src/tcp.rs

@@ -1,9 +1,8 @@
 use std::convert::TryFrom;
+use std::net::TcpStream;
+use std::io::{Read, Write};
 
-use async_trait::async_trait;
 use log::*;
-use tokio::net::TcpStream;
-use tokio::io::{AsyncReadExt, AsyncWriteExt};
 
 use dns::{Request, Response};
 use super::{Transport, Error};
@@ -52,15 +51,14 @@ impl TcpTransport {
 }
 
 
-#[async_trait]
 impl Transport for TcpTransport {
-    async fn send(&self, request: &Request) -> Result<Response, Error> {
+    fn send(&self, request: &Request) -> Result<Response, Error> {
         let mut stream =
             if self.addr.contains(':') {
-                TcpStream::connect(&*self.addr).await?
+                TcpStream::connect(&*self.addr)?
             }
             else {
-                TcpStream::connect((&*self.addr, 53)).await?
+                TcpStream::connect((&*self.addr, 53))?
             };
         info!("Created stream");
 
@@ -73,19 +71,19 @@ impl Transport for TcpTransport {
 
         info!("Sending {} bytes of data to {} over TCP", bytes.len(), self.addr);
 
-        let written_len = stream.write(&bytes).await?;
+        let written_len = stream.write(&bytes)?;
         debug!("Wrote {} bytes", written_len);
 
         info!("Waiting to receive...");
         let mut buf = [0; 4096];
-        let mut read_len = stream.read(&mut buf[..]).await?;
+        let mut read_len = stream.read(&mut buf[..])?;
 
         if read_len == 0 {
             panic!("Received no bytes!");
         }
         else if read_len == 1 {
             info!("Received one byte of data");
-            let second_read_len = stream.read(&mut buf[1..]).await?;
+            let second_read_len = stream.read(&mut buf[1..])?;
             if second_read_len == 0 {
                 panic!("Received no bytes the second time!");
             }
@@ -105,15 +103,15 @@ impl Transport for TcpTransport {
         debug!("We need to read {} bytes total", total_len);
         let mut combined_buffer = buf[2..read_len].to_vec();
         while combined_buffer.len() < usize::from(total_len) {
-            let mut buf = [0; 4096];
-            let read_len = stream.read(&mut buf[..]).await?;
-            info!("Received further {} bytes of data (of {})", read_len, total_len);
+            let mut extend_buf = [0; 4096];
+            let extend_len = stream.read(&mut extend_buf[..])?;
+            info!("Received further {} bytes of data (of {})", extend_len, total_len);
 
             if read_len == 0 {
                 panic!("Read zero bytes!");
             }
 
-            combined_buffer.extend(&buf[0 .. read_len]);
+            combined_buffer.extend(&extend_buf[0 .. extend_len]);
         }
 
         let response = Response::from_bytes(&combined_buffer)?;

+ 11 - 15
dns-transport/src/tls.rs

@@ -1,11 +1,10 @@
 #![cfg_attr(not(feature="tls"), allow(unused))]
 
 use std::convert::TryFrom;
+use std::net::TcpStream;
+use std::io::{Read, Write};
 
-use async_trait::async_trait;
 use log::*;
-use tokio::io::{AsyncReadExt, AsyncWriteExt};
-use tokio::net::TcpStream;
 
 use dns::{Request, Response};
 use super::{Transport, Error};
@@ -49,25 +48,23 @@ impl TlsTransport {
     }
 }
 
-#[async_trait]
 impl Transport for TlsTransport {
 
     #[cfg(feature="tls")]
-    async fn send(&self, request: &Request) -> Result<Response, Error> {
+    fn send(&self, request: &Request) -> Result<Response, Error> {
         let connector = native_tls::TlsConnector::new()?;
-        let connector = tokio_tls::TlsConnector::from(connector);
 
         info!("Opening TLS socket");
         let stream =
             if self.addr.contains(':') {
-                TcpStream::connect(&*self.addr).await?
+                TcpStream::connect(&*self.addr)?
             }
             else {
-                TcpStream::connect((&*self.addr, 853)).await?
+                TcpStream::connect((&*self.addr, 853))?
             };
 
         info!("Connecting");
-        let mut stream = connector.connect(self.sni_domain(), stream).await?;
+        let mut stream = connector.connect(self.sni_domain(), stream).unwrap();
 
         // As with TCP, we need to prepend the message with its length.
         let mut bytes = request.to_bytes().expect("failed to serialise request");
@@ -76,23 +73,22 @@ impl Transport for TlsTransport {
         bytes.insert(1, len_bytes[1]);
 
         info!("Sending {} bytes of data to {}", bytes.len(), self.addr);
-
-        stream.write_all(&bytes).await?;
+        stream.write_all(&bytes)?;
         debug!("Sent");
 
         info!("Waiting to receive...");
         let mut buf = [0; 4096];
-        let len = stream.read(&mut buf).await?;
+        let read_len = stream.read(&mut buf)?;
 
         // Remember to deal with the length again.
-        info!("Received {} bytes of data", buf.len());
-        let response = Response::from_bytes(&buf[2..len])?;
+        info!("Received {} bytes of data", read_len);
+        let response = Response::from_bytes(&buf[2 .. read_len])?;
 
         Ok(response)
     }
 
     #[cfg(not(feature="tls"))]
-    async fn send(&self, _request: &Request) -> Result<Response, Error> {
+    fn send(&self, request: &Request) -> Result<Response, Error> {
         unimplemented!("TLS feature disabled")
     }
 }

+ 10 - 13
dns-transport/src/udp.rs

@@ -1,8 +1,6 @@
-use std::net::Ipv4Addr;
+use std::net::{Ipv4Addr, UdpSocket};
 
-use async_trait::async_trait;
 use log::*;
-use tokio::net::UdpSocket;
 
 use dns::{Request, Response};
 use super::{Transport, Error};
@@ -47,31 +45,30 @@ impl UdpTransport {
 }
 
 
-#[async_trait]
 impl Transport for UdpTransport {
-    async fn send(&self, request: &Request) -> Result<Response, Error> {
+    fn send(&self, request: &Request) -> Result<Response, Error> {
         info!("Opening UDP socket");
-        let mut socket = UdpSocket::bind((Ipv4Addr::UNSPECIFIED, 0)).await?;
+        let socket = UdpSocket::bind((Ipv4Addr::UNSPECIFIED, 0))?;
 
         if self.addr.contains(':') {
-            socket.connect(&*self.addr).await?;
+            socket.connect(&*self.addr)?;
         }
         else {
-            socket.connect((&*self.addr, 53)).await?;
+            socket.connect((&*self.addr, 53))?;
         }
 
         let bytes = request.to_bytes().expect("failed to serialise request");
         info!("Sending {} bytes of data to {} over UDP", bytes.len(), self.addr);
 
-        let len = socket.send(&bytes).await?;
-        debug!("Sent {} bytes", len);
+        let sent_len = socket.send(&bytes)?;
+        debug!("Sent {} bytes", sent_len);
 
         info!("Waiting to receive...");
         let mut buf = vec![0; 1024];
-        let len = socket.recv(&mut buf).await?;
+        let received_len = socket.recv(&mut buf)?;
 
-        info!("Received {} bytes of data", len);
-        let response = Response::from_bytes(&buf[..len])?;
+        info!("Received {} bytes of data", received_len);
+        let response = Response::from_bytes(&buf[.. received_len])?;
 
         Ok(response)
     }

+ 1 - 2
src/main.rs

@@ -95,7 +95,6 @@ fn main() {
 fn run(Options { requests, format, measure_time }: Options) -> i32 {
     use std::time::Instant;
 
-    let mut runtime = dns_transport::Runtime::new().expect("Failed to create runtime");
     let should_show_opt = requests.edns.should_show();
 
     let mut responses = Vec::new();
@@ -103,7 +102,7 @@ fn run(Options { requests, format, measure_time }: Options) -> i32 {
 
     let mut errored = false;
     for (request, transport) in requests.generate() {
-        let result = runtime.block_on(async { transport.send(&request).await });
+        let result = transport.send(&request);
 
         match result {
             Ok(mut response) => {

+ 11 - 12
src/output.rs

@@ -506,26 +506,25 @@ pub fn print_error_code(rcode: ErrorCode) {
 /// to the user so they can debug what went wrong.
 fn erroneous_phase(error: &TransportError) -> &'static str {
 	match error {
-		TransportError::NetworkError(_)  => "network",
-        #[cfg(feature="https")]
-        TransportError::HttpError(_)     => "http",
+		TransportError::NetworkError(_)       => "network",
         #[cfg(feature="tls")]
-		TransportError::TlsError(_)      => "tls",
-		TransportError::BadRequest       => "http-status",
-		TransportError::WireError(_)     => "protocol",
+		TransportError::TlsError(_)           |
+		TransportError::TlsHandshakeError(_)  => "tls",
+		TransportError::BadRequest            => "http-status",
+		TransportError::WireError(_)          => "protocol",
 	}
 }
 
 /// Formats an error into its human-readable message.
 fn error_message(error: TransportError) -> String {
 	match error {
-		TransportError::NetworkError(e)  => e.to_string(),
-        #[cfg(feature="https")]
-		TransportError::HttpError(e)     => e.to_string(),
+		TransportError::NetworkError(e)       => e.to_string(),
         #[cfg(feature="tls")]
-		TransportError::TlsError(e)      => e.to_string(),
-		TransportError::BadRequest       => "Nameserver returned HTTP 400 Bad Request".into(),
-		TransportError::WireError(e)     => wire_error_message(e),
+		TransportError::TlsError(e)           => e.to_string(),
+        #[cfg(feature="tls")]
+		TransportError::TlsHandshakeError(e)  => e.to_string(),
+		TransportError::BadRequest            => "Nameserver returned HTTP 400 Bad Request".into(),
+		TransportError::WireError(e)          => wire_error_message(e),
 	}
 }
 

+ 8 - 8
xtests/badssl.toml

@@ -4,7 +4,7 @@
 name = "Using a DNS-over-HTTPS server with an expired certificate"
 shell = "dog --https @https://expired.badssl.com/ lookup.dog"
 stdout = { empty = true }
-stderr = { string = "Error [http]: error trying to connect: The certificate was not trusted." }
+stderr = { string = "Error [tls]: The certificate was not trusted." }
 status = 1
 tags = [ 'live', 'badssl', 'https' ]
 
@@ -12,7 +12,7 @@ tags = [ 'live', 'badssl', 'https' ]
 name = "Using a DNS-over-HTTPS server with the wrong host in the certificate"
 shell = "dog --https @https://wrong.host.badssl.com/ lookup.dog"
 stdout = { empty = true }
-stderr = { string = "Error [http]: error trying to connect: The certificate was not trusted." }
+stderr = { string = "Error [tls]: The certificate was not trusted." }
 status = 1
 tags = [ 'live', 'badssl', 'https' ]
 
@@ -20,7 +20,7 @@ tags = [ 'live', 'badssl', 'https' ]
 name = "Using a DNS-over-HTTPS server with a self-signed certificate"
 shell = "dog --https @https://self-signed.badssl.com/ lookup.dog"
 stdout = { empty = true }
-stderr = { string = "Error [http]: error trying to connect: The certificate was not trusted." }
+stderr = { string = "Error [tls]: The certificate was not trusted." }
 status = 1
 tags = [ 'live', 'badssl', 'https' ]
 
@@ -28,7 +28,7 @@ tags = [ 'live', 'badssl', 'https' ]
 name = "Using a DNS-over-HTTPS server with an untrusted root certificate"
 shell = "dog --https @https://untrusted-root.badssl.com/ lookup.dog"
 stdout = { empty = true }
-stderr = { string = "Error [http]: error trying to connect: The certificate was not trusted." }
+stderr = { string = "Error [tls]: The certificate was not trusted." }
 status = 1
 tags = [ 'live', 'badssl', 'https' ]
 
@@ -36,7 +36,7 @@ tags = [ 'live', 'badssl', 'https' ]
 name = "Using a DNS-over-HTTPS server with a revoked certificate"
 shell = "dog --https @https://revoked.badssl.com/ lookup.dog"
 stdout = { empty = true }
-stderr = { string = "Error [http]: error trying to connect: The certificate was not trusted." }
+stderr = { string = "Error [tls]: The certificate was not trusted." }
 status = 1
 tags = [ 'live', 'badssl', 'https' ]
 
@@ -44,7 +44,7 @@ tags = [ 'live', 'badssl', 'https' ]
 name = "Using a DNS-over-HTTPS server with a known bad certificate"
 shell = "dog --https @https://superfish.badssl.com/ lookup.dog"
 stdout = { empty = true }
-stderr = { string = "Error [http]: error trying to connect: The certificate was not trusted." }
+stderr = { string = "Error [tls]: The certificate was not trusted." }
 status = 1
 tags = [ 'live', 'badssl', 'https' ]
 
@@ -55,7 +55,7 @@ tags = [ 'live', 'badssl', 'https' ]
 name = "Using a DNS-over-HTTPS server that accepts the null cipher"
 shell = "dog --https @https://null.badssl.com/ lookup.dog"
 stdout = { empty = true }
-stderr = { string = "Error [http]: error trying to connect: handshake failure" }
+stderr = { string = "Error [tls]: handshake failure" }
 status = 1
 tags = [ 'live', 'badssl', 'https' ]
 
@@ -63,6 +63,6 @@ tags = [ 'live', 'badssl', 'https' ]
 name = "Using a DNS-over-HTTPS server that accepts the rc4-md5 cipher"
 shell = "dog --https @https://rc4-md5.badssl.com/ lookup.dog"
 stdout = { empty = true }
-stderr = { string = "Error [http]: error trying to connect: handshake failure" }
+stderr = { string = "Error [tls]: handshake failure" }
 status = 1
 tags = [ 'live', 'badssl', 'https' ]