summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBenjamin Sago <ogham@users.noreply.github.com>2021-10-07 22:31:22 +0100
committerGitHub <noreply@github.com>2021-10-07 22:31:22 +0100
commit1698ea6b4c7d75903d65586bf6171de604ccd0a6 (patch)
tree1eb08a677e2bd4771a2b74dec4af06042c3fb3a5
parent02f2e5ab879aeffb0b55fc7ff65568cac9a45876 (diff)
parente73593eab55c5622deb7e75a91394fbc84fc96db (diff)
Merge pull request #74 from simao/plugable-tls
Allow different tls implementations with cargo features
-rw-r--r--Cargo.lock162
-rw-r--r--Cargo.toml7
-rw-r--r--dns-transport/Cargo.toml17
-rw-r--r--dns-transport/src/error.rs19
-rw-r--r--dns-transport/src/https.rs12
-rw-r--r--dns-transport/src/lib.rs4
-rw-r--r--dns-transport/src/tls.rs22
-rw-r--r--dns-transport/src/tls_stream.rs69
-rw-r--r--src/main.rs4
-rw-r--r--src/output.rs10
10 files changed, 300 insertions, 26 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 93d4c8c..b5fe1bb 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -68,6 +68,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
+name = "bumpalo"
+version = "3.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c59e7af012c713f529e7a3ee57ce9b31ddd858d4b512923602f74608b009631"
+
+[[package]]
name = "byteorder"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -144,10 +150,14 @@ dependencies = [
name = "dns-transport"
version = "0.2.0-pre"
dependencies = [
+ "cfg-if",
"dns",
"httparse",
"log",
"native-tls",
+ "rustls",
+ "webpki",
+ "webpki-roots",
]
[[package]]
@@ -264,6 +274,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
[[package]]
+name = "js-sys"
+version = "0.3.49"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc15e39392125075f60c95ba416f5381ff6c3a948ff02ab12464715adf56c821"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
name = "json"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -390,6 +409,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"
[[package]]
+name = "openssl-src"
+version = "111.15.0+1.1.1k"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1a5f6ae2ac04393b217ea9f700cd04fa9bf3d93fae2872069f3d15d908af70a"
+dependencies = [
+ "cc",
+]
+
+[[package]]
name = "openssl-sys"
version = "0.9.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -398,6 +426,7 @@ dependencies = [
"autocfg",
"cc",
"libc",
+ "openssl-src",
"pkg-config",
"vcpkg",
]
@@ -518,12 +547,40 @@ dependencies = [
]
[[package]]
+name = "ring"
+version = "0.16.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
+dependencies = [
+ "cc",
+ "libc",
+ "once_cell",
+ "spin",
+ "untrusted",
+ "web-sys",
+ "winapi",
+]
+
+[[package]]
name = "rustc-demangle"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e3bad0ee36814ca07d7968269dd4b7ec89ec2da10c4bb613928d3077083c232"
[[package]]
+name = "rustls"
+version = "0.19.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7"
+dependencies = [
+ "base64",
+ "log",
+ "ring",
+ "sct",
+ "webpki",
+]
+
+[[package]]
name = "ryu"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -540,6 +597,16 @@ dependencies = [
]
[[package]]
+name = "sct"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce"
+dependencies = [
+ "ring",
+ "untrusted",
+]
+
+[[package]]
name = "security-framework"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -605,6 +672,12 @@ dependencies = [
]
[[package]]
+name = "spin"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
+
+[[package]]
name = "syn"
version = "1.0.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -757,6 +830,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
[[package]]
+name = "untrusted"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
+
+[[package]]
name = "vcpkg"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -769,6 +848,89 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
[[package]]
+name = "wasm-bindgen"
+version = "0.2.72"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8fe8f61dba8e5d645a4d8132dc7a0a66861ed5e1045d2c0ed940fab33bac0fbe"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.72"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "046ceba58ff062da072c7cb4ba5b22a37f00a302483f7e2a6cdc18fedbdc1fd3"
+dependencies = [
+ "bumpalo",
+ "lazy_static",
+ "log",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.72"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ef9aa01d36cda046f797c57959ff5f3c615c9cc63997a8d545831ec7976819b"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.72"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96eb45c1b2ee33545a813a92dbb53856418bf7eb54ab34f7f7ff1448a5b3735d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.72"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7148f4696fb4960a346eaa60bbfb42a1ac4ebba21f750f75fc1375b098d5ffa"
+
+[[package]]
+name = "web-sys"
+version = "0.3.49"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59fe19d70f5dacc03f6e46777213facae5ac3801575d56ca6cbd4c93dcd12310"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "webpki"
+version = "0.21.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea"
+dependencies = [
+ "ring",
+ "untrusted",
+]
+
+[[package]]
+name = "webpki-roots"
+version = "0.21.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940"
+dependencies = [
+ "webpki",
+]
+
+[[package]]
name = "widestring"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 2a40fb4..0e6a3d0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -69,7 +69,12 @@ datetime = { version = "0.5.1", default_features = false }
pretty_assertions = "0.7"
[features]
-default = ["with_idna", "with_tls", "with_https"]
+default = ["with_idna", "with_tls", "with_https", "with_nativetls"]
with_idna = ["dns/with_idna"]
+
with_tls = ["dns-transport/with_tls"]
with_https = ["dns-transport/with_https"]
+
+with_nativetls = ["dns-transport/with_nativetls"]
+with_nativetls_vendored = ["with_nativetls", "dns-transport/with_nativetls", "dns-transport/with_nativetls_vendored"]
+with_rustls = ["dns-transport/with_rustls"]
diff --git a/dns-transport/Cargo.toml b/dns-transport/Cargo.toml
index 7b8da00..67552c3 100644
--- a/dns-transport/Cargo.toml
+++ b/dns-transport/Cargo.toml
@@ -23,7 +23,20 @@ native-tls = { version = "0.2", optional = true }
# http response parsing
httparse = { version = "1.3", optional = true }
+rustls = { version = "0.19", optional = true }
+
+webpki = { version = "0.21.0", optional = true }
+
+webpki-roots = { version = "0.21.0", optional = true }
+
+cfg-if = "1"
+
[features]
default = [] # these are enabled in the main dog crate
-with_tls = ["native-tls"]
-with_https = ["native-tls", "httparse"]
+
+with_tls = []
+with_https = ["httparse"]
+
+with_nativetls = ["native-tls"]
+with_nativetls_vendored = ["native-tls", "native-tls/vendored"]
+with_rustls = ["rustls", "webpki-roots", "webpki"]
diff --git a/dns-transport/src/error.rs b/dns-transport/src/error.rs
index 54a20a2..ae140d8 100644
--- a/dns-transport/src/error.rs
+++ b/dns-transport/src/error.rs
@@ -14,13 +14,17 @@ pub enum Error {
TruncatedResponse,
/// There was a problem making a TLS request.
- #[cfg(feature = "with_tls")]
+ #[cfg(feature = "with_nativetls")]
TlsError(native_tls::Error),
/// There was a problem _establishing_ a TLS request.
- #[cfg(feature = "with_tls")]
+ #[cfg(feature = "with_nativetls")]
TlsHandshakeError(native_tls::HandshakeError<std::net::TcpStream>),
+ /// Provided dns name is not valid
+ #[cfg(feature = "with_rustls")]
+ RustlsInvalidDnsNameError(webpki::InvalidDNSNameError),
+
/// There was a problem decoding the response HTTP headers or body.
#[cfg(feature = "with_https")]
HttpError(httparse::Error),
@@ -46,20 +50,27 @@ impl From<std::io::Error> for Error {
}
}
-#[cfg(feature = "with_tls")]
+#[cfg(feature = "with_nativetls")]
impl From<native_tls::Error> for Error {
fn from(inner: native_tls::Error) -> Self {
Self::TlsError(inner)
}
}
-#[cfg(feature = "with_tls")]
+#[cfg(feature = "with_nativetls")]
impl From<native_tls::HandshakeError<std::net::TcpStream>> for Error {
fn from(inner: native_tls::HandshakeError<std::net::TcpStream>) -> Self {
Self::TlsHandshakeError(inner)
}
}
+#[cfg(feature = "with_rustls")]
+impl From<webpki::InvalidDNSNameError> for Error {
+ fn from(inner: webpki::InvalidDNSNameError) -> Self {
+ Self::RustlsInvalidDnsNameError(inner)
+ }
+}
+
#[cfg(feature = "with_https")]
impl From<httparse::Error> for Error {
fn from(inner: httparse::Error) -> Self {
diff --git a/dns-transport/src/https.rs b/dns-transport/src/https.rs
index aff8a53..6a3d324 100644
--- a/dns-transport/src/https.rs
+++ b/dns-transport/src/https.rs
@@ -8,6 +8,8 @@ use log::*;
use dns::{Request, Response, WireError};
use super::{Transport, Error};
+use super::tls_stream;
+
/// The **HTTPS transport**, which sends DNS wire data inside HTTP packets
/// encrypted with TLS, using TCP.
pub struct HttpsTransport {
@@ -31,17 +33,17 @@ fn contains_header(buf: &[u8]) -> bool {
find_subsequence(buf, &header_end).is_some()
}
+use tls_stream::TlsStream;
+
impl Transport for HttpsTransport {
- #[cfg(feature = "with_https")]
+ #[cfg(any(feature = "with_https"))]
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 mut stream = Self::stream(&domain, 443)?;
+
debug!("Connected");
let request_bytes = request.to_bytes().expect("failed to serialise request");
diff --git a/dns-transport/src/lib.rs b/dns-transport/src/lib.rs
index 756f8be..ef0a8be 100644
--- a/dns-transport/src/lib.rs
+++ b/dns-transport/src/lib.rs
@@ -41,8 +41,10 @@ mod https;
pub use self::https::HttpsTransport;
mod error;
-pub use self::error::Error;
+mod tls_stream;
+
+pub use self::error::Error;
/// The trait implemented by all transport types.
pub trait Transport {
diff --git a/dns-transport/src/tls.rs b/dns-transport/src/tls.rs
index 5863737..959dbc9 100644
--- a/dns-transport/src/tls.rs
+++ b/dns-transport/src/tls.rs
@@ -7,6 +7,7 @@ use log::*;
use dns::{Request, Response};
use super::{Transport, Error, TcpTransport};
+use super::tls_stream::TlsStream;
/// The **TLS transport**, which sends DNS wire data using TCP through an
@@ -23,24 +24,29 @@ impl TlsTransport {
}
}
+
+
impl Transport for TlsTransport {
#[cfg(feature = "with_tls")]
fn send(&self, request: &Request) -> Result<Response, Error> {
- let connector = native_tls::TlsConnector::new()?;
-
info!("Opening TLS socket");
- let stream =
+
+ let domain = self.sni_domain();
+ info!("Connecting using domain {:?}", domain);
+ let mut stream =
if self.addr.contains(':') {
- TcpStream::connect(&*self.addr)?
+ let mut parts = self.addr.split(":");
+ let domain = parts.nth(0).unwrap();
+ let port = parts.last().unwrap().parse::<u16>().expect("Invalid port number");
+
+ Self::stream(domain, port)?
}
else {
- TcpStream::connect((&*self.addr, 853))?
+ Self::stream(&*self.addr, 853)?
};
- let domain = self.sni_domain();
- info!("Connecting using domain {:?}", domain);
- let mut stream = connector.connect(domain, stream)?;
+
debug!("Connected");
// The message is prepended with the length when sent over TCP,
diff --git a/dns-transport/src/tls_stream.rs b/dns-transport/src/tls_stream.rs
new file mode 100644
index 0000000..be6443a
--- /dev/null
+++ b/dns-transport/src/tls_stream.rs
@@ -0,0 +1,69 @@
+use std::net::TcpStream;
+use super::Error;
+use super::HttpsTransport;
+use super::TlsTransport;
+
+#[cfg(any(feature = "with_nativetls", feature = "with_nativetls_vendored"))]
+fn stream_nativetls(domain: &str, port: u16) -> Result<native_tls::TlsStream<TcpStream>, Error> {
+ let connector = native_tls::TlsConnector::new()?;
+ let stream = TcpStream::connect((domain, port))?;
+ Ok(connector.connect(domain, stream)?)
+}
+
+#[cfg(feature = "with_rustls")]
+fn stream_rustls(domain: &str, port: u16) -> Result<rustls::StreamOwned<rustls::ClientSession,TcpStream>, Error> {
+ use std::sync::Arc;
+
+ let mut config = rustls::ClientConfig::new();
+
+ config.root_store.add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS);
+
+ let dns_name = webpki::DNSNameRef::try_from_ascii_str(domain)?;
+
+ let conn = rustls::ClientSession::new(&Arc::new(config), dns_name);
+
+ let sock = TcpStream::connect((domain, port))?;
+ let tls = rustls::StreamOwned::new(conn, sock);
+
+ Ok(tls)
+}
+
+pub trait TlsStream<S: std::io::Read + std::io::Write> {
+ fn stream(domain: &str, port: u16) -> Result<S, Error>;
+}
+
+#[cfg(any(feature = "with_tls", feature = "with_https"))]
+cfg_if::cfg_if! {
+ if #[cfg(any(feature = "with_nativetls", feature = "with_nativetls_vendored"))] {
+
+ impl TlsStream<native_tls::TlsStream<TcpStream>> for HttpsTransport {
+ fn stream(domain: &str, port: u16) -> Result<native_tls::TlsStream<TcpStream>, Error> {
+ stream_nativetls(domain, port)
+ }
+ }
+
+ impl TlsStream<native_tls::TlsStream<TcpStream>> for TlsTransport {
+ fn stream(domain: &str, port: u16) -> Result<native_tls::TlsStream<TcpStream>, Error> {
+ stream_nativetls(domain, port)
+ }
+ }
+
+ } else if #[cfg(feature = "with_rustls")] {
+
+ impl TlsStream<rustls::StreamOwned<rustls::ClientSession,TcpStream>> for HttpsTransport {
+ fn stream(domain: &str, port: u16) -> Result<rustls::StreamOwned<rustls::ClientSession,TcpStream>, Error> {
+ stream_rustls(domain, port)
+ }
+ }
+
+ impl TlsStream<rustls::StreamOwned<rustls::ClientSession,TcpStream>> for TlsTransport {
+ fn stream(domain: &str, port: u16) -> Result<rustls::StreamOwned<rustls::ClientSession,TcpStream>, Error> {
+ stream_rustls(domain, port)
+ }
+ }
+
+ } else {
+ unreachable!("tls/https enabled but no tls implementation provided")
+ }
+}
+
diff --git a/src/main.rs b/src/main.rs
index 5456f95..9d39727 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -181,13 +181,13 @@ fn disabled_feature_check(options: &Options) {
use std::process::exit;
use crate::connect::TransportType;
- #[cfg(not(feature = "with_tls"))]
+ #[cfg(all(not(feature = "with_tls"), not(feature = "with_rustls_tls")))]
if options.requests.inputs.transport_types.contains(&TransportType::TLS) {
eprintln!("dog: Cannot use '--tls': This version of dog has been compiled without TLS support");
exit(exits::OPTIONS_ERROR);
}
- #[cfg(not(feature = "with_https"))]
+ #[cfg(all(not(feature = "with_https"), not(feature = "with_rustls_https")))]
if options.requests.inputs.transport_types.contains(&TransportType::HTTPS) {
eprintln!("dog: Cannot use '--https': This version of dog has been compiled without HTTPS support");
exit(exits::OPTIONS_ERROR);
diff --git a/src/output.rs b/src/output.rs
index 9ef1fba..3579eb4 100644
--- a/src/output.rs
+++ b/src/output.rs
@@ -647,9 +647,11 @@ fn erroneous_phase(error: &TransportError) -> &'static str {
TransportError::WireError(_) => "protocol",
TransportError::TruncatedResponse |
TransportError::NetworkError(_) => "network",
- #[cfg(feature = "with_tls")]
+ #[cfg(feature = "with_nativetls")]
TransportError::TlsError(_) |
TransportError::TlsHandshakeError(_) => "tls",
+ #[cfg(feature = "with_rustls")]
+ TransportError::RustlsInvalidDnsNameError(_) => "tls", // TODO: Actually wrong, could be https
#[cfg(feature = "with_https")]
TransportError::HttpError(_) |
TransportError::WrongHttpStatus(_,_) => "http",
@@ -662,10 +664,12 @@ fn error_message(error: TransportError) -> String {
TransportError::WireError(e) => wire_error_message(e),
TransportError::TruncatedResponse => "Truncated response".into(),
TransportError::NetworkError(e) => e.to_string(),
- #[cfg(feature = "with_tls")]
+ #[cfg(feature = "with_nativetls")]
TransportError::TlsError(e) => e.to_string(),
- #[cfg(feature = "with_tls")]
+ #[cfg(feature = "with_nativetls")]
TransportError::TlsHandshakeError(e) => e.to_string(),
+ #[cfg(any(feature = "with_rustls"))]
+ TransportError::RustlsInvalidDnsNameError(e) => e.to_string(),
#[cfg(feature = "with_https")]
TransportError::HttpError(e) => e.to_string(),
#[cfg(feature = "with_https")]