Forráskód Böngészése

Allow different tls implementations with cargo features

Simão Mata 2 éve
szülő
commit
e73593eab5

+ 162 - 0
Cargo.lock

@@ -67,6 +67,12 @@ version = "1.2.1"
 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"
@@ -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]]
@@ -263,6 +273,15 @@ version = "0.4.7"
 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"
@@ -389,6 +408,15 @@ version = "0.1.2"
 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"
@@ -398,6 +426,7 @@ dependencies = [
  "autocfg",
  "cc",
  "libc",
+ "openssl-src",
  "pkg-config",
  "vcpkg",
 ]
@@ -517,12 +546,40 @@ dependencies = [
  "winapi",
 ]
 
+[[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"
@@ -539,6 +596,16 @@ dependencies = [
  "winapi",
 ]
 
+[[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"
@@ -604,6 +671,12 @@ dependencies = [
  "winapi",
 ]
 
+[[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"
@@ -756,6 +829,12 @@ version = "0.2.1"
 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"
@@ -768,6 +847,89 @@ version = "0.10.2+wasi-snapshot-preview1"
 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"

+ 6 - 1
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"]

+ 15 - 2
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"]

+ 15 - 4
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 {

+ 7 - 5
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");

+ 3 - 1
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 {

+ 14 - 8
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,

+ 69 - 0
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")
+    }
+}
+

+ 2 - 2
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);

+ 7 - 3
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")]