diff options
author | Benjamin Sago <ogham@users.noreply.github.com> | 2021-10-07 22:31:22 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-10-07 22:31:22 +0100 |
commit | 1698ea6b4c7d75903d65586bf6171de604ccd0a6 (patch) | |
tree | 1eb08a677e2bd4771a2b74dec4af06042c3fb3a5 | |
parent | 02f2e5ab879aeffb0b55fc7ff65568cac9a45876 (diff) | |
parent | e73593eab55c5622deb7e75a91394fbc84fc96db (diff) |
Merge pull request #74 from simao/plugable-tls
Allow different tls implementations with cargo features
-rw-r--r-- | Cargo.lock | 162 | ||||
-rw-r--r-- | Cargo.toml | 7 | ||||
-rw-r--r-- | dns-transport/Cargo.toml | 17 | ||||
-rw-r--r-- | dns-transport/src/error.rs | 19 | ||||
-rw-r--r-- | dns-transport/src/https.rs | 12 | ||||
-rw-r--r-- | dns-transport/src/lib.rs | 4 | ||||
-rw-r--r-- | dns-transport/src/tls.rs | 22 | ||||
-rw-r--r-- | dns-transport/src/tls_stream.rs | 69 | ||||
-rw-r--r-- | src/main.rs | 4 | ||||
-rw-r--r-- | src/output.rs | 10 |
10 files changed, 300 insertions, 26 deletions
@@ -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" @@ -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")] |