Skip to content

Commit

Permalink
Use rustls as TLS implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
kiron1 committed Jul 29, 2023
1 parent d768ac6 commit 149e199
Show file tree
Hide file tree
Showing 27 changed files with 1,211 additions and 984 deletions.
1,434 changes: 732 additions & 702 deletions Cargo.Bazel.lock

Large diffs are not rendered by default.

247 changes: 125 additions & 122 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions detox_net/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ rust_library(
"src/keepalive.rs",
"src/lib.rs",
"src/metered.rs",
"src/path_or_uri.rs",
],
aliases = aliases(),
proc_macro_deps = all_crate_deps(
Expand Down
2 changes: 2 additions & 0 deletions detox_net/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
pub mod host_and_port;
pub mod keepalive;
pub mod metered;
pub mod path_or_uri;

pub use host_and_port::HostAndPort;
pub use keepalive::TcpKeepAlive;
pub use metered::Metered;
pub use path_or_uri::PathOrUri;
70 changes: 70 additions & 0 deletions detox_net/src/path_or_uri.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
use std::{fmt::Display, path::PathBuf, str::FromStr};

use http::Uri;

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PathOrUri {
Path(PathBuf),
Uri(Uri),
}

impl Display for PathOrUri {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Path(p) => p.display().fmt(f),
Self::Uri(u) => u.fmt(f),
}
}
}

impl FromStr for PathOrUri {
type Err = http::uri::InvalidUri;

fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.starts_with("http://") || s.starts_with("https://") {
Ok(Self::from(s.parse::<Uri>()?))
} else {
Ok(Self::from(PathBuf::from(s)))
}
}
}

impl From<PathBuf> for PathOrUri {
fn from(path: PathBuf) -> Self {
Self::Path(path)
}
}

impl From<Uri> for PathOrUri {
fn from(uri: Uri) -> Self {
Self::Uri(uri)
}
}

#[cfg(test)]
mod tests {
use super::PathOrUri;
use http::Uri;
use std::path::PathBuf;

#[test]
fn path_or_uri_test() -> Result<(), Box<dyn std::error::Error>> {
assert_eq!(
PathOrUri::Path(PathBuf::from("README.md")),
"README.md".parse::<PathOrUri>()?
);
assert_eq!(
PathOrUri::Path(PathBuf::from("/etc/motd")),
"/etc/motd".parse::<PathOrUri>()?
);
assert_eq!(
PathOrUri::Uri("http://example.org/index.html".parse::<Uri>()?),
"http://example.org/index.html".parse::<PathOrUri>()?
);
assert_eq!(
PathOrUri::Uri("https://example.org/index.html".parse::<Uri>()?),
"https://example.org/index.html".parse::<PathOrUri>()?
);
Ok(())
}
}
5 changes: 4 additions & 1 deletion dnsdetox/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,15 @@ env_logger = "0.10"
futures-util = { version = "0.3", default-features = false }
http = "0.2"
hyper = { version = "0.14", features = ["http1", "http2", "client", "stream", "tcp"] }
hyper-tls = "0.5"
hyper-rustls = "0.24"
log = "0.4"
paclib = { path = "../paclib" }
proxy_client = { path = "../proxy_client" }
rustls = "0.21"
rustls-native-certs = "0.6"
thiserror = "1.0"
tokio = { version = "1", features = ["rt-multi-thread", "net", "macros", "sync", "signal"] }
tokio-rustls = "0.24"

[lib]
doctest = false
27 changes: 24 additions & 3 deletions dnsdetox/src/doh.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
use hyper::body::Buf;
use hyper::{body::Bytes, Body};
use hyper_tls::HttpsConnector;
use hyper_rustls::HttpsConnector;
use paclib::Proxy;
use proxy_client::HttpConnectConnector;
use std::io::Read;
use std::sync::Arc;
use std::time::Duration;
use tokio::time::timeout;
use tokio_rustls::TlsConnector;

pub struct Client {
remote_addr: http::Uri,
Expand All @@ -15,8 +17,13 @@ pub struct Client {

impl Client {
pub fn new(remote_uri: http::Uri, proxy: Proxy) -> Self {
let http_proxy = HttpConnectConnector::new(proxy);
let https = HttpsConnector::new_with_connector(http_proxy);
let tls_config = default_tls_config();
let http_proxy = HttpConnectConnector::new(proxy, TlsConnector::from(tls_config));
let https = hyper_rustls::HttpsConnectorBuilder::new()
.with_native_roots()
.https_only()
.enable_http1()
.wrap_connector(http_proxy);
let client = hyper::Client::builder().build::<_, hyper::Body>(https);
let timeout = Duration::from_millis(1500);

Expand Down Expand Up @@ -49,3 +56,17 @@ pub async fn read_to_end(res: http::Response<Body>) -> std::io::Result<Vec<u8>>
body.reader().read_to_end(&mut data)?;
Ok(data)
}

fn default_tls_config() -> Arc<rustls::ClientConfig> {
let mut roots = rustls::RootCertStore::empty();
for cert in rustls_native_certs::load_native_certs().expect("load platform certs") {
roots.add(&rustls::Certificate(cert.0)).unwrap();
}

let config = rustls::ClientConfig::builder()
.with_safe_defaults()
.with_root_certificates(roots)
.with_no_client_auth();

Arc::new(config)
}
33 changes: 21 additions & 12 deletions paclib/src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ impl<'a> Engine<'a> {
let pac_script = pac_script.unwrap_or(crate::DEFAULT_PAC_SCRIPT);
self.js
.eval(Source::from_bytes(pac_script))
.map_err(|_| PacScriptError)?;
.map_err(|e| PacScriptError::InternalError(e.to_string()))?;
Ok(())
}

Expand All @@ -71,22 +71,31 @@ impl<'a> Engine<'a> {
let host = uri.host().ok_or(FindProxyError::NoHost)?;

let start = Instant::now();
let result = self.js.eval(Source::from_bytes(&format!(
"FindProxyForURL(\"{}\", \"{}\")",
&uri.to_string(),
host
)));
let find_proxy_fn = self
.js
.global_object()
.get("FindProxyForURL", &mut self.js)
.map_err(|e| FindProxyError::InternalError(e.to_string()))?;
let proxy = match find_proxy_fn {
JsValue::Object(find_proxy_fn) => {
let uri = JsValue::from(uri.to_string());
let host = JsValue::from(host);
find_proxy_fn
.call(&JsValue::Null, &[uri, host], &mut self.js)
.map_err(|e| FindProxyError::InternalError(e.to_string()))
}
_ => Err(FindProxyError::InvalidResult)?,
};
tracing::Span::current().record("duration", debug(&start.elapsed()));
let proxy = proxy?;

match &result {
Ok(JsValue::String(proxies)) => proxies
match &proxy {
JsValue::String(proxies) => proxies
.to_std_string()
.unwrap_or_default()
.parse::<Proxies>()
.map_err(|_| FindProxyError::InvalidResult),

Ok(_value) => Err(FindProxyError::InvalidResult),
Err(_cause) => Err(FindProxyError::InternalError),
_ => Err(FindProxyError::InvalidResult),
}
}
}
Expand All @@ -113,7 +122,7 @@ fn alert(_this: &JsValue, args: &[JsValue], _ctx: &mut Context) -> JsResult<JsVa
}

fn dns_resolve(_this: &JsValue, args: &[JsValue], ctx: &mut Context) -> JsResult<JsValue> {
let global = ctx.global_object().clone();
let global = ctx.global_object();
let dns_cache = global.get("_dnsCache", ctx).expect("_dnsCache");
let dns_cache = dns_cache.as_object();
let mut dns_cache = dns_cache
Expand Down
21 changes: 15 additions & 6 deletions paclib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ pub use crate::proxy::ProxyOrDirect;

const DEFAULT_PAC_SCRIPT: &str = "function FindProxyForURL(url, host) { return \"DIRECT\"; }";

#[derive(thiserror::Error, Debug, PartialEq, Eq, Clone)]
#[derive(thiserror::Error, Debug)]
pub enum CreateEvaluatorError {
#[error("failed to evaluate PAC: {0}")]
EvalPacFile(
Expand All @@ -21,16 +21,25 @@ pub enum CreateEvaluatorError {
),
}

#[derive(thiserror::Error, Debug, PartialEq, Eq, Clone)]
#[derive(thiserror::Error, Debug)]
#[error("Invalid PAC script")]
pub struct PacScriptError;
pub enum PacScriptError {
#[error("internal error: {0}")]
InternalError(String),
#[error("I/O error: {0}")]
Io(
#[from]
#[source]
std::io::Error,
),
}

#[derive(thiserror::Error, Debug, PartialEq, Eq, Clone)]
#[derive(thiserror::Error, Debug)]
pub enum FindProxyError {
#[error("no host in URL")]
NoHost,
#[error("invalid result from PAC script")]
InvalidResult,
#[error("internal error when processing PAC script")]
InternalError,
#[error("internal error when processing PAC script: {0}")]
InternalError(String),
}
4 changes: 3 additions & 1 deletion proxy_client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ edition = "2021"
detox_net = { path = "../detox_net" }
http = "0.2"
hyper = { version = "0.14", features = ["http1", "http2", "client", "server", "tcp"] }
hyper-rustls = "0.24"
paclib = { path = "../paclib" }
rustls = "0.21"
tokio = { version = "1", features = ["rt-multi-thread", "net", "macros", "sync"] }
tokio-native-tls = "0.3"
tokio-rustls = "0.24"
thiserror = "1.0"
tower = { version = "0.4", features = ["util"] }

Expand Down
34 changes: 21 additions & 13 deletions proxy_client/src/http_connect_connector.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use crate::{stream::MaybeTlsStream, HttpConnectStream};
use crate::HttpConnectStream;
use crate::MaybeTlsStream;
use detox_net::{HostAndPort, TcpKeepAlive};
use http::{header::HOST, Request, StatusCode};
use hyper::{body::Bytes, client::conn, Body};
use paclib::Proxy;
use std::{future::Future, io::Cursor, pin::Pin};
use tokio::{io::AsyncReadExt, net::TcpStream};
use tokio_native_tls::{native_tls, TlsConnector};
use tokio_rustls::TlsConnector;
use tower::ServiceExt;

#[derive(thiserror::Error, Debug)]
Expand Down Expand Up @@ -37,11 +38,7 @@ pub enum Error {
#[error("error connecting to {0}")]
ConnectError(HostAndPort, #[source] std::io::Error),
#[error("TLS error")]
TlsError(
#[from]
#[source]
tokio_native_tls::native_tls::Error,
),
TlsError(#[source] std::io::Error),
#[error("internal error: {0}")]
JoinError(
#[from]
Expand All @@ -52,16 +49,18 @@ pub enum Error {

pub type Result<T> = std::result::Result<T, Error>;

#[derive(Debug, Clone)]
#[derive(Clone)]
pub struct HttpConnectConnector {
proxy: Proxy,
tls: TlsConnector,
tcp_keepalive: TcpKeepAlive,
}

impl HttpConnectConnector {
pub fn new(proxy: Proxy) -> Self {
pub fn new(proxy: Proxy, tls: TlsConnector) -> Self {
Self {
proxy,
tls,
tcp_keepalive: Default::default(),
}
}
Expand All @@ -81,10 +80,10 @@ impl HttpConnectConnector {
let stream: MaybeTlsStream<TcpStream> = match self.proxy {
Proxy::Http(_) => stream.into(),
Proxy::Https(_) => {
let tls: TlsConnector = native_tls::TlsConnector::new()
.map(Into::into)
.unwrap_or_else(|e| panic!("HttpProxyConnector::new() failure: {}", e));
let tls = tls.connect(self.proxy.host(), stream).await?;
let domain = rustls::ServerName::try_from(self.proxy.host()).map_err(|_| {
std::io::Error::new(std::io::ErrorKind::InvalidInput, "invalid domain name")
})?;
let tls = self.tls.connect(domain, stream).await?;
tls.into()
}
};
Expand Down Expand Up @@ -161,3 +160,12 @@ impl hyper::service::Service<http::Uri> for HttpConnectConnector {
Box::pin(fut)
}
}

impl std::fmt::Debug for HttpConnectConnector {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.debug_struct("HttpConnectConnector")
.field("proxy", &self.proxy)
.field("tcp_keepalive", &self.tcp_keepalive)
.finish()
}
}
Loading

0 comments on commit 149e199

Please sign in to comment.