From 8fff4f40883fa4adde9b021d58d59847d642eddd Mon Sep 17 00:00:00 2001 From: Jun Kurihara Date: Thu, 16 Jun 2022 20:01:26 -0400 Subject: [PATCH] use hyper-tls and hyper-trust-dns as http(s) clients --- Cargo.toml | 8 ++ src/acceptor.rs | 193 +++++++++++++++++++++++++++++------------------- src/proxy.rs | 10 +++ src/tls.rs | 7 +- 4 files changed, 138 insertions(+), 80 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f361f17..d333ff1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ publish = false [features] default = ["tls"] tls = ["tokio-rustls", "rustls-pemfile"] +forward-hyper-trust-dns = ["hyper-trust-dns"] [dependencies] anyhow = "1.0.57" @@ -40,6 +41,13 @@ tokio-rustls = { version = "0.23.4", features = [ "early-data", ], optional = true } rustls-pemfile = { version = "1.0.0", optional = true } +hyper-trust-dns = { version = "0.4.2", default-features = false, features = [ + "rustls-http2", + "dnssec-ring", + "dns-over-https-rustls", + "rustls-webpki", +], optional = true } +hyper-tls = "0.5.0" [dev-dependencies] diff --git a/src/acceptor.rs b/src/acceptor.rs index 003ba57..3bf0c09 100644 --- a/src/acceptor.rs +++ b/src/acceptor.rs @@ -1,12 +1,12 @@ use crate::{error::*, globals::Globals, log::*}; - use futures::{ task::{Context, Poll}, Future, }; -use hyper::http; -use hyper::server::conn::Http; -use hyper::{Body, HeaderMap, Method, Request, Response, StatusCode}; +use hyper::{ + client::connect::Connect, http, server::conn::Http, Body, Client, HeaderMap, Method, Request, + Response, StatusCode, +}; use std::{net::SocketAddr, pin::Pin, sync::Arc}; use tokio::{ io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}, @@ -46,13 +46,20 @@ where } #[derive(Clone)] -pub struct PacketAcceptor { +pub struct PacketAcceptor +where + T: hyper::client::connect::Connect + Send + Sync + Clone + 'static, +{ pub listening_on: SocketAddr, + pub forwarder: Client, pub globals: Arc, } #[allow(clippy::type_complexity)] -impl hyper::service::Service> for PacketAcceptor { +impl hyper::service::Service> for PacketAcceptor +where + T: Connect + Clone + Send + Sync + 'static, +{ type Response = Response; type Error = http::Error; @@ -63,84 +70,114 @@ impl hyper::service::Service> for PacketAcceptor { } fn call(&mut self, req: Request) -> Self::Future { - debug!("\nserve:{:?}\n{:?}", self.listening_on, req); - // let globals = &self.doh.globals; - // let self_inner = self.clone(); - // if req.uri().path() == globals.path { - // Box::pin(async move { - // let mut subscriber = None; - // if self_inner.doh.globals.enable_auth_target { - // subscriber = match auth::authenticate( - // &self_inner.doh.globals, - // &req, - // ValidationLocation::Target, - // &self_inner.peer_addr, - // ) { - // Ok((sub, aud)) => { - // debug!("Valid token or allowed ip: sub={:?}, aud={:?}", &sub, &aud); - // sub - // } - // Err(e) => { - // error!("{:?}", e); - // return Ok(e); - // } - // }; - // } - // match *req.method() { - // Method::POST => self_inner.doh.serve_post(req, subscriber).await, - // Method::GET => self_inner.doh.serve_get(req, subscriber).await, - // _ => http_error(StatusCode::METHOD_NOT_ALLOWED), - // } - // }) - // } else if req.uri().path() == globals.odoh_configs_path { - // match *req.method() { - // Method::GET => Box::pin(async move { self_inner.doh.serve_odoh_configs().await }), - // _ => Box::pin(async { http_error(StatusCode::METHOD_NOT_ALLOWED) }), - // } - // } else { - // #[cfg(not(feature = "odoh-proxy"))] - // { - // Box::pin(async { http_error(StatusCode::NOT_FOUND) }) - // } - // #[cfg(feature = "odoh-proxy")] - // { - // if req.uri().path() == globals.odoh_proxy_path { - // Box::pin(async move { - // let mut subscriber = None; - // if self_inner.doh.globals.enable_auth_proxy { - // subscriber = match auth::authenticate( - // &self_inner.doh.globals, - // &req, - // ValidationLocation::Proxy, - // &self_inner.peer_addr, - // ) { - // Ok((sub, aud)) => { - // debug!("Valid token or allowed ip: sub={:?}, aud={:?}", &sub, &aud); - // sub - // } - // Err(e) => { - // error!("{:?}", e); - // return Ok(e); - // } - // }; - // } - // // Draft: https://datatracker.ietf.org/doc/html/draft-pauly-dprive-oblivious-doh-11 - // // Golang impl.: https://github.com/cloudflare/odoh-server-go - // // Based on the draft and Golang implementation, only post method is allowed. - // match *req.method() { - // Method::POST => self_inner.doh.serve_odoh_proxy_post(req, subscriber).await, - // _ => http_error(StatusCode::METHOD_NOT_ALLOWED), - // } - // }) - // } else { - Box::pin(async { http_error(StatusCode::NOT_FOUND) }) + debug!("\nserve: {:?}\n{:?}", self.listening_on, req); + let self_inner = self.clone(); + + // 1. check uri (domain queried host name) + // 2. build uri to forwarding target destination + // 3. build request from uri and body + // 4. send request to forwarding target + + if *req.method() == Method::GET { + Box::pin(async move { + // let uri = req.uri(); + let target_uri = hyper::Uri::builder() + .scheme("https") + .authority("www.google.com") + .path_and_query("/") + .build() + .unwrap(); + println!("{:?}", target_uri); + match self_inner.forwarder.get(target_uri).await { + Ok(res) => Ok(res), + Err(e) => { + error!("{:?}", e); + http_error(StatusCode::INTERNAL_SERVER_ERROR) + } + } + }) + } else { + // let globals = &self.doh.globals; + // let self_inner = self.clone(); + // if req.uri().path() == globals.path { + // Box::pin(async move { + // let mut subscriber = None; + // if self_inner.doh.globals.enable_auth_target { + // subscriber = match auth::authenticate( + // &self_inner.doh.globals, + // &req, + // ValidationLocation::Target, + // &self_inner.peer_addr, + // ) { + // Ok((sub, aud)) => { + // debug!("Valid token or allowed ip: sub={:?}, aud={:?}", &sub, &aud); + // sub + // } + // Err(e) => { + // error!("{:?}", e); + // return Ok(e); + // } + // }; + // } + // match *req.method() { + // Method::POST => self_inner.doh.serve_post(req, subscriber).await, + // Method::GET => self_inner.doh.serve_get(req, subscriber).await, + // _ => http_error(StatusCode::METHOD_NOT_ALLOWED), + // } + // }) + // } else if req.uri().path() == globals.odoh_configs_path { + // match *req.method() { + // Method::GET => Box::pin(async move { self_inner.doh.serve_odoh_configs().await }), + // _ => Box::pin(async { http_error(StatusCode::METHOD_NOT_ALLOWED) }), + // } + // } else { + // #[cfg(not(feature = "odoh-proxy"))] + // { + // Box::pin(async { http_error(StatusCode::NOT_FOUND) }) + // } + // #[cfg(feature = "odoh-proxy")] + // { + // if req.uri().path() == globals.odoh_proxy_path { + // Box::pin(async move { + // let mut subscriber = None; + // if self_inner.doh.globals.enable_auth_proxy { + // subscriber = match auth::authenticate( + // &self_inner.doh.globals, + // &req, + // ValidationLocation::Proxy, + // &self_inner.peer_addr, + // ) { + // Ok((sub, aud)) => { + // debug!("Valid token or allowed ip: sub={:?}, aud={:?}", &sub, &aud); + // sub + // } + // Err(e) => { + // error!("{:?}", e); + // return Ok(e); + // } + // }; + // } + // // Draft: https://datatracker.ietf.org/doc/html/draft-pauly-dprive-oblivious-doh-11 + // // Golang impl.: https://github.com/cloudflare/odoh-server-go + // // Based on the draft and Golang implementation, only post method is allowed. + // match *req.method() { + // Method::POST => self_inner.doh.serve_odoh_proxy_post(req, subscriber).await, + // _ => http_error(StatusCode::METHOD_NOT_ALLOWED), + // } + // }) + // } else { + Box::pin(async { http_error(StatusCode::NOT_FOUND) }) + } // } // } // } } } -impl PacketAcceptor { +impl PacketAcceptor +where + T: Connect + Clone + Send + Sync + 'static, +{ pub async fn client_serve(self, stream: I, server: Http, peer_addr: SocketAddr) where I: AsyncRead + AsyncWrite + Send + Unpin + 'static, diff --git a/src/proxy.rs b/src/proxy.rs index ed2f9fb..e0943fd 100644 --- a/src/proxy.rs +++ b/src/proxy.rs @@ -1,5 +1,8 @@ use crate::{acceptor::PacketAcceptor, error::*, globals::Globals, log::*}; use futures::future::select_all; +use hyper::Client; +#[cfg(feature = "forward-hyper-trust-dns")] +use hyper_trust_dns::TrustDnsResolver; use std::sync::Arc; #[derive(Debug, Clone)] @@ -11,9 +14,16 @@ impl Proxy { let addresses = self.globals.listen_addresses.clone(); let futures = select_all(addresses.into_iter().map(|addr| { info!("Listen address: {:?}", addr); + + #[cfg(feature = "forward-hyper-trust-dns")] + let connector = TrustDnsResolver::default().into_rustls_webpki_https_connector(); + #[cfg(not(feature = "forward-hyper-trust-dns"))] + let connector = hyper_tls::HttpsConnector::new(); + let acceptor = PacketAcceptor { listening_on: addr, globals: self.globals.clone(), + forwarder: Client::builder().build::<_, hyper::Body>(connector), }; self.globals.runtime_handle.spawn(acceptor.start()) })); diff --git a/src/tls.rs b/src/tls.rs index 687e949..b503bfb 100644 --- a/src/tls.rs +++ b/src/tls.rs @@ -5,7 +5,7 @@ use std::sync::Arc; use std::time::Duration; use futures::{future::FutureExt, join, select}; -use hyper::server::conn::Http; +use hyper::{client::connect::Connect, server::conn::Http}; use tokio::{ net::TcpListener, sync::mpsc::{self, Receiver}, @@ -109,7 +109,10 @@ where Ok(TlsAcceptor::from(Arc::new(server_config))) } -impl PacketAcceptor { +impl PacketAcceptor +where + T: Connect + Clone + Send + Sync + 'static, +{ async fn start_https_service( self, mut tls_acceptor_receiver: Receiver,