Loading tests/doh/include/lib.rs.h +23 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,12 @@ static const uintptr_t DNS_HEADER_SIZE = 12; static const uintptr_t MAX_UDP_PAYLOAD_SIZE = 1350; /// Default value for max_idle_timeout transport parameter. static const uint64_t QUICHE_IDLE_TIMEOUT_MS = 10000; /// Default value for initial_max_streams_bidi transport parameter. static const uint64_t MAX_STREAMS_BIDI = 100; /// Frontend object. struct DohFrontend; Loading Loading @@ -72,6 +78,23 @@ bool frontend_set_private_key(DohFrontend *doh, const char *private_key); /// are received. This function works even in the middle of the worker thread. bool frontend_set_delay_queries(DohFrontend *doh, int32_t count); /// Configures the `DohFrontend` to use the given value for max_idle_timeout transport parameter. bool frontend_set_max_idle_timeout(DohFrontend *doh, uint64_t value); /// Configures the `DohFrontend` to use the given value for these transport parameters. /// - initial_max_data /// - initial_max_stream_data_bidi_local /// - initial_max_stream_data_bidi_remote /// - initial_max_stream_data_uni bool frontend_set_max_buffer_size(DohFrontend *doh, uint64_t value); /// Configures the `DohFrontend` to use the given value for initial_max_streams_bidi transport /// parameter. bool frontend_set_max_streams_bidi(DohFrontend *doh, uint64_t value); /// Sets the `DohFrontend` to block or unblock sending any data. bool frontend_block_sending(DohFrontend *doh, bool block); /// Gets the statistics of the `DohFrontend` and writes the result to |out|. void frontend_stats(const DohFrontend *doh, Stats *out); Loading tests/doh/src/client.rs +11 −11 Original line number Diff line number Diff line Loading @@ -17,7 +17,7 @@ //! Client management, including the communication with quiche I/O. use anyhow::{anyhow, bail, ensure, Result}; use log::{debug, info, warn}; use log::{debug, error, info, warn}; use quiche::h3::NameValue; use ring::hmac; use ring::rand::SystemRandom; Loading Loading @@ -170,16 +170,16 @@ impl Client { pub fn flush_egress(&mut self) -> Result<Vec<u8>> { let mut ret = vec![]; let mut buf = [0; MAX_UDP_PAYLOAD_SIZE]; loop { let (write, _) = match self.conn.send(&mut buf) { Ok(v) => v, Err(quiche::Error::Done) => break, // Maybe close the connection? Err(e) => bail!(e), Err(quiche::Error::Done) => bail!(quiche::Error::Done), Err(e) => { error!("flush_egress failed: {}", e); bail!(e) } }; ret.append(&mut buf[..write].to_vec()); } Ok(ret) } Loading tests/doh/src/config.rs +23 −1 Original line number Diff line number Diff line Loading @@ -18,13 +18,35 @@ use std::default::Default; /// Default value for max_idle_timeout transport parameter. pub const QUICHE_IDLE_TIMEOUT_MS: u64 = 10_000; /// Default value for these transport parameters: /// - initial_max_data /// - initial_max_stream_data_bidi_local /// - initial_max_stream_data_bidi_remote /// - initial_max_stream_data_uni const MAX_BUFFER_SIZE: u64 = 1_000_000; /// Default value for initial_max_streams_bidi transport parameter. pub const MAX_STREAMS_BIDI: u64 = 100; #[derive(Debug, Default)] pub struct Config { pub delay_queries: i32, pub block_sending: bool, pub max_idle_timeout: u64, pub max_buffer_size: u64, pub max_streams_bidi: u64, } impl Config { pub fn new() -> Self { Default::default() Self { max_idle_timeout: QUICHE_IDLE_TIMEOUT_MS, max_buffer_size: MAX_BUFFER_SIZE, max_streams_bidi: MAX_STREAMS_BIDI, ..Default::default() } } } tests/doh/src/dns_https_frontend.rs +45 −25 Original line number Diff line number Diff line Loading @@ -17,7 +17,7 @@ //! DoH server frontend. use crate::client::{ClientMap, ConnectionID, DNS_HEADER_SIZE, MAX_UDP_PAYLOAD_SIZE}; use crate::config::Config; use crate::config::{Config, QUICHE_IDLE_TIMEOUT_MS}; use crate::stats::Stats; use anyhow::{bail, ensure, Result}; use lazy_static::lazy_static; Loading @@ -44,8 +44,6 @@ lazy_static! { ); } const QUICHE_IDLE_TIMEOUT_MS: u64 = 10_000; #[derive(Debug)] enum Command { MaybeWrite { connection_id: ConnectionID }, Loading @@ -72,10 +70,12 @@ pub struct DohFrontend { // Custom runtime configuration to control the behavior of the worker thread. // It's shared with the worker thread. // TODO: use channel to update worker_thread configuration. config: Arc<Mutex<Config>>, // Stores some statistic to check DohFrontend status. // It's shared with the worker thread. // TODO: use channel to retrieve the stats from worker_thread. stats: Arc<Mutex<Stats>>, } Loading Loading @@ -145,6 +145,26 @@ impl DohFrontend { Ok(()) } pub fn set_max_idle_timeout(&self, value: u64) -> Result<()> { self.config.lock().unwrap().max_idle_timeout = value; Ok(()) } pub fn set_max_buffer_size(&self, value: u64) -> Result<()> { self.config.lock().unwrap().max_buffer_size = value; Ok(()) } pub fn set_max_streams_bidi(&self, value: u64) -> Result<()> { self.config.lock().unwrap().max_streams_bidi = value; Ok(()) } pub fn block_sending(&self, value: bool) -> Result<()> { self.config.lock().unwrap().block_sending = value; Ok(()) } pub fn stats(&self) -> Stats { self.stats.lock().unwrap().clone() } Loading @@ -167,6 +187,7 @@ impl DohFrontend { let clients = ClientMap::new(create_quiche_config( self.certificate.to_string(), self.private_key.to_string(), self.config.clone(), )?)?; Ok(WorkerParams { Loading Loading @@ -279,26 +300,18 @@ async fn worker_thread(params: WorkerParams) -> Result<()> { } } Some(command) = event_rx.recv() => { Some(command) = event_rx.recv(), if !config.lock().unwrap().block_sending => { match command { Command::MaybeWrite {connection_id} => { if let Some(client) = clients.get_mut(&connection_id) { match client.flush_egress() { Ok(v) => { // The DoH engine in DnsResolver can't handle empty response. if !v.is_empty() { while let Ok(v) = client.flush_egress() { let addr = client.addr(); debug!("Sending {} bytes to client {}", v.len(), addr); if let Err(e) = frontend_socket.send_to(&v, addr).await { error!("Failed to send packet to {:?}: {:?}", client, e); } } } Err(e) => { error!("flush_egress failed: {}", e); } } client.process_pending_answers().unwrap(); client.process_pending_answers()?; } } } Loading @@ -307,7 +320,11 @@ async fn worker_thread(params: WorkerParams) -> Result<()> { } } fn create_quiche_config(certificate: String, private_key: String) -> Result<quiche::Config> { fn create_quiche_config( certificate: String, private_key: String, config: Arc<Mutex<Config>>, ) -> Result<quiche::Config> { let mut quiche_config = quiche::Config::new(quiche::PROTOCOL_VERSION)?; // Use pipe as a file path for Quiche to read the certificate and the private key. Loading @@ -328,13 +345,16 @@ fn create_quiche_config(certificate: String, private_key: String) -> Result<quic handle.join().unwrap(); quiche_config.set_application_protos(quiche::h3::APPLICATION_PROTOCOL)?; quiche_config.set_max_idle_timeout(QUICHE_IDLE_TIMEOUT_MS); quiche_config.set_max_idle_timeout(config.lock().unwrap().max_idle_timeout); quiche_config.set_max_recv_udp_payload_size(MAX_UDP_PAYLOAD_SIZE); quiche_config.set_initial_max_data(10000000); quiche_config.set_initial_max_stream_data_bidi_local(1000000); quiche_config.set_initial_max_stream_data_bidi_remote(1000000); quiche_config.set_initial_max_stream_data_uni(1000000); quiche_config.set_initial_max_streams_bidi(100); let max_buffer_size = config.lock().unwrap().max_buffer_size; quiche_config.set_initial_max_data(max_buffer_size); quiche_config.set_initial_max_stream_data_bidi_local(max_buffer_size); quiche_config.set_initial_max_stream_data_bidi_remote(max_buffer_size); quiche_config.set_initial_max_stream_data_uni(max_buffer_size); quiche_config.set_initial_max_streams_bidi(config.lock().unwrap().max_streams_bidi); quiche_config.set_initial_max_streams_uni(100); quiche_config.set_disable_active_migration(true); Loading tests/doh/src/ffi.rs +29 −0 Original line number Diff line number Diff line Loading @@ -124,6 +124,35 @@ pub extern "C" fn frontend_set_delay_queries(doh: &mut DohFrontend, count: i32) doh.set_delay_queries(count).or_else(logging_and_return_err).is_ok() } /// Configures the `DohFrontend` to use the given value for max_idle_timeout transport parameter. #[no_mangle] pub extern "C" fn frontend_set_max_idle_timeout(doh: &mut DohFrontend, value: u64) -> bool { doh.set_max_idle_timeout(value).or_else(logging_and_return_err).is_ok() } /// Configures the `DohFrontend` to use the given value for these transport parameters. /// - initial_max_data /// - initial_max_stream_data_bidi_local /// - initial_max_stream_data_bidi_remote /// - initial_max_stream_data_uni #[no_mangle] pub extern "C" fn frontend_set_max_buffer_size(doh: &mut DohFrontend, value: u64) -> bool { doh.set_max_buffer_size(value).or_else(logging_and_return_err).is_ok() } /// Configures the `DohFrontend` to use the given value for initial_max_streams_bidi transport /// parameter. #[no_mangle] pub extern "C" fn frontend_set_max_streams_bidi(doh: &mut DohFrontend, value: u64) -> bool { doh.set_max_streams_bidi(value).or_else(logging_and_return_err).is_ok() } /// Sets the `DohFrontend` to block or unblock sending any data. #[no_mangle] pub extern "C" fn frontend_block_sending(doh: &mut DohFrontend, block: bool) -> bool { doh.block_sending(block).or_else(logging_and_return_err).is_ok() } /// Gets the statistics of the `DohFrontend` and writes the result to |out|. #[no_mangle] pub extern "C" fn frontend_stats(doh: &DohFrontend, out: &mut Stats) { Loading Loading
tests/doh/include/lib.rs.h +23 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,12 @@ static const uintptr_t DNS_HEADER_SIZE = 12; static const uintptr_t MAX_UDP_PAYLOAD_SIZE = 1350; /// Default value for max_idle_timeout transport parameter. static const uint64_t QUICHE_IDLE_TIMEOUT_MS = 10000; /// Default value for initial_max_streams_bidi transport parameter. static const uint64_t MAX_STREAMS_BIDI = 100; /// Frontend object. struct DohFrontend; Loading Loading @@ -72,6 +78,23 @@ bool frontend_set_private_key(DohFrontend *doh, const char *private_key); /// are received. This function works even in the middle of the worker thread. bool frontend_set_delay_queries(DohFrontend *doh, int32_t count); /// Configures the `DohFrontend` to use the given value for max_idle_timeout transport parameter. bool frontend_set_max_idle_timeout(DohFrontend *doh, uint64_t value); /// Configures the `DohFrontend` to use the given value for these transport parameters. /// - initial_max_data /// - initial_max_stream_data_bidi_local /// - initial_max_stream_data_bidi_remote /// - initial_max_stream_data_uni bool frontend_set_max_buffer_size(DohFrontend *doh, uint64_t value); /// Configures the `DohFrontend` to use the given value for initial_max_streams_bidi transport /// parameter. bool frontend_set_max_streams_bidi(DohFrontend *doh, uint64_t value); /// Sets the `DohFrontend` to block or unblock sending any data. bool frontend_block_sending(DohFrontend *doh, bool block); /// Gets the statistics of the `DohFrontend` and writes the result to |out|. void frontend_stats(const DohFrontend *doh, Stats *out); Loading
tests/doh/src/client.rs +11 −11 Original line number Diff line number Diff line Loading @@ -17,7 +17,7 @@ //! Client management, including the communication with quiche I/O. use anyhow::{anyhow, bail, ensure, Result}; use log::{debug, info, warn}; use log::{debug, error, info, warn}; use quiche::h3::NameValue; use ring::hmac; use ring::rand::SystemRandom; Loading Loading @@ -170,16 +170,16 @@ impl Client { pub fn flush_egress(&mut self) -> Result<Vec<u8>> { let mut ret = vec![]; let mut buf = [0; MAX_UDP_PAYLOAD_SIZE]; loop { let (write, _) = match self.conn.send(&mut buf) { Ok(v) => v, Err(quiche::Error::Done) => break, // Maybe close the connection? Err(e) => bail!(e), Err(quiche::Error::Done) => bail!(quiche::Error::Done), Err(e) => { error!("flush_egress failed: {}", e); bail!(e) } }; ret.append(&mut buf[..write].to_vec()); } Ok(ret) } Loading
tests/doh/src/config.rs +23 −1 Original line number Diff line number Diff line Loading @@ -18,13 +18,35 @@ use std::default::Default; /// Default value for max_idle_timeout transport parameter. pub const QUICHE_IDLE_TIMEOUT_MS: u64 = 10_000; /// Default value for these transport parameters: /// - initial_max_data /// - initial_max_stream_data_bidi_local /// - initial_max_stream_data_bidi_remote /// - initial_max_stream_data_uni const MAX_BUFFER_SIZE: u64 = 1_000_000; /// Default value for initial_max_streams_bidi transport parameter. pub const MAX_STREAMS_BIDI: u64 = 100; #[derive(Debug, Default)] pub struct Config { pub delay_queries: i32, pub block_sending: bool, pub max_idle_timeout: u64, pub max_buffer_size: u64, pub max_streams_bidi: u64, } impl Config { pub fn new() -> Self { Default::default() Self { max_idle_timeout: QUICHE_IDLE_TIMEOUT_MS, max_buffer_size: MAX_BUFFER_SIZE, max_streams_bidi: MAX_STREAMS_BIDI, ..Default::default() } } }
tests/doh/src/dns_https_frontend.rs +45 −25 Original line number Diff line number Diff line Loading @@ -17,7 +17,7 @@ //! DoH server frontend. use crate::client::{ClientMap, ConnectionID, DNS_HEADER_SIZE, MAX_UDP_PAYLOAD_SIZE}; use crate::config::Config; use crate::config::{Config, QUICHE_IDLE_TIMEOUT_MS}; use crate::stats::Stats; use anyhow::{bail, ensure, Result}; use lazy_static::lazy_static; Loading @@ -44,8 +44,6 @@ lazy_static! { ); } const QUICHE_IDLE_TIMEOUT_MS: u64 = 10_000; #[derive(Debug)] enum Command { MaybeWrite { connection_id: ConnectionID }, Loading @@ -72,10 +70,12 @@ pub struct DohFrontend { // Custom runtime configuration to control the behavior of the worker thread. // It's shared with the worker thread. // TODO: use channel to update worker_thread configuration. config: Arc<Mutex<Config>>, // Stores some statistic to check DohFrontend status. // It's shared with the worker thread. // TODO: use channel to retrieve the stats from worker_thread. stats: Arc<Mutex<Stats>>, } Loading Loading @@ -145,6 +145,26 @@ impl DohFrontend { Ok(()) } pub fn set_max_idle_timeout(&self, value: u64) -> Result<()> { self.config.lock().unwrap().max_idle_timeout = value; Ok(()) } pub fn set_max_buffer_size(&self, value: u64) -> Result<()> { self.config.lock().unwrap().max_buffer_size = value; Ok(()) } pub fn set_max_streams_bidi(&self, value: u64) -> Result<()> { self.config.lock().unwrap().max_streams_bidi = value; Ok(()) } pub fn block_sending(&self, value: bool) -> Result<()> { self.config.lock().unwrap().block_sending = value; Ok(()) } pub fn stats(&self) -> Stats { self.stats.lock().unwrap().clone() } Loading @@ -167,6 +187,7 @@ impl DohFrontend { let clients = ClientMap::new(create_quiche_config( self.certificate.to_string(), self.private_key.to_string(), self.config.clone(), )?)?; Ok(WorkerParams { Loading Loading @@ -279,26 +300,18 @@ async fn worker_thread(params: WorkerParams) -> Result<()> { } } Some(command) = event_rx.recv() => { Some(command) = event_rx.recv(), if !config.lock().unwrap().block_sending => { match command { Command::MaybeWrite {connection_id} => { if let Some(client) = clients.get_mut(&connection_id) { match client.flush_egress() { Ok(v) => { // The DoH engine in DnsResolver can't handle empty response. if !v.is_empty() { while let Ok(v) = client.flush_egress() { let addr = client.addr(); debug!("Sending {} bytes to client {}", v.len(), addr); if let Err(e) = frontend_socket.send_to(&v, addr).await { error!("Failed to send packet to {:?}: {:?}", client, e); } } } Err(e) => { error!("flush_egress failed: {}", e); } } client.process_pending_answers().unwrap(); client.process_pending_answers()?; } } } Loading @@ -307,7 +320,11 @@ async fn worker_thread(params: WorkerParams) -> Result<()> { } } fn create_quiche_config(certificate: String, private_key: String) -> Result<quiche::Config> { fn create_quiche_config( certificate: String, private_key: String, config: Arc<Mutex<Config>>, ) -> Result<quiche::Config> { let mut quiche_config = quiche::Config::new(quiche::PROTOCOL_VERSION)?; // Use pipe as a file path for Quiche to read the certificate and the private key. Loading @@ -328,13 +345,16 @@ fn create_quiche_config(certificate: String, private_key: String) -> Result<quic handle.join().unwrap(); quiche_config.set_application_protos(quiche::h3::APPLICATION_PROTOCOL)?; quiche_config.set_max_idle_timeout(QUICHE_IDLE_TIMEOUT_MS); quiche_config.set_max_idle_timeout(config.lock().unwrap().max_idle_timeout); quiche_config.set_max_recv_udp_payload_size(MAX_UDP_PAYLOAD_SIZE); quiche_config.set_initial_max_data(10000000); quiche_config.set_initial_max_stream_data_bidi_local(1000000); quiche_config.set_initial_max_stream_data_bidi_remote(1000000); quiche_config.set_initial_max_stream_data_uni(1000000); quiche_config.set_initial_max_streams_bidi(100); let max_buffer_size = config.lock().unwrap().max_buffer_size; quiche_config.set_initial_max_data(max_buffer_size); quiche_config.set_initial_max_stream_data_bidi_local(max_buffer_size); quiche_config.set_initial_max_stream_data_bidi_remote(max_buffer_size); quiche_config.set_initial_max_stream_data_uni(max_buffer_size); quiche_config.set_initial_max_streams_bidi(config.lock().unwrap().max_streams_bidi); quiche_config.set_initial_max_streams_uni(100); quiche_config.set_disable_active_migration(true); Loading
tests/doh/src/ffi.rs +29 −0 Original line number Diff line number Diff line Loading @@ -124,6 +124,35 @@ pub extern "C" fn frontend_set_delay_queries(doh: &mut DohFrontend, count: i32) doh.set_delay_queries(count).or_else(logging_and_return_err).is_ok() } /// Configures the `DohFrontend` to use the given value for max_idle_timeout transport parameter. #[no_mangle] pub extern "C" fn frontend_set_max_idle_timeout(doh: &mut DohFrontend, value: u64) -> bool { doh.set_max_idle_timeout(value).or_else(logging_and_return_err).is_ok() } /// Configures the `DohFrontend` to use the given value for these transport parameters. /// - initial_max_data /// - initial_max_stream_data_bidi_local /// - initial_max_stream_data_bidi_remote /// - initial_max_stream_data_uni #[no_mangle] pub extern "C" fn frontend_set_max_buffer_size(doh: &mut DohFrontend, value: u64) -> bool { doh.set_max_buffer_size(value).or_else(logging_and_return_err).is_ok() } /// Configures the `DohFrontend` to use the given value for initial_max_streams_bidi transport /// parameter. #[no_mangle] pub extern "C" fn frontend_set_max_streams_bidi(doh: &mut DohFrontend, value: u64) -> bool { doh.set_max_streams_bidi(value).or_else(logging_and_return_err).is_ok() } /// Sets the `DohFrontend` to block or unblock sending any data. #[no_mangle] pub extern "C" fn frontend_block_sending(doh: &mut DohFrontend, block: bool) -> bool { doh.block_sending(block).or_else(logging_and_return_err).is_ok() } /// Gets the statistics of the `DohFrontend` and writes the result to |out|. #[no_mangle] pub extern "C" fn frontend_stats(doh: &DohFrontend, out: &mut Stats) { Loading