use crate::{
mitm::{
HttpHandler, Proxy, WebSocketHandler, rustls,
certificate_authority::{
RcgenAuthority, CertificateAuthority
},
rustls::{
DigitallySignedStruct, SignatureScheme,
client::{
WebPkiServerVerifier,
danger::{
HandshakeSignatureValid, ServerCertVerified
}
},
RootCertStore
}
}, mitm, ProxyMiddleware
};
use rustls_pki_types::{ DnsName, IpAddr, CertificateDer, ServerName, UnixTime, Ipv4Addr, Ipv6Addr };
use tokio::net::TcpListener;
use tokio_tungstenite::Connector;
use rcgen::{Certificate, Error, KeyPair};
use product_os_utilities::ProductOSError;
use product_os_request::ProductOSRequestClient;
use product_os_security::certificates::Certificates;
use product_os_configuration::NetworkProxyCompression;
use product_os_http_body::BodyBytes;
use hyper_util::{
client::legacy::{
connect::{Connect, HttpConnector},
Client,
},
rt::TokioExecutor,
server::conn::auto::Builder,
};
use hyper_rustls::HttpsConnector;
use std::{
net::SocketAddr,
sync::Arc,
future::{pending, Future, Pending},
};
use std::fmt::{Debug, Formatter};
#[cfg(feature = "tor")]
use arti_client::TorClientConfig;
#[derive(Debug)]
pub(crate) enum AddrOrListener {
Addr(SocketAddr),
Listener(TcpListener),
}
pub struct ProxyBuilder<C, CA, H, W, F> {
address_or_listener: Result<AddrOrListener, ProductOSError>,
ca: Result<CA, ProductOSError>,
client: Result<Client<C, BodyBytes>, ProductOSError>,
websocket_connector: Option<Connector>,
http_handler: H,
websocket_handler: W,
compression: NetworkProxyCompression,
certificates: Certificates,
custom_requester: Option<ProductOSRequestClient>,
graceful_shutdown: F,
#[cfg(feature = "tor")]
tor_client: Option<arti_client::TorClient<tor_rtcompat::PreferredRuntime>>,
#[cfg(feature = "vpn")]
vpn_client: Option<product_os_vpn::ProductOSVPN>
}
impl<CA, H, W, F> ProxyBuilder<HttpConnector, CA, H, W, F> {
pub fn use_http_client(self) -> Self {
let http = HttpConnector::new();
Self {
address_or_listener: self.address_or_listener,
ca: self.ca,
client: Ok(Client::builder(TokioExecutor::new())
.http1_title_case_headers(true)
.http1_preserve_header_case(true)
.build(http)),
websocket_connector: self.websocket_connector,
http_handler: self.http_handler,
websocket_handler: self.websocket_handler,
compression: self.compression,
certificates: self.certificates,
custom_requester: self.custom_requester,
graceful_shutdown: self.graceful_shutdown,
#[cfg(feature = "tor")]
tor_client: self.tor_client,
#[cfg(feature = "vpn")]
vpn_client: self.vpn_client
}
}
}
#[derive(Debug)]
struct CustomCertificateVerifier {
certifier: Arc<WebPkiServerVerifier>
}
impl CustomCertificateVerifier {
pub fn new() -> Self {
let mut root_store = RootCertStore::empty();
root_store.roots = webpki_roots::TLS_SERVER_ROOTS.to_vec();
let certifier_builder = WebPkiServerVerifier::builder(Arc::new(root_store));
let certifier = certifier_builder.build().unwrap();
Self {
certifier
}
}
}
impl rustls::client::danger::ServerCertVerifier for CustomCertificateVerifier {
fn verify_server_cert(&self, end_entity: &CertificateDer<'_>, intermediates: &[CertificateDer<'_>], server_name: &ServerName<'_>, ocsp_response: &[u8], now: UnixTime) -> Result<ServerCertVerified, rustls::Error> {
match server_name {
ServerName::DnsName(name) => {
match name.to_lowercase_owned().as_ref() {
"localhost" => Ok(ServerCertVerified::assertion()),
_ => self.certifier.verify_server_cert(end_entity, intermediates, server_name, ocsp_response, now)
}
}
ServerName::IpAddress(ip) => {
let v4_localhost = Ipv4Addr::from(core::net::Ipv4Addr::LOCALHOST);
let v6_localhost = Ipv6Addr::from(core::net::Ipv6Addr::LOCALHOST);
match ip {
IpAddr::V4(ip) => {
if ip.eq(&v4_localhost) {
Ok(ServerCertVerified::assertion())
}
else {
self.certifier.verify_server_cert(end_entity, intermediates, server_name, ocsp_response, now)
}
}
IpAddr::V6(ip) => {
if ip.eq(&v6_localhost) {
Ok(ServerCertVerified::assertion())
}
else {
self.certifier.verify_server_cert(end_entity, intermediates, server_name, ocsp_response, now)
}
}
_ => self.certifier.verify_server_cert(end_entity, intermediates, server_name, ocsp_response, now)
}
}
_ => self.certifier.verify_server_cert(end_entity, intermediates, server_name, ocsp_response, now)
}
}
fn verify_tls12_signature(&self, message: &[u8], cert: &CertificateDer<'_>, dss: &DigitallySignedStruct) -> Result<HandshakeSignatureValid, rustls::Error> {
self.certifier.verify_tls12_signature(message, cert, dss)
}
fn verify_tls13_signature(&self, message: &[u8], cert: &CertificateDer<'_>, dss: &DigitallySignedStruct) -> Result<HandshakeSignatureValid, rustls::Error> {
self.certifier.verify_tls13_signature(message, cert, dss)
}
fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
self.certifier.supported_verify_schemes()
}
}
impl<CA, H, W, F> ProxyBuilder<HttpsConnector<HttpConnector>, CA, H, W, F> {
#[cfg(feature = "rustls_client")]
pub fn use_rustls_client(self, provider: rustls::crypto::CryptoProvider) -> Self {
use hyper_rustls::ConfigBuilderExt;
match rustls::ClientConfig::builder_with_provider(Arc::new(provider))
.with_safe_default_protocol_versions()
{
Ok(config) => {
let rustls_config = config
.dangerous()
.with_custom_certificate_verifier(Arc::new(CustomCertificateVerifier::new()))
.with_no_client_auth();
let https = hyper_rustls::HttpsConnectorBuilder::new()
.with_tls_config(rustls_config.clone())
.https_or_http()
.enable_all_versions();
let https = https.build();
Self {
address_or_listener: self.address_or_listener,
ca: self.ca,
client: Ok(Client::builder(TokioExecutor::new())
.http1_title_case_headers(true)
.http1_preserve_header_case(true)
.build(https)),
websocket_connector: Some(Connector::Rustls(Arc::new(rustls_config))),
http_handler: self.http_handler,
websocket_handler: self.websocket_handler,
compression: self.compression,
certificates: self.certificates,
custom_requester: self.custom_requester,
graceful_shutdown: self.graceful_shutdown,
#[cfg(feature = "tor")]
tor_client: self.tor_client,
#[cfg(feature = "vpn")]
vpn_client: self.vpn_client
}
}
Err(e) => {
Self {
address_or_listener: self.address_or_listener,
ca: self.ca,
client: Err(ProductOSError::GenericError(e.to_string())),
websocket_connector: None,
http_handler: self.http_handler,
websocket_handler: self.websocket_handler,
compression: self.compression,
certificates: self.certificates,
custom_requester: self.custom_requester,
graceful_shutdown: self.graceful_shutdown,
#[cfg(feature = "tor")]
tor_client: self.tor_client,
#[cfg(feature = "vpn")]
vpn_client: self.vpn_client
}
}
}
}
}
impl<C, H, W, F> ProxyBuilder<C, RcgenAuthority, H, W, F> {
pub fn use_certificate_authority(self, certificates: Certificates) -> Self {
let private = certificates.private.to_owned();
let certs = certificates.certificates.to_owned();
let key_pair = match rcgen::KeyPair::try_from(&rustls::pki_types::PrivatePkcs8KeyDer::from(private)) {
Ok(pk) => pk,
Err(e) => panic!("Error getting private key: {:?}", e)
};
let cert_params = match rcgen::CertificateParams::from_ca_cert_der(&rustls::pki_types::CertificateDer::from(certs.get(0).unwrap().to_owned())) {
Ok(cert_params) => cert_params,
Err(e) => panic!("Error getting public key params: {:?}", e)
};
let cert = match cert_params.self_signed(&key_pair) {
Ok(cert) => cert,
Err(e) => panic!("Error getting public key: {:?}", e)
};
let ca = RcgenAuthority::new(key_pair, cert, 1_000);
Self {
address_or_listener: self.address_or_listener,
ca: Ok(ca),
client: self.client,
websocket_connector: self.websocket_connector,
http_handler: self.http_handler,
websocket_handler: self.websocket_handler,
compression: self.compression,
certificates: self.certificates,
custom_requester: self.custom_requester,
graceful_shutdown: self.graceful_shutdown,
#[cfg(feature = "tor")]
tor_client: self.tor_client,
#[cfg(feature = "vpn")]
vpn_client: self.vpn_client
}
}
}
impl<C, CA, F> ProxyBuilder<C, CA, ProxyMiddleware, ProxyMiddleware, F> {
pub fn use_http_handler(self, http_handler: ProxyMiddleware) -> Self {
Self {
address_or_listener: self.address_or_listener,
ca: self.ca,
client: self.client,
websocket_connector: self.websocket_connector,
http_handler,
websocket_handler: self.websocket_handler,
compression: self.compression,
certificates: self.certificates,
custom_requester: self.custom_requester,
graceful_shutdown: self.graceful_shutdown,
#[cfg(feature = "tor")]
tor_client: self.tor_client,
#[cfg(feature = "vpn")]
vpn_client: self.vpn_client
}
}
pub fn use_websocket_handler(self, websocket_handler: ProxyMiddleware) -> Self {
Self {
address_or_listener: self.address_or_listener,
ca: self.ca,
client: self.client,
websocket_connector: self.websocket_connector,
http_handler: self.http_handler,
websocket_handler,
compression: self.compression,
certificates: self.certificates,
custom_requester: self.custom_requester,
graceful_shutdown: self.graceful_shutdown,
#[cfg(feature = "tor")]
tor_client: self.tor_client,
#[cfg(feature = "vpn")]
vpn_client: self.vpn_client
}
}
}
impl<C, CA> ProxyBuilder<C, CA, ProxyMiddleware, ProxyMiddleware, Pending<()>> {
pub fn new() -> Self {
ProxyBuilder::default()
}
}
impl<C, CA, H, W, F> ProxyBuilder<C, CA, H, W, F> {
pub fn use_address(self, address: SocketAddr) -> Self {
Self {
address_or_listener: Ok(AddrOrListener::Addr(address)),
ca: self.ca,
client: self.client,
websocket_connector: self.websocket_connector,
http_handler: self.http_handler,
websocket_handler: self.websocket_handler,
compression: self.compression,
certificates: self.certificates,
custom_requester: self.custom_requester,
graceful_shutdown: self.graceful_shutdown,
#[cfg(feature = "tor")]
tor_client: self.tor_client,
#[cfg(feature = "vpn")]
vpn_client: self.vpn_client
}
}
pub fn use_listener(self, listener: TcpListener) -> Self {
Self {
address_or_listener: Ok(AddrOrListener::Listener(listener)),
ca: self.ca,
client: self.client,
websocket_connector: self.websocket_connector,
http_handler: self.http_handler,
websocket_handler: self.websocket_handler,
compression: self.compression,
certificates: self.certificates,
custom_requester: self.custom_requester,
graceful_shutdown: self.graceful_shutdown,
#[cfg(feature = "tor")]
tor_client: self.tor_client,
#[cfg(feature = "vpn")]
vpn_client: self.vpn_client
}
}
pub fn use_custom_websocket_connector(self, connector: Connector) -> Self {
Self {
address_or_listener: self.address_or_listener,
ca: self.ca,
client: self.client,
websocket_connector: Some(connector),
http_handler: self.http_handler,
websocket_handler: self.websocket_handler,
compression: self.compression,
certificates: self.certificates,
custom_requester: self.custom_requester,
graceful_shutdown: self.graceful_shutdown,
#[cfg(feature = "tor")]
tor_client: self.tor_client,
#[cfg(feature = "vpn")]
vpn_client: self.vpn_client
}
}
pub fn use_custom_requester(self, requester: ProductOSRequestClient) -> Self {
Self {
address_or_listener: self.address_or_listener,
ca: self.ca,
client: self.client,
websocket_connector: self.websocket_connector,
http_handler: self.http_handler,
websocket_handler: self.websocket_handler,
compression: self.compression,
certificates: self.certificates,
custom_requester: Some(requester),
graceful_shutdown: self.graceful_shutdown,
#[cfg(feature = "tor")]
tor_client: self.tor_client,
#[cfg(feature = "vpn")]
vpn_client: self.vpn_client
}
}
pub fn use_compression(self, compression: NetworkProxyCompression) -> Self {
Self {
address_or_listener: self.address_or_listener,
ca: self.ca,
client: self.client,
websocket_connector: self.websocket_connector,
http_handler: self.http_handler,
websocket_handler: self.websocket_handler,
compression,
certificates: self.certificates,
custom_requester: self.custom_requester,
graceful_shutdown: self.graceful_shutdown,
#[cfg(feature = "tor")]
tor_client: self.tor_client,
#[cfg(feature = "vpn")]
vpn_client: self.vpn_client
}
}
pub fn use_custom_client(self, client: Client<C, BodyBytes>) -> Self {
Self {
address_or_listener: self.address_or_listener,
ca: self.ca,
client: Ok(client),
websocket_connector: self.websocket_connector,
http_handler: self.http_handler,
websocket_handler: self.websocket_handler,
compression: self.compression,
certificates: self.certificates,
custom_requester: self.custom_requester,
graceful_shutdown: self.graceful_shutdown,
#[cfg(feature = "tor")]
tor_client: self.tor_client,
#[cfg(feature = "vpn")]
vpn_client: self.vpn_client
}
}
#[cfg(feature = "tor")]
pub async fn use_tor_client(self) -> Result<Self, ProductOSError> {
let config = arti_client::TorClientConfig::default();
match arti_client::TorClient::builder()
.config(config)
.bootstrap_behavior(arti_client::BootstrapBehavior::OnDemand)
.create_bootstrapped().await {
Ok(client) => {
Ok(Self {
address_or_listener: self.address_or_listener,
ca: self.ca,
client: self.client,
websocket_connector: self.websocket_connector,
http_handler: self.http_handler,
websocket_handler: self.websocket_handler,
compression: self.compression,
certificates: self.certificates,
custom_requester: self.custom_requester,
graceful_shutdown: self.graceful_shutdown,
#[cfg(feature = "tor")]
tor_client: Some(client),
#[cfg(feature = "vpn")]
vpn_client: self.vpn_client
})
},
Err(e) => Err(ProductOSError::GenericError(e.to_string()))
}
}
#[cfg(feature = "vpn")]
pub fn use_vpn_client(self, vpn_conf: product_os_vpn::VPNConfig) -> Self {
let mut vpn_client = product_os_vpn::ProductOSVPN::new(vpn_conf.to_owned());
match vpn_client.connect() {
product_os_vpn::Status::CONNECTING => { tracing::info!("Still connecting to VPN..."); }
product_os_vpn::Status::FAILED => { tracing::info!("Failed to connect to VPN"); }
product_os_vpn::Status::CONNECTED => { tracing::info!("Successfully connected to VPN"); }
product_os_vpn::Status::ERROR(e) => { tracing::info!("Error connection to VPN: {:?}", e); }
product_os_vpn::Status::DISCONNECTED => { tracing::info!("Disconnected from VPN early"); }
};
Self {
address_or_listener: self.address_or_listener,
ca: self.ca,
client: self.client,
websocket_connector: self.websocket_connector,
http_handler: self.http_handler,
websocket_handler: self.websocket_handler,
compression: self.compression,
certificates: self.certificates,
custom_requester: self.custom_requester,
graceful_shutdown: self.graceful_shutdown,
#[cfg(feature = "tor")]
tor_client: self.tor_client,
#[cfg(feature = "vpn")]
vpn_client: Some(vpn_client)
}
}
pub fn build(self) -> Result<Proxy<C, CA, H, W, F>, ProductOSError> {
let address_or_listener = match self.address_or_listener {
Ok(address_or_listener) => address_or_listener,
Err(e) => return Err(e)
};
let client = match self.client {
Ok(client) => client,
Err(e) => return Err(e)
};
let certificate_authority = match self.ca {
Ok(ca) => ca,
Err(e) => return Err(e)
};
Ok(Proxy {
address_or_listener,
ca: Arc::new(certificate_authority),
client,
websocket_connector: self.websocket_connector,
http_handler: self.http_handler,
websocket_handler: self.websocket_handler,
certificates: self.certificates,
custom_requester: self.custom_requester,
compression: self.compression,
server: None,
graceful_shutdown: self.graceful_shutdown,
#[cfg(feature = "tor")]
tor_client: self.tor_client,
#[cfg(feature = "vpn")]
vpn_client: self.vpn_client,
})
}
}
impl<C, CA> Default for ProxyBuilder<C, CA, ProxyMiddleware, ProxyMiddleware, Pending<()>> {
fn default() -> Self {
Self {
address_or_listener: Err(ProductOSError::GenericError(String::from("No address or listener defined yet"))),
ca: Err(ProductOSError::GenericError(String::from("No certificate authority defined yet"))),
client: Err(ProductOSError::GenericError(String::from("No client defined yet"))),
websocket_connector: None,
http_handler: ProxyMiddleware::default(),
websocket_handler: ProxyMiddleware::default(),
certificates: Certificates::new(None, None, None, None, None),
custom_requester: None,
compression: NetworkProxyCompression::None,
graceful_shutdown: pending(),
#[cfg(feature = "tor")]
tor_client: None,
#[cfg(feature = "vpn")]
vpn_client: None
}
}
}