From c3f724118c2183e581149042d402611efb56e195 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sun, 14 Feb 2021 23:48:24 +0100 Subject: [PATCH 001/150] Properly parse the -I and -i options (key file and cert file) --- src/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.rs b/src/config.rs index 1d1026b..9e07dfe 100644 --- a/src/config.rs +++ b/src/config.rs @@ -160,7 +160,7 @@ pub fn parse_opts(globals: &mut Globals) { #[cfg(feature = "tls")] { - globals.tls_cert_path = matches.value_of("tls_cert_key_path").map(PathBuf::from); + globals.tls_cert_path = matches.value_of("tls_cert_path").map(PathBuf::from); globals.tls_cert_key_path = matches .value_of("tls_cert_key_path") .map(PathBuf::from) From 9f092224cd2c551ac9a92186c2d9fb524b2598d5 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Mon, 15 Feb 2021 00:00:57 +0100 Subject: [PATCH 002/150] Parse PKCS8 and RSA keys separately --- src/libdoh/src/tls.rs | 41 ++++++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/src/libdoh/src/tls.rs b/src/libdoh/src/tls.rs index 64a6988..bd8b882 100644 --- a/src/libdoh/src/tls.rs +++ b/src/libdoh/src/tls.rs @@ -3,7 +3,7 @@ use crate::{DoH, LocalExecutor}; use hyper::server::conn::Http; use std::fs::File; -use std::io::{self, BufReader}; +use std::io::{self, BufReader, Cursor, Read}; use std::path::Path; use std::sync::Arc; use tokio::net::TcpListener; @@ -38,22 +38,37 @@ where }; let certs_keys = { let certs_keys_path_str = certs_keys_path.as_ref().display().to_string(); - let mut reader = BufReader::new(File::open(certs_keys_path).map_err(|e| { - io::Error::new( - e.kind(), - format!( - "Unable to load the certificate keys [{}]: {}", - certs_keys_path_str, - e.to_string() - ), - ) - })?); - let keys = pemfile::pkcs8_private_keys(&mut reader).map_err(|_| { + let encoded_keys = { + let mut encoded_keys = vec![]; + File::open(certs_keys_path) + .map_err(|e| { + io::Error::new( + e.kind(), + format!( + "Unable to load the certificate keys [{}]: {}", + certs_keys_path_str, + e.to_string() + ), + ) + })? + .read_to_end(&mut encoded_keys)?; + encoded_keys + }; + let mut reader = Cursor::new(encoded_keys); + let pkcs8_keys = pemfile::pkcs8_private_keys(&mut reader).map_err(|_| { io::Error::new( io::ErrorKind::InvalidInput, - "Unable to parse the certificates private keys", + "Unable to parse the certificates private keys (PKCS8)", ) })?; + let mut rsa_keys = pemfile::rsa_private_keys(&mut reader).map_err(|_| { + io::Error::new( + io::ErrorKind::InvalidInput, + "Unable to parse the certificates private keys (RSA)", + ) + })?; + let mut keys = pkcs8_keys; + keys.append(&mut rsa_keys); if keys.is_empty() { return Err(io::Error::new( io::ErrorKind::InvalidInput, From 2179ceae678d69188e99863693778d2b224538ba Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Mon, 15 Feb 2021 00:06:50 +0100 Subject: [PATCH 003/150] Rewind cursor --- src/libdoh/src/tls.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libdoh/src/tls.rs b/src/libdoh/src/tls.rs index bd8b882..0d804a1 100644 --- a/src/libdoh/src/tls.rs +++ b/src/libdoh/src/tls.rs @@ -61,6 +61,7 @@ where "Unable to parse the certificates private keys (PKCS8)", ) })?; + reader.set_position(0); let mut rsa_keys = pemfile::rsa_private_keys(&mut reader).map_err(|_| { io::Error::new( io::ErrorKind::InvalidInput, From c6c5c71458324205854e32e3fb66338fd338e1c9 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Mon, 15 Feb 2021 00:11:52 +0100 Subject: [PATCH 004/150] Enable support for early data --- Cargo.toml | 4 ++-- src/libdoh/Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6cd5789..f563dd0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "doh-proxy" -version = "0.3.5" +version = "0.3.6" authors = ["Frank Denis "] description = "A DNS-over-HTTPS (DoH) proxy" keywords = ["dns","https","doh","proxy"] @@ -16,7 +16,7 @@ default = ["tls"] tls = ["libdoh/tls"] [dependencies] -libdoh = { path = "src/libdoh", version = "0.3.5", default-features = false } +libdoh = { path = "src/libdoh", version = "0.3.6", default-features = false } clap = "2.33.3" jemallocator = "0.3.2" tokio = { version = "1.2.0", features = ["net", "rt-multi-thread", "parking_lot", "time"] } diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index f80f49f..d0eb9b5 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libdoh" -version = "0.3.5" +version = "0.3.6" authors = ["Frank Denis "] description = "DoH library for the rust-doh app" keywords = ["dns","https","doh","proxy"] @@ -21,7 +21,7 @@ base64 = "0.13.0" futures = "0.3.12" hyper = { version = "0.14.4", default-features = false, features = ["server", "http1", "http2", "stream"] } tokio = { version = "1.2.0", features = ["net", "rt-multi-thread", "parking_lot", "time"] } -tokio-rustls = { version = "0.22.0", optional = true } +tokio-rustls = { version = "0.22.0", features = ["early-data"], optional = true } [profile.release] codegen-units = 1 From 4326f1afa78dccfd95fd7394ffea7c248386335b Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Mon, 15 Feb 2021 00:41:16 +0100 Subject: [PATCH 005/150] Set ALPN config to advertise HTTP/2 --- src/libdoh/src/tls.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libdoh/src/tls.rs b/src/libdoh/src/tls.rs index 0d804a1..c04eff2 100644 --- a/src/libdoh/src/tls.rs +++ b/src/libdoh/src/tls.rs @@ -79,6 +79,7 @@ where keys }; let mut server_config = ServerConfig::new(NoClientAuth::new()); + server_config.set_protocols(&[b"h2".to_vec(), b"http/1.1".to_vec()]); let has_valid_cert_and_key = certs_keys.into_iter().any(|certs_key| { server_config .set_single_cert(certs.clone(), certs_key) From 0a99d0d21243182a5a7ba1ac647b1d08deba6504 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Mon, 15 Feb 2021 00:42:08 +0100 Subject: [PATCH 006/150] Bump --- Cargo.toml | 4 ++-- src/libdoh/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f563dd0..07c54a5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "doh-proxy" -version = "0.3.6" +version = "0.3.7" authors = ["Frank Denis "] description = "A DNS-over-HTTPS (DoH) proxy" keywords = ["dns","https","doh","proxy"] @@ -16,7 +16,7 @@ default = ["tls"] tls = ["libdoh/tls"] [dependencies] -libdoh = { path = "src/libdoh", version = "0.3.6", default-features = false } +libdoh = { path = "src/libdoh", version = "0.3.7", default-features = false } clap = "2.33.3" jemallocator = "0.3.2" tokio = { version = "1.2.0", features = ["net", "rt-multi-thread", "parking_lot", "time"] } diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index d0eb9b5..5df0fe6 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libdoh" -version = "0.3.6" +version = "0.3.7" authors = ["Frank Denis "] description = "DoH library for the rust-doh app" keywords = ["dns","https","doh","proxy"] From a2f342379eef2e18b614ef45597ac2a77a6447f9 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Mon, 15 Feb 2021 21:04:01 +0100 Subject: [PATCH 007/150] Automatically update the certificates without restarting --- Cargo.toml | 2 +- src/libdoh/Cargo.toml | 2 +- src/libdoh/src/lib.rs | 47 +++++++++++++++++++++++++++++++------------ src/libdoh/src/tls.rs | 31 ++++++++++++++++++++-------- 4 files changed, 59 insertions(+), 23 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 07c54a5..1859aca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ tls = ["libdoh/tls"] libdoh = { path = "src/libdoh", version = "0.3.7", default-features = false } clap = "2.33.3" jemallocator = "0.3.2" -tokio = { version = "1.2.0", features = ["net", "rt-multi-thread", "parking_lot", "time"] } +tokio = { version = "1.2.0", features = ["net", "rt-multi-thread", "parking_lot", "time", "sync"] } [package.metadata.deb] extended-description = """\ diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index 5df0fe6..2d33fae 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -20,7 +20,7 @@ byteorder = "1.4.2" base64 = "0.13.0" futures = "0.3.12" hyper = { version = "0.14.4", default-features = false, features = ["server", "http1", "http2", "stream"] } -tokio = { version = "1.2.0", features = ["net", "rt-multi-thread", "parking_lot", "time"] } +tokio = { version = "1.2.0", features = ["net", "rt-multi-thread", "parking_lot", "time", "sync"] } tokio-rustls = { version = "0.22.0", features = ["early-data"], optional = true } [profile.release] diff --git a/src/libdoh/src/lib.rs b/src/libdoh/src/lib.rs index 567b299..3f7dca6 100644 --- a/src/libdoh/src/lib.rs +++ b/src/libdoh/src/lib.rs @@ -12,6 +12,7 @@ pub use crate::globals::*; #[cfg(feature = "tls")] use crate::tls::*; +use futures::join; use futures::prelude::*; use futures::task::{Context, Poll}; use hyper::http; @@ -23,12 +24,14 @@ use std::time::Duration; use tokio::io::{AsyncRead, AsyncWrite}; use tokio::net::{TcpListener, UdpSocket}; use tokio::runtime; +use tokio::sync::mpsc; #[derive(Clone, Debug)] pub struct DoH { pub globals: Arc, } +#[allow(clippy::unnecessary_wraps)] fn http_error(status_code: StatusCode) -> Result, http::Error> { let response = Response::builder() .status(status_code) @@ -58,6 +61,7 @@ where } } +#[allow(clippy::type_complexity)] impl hyper::service::Service> for DoH { type Response = Response; type Error = http::Error; @@ -265,17 +269,17 @@ impl DoH { .map_err(DoHError::Io)?; let path = &self.globals.path; - #[cfg(feature = "tls")] - let tls_acceptor = match (&self.globals.tls_cert_path, &self.globals.tls_cert_key_path) { - (Some(tls_cert_path), Some(tls_cert_key_path)) => { - Some(create_tls_acceptor(tls_cert_path, tls_cert_key_path).unwrap()) - } - _ => None, - }; + let tls_enabled: bool; #[cfg(not(feature = "tls"))] - let tls_acceptor: Option<()> = None; - - if tls_acceptor.is_some() { + { + tls_enabled = false; + } + #[cfg(feature = "tls")] + { + tls_enabled = + self.globals.tls_cert_path.is_some() && self.globals.tls_cert_key_path.is_some(); + } + if tls_enabled { println!("Listening on https://{}{}", listen_address, path); } else { println!("Listening on http://{}{}", listen_address, path); @@ -289,9 +293,26 @@ impl DoH { #[cfg(feature = "tls")] { - if let Some(tls_acceptor) = tls_acceptor { - self.start_with_tls(tls_acceptor, listener, server).await?; - return Ok(()); + if tls_enabled { + let certs_path = self.globals.tls_cert_path.as_ref().unwrap().clone(); + let certs_keys_path = self.globals.tls_cert_key_path.as_ref().unwrap().clone(); + let (tls_acceptor_sender, tls_acceptor_receiver) = mpsc::channel(1); + let http_service = self.start_with_tls(tls_acceptor_receiver, listener, server); + let cert_service = async { + loop { + match create_tls_acceptor(&certs_path, &certs_keys_path) { + Ok(tls_acceptor) => { + if tls_acceptor_sender.send(tls_acceptor).await.is_err() { + break; + } + } + Err(e) => eprintln!("TLS certificates error: {}", e), + } + tokio::time::sleep(Duration::from_secs(5)).await; + } + Ok::<_, DoHError>(()) + }; + return join!(http_service, cert_service).0; } } self.start_without_tls(listener, server).await?; diff --git a/src/libdoh/src/tls.rs b/src/libdoh/src/tls.rs index c04eff2..932edcf 100644 --- a/src/libdoh/src/tls.rs +++ b/src/libdoh/src/tls.rs @@ -1,12 +1,13 @@ use crate::errors::*; use crate::{DoH, LocalExecutor}; +use futures::{future::FutureExt, select}; use hyper::server::conn::Http; use std::fs::File; use std::io::{self, BufReader, Cursor, Read}; use std::path::Path; use std::sync::Arc; -use tokio::net::TcpListener; +use tokio::{net::TcpListener, sync::mpsc::Receiver}; use tokio_rustls::{ rustls::{internal::pemfile, NoClientAuth, ServerConfig}, TlsAcceptor, @@ -97,17 +98,31 @@ where impl DoH { pub async fn start_with_tls( self, - tls_acceptor: TlsAcceptor, + mut tls_acceptor_receiver: Receiver, listener: TcpListener, server: Http, ) -> Result<(), DoHError> { + let mut tls_acceptor: Option = None; let listener_service = async { - while let Ok((raw_stream, _client_addr)) = listener.accept().await { - let stream = match tls_acceptor.accept(raw_stream).await { - Ok(stream) => stream, - Err(_) => continue, - }; - self.clone().client_serve(stream, server.clone()).await; + loop { + select! { + tcp_cnx = listener.accept().fuse() => { + if tls_acceptor.is_none() || tcp_cnx.is_err() { + continue; + } + let (raw_stream, _client_addr) = tcp_cnx.unwrap(); + if let Ok(stream) = tls_acceptor.as_ref().unwrap().accept(raw_stream).await { + self.clone().client_serve(stream, server.clone()).await + } + } + new_tls_acceptor = tls_acceptor_receiver.recv().fuse() => { + if new_tls_acceptor.is_none() { + break; + } + tls_acceptor = new_tls_acceptor; + } + complete => break + } } Ok(()) as Result<(), DoHError> }; From b4d4eaae50935aa6ddd82046c16165ec68a8e9a4 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Mon, 15 Feb 2021 23:35:05 +0100 Subject: [PATCH 008/150] Limit the number of concurrent streams per client --- src/config.rs | 10 ++++++++++ src/constants.rs | 1 + src/libdoh/src/globals.rs | 1 + src/libdoh/src/lib.rs | 1 + src/main.rs | 1 + 5 files changed, 14 insertions(+) diff --git a/src/config.rs b/src/config.rs index 9e07dfe..11c5401 100644 --- a/src/config.rs +++ b/src/config.rs @@ -14,6 +14,7 @@ pub fn parse_opts(globals: &mut Globals) { let max_clients = MAX_CLIENTS.to_string(); let timeout_sec = TIMEOUT_SEC.to_string(); + let max_concurrent_streams = MAX_CONCURRENT_STREAMS.to_string(); let min_ttl = MIN_TTL.to_string(); let max_ttl = MAX_TTL.to_string(); let err_ttl = ERR_TTL.to_string(); @@ -62,6 +63,14 @@ pub fn parse_opts(globals: &mut Globals) { .default_value(&max_clients) .help("Maximum number of simultaneous clients"), ) + .arg( + Arg::with_name("max_concurrent") + .short("C") + .long("max-concurrent") + .takes_value(true) + .default_value(&max_concurrent_streams) + .help("Maximum number of concurrent requests per client"), + ) .arg( Arg::with_name("timeout") .short("t") @@ -152,6 +161,7 @@ pub fn parse_opts(globals: &mut Globals) { } globals.max_clients = matches.value_of("max_clients").unwrap().parse().unwrap(); globals.timeout = Duration::from_secs(matches.value_of("timeout").unwrap().parse().unwrap()); + globals.max_concurrent_streams = matches.value_of("max_concurrent").unwrap().parse().unwrap(); globals.min_ttl = matches.value_of("min_ttl").unwrap().parse().unwrap(); globals.max_ttl = matches.value_of("max_ttl").unwrap().parse().unwrap(); globals.err_ttl = matches.value_of("err_ttl").unwrap().parse().unwrap(); diff --git a/src/constants.rs b/src/constants.rs index 7a133f5..a784f2a 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -1,5 +1,6 @@ pub const LISTEN_ADDRESS: &str = "127.0.0.1:3000"; pub const MAX_CLIENTS: usize = 512; +pub const MAX_CONCURRENT_STREAMS: u32 = 16; pub const PATH: &str = "/dns-query"; pub const SERVER_ADDRESS: &str = "9.9.9.9:53"; pub const TIMEOUT_SEC: u64 = 10; diff --git a/src/libdoh/src/globals.rs b/src/libdoh/src/globals.rs index 5af43a8..3fbba84 100644 --- a/src/libdoh/src/globals.rs +++ b/src/libdoh/src/globals.rs @@ -22,6 +22,7 @@ pub struct Globals { pub max_clients: usize, pub timeout: Duration, pub clients_count: ClientsCount, + pub max_concurrent_streams: u32, pub min_ttl: u32, pub max_ttl: u32, pub err_ttl: u32, diff --git a/src/libdoh/src/lib.rs b/src/libdoh/src/lib.rs index 3f7dca6..3b8908f 100644 --- a/src/libdoh/src/lib.rs +++ b/src/libdoh/src/lib.rs @@ -287,6 +287,7 @@ impl DoH { let mut server = Http::new(); server.http1_keep_alive(self.globals.keepalive); + server.http2_max_concurrent_streams(self.globals.max_concurrent_streams); server.pipeline_flush(true); let executor = LocalExecutor::new(self.globals.runtime_handle.clone()); let server = server.with_executor(executor); diff --git a/src/main.rs b/src/main.rs index 39fc8ef..4214ca9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -36,6 +36,7 @@ fn main() { max_clients: MAX_CLIENTS, timeout: Duration::from_secs(TIMEOUT_SEC), clients_count: Default::default(), + max_concurrent_streams: MAX_CONCURRENT_STREAMS, min_ttl: MIN_TTL, max_ttl: MAX_TTL, err_ttl: ERR_TTL, From 39124df9fc457d98c0a8c0312f882a96e860851e Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Mon, 15 Feb 2021 23:46:24 +0100 Subject: [PATCH 009/150] Reexport tokio --- Cargo.toml | 5 ++--- src/libdoh/Cargo.toml | 2 +- src/libdoh/src/lib.rs | 4 ++++ src/main.rs | 1 + 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1859aca..1ed4f74 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "doh-proxy" -version = "0.3.7" +version = "0.3.8" authors = ["Frank Denis "] description = "A DNS-over-HTTPS (DoH) proxy" keywords = ["dns","https","doh","proxy"] @@ -16,10 +16,9 @@ default = ["tls"] tls = ["libdoh/tls"] [dependencies] -libdoh = { path = "src/libdoh", version = "0.3.7", default-features = false } +libdoh = { path = "src/libdoh", version = "0.3.8", default-features = false } clap = "2.33.3" jemallocator = "0.3.2" -tokio = { version = "1.2.0", features = ["net", "rt-multi-thread", "parking_lot", "time", "sync"] } [package.metadata.deb] extended-description = """\ diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index 2d33fae..cdc845f 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libdoh" -version = "0.3.7" +version = "0.3.8" authors = ["Frank Denis "] description = "DoH library for the rust-doh app" keywords = ["dns","https","doh","proxy"] diff --git a/src/libdoh/src/lib.rs b/src/libdoh/src/lib.rs index 3b8908f..98dbbca 100644 --- a/src/libdoh/src/lib.rs +++ b/src/libdoh/src/lib.rs @@ -26,6 +26,10 @@ use tokio::net::{TcpListener, UdpSocket}; use tokio::runtime; use tokio::sync::mpsc; +pub mod reexports { + pub use tokio; +} + #[derive(Clone, Debug)] pub struct DoH { pub globals: Arc, diff --git a/src/main.rs b/src/main.rs index 4214ca9..f7f69a4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,6 +13,7 @@ use libdoh::*; use crate::config::*; use crate::constants::*; +use libdoh::reexports::tokio; use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::sync::Arc; use std::time::Duration; From 518341df372c07eb502e45dd11b3dae8969b3e70 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Tue, 16 Feb 2021 00:00:02 +0100 Subject: [PATCH 010/150] Reorganize a bit --- src/libdoh/src/lib.rs | 26 ++------------------------ src/libdoh/src/tls.rs | 36 +++++++++++++++++++++++++++++++++--- 2 files changed, 35 insertions(+), 27 deletions(-) diff --git a/src/libdoh/src/lib.rs b/src/libdoh/src/lib.rs index 98dbbca..03eb14b 100644 --- a/src/libdoh/src/lib.rs +++ b/src/libdoh/src/lib.rs @@ -9,10 +9,6 @@ use crate::constants::*; pub use crate::errors::*; pub use crate::globals::*; -#[cfg(feature = "tls")] -use crate::tls::*; - -use futures::join; use futures::prelude::*; use futures::task::{Context, Poll}; use hyper::http; @@ -24,7 +20,6 @@ use std::time::Duration; use tokio::io::{AsyncRead, AsyncWrite}; use tokio::net::{TcpListener, UdpSocket}; use tokio::runtime; -use tokio::sync::mpsc; pub mod reexports { pub use tokio; @@ -299,25 +294,8 @@ impl DoH { #[cfg(feature = "tls")] { if tls_enabled { - let certs_path = self.globals.tls_cert_path.as_ref().unwrap().clone(); - let certs_keys_path = self.globals.tls_cert_key_path.as_ref().unwrap().clone(); - let (tls_acceptor_sender, tls_acceptor_receiver) = mpsc::channel(1); - let http_service = self.start_with_tls(tls_acceptor_receiver, listener, server); - let cert_service = async { - loop { - match create_tls_acceptor(&certs_path, &certs_keys_path) { - Ok(tls_acceptor) => { - if tls_acceptor_sender.send(tls_acceptor).await.is_err() { - break; - } - } - Err(e) => eprintln!("TLS certificates error: {}", e), - } - tokio::time::sleep(Duration::from_secs(5)).await; - } - Ok::<_, DoHError>(()) - }; - return join!(http_service, cert_service).0; + self.start_with_tls(listener, server).await?; + return Ok(()); } } self.start_without_tls(listener, server).await?; diff --git a/src/libdoh/src/tls.rs b/src/libdoh/src/tls.rs index 932edcf..fcc1784 100644 --- a/src/libdoh/src/tls.rs +++ b/src/libdoh/src/tls.rs @@ -1,13 +1,17 @@ use crate::errors::*; use crate::{DoH, LocalExecutor}; -use futures::{future::FutureExt, select}; +use futures::{future::FutureExt, join, select}; use hyper::server::conn::Http; use std::fs::File; use std::io::{self, BufReader, Cursor, Read}; use std::path::Path; use std::sync::Arc; -use tokio::{net::TcpListener, sync::mpsc::Receiver}; +use std::time::Duration; +use tokio::{ + net::TcpListener, + sync::mpsc::{self, Receiver}, +}; use tokio_rustls::{ rustls::{internal::pemfile, NoClientAuth, ServerConfig}, TlsAcceptor, @@ -96,7 +100,7 @@ where } impl DoH { - pub async fn start_with_tls( + async fn start_https_service( self, mut tls_acceptor_receiver: Receiver, listener: TcpListener, @@ -129,4 +133,30 @@ impl DoH { listener_service.await?; Ok(()) } + + pub async fn start_with_tls( + self, + listener: TcpListener, + server: Http, + ) -> Result<(), DoHError> { + let certs_path = self.globals.tls_cert_path.as_ref().unwrap().clone(); + let certs_keys_path = self.globals.tls_cert_key_path.as_ref().unwrap().clone(); + let (tls_acceptor_sender, tls_acceptor_receiver) = mpsc::channel(1); + let https_service = self.start_https_service(tls_acceptor_receiver, listener, server); + let cert_service = async { + loop { + match create_tls_acceptor(&certs_path, &certs_keys_path) { + Ok(tls_acceptor) => { + if tls_acceptor_sender.send(tls_acceptor).await.is_err() { + break; + } + } + Err(e) => eprintln!("TLS certificates error: {}", e), + } + tokio::time::sleep(Duration::from_secs(5)).await; + } + Ok::<_, DoHError>(()) + }; + return join!(https_service, cert_service).0; + } } From ba663ef4d9f1091386fb7855a5f4f511ddad3eee Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Tue, 16 Feb 2021 00:02:42 +0100 Subject: [PATCH 011/150] Reload certs every 10 sec --- src/libdoh/src/constants.rs | 1 + src/libdoh/src/tls.rs | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libdoh/src/constants.rs b/src/libdoh/src/constants.rs index 6032de9..f1dccbb 100644 --- a/src/libdoh/src/constants.rs +++ b/src/libdoh/src/constants.rs @@ -4,3 +4,4 @@ pub const MAX_DNS_RESPONSE_LEN: usize = 4096; pub const MIN_DNS_PACKET_LEN: usize = 17; pub const STALE_IF_ERROR_SECS: u32 = 86400; pub const STALE_WHILE_REVALIDATE_SECS: u32 = 60; +pub const CERTS_WATCH_DELAY_SECS: u32 = 10; diff --git a/src/libdoh/src/tls.rs b/src/libdoh/src/tls.rs index fcc1784..f3d67fe 100644 --- a/src/libdoh/src/tls.rs +++ b/src/libdoh/src/tls.rs @@ -1,3 +1,4 @@ +use crate::constants::CERTS_WATCH_DELAY_SECS; use crate::errors::*; use crate::{DoH, LocalExecutor}; @@ -153,7 +154,7 @@ impl DoH { } Err(e) => eprintln!("TLS certificates error: {}", e), } - tokio::time::sleep(Duration::from_secs(5)).await; + tokio::time::sleep(Duration::from_secs(CERTS_WATCH_DELAY_SECS.into())).await; } Ok::<_, DoHError>(()) }; From dbc5dc702f50be53d7a680e5de6a28964b43bdf2 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Tue, 16 Feb 2021 00:13:44 +0100 Subject: [PATCH 012/150] Documentation updates --- README.md | 15 ++++++++++++++- src/config.rs | 2 +- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1580657..557c107 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,8 @@ cargo install doh-proxy --no-default-features ## Usage ```text +A DNS-over-HTTPS (DoH) proxy + USAGE: doh-proxy [FLAGS] [OPTIONS] @@ -37,6 +39,7 @@ OPTIONS: -l, --listen-address Address to listen to [default: 127.0.0.1:3000] -b, --local-bind-address Address to connect from -c, --max-clients Maximum number of simultaneous clients [default: 512] + -C, --max-concurrent Maximum number of concurrent requests per client [default: 16] -X, --max-ttl Maximum TTL, in seconds [default: 604800] -T, --min-ttl Minimum TTL, in seconds [default: 10] -p, --path URI path [default: /dns-query] @@ -45,7 +48,7 @@ OPTIONS: -I, --tls-cert-key-path Path to the PEM-encoded secret keys (only required for built-in TLS) - -i, --tls-cert-path Path to a PEM-encoded identity (only required for built-in TLS) + -i, --tls-cert-path Path to the PEM-encoded certificates (only required for built-in TLS) ``` ## HTTP/2 termination @@ -67,6 +70,16 @@ Once HTTPS is enabled, HTTP connections will not be accepted. A sample self-signed certificate [`localhost.pem`](https://github.com/jedisct1/rust-doh/raw/master/localhost.pem) can be used for testing. The file also includes the private key. +[`acme.sh`](https://github.com/acmesh-official/acme.sh) can be used to create and update TLS certificates using Let's Encrypt and other ACME-compliant providers. + +The certificates path must be set to the full certificates chain (`fullchain.cer`) and the key path to the secret keys (the `.key` file): + +```sh +doh-proxy -i /path/to/fullchain.cer -I /path/to/domain.key ... +``` + +Once started, `doh-proxy` automatically reloads the certificates as they change; there is no need to restart the server. + ## Accepting both DNSCrypt and DoH connections on port 443 DNSCrypt is an alternative encrypted DNS protocol that is faster and more lightweight than DoH. diff --git a/src/config.rs b/src/config.rs index 11c5401..551ef8a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -123,7 +123,7 @@ pub fn parse_opts(globals: &mut Globals) { .short("i") .long("tls-cert-path") .takes_value(true) - .help("Path to a PEM-encoded certificates (only required for built-in TLS)"), + .help("Path to the PEM-encoded certificates (only required for built-in TLS)"), ) .arg( Arg::with_name("tls_cert_key_path") From 03581234b55f70db6720562f779c8816734cfb0a Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Tue, 16 Feb 2021 00:54:06 +0100 Subject: [PATCH 013/150] CI simplification --- .github/workflows/rust.yml | 36 ------------------------------------ .github/workflows/test.yml | 23 +++++++++++++++++++++++ 2 files changed, 23 insertions(+), 36 deletions(-) delete mode 100644 .github/workflows/rust.yml create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml deleted file mode 100644 index 1e6e3bb..0000000 --- a/.github/workflows/rust.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: Rust - -on: [push] - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@master - - uses: hecrj/setup-rust-action@master - with: - rust-version: nightly - - name: Check Cargo availability - run: cargo --version - - name: Check Rustup default toolchain - run: rustup default | grep nightly - - name: Install cargo-deb - run: cargo install --debug cargo-deb - - name: Build - run: | - env RUSTFLAGS="-C link-arg=-s" cargo build --release --features=tls - mkdir doh-proxy - mv target/release/doh-proxy doh-proxy/ - cp README.md doh-proxy/ - - name: Debian package - run: | - cargo deb - - uses: actions/upload-artifact@master - with: - name: doh-proxy-linux-x86_64 - path: doh-proxy - - uses: actions/upload-artifact@master - with: - name: debian - path: target/debian diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..a29dfee --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,23 @@ +name: Rust + +on: [push] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@master + - uses: hecrj/setup-rust-action@master + with: + rust-version: stable + - name: Check Cargo availability + run: cargo --version + - name: Check Rustup default toolchain + run: rustup default | grep stable + - name: Build without default features + run: | + env RUSTFLAGS="-C link-arg=-s" cargo check --no-default-features + - name: Build with default features + run: | + env RUSTFLAGS="-C link-arg=-s" cargo check From 30abc95e48a07cc0a56f9adfc7b08e651991ca22 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Tue, 16 Feb 2021 01:02:12 +0100 Subject: [PATCH 014/150] CI: add a release task --- .github/workflows/release.yml | 74 +++++++++++++++++++++++++++++++++++ README.md | 10 +++-- 2 files changed, 81 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..efeb24a --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,74 @@ +name: Release + +on: + push: + tags: + - "*" + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Get the version + id: get_version + run: echo ::set-output name=VERSION::${GITHUB_REF#refs/tags/} + + - uses: actions/checkout@master + + - uses: hecrj/setup-rust-action@master + with: + rust-version: stable + + - name: Check Cargo availability + run: cargo --version + + - name: Check Rustup default toolchain + run: rustup default | grep stable + + - name: Install cargo-deb + run: cargo install --debug cargo-deb + + - name: Release build + run: | + env RUSTFLAGS="-C link-arg=-s" cargo build --release + mkdir doh-proxy + mv target/release/doh-proxy doh-proxy/ + cp README.md localhost.pem doh-proxy/ + tar cJpf doh-proxy_${{ steps.get_version.outputs.VERSION }}_linux-x86_64.tar.bz2 doh-proxy + - name: Debian package + run: | + cargo deb + + - name: Create release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: Release ${{ github.ref }} + draft: true + prerelease: false + + - name: Upload Debian package + id: upload-release-asset-debian + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_name: "doh-proxy_${{ steps.get_version.outputs.VERSION }}_amd64.deb" + asset_path: "target/debian/doh-proxy_${{ steps.get_version.outputs.VERSION }}_amd64.deb" + asset_content_type: application/x-debian-package + + - name: Upload tarball + id: upload-release-asset-tarball + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_name: "doh-proxy_${{ steps.get_version.outputs.VERSION }}_linux-x86_64.tar.bz2" + asset_path: "doh-proxy_${{ steps.get_version.outputs.VERSION }}_linux-x86_64.tar.bz2" + asset_content_type: application/x-tar diff --git a/README.md b/README.md index 557c107..7d6a7f6 100644 --- a/README.md +++ b/README.md @@ -4,17 +4,21 @@ A fast and secure DoH (DNS-over-HTTPS) server written in Rust. ## Installation -### From source code +### Option 1: precompiled binaries for Linux + +Precompiled tarballs and Debian packages for Linux/x86_64 [can be downloaded here](https://github.com/jedisct1/doh-server/releases/latest). + +### Option 2: from source code This requires the [`rust`](https://rustup.rs) compiler to be installed. -With built-in support for HTTPS (default): +* With built-in support for HTTPS (default): ```sh cargo install doh-proxy ``` -Without built-in support for HTTPS: +* Without built-in support for HTTPS: ```sh cargo install doh-proxy --no-default-features From de0e8a39c35fefc2322ea01f7ccdd58c47acf4b8 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Tue, 16 Feb 2021 01:30:52 +0100 Subject: [PATCH 015/150] Nits --- README.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 7d6a7f6..78707fc 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # doh-proxy -A fast and secure DoH (DNS-over-HTTPS) server written in Rust. +A fast and secure DoH (DNS-over-HTTPS) server. + +`doh-proxy` is written in Rust, and has been battle-tested in production since February 2018. It doesn't do DNS resolution on its own, but can sit in front of any DNS resolver in order to augment it with DoH support. ## Installation @@ -71,7 +73,7 @@ In order to enable built-in HTTPS support, add the `--tls-cert-path` option to s Once HTTPS is enabled, HTTP connections will not be accepted. -A sample self-signed certificate [`localhost.pem`](https://github.com/jedisct1/rust-doh/raw/master/localhost.pem) can be used for testing. +A sample self-signed certificate [`localhost.pem`](https://github.com/jedisct1/doh-server/raw/master/localhost.pem) can be used for testing. The file also includes the private key. [`acme.sh`](https://github.com/acmesh-official/acme.sh) can be used to create and update TLS certificates using Let's Encrypt and other ACME-compliant providers. @@ -90,11 +92,11 @@ DNSCrypt is an alternative encrypted DNS protocol that is faster and more lightw Both DNSCrypt and DoH connections can be accepted on the same TCP port using [Encrypted DNS Server](https://github.com/jedisct1/encrypted-dns-server). -Encrypted DNS Server forwards DoH queries to Nginx or `rust-doh` when a TLS connection is detected, or directly responds to DNSCrypt queries. +Encrypted DNS Server forwards DoH queries to Nginx or `doh-proxy` when a TLS connection is detected, or directly responds to DNSCrypt queries. It also provides DNS caching, server-side filtering, metrics, and TCP connection reuse in order to mitigate exhaustion attacks. -Unless the front-end is a CDN, an ideal setup is to use `rust-doh` behind `Encrypted DNS Server`. +Unless the front-end is a CDN, an ideal setup is to use `doh-proxy` behind `Encrypted DNS Server`. ## Operational recommendations @@ -167,8 +169,7 @@ This [Go code snippet](https://gist.github.com/d6cb41742a1ceb54d48cc286f3d5c5fa) ## Clients -`doh-proxy` can be used with [dnscrypt-proxy](https://github.com/DNSCrypt/dnscrypt-proxy) -as a client. +`doh-proxy` can be used with [dnscrypt-proxy](https://github.com/DNSCrypt/dnscrypt-proxy) as a client. `doh-proxy` is used in production for the `doh.crypto.sx` public DNS resolver and many others. From 0404b8f8a7e2470831382bf71c43d48ba39315c4 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Tue, 16 Feb 2021 01:42:09 +0100 Subject: [PATCH 016/150] space --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 1ed4f74..851ee39 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ keywords = ["dns","https","doh","proxy"] license = "MIT" homepage = "https://github.com/jedisct1/rust-doh" repository = "https://github.com/jedisct1/rust-doh" -categories = ["asynchronous", "network-programming","command-line-utilities"] +categories = ["asynchronous", "network-programming", "command-line-utilities"] edition = "2018" readme = "README.md" From 623328d37f7bf1e6eff10e1823a7e320519bd2cd Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sat, 6 Mar 2021 21:04:54 +0100 Subject: [PATCH 017/150] Mention that certs must be RSA --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 78707fc..e664617 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ If `doh-proxy` and the HTTP/2 front-end run on the same host, using the HTTP pro If both are on distinct networks, such as when using a CDN, `doh-proxy` can handle HTTPS requests, provided that it was compiled with the `tls` feature. -The certificates and private keys must be encoded in PEM format. They can be stored in the same file. +The certificates and private keys must use RSA and have to be encoded in PEM format. They can be stored in the same file. In order to enable built-in HTTPS support, add the `--tls-cert-path` option to specify the location of the certificates file, as well as the private keys file using `--tls-cert-key-path`. From ecacd6eca97e94c3b6a733d4595b3881f4fd52b9 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sat, 6 Mar 2021 21:14:04 +0100 Subject: [PATCH 018/150] Add the command to convert SEC1 to PKCS8 --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e664617..eb47469 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,13 @@ If `doh-proxy` and the HTTP/2 front-end run on the same host, using the HTTP pro If both are on distinct networks, such as when using a CDN, `doh-proxy` can handle HTTPS requests, provided that it was compiled with the `tls` feature. -The certificates and private keys must use RSA and have to be encoded in PEM format. They can be stored in the same file. +The certificates and private keys must be encoded in PEM/PKCS#8 format. They can be stored in the same file. + +If your ECDSA private keys start with `-----BEGIN EC PRIVATE KEY-----` and not `-----BEGIN PRIVATE KEY-----`, convert them to PKCS#8 with: + +```sh +openssl pkcs8 -topk8 -nocrypt -in sec1.pem -out pkcs8.pem +``` In order to enable built-in HTTPS support, add the `--tls-cert-path` option to specify the location of the certificates file, as well as the private keys file using `--tls-cert-key-path`. From eb8ea3dc8495491efd484660c3f1273ead7e7142 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sat, 6 Mar 2021 21:32:01 +0100 Subject: [PATCH 019/150] Clarify --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index eb47469..069dca7 100644 --- a/README.md +++ b/README.md @@ -69,10 +69,10 @@ If both are on distinct networks, such as when using a CDN, `doh-proxy` can hand The certificates and private keys must be encoded in PEM/PKCS#8 format. They can be stored in the same file. -If your ECDSA private keys start with `-----BEGIN EC PRIVATE KEY-----` and not `-----BEGIN PRIVATE KEY-----`, convert them to PKCS#8 with: +If your ECDSA private keys start with `-----BEGIN EC PRIVATE KEY-----` and not `-----BEGIN PRIVATE KEY-----`, convert them to PKCS#8 with (in this example, `example.key` is the original file): ```sh -openssl pkcs8 -topk8 -nocrypt -in sec1.pem -out pkcs8.pem +openssl pkcs8 -topk8 -nocrypt -in example.key -out example.pkcs8.pem ``` In order to enable built-in HTTPS support, add the `--tls-cert-path` option to specify the location of the certificates file, as well as the private keys file using `--tls-cert-key-path`. From 6f40f792e3fbd16676171facadceb751f3bbbc8d Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sat, 6 Mar 2021 21:32:59 +0100 Subject: [PATCH 020/150] Clarify more --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 069dca7..9b488bc 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ Once HTTPS is enabled, HTTP connections will not be accepted. A sample self-signed certificate [`localhost.pem`](https://github.com/jedisct1/doh-server/raw/master/localhost.pem) can be used for testing. The file also includes the private key. -[`acme.sh`](https://github.com/acmesh-official/acme.sh) can be used to create and update TLS certificates using Let's Encrypt and other ACME-compliant providers. +[`acme.sh`](https://github.com/acmesh-official/acme.sh) can be used to create and update TLS certificates using Let's Encrypt and other ACME-compliant providers. If you are using it to create ECDSA keys, see above for converting the secret key into PKCS#8.. The certificates path must be set to the full certificates chain (`fullchain.cer`) and the key path to the secret keys (the `.key` file): From 4d685d8948d0e4910a216bc9e63006c61cb2cd66 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sat, 6 Mar 2021 21:41:39 +0100 Subject: [PATCH 021/150] LE ECDSA certs don't play well with (at least Go) --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9b488bc..9913f30 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ If both are on distinct networks, such as when using a CDN, `doh-proxy` can hand The certificates and private keys must be encoded in PEM/PKCS#8 format. They can be stored in the same file. -If your ECDSA private keys start with `-----BEGIN EC PRIVATE KEY-----` and not `-----BEGIN PRIVATE KEY-----`, convert them to PKCS#8 with (in this example, `example.key` is the original file): +If you are using ECDSA certificates and ECDSA private keys start with `-----BEGIN EC PRIVATE KEY-----` and not `-----BEGIN PRIVATE KEY-----`, convert them to PKCS#8 with (in this example, `example.key` is the original file): ```sh openssl pkcs8 -topk8 -nocrypt -in example.key -out example.pkcs8.pem @@ -82,7 +82,9 @@ Once HTTPS is enabled, HTTP connections will not be accepted. A sample self-signed certificate [`localhost.pem`](https://github.com/jedisct1/doh-server/raw/master/localhost.pem) can be used for testing. The file also includes the private key. -[`acme.sh`](https://github.com/acmesh-official/acme.sh) can be used to create and update TLS certificates using Let's Encrypt and other ACME-compliant providers. If you are using it to create ECDSA keys, see above for converting the secret key into PKCS#8.. +[`acme.sh`](https://github.com/acmesh-official/acme.sh) can be used to create and update TLS certificates using Let's Encrypt and other ACME-compliant providers. If you are using it to create ECDSA keys, see above for converting the secret key into PKCS#8. + +*WARNING*: Let's Encrypt ECDSA certificates are currently rejected by most clients. Stick to RSA. The certificates path must be set to the full certificates chain (`fullchain.cer`) and the key path to the secret keys (the `.key` file): From 4de5310430650ef88a47d7024aa95affa81747a5 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sat, 6 Mar 2021 21:45:33 +0100 Subject: [PATCH 022/150] fullchain.cer works --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 9913f30..a53b8d3 100644 --- a/README.md +++ b/README.md @@ -84,8 +84,6 @@ The file also includes the private key. [`acme.sh`](https://github.com/acmesh-official/acme.sh) can be used to create and update TLS certificates using Let's Encrypt and other ACME-compliant providers. If you are using it to create ECDSA keys, see above for converting the secret key into PKCS#8. -*WARNING*: Let's Encrypt ECDSA certificates are currently rejected by most clients. Stick to RSA. - The certificates path must be set to the full certificates chain (`fullchain.cer`) and the key path to the secret keys (the `.key` file): ```sh From 63d672895fbd9634a96229a1feadb297de51c019 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sat, 6 Mar 2021 21:46:39 +0100 Subject: [PATCH 023/150] Clarify --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index a53b8d3..2b4c030 100644 --- a/README.md +++ b/README.md @@ -92,6 +92,8 @@ doh-proxy -i /path/to/fullchain.cer -I /path/to/domain.key ... Once started, `doh-proxy` automatically reloads the certificates as they change; there is no need to restart the server. +If clients are getting the `x509: certificate signed by unknown authority` error, double check that the certificate file is the full chain, not the other `.cer` file. + ## Accepting both DNSCrypt and DoH connections on port 443 DNSCrypt is an alternative encrypted DNS protocol that is faster and more lightweight than DoH. From 00cc43e2bbab92be23d51f7c15738dd1edb0dd31 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sat, 6 Mar 2021 22:02:28 +0100 Subject: [PATCH 024/150] Clarify --- src/libdoh/src/tls.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libdoh/src/tls.rs b/src/libdoh/src/tls.rs index f3d67fe..289b9dd 100644 --- a/src/libdoh/src/tls.rs +++ b/src/libdoh/src/tls.rs @@ -79,7 +79,7 @@ where if keys.is_empty() { return Err(io::Error::new( io::ErrorKind::InvalidInput, - "No private keys found", + "No private keys found - Make sure that they are in PKCS#8/PEM format", )); } keys From 0403de66f16544281d648482d7a9a3ac9cbbd873 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sat, 6 Mar 2021 22:21:19 +0100 Subject: [PATCH 025/150] Compute a preliminary stamp --- Cargo.toml | 1 + src/config.rs | 32 +++++++++++++++++++++++++++++++- src/libdoh/Cargo.toml | 2 +- 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 851ee39..003944d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ tls = ["libdoh/tls"] [dependencies] libdoh = { path = "src/libdoh", version = "0.3.8", default-features = false } clap = "2.33.3" +dnsstamps = "0.1.5" jemallocator = "0.3.2" [package.metadata.deb] diff --git a/src/config.rs b/src/config.rs index 551ef8a..34b7f9a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -21,6 +21,20 @@ pub fn parse_opts(globals: &mut Globals) { let _ = include_str!("../Cargo.toml"); let options = app_from_crate!() + .arg( + Arg::with_name("hostname") + .short("H") + .long("hostname") + .takes_value(true) + .help("Host name (not IP address) DoH clients will use to connect"), + ) + .arg( + Arg::with_name("public_address") + .short("g") + .long("public-address") + .takes_value(true) + .help("External IP address DoH clients will connect to"), + ) .arg( Arg::with_name("listen_address") .short("l") @@ -123,7 +137,9 @@ pub fn parse_opts(globals: &mut Globals) { .short("i") .long("tls-cert-path") .takes_value(true) - .help("Path to the PEM-encoded certificates (only required for built-in TLS)"), + .help( + "Path to the PEM/PKCS#8-encoded certificates (only required for built-in TLS)", + ), ) .arg( Arg::with_name("tls_cert_key_path") @@ -176,4 +192,18 @@ pub fn parse_opts(globals: &mut Globals) { .map(PathBuf::from) .or_else(|| globals.tls_cert_path.clone()); } + + if let Some(hostname) = matches.value_of("hostname") { + let mut builder = + dnsstamps::DoHBuilder::new(hostname.to_string(), globals.path.to_string()); + if let Some(public_address) = matches.value_of("public_address") { + builder = builder.with_address(public_address.to_string()); + } + println!( + "Test DNS stamp to reach [{}]: [{}]", + hostname, + builder.serialize().unwrap() + ); + println!("Check out https://dnscrypt.info/stamps/ to compute the actual stamp.\n") + } } diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index cdc845f..be30591 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -18,7 +18,7 @@ tls = ["tokio-rustls"] anyhow = "1.0.38" byteorder = "1.4.2" base64 = "0.13.0" -futures = "0.3.12" +futures = "0.3.13" hyper = { version = "0.14.4", default-features = false, features = ["server", "http1", "http2", "stream"] } tokio = { version = "1.2.0", features = ["net", "rt-multi-thread", "parking_lot", "time", "sync"] } tokio-rustls = { version = "0.22.0", features = ["early-data"], optional = true } From 42211d4f5e4555cac0c78b55745012c18e3c848d Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sat, 6 Mar 2021 22:22:34 +0100 Subject: [PATCH 026/150] Sync usage --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2b4c030..9d3fab3 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ FLAGS: OPTIONS: -E, --err-ttl TTL for errors, in seconds [default: 2] + -H, --hostname Host name (not IP address) DoH clients will use to connect -l, --listen-address Address to listen to [default: 127.0.0.1:3000] -b, --local-bind-address Address to connect from -c, --max-clients Maximum number of simultaneous clients [default: 512] @@ -49,12 +50,14 @@ OPTIONS: -X, --max-ttl Maximum TTL, in seconds [default: 604800] -T, --min-ttl Minimum TTL, in seconds [default: 10] -p, --path URI path [default: /dns-query] + -g, --public-address External IP address DoH clients will connect to -u, --server-address Address to connect to [default: 9.9.9.9:53] -t, --timeout Timeout, in seconds [default: 10] -I, --tls-cert-key-path Path to the PEM-encoded secret keys (only required for built-in TLS) - -i, --tls-cert-path Path to the PEM-encoded certificates (only required for built-in TLS) + -i, --tls-cert-path + Path to the PEM/PKCS#8-encoded certificates (only required for built-in TLS) ``` ## HTTP/2 termination From 05a60818ce8d632a74d3306dd09c99d4b3ee5a5a Mon Sep 17 00:00:00 2001 From: Christopher Wood Date: Mon, 26 Apr 2021 13:05:52 -0700 Subject: [PATCH 027/150] Add Oblivious DoH target support as a default feature. This change adds Oblivious DoH (ODoH) target support to doh-server. This change does include support for ODoH key rotation or algorithm agility. ODoH is a default feature and not conditionally compiled out. --- src/constants.rs | 1 + src/libdoh/Cargo.toml | 3 + src/libdoh/src/globals.rs | 3 + src/libdoh/src/lib.rs | 151 +++++++++++++++++++++++++++++++------- src/libdoh/src/odoh.rs | 96 ++++++++++++++++++++++++ src/main.rs | 8 ++ 6 files changed, 234 insertions(+), 28 deletions(-) create mode 100644 src/libdoh/src/odoh.rs diff --git a/src/constants.rs b/src/constants.rs index a784f2a..e7e549c 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -2,6 +2,7 @@ pub const LISTEN_ADDRESS: &str = "127.0.0.1:3000"; pub const MAX_CLIENTS: usize = 512; pub const MAX_CONCURRENT_STREAMS: u32 = 16; pub const PATH: &str = "/dns-query"; +pub const ODOH_CONFIGS_PATH: &str = "/.well-known/odohconfigs"; pub const SERVER_ADDRESS: &str = "9.9.9.9:53"; pub const TIMEOUT_SEC: u64 = 10; pub const MAX_TTL: u32 = 86400 * 7; diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index be30591..3df9bbe 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -22,6 +22,9 @@ futures = "0.3.13" hyper = { version = "0.14.4", default-features = false, features = ["server", "http1", "http2", "stream"] } tokio = { version = "1.2.0", features = ["net", "rt-multi-thread", "parking_lot", "time", "sync"] } tokio-rustls = { version = "0.22.0", features = ["early-data"], optional = true } +odoh-rs = "0.1.11" +rand = "0.7" +hpke = "0.5.0" [profile.release] codegen-units = 1 diff --git a/src/libdoh/src/globals.rs b/src/libdoh/src/globals.rs index 3fbba84..f5327e8 100644 --- a/src/libdoh/src/globals.rs +++ b/src/libdoh/src/globals.rs @@ -1,3 +1,4 @@ +use crate::odoh::ODoHPublicKey; use std::net::SocketAddr; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; @@ -28,6 +29,8 @@ pub struct Globals { pub err_ttl: u32, pub keepalive: bool, pub disable_post: bool, + pub odoh_configs_path: String, + pub odoh_public_key: Arc, pub runtime_handle: runtime::Handle, } diff --git a/src/libdoh/src/lib.rs b/src/libdoh/src/lib.rs index 03eb14b..19313b0 100644 --- a/src/libdoh/src/lib.rs +++ b/src/libdoh/src/lib.rs @@ -1,5 +1,6 @@ mod constants; pub mod dns; +pub mod odoh; mod errors; mod globals; #[cfg(feature = "tls")] @@ -25,9 +26,30 @@ pub mod reexports { pub use tokio; } +#[derive(Clone, Debug)] +struct DnsResponse { + packet: Vec, + ttl: u32, +} + +#[derive(Clone, Debug)] +enum DoHType { + Standard, + Oblivious, +} + +impl DoHType { + fn as_str(&self) -> String { + match self { + DoHType::Standard => String::from("application/dns-message"), + DoHType::Oblivious => String::from("application/oblivious-dns-message"), + } + } +} + #[derive(Clone, Debug)] pub struct DoH { - pub globals: Arc, + pub globals: Arc } #[allow(clippy::unnecessary_wraps)] @@ -72,14 +94,20 @@ impl hyper::service::Service> for DoH { fn call(&mut self, req: Request) -> Self::Future { let globals = &self.globals; - if req.uri().path() != globals.path { - return Box::pin(async { http_error(StatusCode::NOT_FOUND) }); - } let self_inner = self.clone(); - match *req.method() { - Method::POST => Box::pin(async move { self_inner.serve_post(req).await }), - Method::GET => Box::pin(async move { self_inner.serve_get(req).await }), - _ => Box::pin(async { http_error(StatusCode::METHOD_NOT_ALLOWED) }), + if req.uri().path() == globals.path { + match *req.method() { + Method::POST => Box::pin(async move { self_inner.serve_post(req).await }), + Method::GET => Box::pin(async move { self_inner.serve_get(req).await }), + _ => Box::pin(async { 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.serve_odoh_configs().await }), + _ => Box::pin(async { http_error(StatusCode::METHOD_NOT_ALLOWED) }), + } + } else { + Box::pin(async { http_error(StatusCode::NOT_FOUND) }) } } } @@ -89,12 +117,64 @@ impl DoH { if self.globals.disable_post { return http_error(StatusCode::METHOD_NOT_ALLOWED); } - if let Err(response) = Self::check_content_type(&req) { - return Ok(response); + + match Self::parse_content_type(&req) { + Ok(DoHType::Standard) => self.serve_doh_post(req).await, + Ok(DoHType::Oblivious) => self.serve_odoh_post(req).await, + Err(response) => return Ok(response) } - match self.read_body_and_proxy(req.into_body()).await { + } + + async fn serve_doh_post(&self, req:Request) -> Result, http::Error> { + let query = match self.read_body(req.into_body()).await { + Ok(q) => q, + Err(e) => return http_error(StatusCode::from(e)) + }; + + let resp = match self.proxy(query).await { + Ok(resp) => self.build_response(resp.packet, resp.ttl, DoHType::Standard.as_str()), + Err(e) => return http_error(StatusCode::from(e)), + }; + + match resp { + Ok(resp) => Ok(resp), + Err(e) => http_error(StatusCode::from(e)), + } + } + + // #[cfg(feature = "odoh")] + async fn serve_odoh_post(&self, req:Request) -> Result, http::Error> { + let query_body = match self.read_body(req.into_body()).await { + Ok(q) => q, + Err(e) => return http_error(StatusCode::from(e)) + }; + + let (query, context) = match (*self.globals.odoh_public_key).clone().decrypt_query(query_body).await { + Ok((q, context)) => (q.to_vec(), context), + Err(_) => return http_error(StatusCode::from(DoHError::InvalidData)) + }; + + let resp_body = match self.proxy(query).await { + Ok(resp) => resp, + Err(e) => return http_error(StatusCode::from(e)) + }; + + let resp = match context.encrypt_response(resp_body.packet).await { + Ok(resp) => self.build_response(resp, 0u32, DoHType::Oblivious.as_str()), + Err(e) => return http_error(StatusCode::from(e)), + }; + + match resp { + Ok(resp) => Ok(resp), + Err(e) => http_error(StatusCode::from(e)), + } + } + + async fn serve_odoh_configs(&self) -> Result, http::Error> { + let configs = (*self.globals.odoh_public_key).clone().config(); + match self.build_response(configs, 0, "application/octet-stream".to_string()) { + Ok(resp) => Ok(resp), Err(e) => http_error(StatusCode::from(e)), - Ok(res) => Ok(res), } } @@ -117,13 +197,18 @@ impl DoH { return http_error(StatusCode::BAD_REQUEST); } }; - match self.proxy(question).await { + + let resp = match self.proxy(question).await { + Ok(dns_resp) => self.build_response(dns_resp.packet, dns_resp.ttl, DoHType::Standard.as_str()), + Err(e) => Err(e), + }; + match resp { + Ok(resp) => Ok(resp), Err(e) => http_error(StatusCode::from(e)), - Ok(res) => Ok(res), } } - fn check_content_type(req: &Request) -> Result<(), Response> { + fn parse_content_type(req: &Request) -> Result> { let headers = req.headers(); let content_type = match headers.get(hyper::header::CONTENT_TYPE) { None => { @@ -145,17 +230,21 @@ impl DoH { } Ok(content_type) => content_type.to_lowercase(), }; - if content_type != "application/dns-message" { - let response = Response::builder() - .status(StatusCode::UNSUPPORTED_MEDIA_TYPE) - .body(Body::empty()) - .unwrap(); - return Err(response); + + match content_type.as_str() { + "application/dns-message" => Ok(DoHType::Standard), + "application/oblivious-dns-message" => Ok(DoHType::Oblivious), + _ => { + let response = Response::builder() + .status(StatusCode::UNSUPPORTED_MEDIA_TYPE) + .body(Body::empty()) + .unwrap(); + return Err(response); + } } - Ok(()) } - async fn read_body_and_proxy(&self, mut body: Body) -> Result, DoHError> { + async fn read_body(&self, mut body: Body) -> Result, DoHError> { let mut sum_size = 0; let mut query = vec![]; while let Some(chunk) = body.next().await { @@ -166,17 +255,16 @@ impl DoH { } query.extend(chunk); } - let response = self.proxy(query).await?; - Ok(response) + Ok(query) } - async fn proxy(&self, query: Vec) -> Result, DoHError> { + async fn proxy(&self, query: Vec) -> Result { let proxy_timeout = self.globals.timeout; let timeout_res = tokio::time::timeout(proxy_timeout, self._proxy(query)).await; timeout_res.map_err(|_| DoHError::UpstreamTimeout)? } - async fn _proxy(&self, mut query: Vec) -> Result, DoHError> { + async fn _proxy(&self, mut query: Vec) -> Result { if query.len() < MIN_DNS_PACKET_LEN { return Err(DoHError::Incomplete); } @@ -209,10 +297,17 @@ impl DoH { dns::add_edns_padding(&mut packet) .map_err(|_| DoHError::TooLarge) .ok(); + Ok(DnsResponse{ + packet, + ttl, + }) + } + + fn build_response(&self, packet: Vec, ttl: u32, content_type: String) -> Result, DoHError> { let packet_len = packet.len(); let response = Response::builder() .header(hyper::header::CONTENT_LENGTH, packet_len) - .header(hyper::header::CONTENT_TYPE, "application/dns-message") + .header(hyper::header::CONTENT_TYPE, content_type.as_str()) .header( hyper::header::CACHE_CONTROL, format!( diff --git a/src/libdoh/src/odoh.rs b/src/libdoh/src/odoh.rs new file mode 100644 index 0000000..b7e499c --- /dev/null +++ b/src/libdoh/src/odoh.rs @@ -0,0 +1,96 @@ +use crate::errors::DoHError; +use hpke::kex::Serializable; +use odoh_rs::key_utils::{derive_keypair_from_seed}; +use odoh_rs::protocol::{create_response_msg, + parse_received_query, RESPONSE_NONCE_SIZE, + ObliviousDoHQueryBody, Serialize, + ObliviousDoHKeyPair, ObliviousDoHConfigContents, + ObliviousDoHConfig, ObliviousDoHConfigs +}; +use rand::Rng; +use std::fmt; + +// https://cfrg.github.io/draft-irtf-cfrg-hpke/draft-irtf-cfrg-hpke.html#name-algorithm-identifiers +const DEFAULT_HPKE_SEED_SIZE: usize = 32; +const DEFAULT_HPKE_KEM: u16 = 0x0020; // DHKEM(X25519, HKDF-SHA256) +const DEFAULT_HPKE_KDF: u16 = 0x0001; // KDF(SHA-256) +const DEFAULT_HPKE_AEAD: u16 = 0x0001; // AEAD(AES-GCM-128) + +#[derive(Clone)] +pub struct ODoHPublicKey { + key: ObliviousDoHKeyPair, + serialized_configs: Vec, +} + +impl fmt::Debug for ODoHPublicKey { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ODoHPublicKey").finish() + } +} + +#[derive(Clone, Debug)] +pub struct ODoHQueryContext { + query: ObliviousDoHQueryBody, + secret: Vec, +} + +fn generate_key_pair() -> ObliviousDoHKeyPair { + let ikm = rand::thread_rng().gen::<[u8; DEFAULT_HPKE_SEED_SIZE]>(); + let (secret_key, public_key) = derive_keypair_from_seed(&ikm); + let public_key_bytes = public_key.to_bytes().to_vec(); + let odoh_public_key = ObliviousDoHConfigContents { + kem_id: DEFAULT_HPKE_KEM, + kdf_id: DEFAULT_HPKE_KDF, + aead_id: DEFAULT_HPKE_AEAD, + public_key: public_key_bytes, + }; + ObliviousDoHKeyPair { + private_key: secret_key, + public_key: odoh_public_key, + } +} + +impl ODoHPublicKey { + pub fn new() -> Result { + let key_pair = generate_key_pair(); + let config = ObliviousDoHConfig::new(&key_pair.public_key.clone().to_bytes().unwrap()).unwrap(); + let serialized_configs = ObliviousDoHConfigs { + configs: vec![config.clone()], + } + .to_bytes() + .unwrap() + .to_vec(); + + Ok(ODoHPublicKey{ + key: key_pair, + serialized_configs: serialized_configs + }) + } + + pub fn config(self) -> Vec { + self.serialized_configs + } + + pub async fn decrypt_query(self, encrypted_query: Vec) -> Result<(Vec, ODoHQueryContext), DoHError> { + let (query, server_secret) = match parse_received_query(&self.key, &encrypted_query).await { + Ok((pq, ss)) => (pq, ss), + Err(_) => return Err(DoHError::InvalidData) + }; + let context = ODoHQueryContext{ + query: query.clone(), + secret: server_secret, + }; + Ok((query.dns_msg.clone(), context)) + } +} + +impl ODoHQueryContext { + pub async fn encrypt_response(self, response_body: Vec) -> Result, DoHError> { + let response_nonce = rand::thread_rng().gen::<[u8; RESPONSE_NONCE_SIZE]>(); + create_response_msg(&self.secret, &response_body, None, Some(response_nonce.to_vec()), &self.query) + .await + .map_err(|_| { + DoHError::InvalidData + }) + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index f7f69a4..fbf249c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,6 +13,7 @@ use libdoh::*; use crate::config::*; use crate::constants::*; +use libdoh::odoh::ODoHPublicKey; use libdoh::reexports::tokio; use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::sync::Arc; @@ -24,6 +25,11 @@ fn main() { runtime_builder.thread_name("doh-proxy"); let runtime = runtime_builder.build().unwrap(); + let odoh_key = match ODoHPublicKey::new() { + Ok(key) => key, + Err(_) => panic!("Failed to generate ODoH public key configuration"), + }; + let mut globals = Globals { #[cfg(feature = "tls")] tls_cert_path: None, @@ -43,6 +49,8 @@ fn main() { err_ttl: ERR_TTL, keepalive: true, disable_post: false, + odoh_configs_path: ODOH_CONFIGS_PATH.to_string(), + odoh_public_key: Arc::new(odoh_key), runtime_handle: runtime.handle().clone(), }; From 25a9c285db66e29deb98620cbc461a9072c4a4b9 Mon Sep 17 00:00:00 2001 From: Christopher Wood Date: Mon, 26 Apr 2021 13:08:54 -0700 Subject: [PATCH 028/150] Remove dead comment. --- src/libdoh/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libdoh/src/lib.rs b/src/libdoh/src/lib.rs index 19313b0..db825ea 100644 --- a/src/libdoh/src/lib.rs +++ b/src/libdoh/src/lib.rs @@ -142,7 +142,6 @@ impl DoH { } } - // #[cfg(feature = "odoh")] async fn serve_odoh_post(&self, req:Request) -> Result, http::Error> { let query_body = match self.read_body(req.into_body()).await { Ok(q) => q, From e34f60e2eba7952acddef05799aa1f0f3cb974ec Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 29 Apr 2021 20:50:32 +0000 Subject: [PATCH 029/150] Upgrade to GitHub-native Dependabot --- .github/dependabot.yml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..c11601f --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,8 @@ +version: 2 +updates: +- package-ecosystem: cargo + directory: "/" + schedule: + interval: daily + time: "04:00" + open-pull-requests-limit: 10 From 822d3d9a516902c987928f391db7324dc477996d Mon Sep 17 00:00:00 2001 From: Christopher Wood Date: Sat, 1 May 2021 07:56:10 -0700 Subject: [PATCH 030/150] Implement ODoH key rotation. --- src/libdoh/Cargo.toml | 1 + src/libdoh/src/constants.rs | 1 + src/libdoh/src/errors.rs | 3 ++ src/libdoh/src/globals.rs | 4 +-- src/libdoh/src/lib.rs | 8 +++-- src/libdoh/src/odoh.rs | 66 +++++++++++++++++++++++++++++++++++-- src/main.rs | 10 +++--- 7 files changed, 81 insertions(+), 12 deletions(-) diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index 3df9bbe..a24df07 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -25,6 +25,7 @@ tokio-rustls = { version = "0.22.0", features = ["early-data"], optional = true odoh-rs = "0.1.11" rand = "0.7" hpke = "0.5.0" +arc-swap = "1.2.0" [profile.release] codegen-units = 1 diff --git a/src/libdoh/src/constants.rs b/src/libdoh/src/constants.rs index f1dccbb..976175a 100644 --- a/src/libdoh/src/constants.rs +++ b/src/libdoh/src/constants.rs @@ -5,3 +5,4 @@ pub const MIN_DNS_PACKET_LEN: usize = 17; pub const STALE_IF_ERROR_SECS: u32 = 86400; pub const STALE_WHILE_REVALIDATE_SECS: u32 = 60; pub const CERTS_WATCH_DELAY_SECS: u32 = 10; +pub const ODOH_KEY_ROTATION_SECS: u32 = 86400; diff --git a/src/libdoh/src/errors.rs b/src/libdoh/src/errors.rs index 707b6bb..55ec2fb 100644 --- a/src/libdoh/src/errors.rs +++ b/src/libdoh/src/errors.rs @@ -9,6 +9,7 @@ pub enum DoHError { TooLarge, UpstreamIssue, UpstreamTimeout, + StaleKey, Hyper(hyper::Error), Io(io::Error), } @@ -23,6 +24,7 @@ impl std::fmt::Display for DoHError { DoHError::TooLarge => write!(fmt, "Too large"), DoHError::UpstreamIssue => write!(fmt, "Upstream error"), DoHError::UpstreamTimeout => write!(fmt, "Upstream timeout"), + DoHError::StaleKey => write!(fmt, "Stale key material"), DoHError::Hyper(e) => write!(fmt, "HTTP error: {}", e), DoHError::Io(e) => write!(fmt, "IO error: {}", e), } @@ -37,6 +39,7 @@ impl From for StatusCode { DoHError::TooLarge => StatusCode::PAYLOAD_TOO_LARGE, DoHError::UpstreamIssue => StatusCode::BAD_GATEWAY, DoHError::UpstreamTimeout => StatusCode::BAD_GATEWAY, + DoHError::StaleKey => StatusCode::UNAUTHORIZED, DoHError::Hyper(_) => StatusCode::SERVICE_UNAVAILABLE, DoHError::Io(_) => StatusCode::INTERNAL_SERVER_ERROR, } diff --git a/src/libdoh/src/globals.rs b/src/libdoh/src/globals.rs index f5327e8..2e8f002 100644 --- a/src/libdoh/src/globals.rs +++ b/src/libdoh/src/globals.rs @@ -1,4 +1,4 @@ -use crate::odoh::ODoHPublicKey; +use crate::odoh::ODoHRotator; use std::net::SocketAddr; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; @@ -30,7 +30,7 @@ pub struct Globals { pub keepalive: bool, pub disable_post: bool, pub odoh_configs_path: String, - pub odoh_public_key: Arc, + pub odoh_rotator: Arc, pub runtime_handle: runtime::Handle, } diff --git a/src/libdoh/src/lib.rs b/src/libdoh/src/lib.rs index db825ea..9f5329f 100644 --- a/src/libdoh/src/lib.rs +++ b/src/libdoh/src/lib.rs @@ -148,9 +148,10 @@ impl DoH { Err(e) => return http_error(StatusCode::from(e)) }; - let (query, context) = match (*self.globals.odoh_public_key).clone().decrypt_query(query_body).await { + let odoh_public_key = (*self.globals.odoh_rotator).clone().current_key(); + let (query, context) = match (*odoh_public_key).clone().decrypt_query(query_body).await { Ok((q, context)) => (q.to_vec(), context), - Err(_) => return http_error(StatusCode::from(DoHError::InvalidData)) + Err(e) => return http_error(StatusCode::from(e)) }; let resp_body = match self.proxy(query).await { @@ -170,7 +171,8 @@ impl DoH { } async fn serve_odoh_configs(&self) -> Result, http::Error> { - let configs = (*self.globals.odoh_public_key).clone().config(); + let odoh_public_key = (*self.globals.odoh_rotator).clone().current_key(); + let configs = (*odoh_public_key).clone().config(); match self.build_response(configs, 0, "application/octet-stream".to_string()) { Ok(resp) => Ok(resp), Err(e) => http_error(StatusCode::from(e)), diff --git a/src/libdoh/src/odoh.rs b/src/libdoh/src/odoh.rs index b7e499c..5169cc6 100644 --- a/src/libdoh/src/odoh.rs +++ b/src/libdoh/src/odoh.rs @@ -1,11 +1,17 @@ +use crate::constants::ODOH_KEY_ROTATION_SECS; use crate::errors::DoHError; +use std::sync::Arc; +use arc_swap::ArcSwap; +use std::time::Duration; +use tokio::runtime; use hpke::kex::Serializable; use odoh_rs::key_utils::{derive_keypair_from_seed}; use odoh_rs::protocol::{create_response_msg, parse_received_query, RESPONSE_NONCE_SIZE, - ObliviousDoHQueryBody, Serialize, + ObliviousDoHQueryBody, Serialize, Deserialize, ObliviousDoHKeyPair, ObliviousDoHConfigContents, - ObliviousDoHConfig, ObliviousDoHConfigs + ObliviousDoHConfig, ObliviousDoHConfigs, + ObliviousDoHMessage, ObliviousDoHMessageType, }; use rand::Rng; use std::fmt; @@ -72,6 +78,25 @@ impl ODoHPublicKey { } pub async fn decrypt_query(self, encrypted_query: Vec) -> Result<(Vec, ODoHQueryContext), DoHError> { + let odoh_query = match ObliviousDoHMessage::from_bytes(&encrypted_query) { + Ok(q) => { + if q.msg_type != ObliviousDoHMessageType::Query { + return Err(DoHError::InvalidData); + } + q + }, + Err(_) => return Err(DoHError::InvalidData) + }; + + match self.key.public_key.identifier() { + Ok(key_id) => { + if !key_id.eq(&odoh_query.key_id) { + return Err(DoHError::StaleKey); + } + }, + Err(_) => return Err(DoHError::InvalidData) + }; + let (query, server_secret) = match parse_received_query(&self.key, &encrypted_query).await { Ok((pq, ss)) => (pq, ss), Err(_) => return Err(DoHError::InvalidData) @@ -93,4 +118,41 @@ impl ODoHQueryContext { DoHError::InvalidData }) } +} + +#[derive(Clone, Debug)] +pub struct ODoHRotator { + key: Arc>, +} + +impl ODoHRotator { + pub fn new(runtime_handle: runtime::Handle) -> Result { + let odoh_key = match ODoHPublicKey::new() { + Ok(key) => Arc::new(ArcSwap::from_pointee(key)), + Err(e) => panic!("ODoH key rotation error: {}", e), + }; + + let current_key = Arc::clone(&odoh_key); + + runtime_handle.clone().spawn(async move { + loop { + tokio::time::sleep(Duration::from_secs(ODOH_KEY_ROTATION_SECS.into())).await; + match ODoHPublicKey::new() { + Ok(key) => { + current_key.store(Arc::new(key)); + }, + Err(e) => eprintln!("ODoH key rotation error: {}", e), + }; + } + }); + + Ok(ODoHRotator{ + key: Arc::clone(&odoh_key) + }) + } + + pub fn current_key(&self) -> Arc { + let key = Arc::clone(&self.key); + Arc::clone(&key.load()) + } } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index fbf249c..86ecfe0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,7 +13,7 @@ use libdoh::*; use crate::config::*; use crate::constants::*; -use libdoh::odoh::ODoHPublicKey; +use libdoh::odoh::ODoHRotator; use libdoh::reexports::tokio; use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::sync::Arc; @@ -25,9 +25,9 @@ fn main() { runtime_builder.thread_name("doh-proxy"); let runtime = runtime_builder.build().unwrap(); - let odoh_key = match ODoHPublicKey::new() { - Ok(key) => key, - Err(_) => panic!("Failed to generate ODoH public key configuration"), + let rotator = match ODoHRotator::new(runtime.handle().clone()) { + Ok(r) => r, + Err(_) => panic!("Failed to create ODoHRotator"), }; let mut globals = Globals { @@ -50,7 +50,7 @@ fn main() { keepalive: true, disable_post: false, odoh_configs_path: ODOH_CONFIGS_PATH.to_string(), - odoh_public_key: Arc::new(odoh_key), + odoh_rotator: Arc::new(rotator), runtime_handle: runtime.handle().clone(), }; From 338d6436c0792656d41dbac7762d706648f3b764 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Wed, 12 May 2021 16:06:39 +0200 Subject: [PATCH 031/150] Let's Encrypt retired X3, introduced E1 --- README.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 9d3fab3..833f177 100644 --- a/README.md +++ b/README.md @@ -165,18 +165,20 @@ Go back to the online DNS stamp calculator, and copy&paste the hash (in this exa If you are using Let's Encrypt, the last line is likely to be: ```text -Advertised cert: [CN=Let's Encrypt Authority X3,O=Let's Encrypt,C=US] [3e1a1a0f6c53f3e97a492d57084b5b9807059ee057ab1505876fd83fda3db838] +Advertised cert: [CN=Let's Encrypt Authority R3,O=Let's Encrypt,C=US] [444ebd67bb83f8807b3921e938ac9178b882bd50aadb11231f044cf5f08df7ce] ``` -There you have it. Your certificate hash is `3e1a1a0f6c53f3e97a492d57084b5b9807059ee057ab1505876fd83fda3db838`. +There you have it. Your certificate hash is `444ebd67bb83f8807b3921e938ac9178b882bd50aadb11231f044cf5f08df7ce`. This [Go code snippet](https://gist.github.com/d6cb41742a1ceb54d48cc286f3d5c5fa) can also compute the hash of certificates given a `.der` file. ### Common certificate hashes -* Let's Encrypt X3: `3e1a1a0f6c53f3e97a492d57084b5b9807059ee057ab1505876fd83fda3db838` -* Let's Encrypt R3: `3286ff65a65faf32085eea1388c3738ba7e37873c906cce3c4a28b4cc2a58988` -* Let's Encrypt E1: `cc1060d39c8329b62b6fbc7d0d6df9309869b981e7e6392d5cd8fa408f4d80e6` +* Let's Encrypt R3: + * `3286ff65a65faf32085eea1388c3738ba7e37873c906cce3c4a28b4cc2a58988` and + * `444ebd67bb83f8807b3921e938ac9178b882bd50aadb11231f044cf5f08df7ce` +* Let's Encrypt E1: + * `cc1060d39c8329b62b6fbc7d0d6df9309869b981e7e6392d5cd8fa408f4d80e6` ## Clients From 9445e950144900a28b8e8638f08573f958adbfd7 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Fri, 14 May 2021 23:36:37 +0200 Subject: [PATCH 032/150] Update deps, format --- src/libdoh/Cargo.toml | 2 +- src/libdoh/src/lib.rs | 34 +++++++++++--------- src/libdoh/src/odoh.rs | 72 +++++++++++++++++++++++------------------- 3 files changed, 59 insertions(+), 49 deletions(-) diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index a24df07..49d6500 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -20,7 +20,7 @@ byteorder = "1.4.2" base64 = "0.13.0" futures = "0.3.13" hyper = { version = "0.14.4", default-features = false, features = ["server", "http1", "http2", "stream"] } -tokio = { version = "1.2.0", features = ["net", "rt-multi-thread", "parking_lot", "time", "sync"] } +tokio = { version = "1.6.0", features = ["net", "rt-multi-thread", "parking_lot", "time", "sync"] } tokio-rustls = { version = "0.22.0", features = ["early-data"], optional = true } odoh-rs = "0.1.11" rand = "0.7" diff --git a/src/libdoh/src/lib.rs b/src/libdoh/src/lib.rs index 9f5329f..80521d7 100644 --- a/src/libdoh/src/lib.rs +++ b/src/libdoh/src/lib.rs @@ -1,8 +1,8 @@ mod constants; pub mod dns; -pub mod odoh; mod errors; mod globals; +pub mod odoh; #[cfg(feature = "tls")] mod tls; @@ -49,7 +49,7 @@ impl DoHType { #[derive(Clone, Debug)] pub struct DoH { - pub globals: Arc + pub globals: Arc, } #[allow(clippy::unnecessary_wraps)] @@ -121,14 +121,14 @@ impl DoH { match Self::parse_content_type(&req) { Ok(DoHType::Standard) => self.serve_doh_post(req).await, Ok(DoHType::Oblivious) => self.serve_odoh_post(req).await, - Err(response) => return Ok(response) + Err(response) => return Ok(response), } } - async fn serve_doh_post(&self, req:Request) -> Result, http::Error> { + async fn serve_doh_post(&self, req: Request) -> Result, http::Error> { let query = match self.read_body(req.into_body()).await { Ok(q) => q, - Err(e) => return http_error(StatusCode::from(e)) + Err(e) => return http_error(StatusCode::from(e)), }; let resp = match self.proxy(query).await { @@ -142,21 +142,21 @@ impl DoH { } } - async fn serve_odoh_post(&self, req:Request) -> Result, http::Error> { + async fn serve_odoh_post(&self, req: Request) -> Result, http::Error> { let query_body = match self.read_body(req.into_body()).await { Ok(q) => q, - Err(e) => return http_error(StatusCode::from(e)) + Err(e) => return http_error(StatusCode::from(e)), }; let odoh_public_key = (*self.globals.odoh_rotator).clone().current_key(); let (query, context) = match (*odoh_public_key).clone().decrypt_query(query_body).await { Ok((q, context)) => (q.to_vec(), context), - Err(e) => return http_error(StatusCode::from(e)) + Err(e) => return http_error(StatusCode::from(e)), }; let resp_body = match self.proxy(query).await { Ok(resp) => resp, - Err(e) => return http_error(StatusCode::from(e)) + Err(e) => return http_error(StatusCode::from(e)), }; let resp = match context.encrypt_response(resp_body.packet).await { @@ -200,7 +200,9 @@ impl DoH { }; let resp = match self.proxy(question).await { - Ok(dns_resp) => self.build_response(dns_resp.packet, dns_resp.ttl, DoHType::Standard.as_str()), + Ok(dns_resp) => { + self.build_response(dns_resp.packet, dns_resp.ttl, DoHType::Standard.as_str()) + } Err(e) => Err(e), }; match resp { @@ -298,13 +300,15 @@ impl DoH { dns::add_edns_padding(&mut packet) .map_err(|_| DoHError::TooLarge) .ok(); - Ok(DnsResponse{ - packet, - ttl, - }) + Ok(DnsResponse { packet, ttl }) } - fn build_response(&self, packet: Vec, ttl: u32, content_type: String) -> Result, DoHError> { + fn build_response( + &self, + packet: Vec, + ttl: u32, + content_type: String, + ) -> Result, DoHError> { let packet_len = packet.len(); let response = Response::builder() .header(hyper::header::CONTENT_LENGTH, packet_len) diff --git a/src/libdoh/src/odoh.rs b/src/libdoh/src/odoh.rs index 5169cc6..a6eea48 100644 --- a/src/libdoh/src/odoh.rs +++ b/src/libdoh/src/odoh.rs @@ -1,20 +1,18 @@ use crate::constants::ODOH_KEY_ROTATION_SECS; use crate::errors::DoHError; -use std::sync::Arc; use arc_swap::ArcSwap; -use std::time::Duration; -use tokio::runtime; use hpke::kex::Serializable; -use odoh_rs::key_utils::{derive_keypair_from_seed}; -use odoh_rs::protocol::{create_response_msg, - parse_received_query, RESPONSE_NONCE_SIZE, - ObliviousDoHQueryBody, Serialize, Deserialize, - ObliviousDoHKeyPair, ObliviousDoHConfigContents, - ObliviousDoHConfig, ObliviousDoHConfigs, - ObliviousDoHMessage, ObliviousDoHMessageType, +use odoh_rs::key_utils::derive_keypair_from_seed; +use odoh_rs::protocol::{ + create_response_msg, parse_received_query, Deserialize, ObliviousDoHConfig, + ObliviousDoHConfigContents, ObliviousDoHConfigs, ObliviousDoHKeyPair, ObliviousDoHMessage, + ObliviousDoHMessageType, ObliviousDoHQueryBody, Serialize, RESPONSE_NONCE_SIZE, }; use rand::Rng; use std::fmt; +use std::sync::Arc; +use std::time::Duration; +use tokio::runtime; // https://cfrg.github.io/draft-irtf-cfrg-hpke/draft-irtf-cfrg-hpke.html#name-algorithm-identifiers const DEFAULT_HPKE_SEED_SIZE: usize = 32; @@ -45,9 +43,9 @@ fn generate_key_pair() -> ObliviousDoHKeyPair { let (secret_key, public_key) = derive_keypair_from_seed(&ikm); let public_key_bytes = public_key.to_bytes().to_vec(); let odoh_public_key = ObliviousDoHConfigContents { - kem_id: DEFAULT_HPKE_KEM, - kdf_id: DEFAULT_HPKE_KDF, - aead_id: DEFAULT_HPKE_AEAD, + kem_id: DEFAULT_HPKE_KEM, + kdf_id: DEFAULT_HPKE_KDF, + aead_id: DEFAULT_HPKE_AEAD, public_key: public_key_bytes, }; ObliviousDoHKeyPair { @@ -56,10 +54,11 @@ fn generate_key_pair() -> ObliviousDoHKeyPair { } } -impl ODoHPublicKey { +impl ODoHPublicKey { pub fn new() -> Result { let key_pair = generate_key_pair(); - let config = ObliviousDoHConfig::new(&key_pair.public_key.clone().to_bytes().unwrap()).unwrap(); + let config = + ObliviousDoHConfig::new(&key_pair.public_key.clone().to_bytes().unwrap()).unwrap(); let serialized_configs = ObliviousDoHConfigs { configs: vec![config.clone()], } @@ -67,9 +66,9 @@ impl ODoHPublicKey { .unwrap() .to_vec(); - Ok(ODoHPublicKey{ + Ok(ODoHPublicKey { key: key_pair, - serialized_configs: serialized_configs + serialized_configs: serialized_configs, }) } @@ -77,15 +76,18 @@ impl ODoHPublicKey { self.serialized_configs } - pub async fn decrypt_query(self, encrypted_query: Vec) -> Result<(Vec, ODoHQueryContext), DoHError> { + pub async fn decrypt_query( + self, + encrypted_query: Vec, + ) -> Result<(Vec, ODoHQueryContext), DoHError> { let odoh_query = match ObliviousDoHMessage::from_bytes(&encrypted_query) { Ok(q) => { if q.msg_type != ObliviousDoHMessageType::Query { return Err(DoHError::InvalidData); } q - }, - Err(_) => return Err(DoHError::InvalidData) + } + Err(_) => return Err(DoHError::InvalidData), }; match self.key.public_key.identifier() { @@ -93,15 +95,15 @@ impl ODoHPublicKey { if !key_id.eq(&odoh_query.key_id) { return Err(DoHError::StaleKey); } - }, - Err(_) => return Err(DoHError::InvalidData) + } + Err(_) => return Err(DoHError::InvalidData), }; let (query, server_secret) = match parse_received_query(&self.key, &encrypted_query).await { Ok((pq, ss)) => (pq, ss), - Err(_) => return Err(DoHError::InvalidData) + Err(_) => return Err(DoHError::InvalidData), }; - let context = ODoHQueryContext{ + let context = ODoHQueryContext { query: query.clone(), secret: server_secret, }; @@ -112,11 +114,15 @@ impl ODoHPublicKey { impl ODoHQueryContext { pub async fn encrypt_response(self, response_body: Vec) -> Result, DoHError> { let response_nonce = rand::thread_rng().gen::<[u8; RESPONSE_NONCE_SIZE]>(); - create_response_msg(&self.secret, &response_body, None, Some(response_nonce.to_vec()), &self.query) - .await - .map_err(|_| { - DoHError::InvalidData - }) + create_response_msg( + &self.secret, + &response_body, + None, + Some(response_nonce.to_vec()), + &self.query, + ) + .await + .map_err(|_| DoHError::InvalidData) } } @@ -140,14 +146,14 @@ impl ODoHRotator { match ODoHPublicKey::new() { Ok(key) => { current_key.store(Arc::new(key)); - }, + } Err(e) => eprintln!("ODoH key rotation error: {}", e), }; } }); - Ok(ODoHRotator{ - key: Arc::clone(&odoh_key) + Ok(ODoHRotator { + key: Arc::clone(&odoh_key), }) } @@ -155,4 +161,4 @@ impl ODoHRotator { let key = Arc::clone(&self.key); Arc::clone(&key.load()) } -} \ No newline at end of file +} From 1389c8287227fb741f6bc2f61293a67ee83cc3ae Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sat, 5 Jun 2021 17:11:09 +0200 Subject: [PATCH 033/150] Move file comment up --- src/utils.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/utils.rs b/src/utils.rs index 0214ed5..0c2137b 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,6 +1,7 @@ +// functions to verify the startup arguments as correct + use std::net::{SocketAddr, ToSocketAddrs}; -// functions to verify the startup arguments as correct pub(crate) fn verify_sock_addr(arg_val: String) -> Result<(), String> { match arg_val.parse::() { Ok(_addr) => Ok(()), From 90c30c8905eef7ee31741dd5553851b0cb0628d8 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sat, 5 Jun 2021 17:24:50 +0200 Subject: [PATCH 034/150] Avoid unwrap() --- src/libdoh/src/errors.rs | 3 +++ src/libdoh/src/odoh.rs | 13 ++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/libdoh/src/errors.rs b/src/libdoh/src/errors.rs index 55ec2fb..2d0baf9 100644 --- a/src/libdoh/src/errors.rs +++ b/src/libdoh/src/errors.rs @@ -12,6 +12,7 @@ pub enum DoHError { StaleKey, Hyper(hyper::Error), Io(io::Error), + ODoHConfigError(anyhow::Error), } impl std::error::Error for DoHError {} @@ -27,6 +28,7 @@ impl std::fmt::Display for DoHError { DoHError::StaleKey => write!(fmt, "Stale key material"), DoHError::Hyper(e) => write!(fmt, "HTTP error: {}", e), DoHError::Io(e) => write!(fmt, "IO error: {}", e), + DoHError::ODoHConfigError(e) => write!(fmt, "ODoH config error: {}", e), } } } @@ -42,6 +44,7 @@ impl From for StatusCode { DoHError::StaleKey => StatusCode::UNAUTHORIZED, DoHError::Hyper(_) => StatusCode::SERVICE_UNAVAILABLE, DoHError::Io(_) => StatusCode::INTERNAL_SERVER_ERROR, + DoHError::ODoHConfigError(_) => StatusCode::INTERNAL_SERVER_ERROR, } } } diff --git a/src/libdoh/src/odoh.rs b/src/libdoh/src/odoh.rs index a6eea48..1fc39ec 100644 --- a/src/libdoh/src/odoh.rs +++ b/src/libdoh/src/odoh.rs @@ -57,13 +57,20 @@ fn generate_key_pair() -> ObliviousDoHKeyPair { impl ODoHPublicKey { pub fn new() -> Result { let key_pair = generate_key_pair(); - let config = - ObliviousDoHConfig::new(&key_pair.public_key.clone().to_bytes().unwrap()).unwrap(); + let config = ObliviousDoHConfig::new( + &key_pair + .public_key + .clone() + .to_bytes() + .map_err(|e| DoHError::ODoHConfigError(e))?, + ) + .map_err(|e| DoHError::ODoHConfigError(e))?; + let serialized_configs = ObliviousDoHConfigs { configs: vec![config.clone()], } .to_bytes() - .unwrap() + .map_err(|e| DoHError::ODoHConfigError(e))? .to_vec(); Ok(ODoHPublicKey { From 6edccca03eaddfab09db3645e063cb1f4640e5c8 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sun, 6 Jun 2021 15:53:40 +0200 Subject: [PATCH 035/150] Factor DoH serving code --- src/libdoh/src/lib.rs | 84 +++++++++++++++++++++---------------------- 1 file changed, 40 insertions(+), 44 deletions(-) diff --git a/src/libdoh/src/lib.rs b/src/libdoh/src/lib.rs index 80521d7..e9a973d 100644 --- a/src/libdoh/src/lib.rs +++ b/src/libdoh/src/lib.rs @@ -125,46 +125,74 @@ impl DoH { } } - async fn serve_doh_post(&self, req: Request) -> Result, http::Error> { - let query = match self.read_body(req.into_body()).await { - Ok(q) => q, - Err(e) => return http_error(StatusCode::from(e)), - }; - + async fn _serve_doh_query(&self, query: Vec) -> Result, http::Error> { let resp = match self.proxy(query).await { Ok(resp) => self.build_response(resp.packet, resp.ttl, DoHType::Standard.as_str()), Err(e) => return http_error(StatusCode::from(e)), }; - match resp { Ok(resp) => Ok(resp), Err(e) => http_error(StatusCode::from(e)), } } + async fn serve_get(&self, req: Request) -> Result, http::Error> { + let http_query = req.uri().query().unwrap_or(""); + let mut question_str = None; + for parts in http_query.split('&') { + let mut kv = parts.split('='); + if let Some(k) = kv.next() { + if k == DNS_QUERY_PARAM { + question_str = kv.next(); + } + } + } + let query = match question_str.and_then(|question_str| { + base64::decode_config(question_str, base64::URL_SAFE_NO_PAD).ok() + }) { + Some(query) => query, + _ => { + return http_error(StatusCode::BAD_REQUEST); + } + }; + self._serve_doh_query(query).await + } + + async fn serve_doh_post(&self, req: Request) -> Result, http::Error> { + let query = match self.read_body(req.into_body()).await { + Ok(q) => q, + Err(e) => return http_error(StatusCode::from(e)), + }; + self._serve_doh_query(query).await + } + async fn serve_odoh_post(&self, req: Request) -> Result, http::Error> { - let query_body = match self.read_body(req.into_body()).await { + let encrypted_query = match self.read_body(req.into_body()).await { Ok(q) => q, Err(e) => return http_error(StatusCode::from(e)), }; let odoh_public_key = (*self.globals.odoh_rotator).clone().current_key(); - let (query, context) = match (*odoh_public_key).clone().decrypt_query(query_body).await { + let (query, context) = match (*odoh_public_key) + .clone() + .decrypt_query(encrypted_query) + .await + { Ok((q, context)) => (q.to_vec(), context), Err(e) => return http_error(StatusCode::from(e)), }; - let resp_body = match self.proxy(query).await { + let resp = match self.proxy(query).await { Ok(resp) => resp, Err(e) => return http_error(StatusCode::from(e)), }; - let resp = match context.encrypt_response(resp_body.packet).await { + let encrypted_resp = match context.encrypt_response(resp.packet).await { Ok(resp) => self.build_response(resp, 0u32, DoHType::Oblivious.as_str()), Err(e) => return http_error(StatusCode::from(e)), }; - match resp { + match encrypted_resp { Ok(resp) => Ok(resp), Err(e) => http_error(StatusCode::from(e)), } @@ -179,38 +207,6 @@ impl DoH { } } - async fn serve_get(&self, req: Request) -> Result, http::Error> { - let query = req.uri().query().unwrap_or(""); - let mut question_str = None; - for parts in query.split('&') { - let mut kv = parts.split('='); - if let Some(k) = kv.next() { - if k == DNS_QUERY_PARAM { - question_str = kv.next(); - } - } - } - let question = match question_str.and_then(|question_str| { - base64::decode_config(question_str, base64::URL_SAFE_NO_PAD).ok() - }) { - Some(question) => question, - _ => { - return http_error(StatusCode::BAD_REQUEST); - } - }; - - let resp = match self.proxy(question).await { - Ok(dns_resp) => { - self.build_response(dns_resp.packet, dns_resp.ttl, DoHType::Standard.as_str()) - } - Err(e) => Err(e), - }; - match resp { - Ok(resp) => Ok(resp), - Err(e) => http_error(StatusCode::from(e)), - } - } - fn parse_content_type(req: &Request) -> Result> { let headers = req.headers(); let content_type = match headers.get(hyper::header::CONTENT_TYPE) { From 21fc7441b3acd706ea7fce2d7d8ad840b8bd472e Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sun, 6 Jun 2021 15:58:58 +0200 Subject: [PATCH 036/150] Shuffle --- src/libdoh/src/lib.rs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/libdoh/src/lib.rs b/src/libdoh/src/lib.rs index e9a973d..a682e28 100644 --- a/src/libdoh/src/lib.rs +++ b/src/libdoh/src/lib.rs @@ -113,6 +113,14 @@ impl hyper::service::Service> for DoH { } impl DoH { + async fn serve_get(&self, req: Request) -> Result, http::Error> { + match Self::parse_content_type(&req) { + Ok(DoHType::Standard) => self.serve_doh_get(req).await, + Ok(DoHType::Oblivious) => self.serve_odoh_get(req).await, + Err(response) => return Ok(response), + } + } + async fn serve_post(&self, req: Request) -> Result, http::Error> { if self.globals.disable_post { return http_error(StatusCode::METHOD_NOT_ALLOWED); @@ -125,7 +133,7 @@ impl DoH { } } - async fn _serve_doh_query(&self, query: Vec) -> Result, http::Error> { + async fn serve_doh_query(&self, query: Vec) -> Result, http::Error> { let resp = match self.proxy(query).await { Ok(resp) => self.build_response(resp.packet, resp.ttl, DoHType::Standard.as_str()), Err(e) => return http_error(StatusCode::from(e)), @@ -136,7 +144,7 @@ impl DoH { } } - async fn serve_get(&self, req: Request) -> Result, http::Error> { + async fn serve_doh_get(&self, req: Request) -> Result, http::Error> { let http_query = req.uri().query().unwrap_or(""); let mut question_str = None; for parts in http_query.split('&') { @@ -155,7 +163,7 @@ impl DoH { return http_error(StatusCode::BAD_REQUEST); } }; - self._serve_doh_query(query).await + self.serve_doh_query(query).await } async fn serve_doh_post(&self, req: Request) -> Result, http::Error> { @@ -163,7 +171,11 @@ impl DoH { Ok(q) => q, Err(e) => return http_error(StatusCode::from(e)), }; - self._serve_doh_query(query).await + self.serve_doh_query(query).await + } + + async fn serve_odoh_get(&self, req: Request) -> Result, http::Error> { + unimplemented!() } async fn serve_odoh_post(&self, req: Request) -> Result, http::Error> { From 62744d53906e3255c8d6b7dc0745758a730f2a74 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sun, 6 Jun 2021 16:11:45 +0200 Subject: [PATCH 037/150] Handle ODoH queries using GET --- src/libdoh/src/lib.rs | 41 +++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/src/libdoh/src/lib.rs b/src/libdoh/src/lib.rs index a682e28..d70a858 100644 --- a/src/libdoh/src/lib.rs +++ b/src/libdoh/src/lib.rs @@ -144,7 +144,7 @@ impl DoH { } } - async fn serve_doh_get(&self, req: Request) -> Result, http::Error> { + fn query_from_query_string(&self, req: Request) -> Option> { let http_query = req.uri().query().unwrap_or(""); let mut question_str = None; for parts in http_query.split('&') { @@ -159,9 +159,15 @@ impl DoH { base64::decode_config(question_str, base64::URL_SAFE_NO_PAD).ok() }) { Some(query) => query, - _ => { - return http_error(StatusCode::BAD_REQUEST); - } + _ => return None, + }; + Some(query) + } + + async fn serve_doh_get(&self, req: Request) -> Result, http::Error> { + let query = match self.query_from_query_string(req) { + Some(query) => query, + _ => return http_error(StatusCode::BAD_REQUEST), }; self.serve_doh_query(query).await } @@ -174,16 +180,7 @@ impl DoH { self.serve_doh_query(query).await } - async fn serve_odoh_get(&self, req: Request) -> Result, http::Error> { - unimplemented!() - } - - async fn serve_odoh_post(&self, req: Request) -> Result, http::Error> { - let encrypted_query = match self.read_body(req.into_body()).await { - Ok(q) => q, - Err(e) => return http_error(StatusCode::from(e)), - }; - + async fn serve_odoh(&self, encrypted_query: Vec) -> Result, http::Error> { let odoh_public_key = (*self.globals.odoh_rotator).clone().current_key(); let (query, context) = match (*odoh_public_key) .clone() @@ -210,6 +207,22 @@ impl DoH { } } + async fn serve_odoh_get(&self, req: Request) -> Result, http::Error> { + let encrypted_query = match self.query_from_query_string(req) { + Some(encrypted_query) => encrypted_query, + _ => return http_error(StatusCode::BAD_REQUEST), + }; + self.serve_odoh(encrypted_query).await + } + + async fn serve_odoh_post(&self, req: Request) -> Result, http::Error> { + let encrypted_query = match self.read_body(req.into_body()).await { + Ok(q) => q, + Err(e) => return http_error(StatusCode::from(e)), + }; + self.serve_odoh(encrypted_query).await + } + async fn serve_odoh_configs(&self) -> Result, http::Error> { let odoh_public_key = (*self.globals.odoh_rotator).clone().current_key(); let configs = (*odoh_public_key).clone().config(); From 9be0d1ed747936c8e0bb3fac4bf02c3e283fe630 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sun, 6 Jun 2021 17:31:15 +0200 Subject: [PATCH 038/150] Check Accept: if there is no Content-Type: --- src/libdoh/src/lib.rs | 75 +++++++++++++++++++++++++++++++------------ 1 file changed, 55 insertions(+), 20 deletions(-) diff --git a/src/libdoh/src/lib.rs b/src/libdoh/src/lib.rs index d70a858..bbdb212 100644 --- a/src/libdoh/src/lib.rs +++ b/src/libdoh/src/lib.rs @@ -14,7 +14,7 @@ use futures::prelude::*; use futures::task::{Context, Poll}; use hyper::http; use hyper::server::conn::Http; -use hyper::{Body, Method, Request, Response, StatusCode}; +use hyper::{Body, HeaderMap, Method, Request, Response, StatusCode}; use std::pin::Pin; use std::sync::Arc; use std::time::Duration; @@ -232,32 +232,67 @@ impl DoH { } } + fn acceptable_content_type( + headers: &HeaderMap, + content_types: &[&'static str], + ) -> Option<&'static str> { + let accept = headers.get(hyper::header::ACCEPT); + let accept = match accept { + None => return None, + Some(accept) => accept, + }; + for part in accept.to_str().unwrap_or("").split(",").map(|s| s.trim()) { + if let Some(found) = part + .split(";") + .next() + .map(|s| s.trim().to_ascii_lowercase()) + { + if let Some(&content_type) = content_types + .iter() + .find(|&&content_type| content_type == found) + { + return Some(content_type); + } + } + } + None + } + fn parse_content_type(req: &Request) -> Result> { + const CT_DOH: &str = "application/dns-message"; + const CT_ODOH: &str = "application/oblivious-dns-message"; + let headers = req.headers(); let content_type = match headers.get(hyper::header::CONTENT_TYPE) { None => { - let response = Response::builder() - .status(StatusCode::NOT_ACCEPTABLE) - .body(Body::empty()) - .unwrap(); - return Err(response); + let acceptable_content_type = + Self::acceptable_content_type(headers, &[CT_DOH, CT_ODOH]); + match acceptable_content_type { + None => { + let response = Response::builder() + .status(StatusCode::NOT_ACCEPTABLE) + .body(Body::empty()) + .unwrap(); + return Err(response); + } + Some(content_type) => content_type, + } } - Some(content_type) => content_type.to_str(), - }; - let content_type = match content_type { - Err(_) => { - let response = Response::builder() - .status(StatusCode::BAD_REQUEST) - .body(Body::empty()) - .unwrap(); - return Err(response); - } - Ok(content_type) => content_type.to_lowercase(), + Some(content_type) => match content_type.to_str() { + Err(_) => { + let response = Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::empty()) + .unwrap(); + return Err(response); + } + Ok(content_type) => content_type, + }, }; - match content_type.as_str() { - "application/dns-message" => Ok(DoHType::Standard), - "application/oblivious-dns-message" => Ok(DoHType::Oblivious), + match content_type.to_ascii_lowercase().as_str() { + CT_DOH => Ok(DoHType::Standard), + CT_ODOH => Ok(DoHType::Oblivious), _ => { let response = Response::builder() .status(StatusCode::UNSUPPORTED_MEDIA_TYPE) From a746e2822a9a4fdc637099f4548b4e04c58e46ae Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sun, 6 Jun 2021 17:36:04 +0200 Subject: [PATCH 039/150] Reject large query strings --- src/libdoh/src/lib.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libdoh/src/lib.rs b/src/libdoh/src/lib.rs index bbdb212..5c1483c 100644 --- a/src/libdoh/src/lib.rs +++ b/src/libdoh/src/lib.rs @@ -155,6 +155,11 @@ impl DoH { } } } + if let Some(question_str) = question_str { + if question_str.len() > MAX_DNS_QUESTION_LEN * 4 / 3 { + return None; + } + } let query = match question_str.and_then(|question_str| { base64::decode_config(question_str, base64::URL_SAFE_NO_PAD).ok() }) { From 3bc0d22f691eb1ec5534ade3852a12c00e52bd77 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sun, 6 Jun 2021 17:41:48 +0200 Subject: [PATCH 040/150] Add --allow-odoh-post --- src/config.rs | 7 +++++++ src/libdoh/src/globals.rs | 1 + src/libdoh/src/lib.rs | 10 ++++++---- src/main.rs | 1 + 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/config.rs b/src/config.rs index 34b7f9a..ebe4eb4 100644 --- a/src/config.rs +++ b/src/config.rs @@ -128,6 +128,12 @@ pub fn parse_opts(globals: &mut Globals) { .short("P") .long("disable-post") .help("Disable POST queries"), + ) + .arg( + Arg::with_name("allow_odoh_post") + .short("O") + .long("allow-odoh-post") + .help("Allow POST queries over ODoH even with they have been disabed for DoH"), ); #[cfg(feature = "tls")] @@ -183,6 +189,7 @@ pub fn parse_opts(globals: &mut Globals) { globals.err_ttl = matches.value_of("err_ttl").unwrap().parse().unwrap(); globals.keepalive = !matches.is_present("disable_keepalive"); globals.disable_post = matches.is_present("disable_post"); + globals.allow_odoh_post = matches.is_present("allow_odoh_post"); #[cfg(feature = "tls")] { diff --git a/src/libdoh/src/globals.rs b/src/libdoh/src/globals.rs index 2e8f002..df32cbd 100644 --- a/src/libdoh/src/globals.rs +++ b/src/libdoh/src/globals.rs @@ -29,6 +29,7 @@ pub struct Globals { pub err_ttl: u32, pub keepalive: bool, pub disable_post: bool, + pub allow_odoh_post: bool, pub odoh_configs_path: String, pub odoh_rotator: Arc, diff --git a/src/libdoh/src/lib.rs b/src/libdoh/src/lib.rs index 5c1483c..10e92e8 100644 --- a/src/libdoh/src/lib.rs +++ b/src/libdoh/src/lib.rs @@ -122,10 +122,6 @@ impl DoH { } async fn serve_post(&self, req: Request) -> Result, http::Error> { - if self.globals.disable_post { - return http_error(StatusCode::METHOD_NOT_ALLOWED); - } - match Self::parse_content_type(&req) { Ok(DoHType::Standard) => self.serve_doh_post(req).await, Ok(DoHType::Oblivious) => self.serve_odoh_post(req).await, @@ -178,6 +174,9 @@ impl DoH { } async fn serve_doh_post(&self, req: Request) -> Result, http::Error> { + if self.globals.disable_post { + return http_error(StatusCode::METHOD_NOT_ALLOWED); + } let query = match self.read_body(req.into_body()).await { Ok(q) => q, Err(e) => return http_error(StatusCode::from(e)), @@ -221,6 +220,9 @@ impl DoH { } async fn serve_odoh_post(&self, req: Request) -> Result, http::Error> { + if self.globals.disable_post && !self.globals.allow_odoh_post { + return http_error(StatusCode::METHOD_NOT_ALLOWED); + } let encrypted_query = match self.read_body(req.into_body()).await { Ok(q) => q, Err(e) => return http_error(StatusCode::from(e)), diff --git a/src/main.rs b/src/main.rs index 86ecfe0..582724c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -49,6 +49,7 @@ fn main() { err_ttl: ERR_TTL, keepalive: true, disable_post: false, + allow_odoh_post: false, odoh_configs_path: ODOH_CONFIGS_PATH.to_string(), odoh_rotator: Arc::new(rotator), From 5c369fc610bcf2d55f8fda2cbe091a66f46924c8 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sun, 6 Jun 2021 17:46:18 +0200 Subject: [PATCH 041/150] Don't use a 0 TTL for ODoH configs --- src/libdoh/src/lib.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libdoh/src/lib.rs b/src/libdoh/src/lib.rs index 10e92e8..9269463 100644 --- a/src/libdoh/src/lib.rs +++ b/src/libdoh/src/lib.rs @@ -233,7 +233,11 @@ impl DoH { async fn serve_odoh_configs(&self) -> Result, http::Error> { let odoh_public_key = (*self.globals.odoh_rotator).clone().current_key(); let configs = (*odoh_public_key).clone().config(); - match self.build_response(configs, 0, "application/octet-stream".to_string()) { + match self.build_response( + configs, + ODOH_KEY_ROTATION_SECS, + "application/octet-stream".to_string(), + ) { Ok(resp) => Ok(resp), Err(e) => http_error(StatusCode::from(e)), } From fd1081e0b0da95f38a19e91c7ab70a3193ea8f58 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sun, 6 Jun 2021 22:50:50 +0200 Subject: [PATCH 042/150] up --- Cargo.toml | 2 +- README.md | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 003944d..4508223 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "doh-proxy" version = "0.3.8" authors = ["Frank Denis "] -description = "A DNS-over-HTTPS (DoH) proxy" +description = "A DNS-over-HTTPS (DoH) and ODoH (Oblivious DoH) proxy" keywords = ["dns","https","doh","proxy"] license = "MIT" homepage = "https://github.com/jedisct1/rust-doh" diff --git a/README.md b/README.md index 833f177..8b0bc97 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # doh-proxy -A fast and secure DoH (DNS-over-HTTPS) server. +A fast and secure DoH (DNS-over-HTTPS) and ODoH (Oblivious DoH) server. `doh-proxy` is written in Rust, and has been battle-tested in production since February 2018. It doesn't do DNS resolution on its own, but can sit in front of any DNS resolver in order to augment it with DoH support. @@ -29,12 +29,11 @@ cargo install doh-proxy --no-default-features ## Usage ```text -A DNS-over-HTTPS (DoH) proxy - USAGE: doh-proxy [FLAGS] [OPTIONS] FLAGS: + -O, --allow-odoh-post Allow POST queries over ODoH even with they have been disabed for DoH -K, --disable-keepalive Disable keepalive -P, --disable-post Disable POST queries -h, --help Prints help information @@ -109,6 +108,12 @@ It also provides DNS caching, server-side filtering, metrics, and TCP connection Unless the front-end is a CDN, an ideal setup is to use `doh-proxy` behind `Encrypted DNS Server`. +## Oblivious DoH (ODoH) + +Oblivious DoH is similar to Anonymized DNSCrypt, but for DoH. It requires relays, but also upstream DoH servers that support the protocol. + +This proxy supports ODoH termination (not relaying) out of the box. + ## Operational recommendations * DoH can be easily detected and blocked using SNI inspection. As a mitigation, DoH endpoints should preferably share the same virtual host as existing, popular websites, rather than being on dedicated virtual hosts. From eebd6b835649d6265b302b3260f7fff9e0f81ed9 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sun, 6 Jun 2021 22:52:54 +0200 Subject: [PATCH 043/150] Add a note on keys --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 8b0bc97..c9b68d9 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,8 @@ Oblivious DoH is similar to Anonymized DNSCrypt, but for DoH. It requires relays This proxy supports ODoH termination (not relaying) out of the box. +However, ephemeral keys are currently only stored in memory. In a load-balanced configuration, sticky sessions must be used. + ## Operational recommendations * DoH can be easily detected and blocked using SNI inspection. As a mitigation, DoH endpoints should preferably share the same virtual host as existing, popular websites, rather than being on dedicated virtual hosts. From ece8a445cb73a8ed6df2b9848cc36e70160fb0c0 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Mon, 7 Jun 2021 14:42:16 +0200 Subject: [PATCH 044/150] DOcument --allow-odoh-post --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index c9b68d9..14f45d2 100644 --- a/README.md +++ b/README.md @@ -116,6 +116,11 @@ This proxy supports ODoH termination (not relaying) out of the box. However, ephemeral keys are currently only stored in memory. In a load-balanced configuration, sticky sessions must be used. +Currently available ODoH relays only use `POST` queries. +So, `POST` queries have been disabled for regular DoH queries, accepting them is required to be compatible with ODoH relays. + +This can be achieved with the `--allow-odoh-post` command-line switch. + ## Operational recommendations * DoH can be easily detected and blocked using SNI inspection. As a mitigation, DoH endpoints should preferably share the same virtual host as existing, popular websites, rather than being on dedicated virtual hosts. From 3b77ff2e341d94b125b91c55e0ae59a2b137c34e Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Mon, 7 Jun 2021 14:42:47 +0200 Subject: [PATCH 045/150] Typo --- src/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.rs b/src/config.rs index ebe4eb4..5bab9bf 100644 --- a/src/config.rs +++ b/src/config.rs @@ -133,7 +133,7 @@ pub fn parse_opts(globals: &mut Globals) { Arg::with_name("allow_odoh_post") .short("O") .long("allow-odoh-post") - .help("Allow POST queries over ODoH even with they have been disabed for DoH"), + .help("Allow POST queries over ODoH even if they have been disabed for DoH"), ); #[cfg(feature = "tls")] From 474701ec1e29e241677f2d1ee49103ffa6fb482e Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Mon, 7 Jun 2021 14:45:21 +0200 Subject: [PATCH 046/150] Up --- Cargo.toml | 6 +++--- src/libdoh/Cargo.toml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4508223..faaceab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "doh-proxy" -version = "0.3.8" +version = "0.4.0" authors = ["Frank Denis "] description = "A DNS-over-HTTPS (DoH) and ODoH (Oblivious DoH) proxy" keywords = ["dns","https","doh","proxy"] @@ -16,14 +16,14 @@ default = ["tls"] tls = ["libdoh/tls"] [dependencies] -libdoh = { path = "src/libdoh", version = "0.3.8", default-features = false } +libdoh = { path = "src/libdoh", version = "0.4.0", default-features = false } clap = "2.33.3" dnsstamps = "0.1.5" jemallocator = "0.3.2" [package.metadata.deb] extended-description = """\ -A fast and secure DoH (DNS-over-HTTPS) server written in Rust.""" +A fast and secure DoH (DNS-over-HTTPS) and ODoH server written in Rust.""" assets = [ ["target/release/doh-proxy", "usr/bin/", "755"], ["README.md", "usr/share/doc/doh-proxy/README.md", "644"] diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index 49d6500..5a93224 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libdoh" -version = "0.3.8" +version = "0.4.0" authors = ["Frank Denis "] description = "DoH library for the rust-doh app" keywords = ["dns","https","doh","proxy"] @@ -23,7 +23,7 @@ hyper = { version = "0.14.4", default-features = false, features = ["server", "h tokio = { version = "1.6.0", features = ["net", "rt-multi-thread", "parking_lot", "time", "sync"] } tokio-rustls = { version = "0.22.0", features = ["early-data"], optional = true } odoh-rs = "0.1.11" -rand = "0.7" +rand = "0.7.0" hpke = "0.5.0" arc-swap = "1.2.0" From 324bbcde600fc56799a1e4c8bbcdb5a20914b4a4 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Mon, 7 Jun 2021 14:47:36 +0200 Subject: [PATCH 047/150] Add Cargo keyword --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index faaceab..f6f0d88 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "doh-proxy" version = "0.4.0" authors = ["Frank Denis "] description = "A DNS-over-HTTPS (DoH) and ODoH (Oblivious DoH) proxy" -keywords = ["dns","https","doh","proxy"] +keywords = ["dns","https","doh","odoh","proxy"] license = "MIT" homepage = "https://github.com/jedisct1/rust-doh" repository = "https://github.com/jedisct1/rust-doh" From 0f268055b79e8eb044afb564c65552192375cdc7 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Mon, 7 Jun 2021 14:48:03 +0200 Subject: [PATCH 048/150] up --- src/libdoh/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index 5a93224..03a4ddc 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -3,7 +3,7 @@ name = "libdoh" version = "0.4.0" authors = ["Frank Denis "] description = "DoH library for the rust-doh app" -keywords = ["dns","https","doh","proxy"] +keywords = ["dns","https","doh","odoh","proxy"] license = "MIT" homepage = "https://github.com/jedisct1/rust-doh" repository = "https://github.com/jedisct1/rust-doh" From 485afd5976143768a9726359f46a01469870350c Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Wed, 9 Jun 2021 10:50:28 +0200 Subject: [PATCH 049/150] Add retries over TCP Fixes #62 --- src/libdoh/src/constants.rs | 1 + src/libdoh/src/dns.rs | 9 +++++ src/libdoh/src/errors.rs | 4 +- src/libdoh/src/globals.rs | 4 ++ src/libdoh/src/lib.rs | 77 +++++++++++++++++++++++++++++-------- 5 files changed, 78 insertions(+), 17 deletions(-) diff --git a/src/libdoh/src/constants.rs b/src/libdoh/src/constants.rs index 976175a..2ba0361 100644 --- a/src/libdoh/src/constants.rs +++ b/src/libdoh/src/constants.rs @@ -6,3 +6,4 @@ pub const STALE_IF_ERROR_SECS: u32 = 86400; pub const STALE_WHILE_REVALIDATE_SECS: u32 = 60; pub const CERTS_WATCH_DELAY_SECS: u32 = 10; pub const ODOH_KEY_ROTATION_SECS: u32 = 86400; +pub const UDP_TCP_RATIO: usize = 8; diff --git a/src/libdoh/src/dns.rs b/src/libdoh/src/dns.rs index 321f57a..fdea9f5 100644 --- a/src/libdoh/src/dns.rs +++ b/src/libdoh/src/dns.rs @@ -2,9 +2,13 @@ use anyhow::{ensure, Error}; use byteorder::{BigEndian, ByteOrder}; const DNS_HEADER_SIZE: usize = 12; +pub const DNS_OFFSET_FLAGS: usize = 2; const DNS_MAX_HOSTNAME_SIZE: usize = 256; const DNS_MAX_PACKET_SIZE: usize = 4096; const DNS_OFFSET_QUESTION: usize = DNS_HEADER_SIZE; + +const DNS_FLAGS_TC: u16 = 1u16 << 9; + const DNS_TYPE_OPT: u16 = 41; const DNS_PTYPE_PADDING: u16 = 12; @@ -51,6 +55,11 @@ pub fn is_recoverable_error(packet: &[u8]) -> bool { rcode == DNS_RCODE_SERVFAIL || rcode == DNS_RCODE_REFUSED } +#[inline] +pub fn is_truncated(packet: &[u8]) -> bool { + BigEndian::read_u16(&packet[DNS_OFFSET_FLAGS..]) & DNS_FLAGS_TC == DNS_FLAGS_TC +} + fn skip_name(packet: &[u8], offset: usize) -> Result { let packet_len = packet.len(); ensure!(offset < packet_len - 1, "Short packet"); diff --git a/src/libdoh/src/errors.rs b/src/libdoh/src/errors.rs index 2d0baf9..82bae1a 100644 --- a/src/libdoh/src/errors.rs +++ b/src/libdoh/src/errors.rs @@ -1,7 +1,6 @@ use hyper::StatusCode; use std::io; -#[allow(dead_code)] #[derive(Debug)] pub enum DoHError { Incomplete, @@ -13,6 +12,7 @@ pub enum DoHError { Hyper(hyper::Error), Io(io::Error), ODoHConfigError(anyhow::Error), + TooManyTcpSessions, } impl std::error::Error for DoHError {} @@ -29,6 +29,7 @@ impl std::fmt::Display for DoHError { DoHError::Hyper(e) => write!(fmt, "HTTP error: {}", e), DoHError::Io(e) => write!(fmt, "IO error: {}", e), DoHError::ODoHConfigError(e) => write!(fmt, "ODoH config error: {}", e), + DoHError::TooManyTcpSessions => write!(fmt, "Too many TCP sessions"), } } } @@ -45,6 +46,7 @@ impl From for StatusCode { DoHError::Hyper(_) => StatusCode::SERVICE_UNAVAILABLE, DoHError::Io(_) => StatusCode::INTERNAL_SERVER_ERROR, DoHError::ODoHConfigError(_) => StatusCode::INTERNAL_SERVER_ERROR, + DoHError::TooManyTcpSessions => StatusCode::SERVICE_UNAVAILABLE, } } } diff --git a/src/libdoh/src/globals.rs b/src/libdoh/src/globals.rs index df32cbd..85b9808 100644 --- a/src/libdoh/src/globals.rs +++ b/src/libdoh/src/globals.rs @@ -40,6 +40,10 @@ pub struct Globals { pub struct ClientsCount(Arc); impl ClientsCount { + pub fn current(&self) -> usize { + self.0.load(Ordering::Relaxed) + } + pub fn increment(&self) -> usize { self.0.fetch_add(1, Ordering::Relaxed) } diff --git a/src/libdoh/src/lib.rs b/src/libdoh/src/lib.rs index 9269463..e87bc1b 100644 --- a/src/libdoh/src/lib.rs +++ b/src/libdoh/src/lib.rs @@ -10,16 +10,18 @@ use crate::constants::*; pub use crate::errors::*; pub use crate::globals::*; +use byteorder::{BigEndian, ByteOrder}; use futures::prelude::*; use futures::task::{Context, Poll}; use hyper::http; use hyper::server::conn::Http; use hyper::{Body, HeaderMap, Method, Request, Response, StatusCode}; +use std::net::SocketAddr; use std::pin::Pin; use std::sync::Arc; use std::time::Duration; -use tokio::io::{AsyncRead, AsyncWrite}; -use tokio::net::{TcpListener, UdpSocket}; +use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; +use tokio::net::{TcpListener, TcpSocket, UdpSocket}; use tokio::runtime; pub mod reexports { @@ -340,22 +342,65 @@ impl DoH { } let _ = dns::set_edns_max_payload_size(&mut query, MAX_DNS_RESPONSE_LEN as _); let globals = &self.globals; - let socket = UdpSocket::bind(&globals.local_bind_address) - .await - .map_err(DoHError::Io)?; - let expected_server_address = globals.server_address; - let (min_ttl, max_ttl, err_ttl) = (globals.min_ttl, globals.max_ttl, globals.err_ttl); - socket - .send_to(&query, &globals.server_address) - .map_err(DoHError::Io) - .await?; let mut packet = vec![0; MAX_DNS_RESPONSE_LEN]; - let (len, response_server_address) = - socket.recv_from(&mut packet).map_err(DoHError::Io).await?; - if len < MIN_DNS_PACKET_LEN || expected_server_address != response_server_address { - return Err(DoHError::UpstreamIssue); + let (min_ttl, max_ttl, err_ttl) = (globals.min_ttl, globals.max_ttl, globals.err_ttl); + + // UDP + { + let socket = UdpSocket::bind(&globals.local_bind_address) + .await + .map_err(DoHError::Io)?; + let expected_server_address = globals.server_address; + socket + .send_to(&query, &globals.server_address) + .map_err(DoHError::Io) + .await?; + let (len, response_server_address) = + socket.recv_from(&mut packet).map_err(DoHError::Io).await?; + if len < MIN_DNS_PACKET_LEN || expected_server_address != response_server_address { + return Err(DoHError::UpstreamIssue); + } + packet.truncate(len); } - packet.truncate(len); + + // TCP + if dns::is_truncated(&packet) { + let clients_count = self.globals.clients_count.current(); + if self.globals.max_clients >= UDP_TCP_RATIO + && clients_count >= self.globals.max_clients / UDP_TCP_RATIO + { + return Err(DoHError::TooManyTcpSessions); + } + let socket = match globals.server_address { + SocketAddr::V4(_) => TcpSocket::new_v4(), + SocketAddr::V6(_) => TcpSocket::new_v6(), + } + .map_err(DoHError::Io)?; + let mut ext_socket = socket + .connect(globals.server_address) + .await + .map_err(DoHError::Io)?; + ext_socket.set_nodelay(true).map_err(DoHError::Io)?; + let mut binlen = [0u8, 0]; + BigEndian::write_u16(&mut binlen, query.len() as u16); + ext_socket.write_all(&binlen).await.map_err(DoHError::Io)?; + ext_socket.write_all(&query).await.map_err(DoHError::Io)?; + ext_socket.flush().await.map_err(DoHError::Io)?; + ext_socket + .read_exact(&mut binlen) + .await + .map_err(DoHError::Io)?; + let packet_len = BigEndian::read_u16(&binlen) as usize; + if packet_len < MIN_DNS_PACKET_LEN || packet_len > MAX_DNS_RESPONSE_LEN { + return Err(DoHError::UpstreamIssue); + } + packet = vec![0u8; packet_len]; + ext_socket + .read_exact(&mut packet) + .await + .map_err(DoHError::Io)?; + } + let ttl = if dns::is_recoverable_error(&packet) { err_ttl } else { From f4a1dee971e24e929a9d89be022376e1493924eb Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sat, 12 Jun 2021 10:28:27 +0200 Subject: [PATCH 050/150] Update odoh-rs Same thing, just more complicated to use --- src/libdoh/Cargo.toml | 11 +++-- src/libdoh/src/odoh.rs | 106 +++++++++++++---------------------------- 2 files changed, 40 insertions(+), 77 deletions(-) diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index 03a4ddc..59cd67b 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -16,16 +16,17 @@ tls = ["tokio-rustls"] [dependencies] anyhow = "1.0.38" -byteorder = "1.4.2" +arc-swap = "1.2.0" base64 = "0.13.0" +byteorder = "1.4.2" +bytes = "1.0.1" futures = "0.3.13" +hpke = "0.6.0" hyper = { version = "0.14.4", default-features = false, features = ["server", "http1", "http2", "stream"] } +odoh-rs = "1.0.0-alpha" +rand = "0.8.3" tokio = { version = "1.6.0", features = ["net", "rt-multi-thread", "parking_lot", "time", "sync"] } tokio-rustls = { version = "0.22.0", features = ["early-data"], optional = true } -odoh-rs = "0.1.11" -rand = "0.7.0" -hpke = "0.5.0" -arc-swap = "1.2.0" [profile.release] codegen-units = 1 diff --git a/src/libdoh/src/odoh.rs b/src/libdoh/src/odoh.rs index 1fc39ec..ff74d08 100644 --- a/src/libdoh/src/odoh.rs +++ b/src/libdoh/src/odoh.rs @@ -1,12 +1,10 @@ use crate::constants::ODOH_KEY_ROTATION_SECS; use crate::errors::DoHError; use arc_swap::ArcSwap; -use hpke::kex::Serializable; -use odoh_rs::key_utils::derive_keypair_from_seed; -use odoh_rs::protocol::{ - create_response_msg, parse_received_query, Deserialize, ObliviousDoHConfig, - ObliviousDoHConfigContents, ObliviousDoHConfigs, ObliviousDoHKeyPair, ObliviousDoHMessage, - ObliviousDoHMessageType, ObliviousDoHQueryBody, Serialize, RESPONSE_NONCE_SIZE, + +use odoh_rs::{ + Deserialize, ObliviousDoHConfig, ObliviousDoHConfigs, ObliviousDoHKeyPair, ObliviousDoHMessage, + ObliviousDoHMessagePlaintext, OdohSecret, ResponseNonce, Serialize, }; use rand::Rng; use std::fmt; @@ -14,12 +12,6 @@ use std::sync::Arc; use std::time::Duration; use tokio::runtime; -// https://cfrg.github.io/draft-irtf-cfrg-hpke/draft-irtf-cfrg-hpke.html#name-algorithm-identifiers -const DEFAULT_HPKE_SEED_SIZE: usize = 32; -const DEFAULT_HPKE_KEM: u16 = 0x0020; // DHKEM(X25519, HKDF-SHA256) -const DEFAULT_HPKE_KDF: u16 = 0x0001; // KDF(SHA-256) -const DEFAULT_HPKE_AEAD: u16 = 0x0001; // AEAD(AES-GCM-128) - #[derive(Clone)] pub struct ODoHPublicKey { key: ObliviousDoHKeyPair, @@ -34,45 +26,18 @@ impl fmt::Debug for ODoHPublicKey { #[derive(Clone, Debug)] pub struct ODoHQueryContext { - query: ObliviousDoHQueryBody, - secret: Vec, -} - -fn generate_key_pair() -> ObliviousDoHKeyPair { - let ikm = rand::thread_rng().gen::<[u8; DEFAULT_HPKE_SEED_SIZE]>(); - let (secret_key, public_key) = derive_keypair_from_seed(&ikm); - let public_key_bytes = public_key.to_bytes().to_vec(); - let odoh_public_key = ObliviousDoHConfigContents { - kem_id: DEFAULT_HPKE_KEM, - kdf_id: DEFAULT_HPKE_KDF, - aead_id: DEFAULT_HPKE_AEAD, - public_key: public_key_bytes, - }; - ObliviousDoHKeyPair { - private_key: secret_key, - public_key: odoh_public_key, - } + query: ObliviousDoHMessagePlaintext, + secret: OdohSecret, } impl ODoHPublicKey { pub fn new() -> Result { - let key_pair = generate_key_pair(); - let config = ObliviousDoHConfig::new( - &key_pair - .public_key - .clone() - .to_bytes() - .map_err(|e| DoHError::ODoHConfigError(e))?, - ) - .map_err(|e| DoHError::ODoHConfigError(e))?; - - let serialized_configs = ObliviousDoHConfigs { - configs: vec![config.clone()], - } - .to_bytes() - .map_err(|e| DoHError::ODoHConfigError(e))? - .to_vec(); - + let key_pair = ObliviousDoHKeyPair::new(&mut rand::thread_rng()); + let config = ObliviousDoHConfig::from(key_pair.public().clone()); + let mut serialized_configs = Vec::new(); + ObliviousDoHConfigs::from(vec![config.clone()]) + .serialize(&mut serialized_configs) + .map_err(|e| DoHError::ODoHConfigError(e.into()))?; Ok(ODoHPublicKey { key: key_pair, serialized_configs: serialized_configs, @@ -87,26 +52,17 @@ impl ODoHPublicKey { self, encrypted_query: Vec, ) -> Result<(Vec, ODoHQueryContext), DoHError> { - let odoh_query = match ObliviousDoHMessage::from_bytes(&encrypted_query) { - Ok(q) => { - if q.msg_type != ObliviousDoHMessageType::Query { - return Err(DoHError::InvalidData); - } - q - } - Err(_) => return Err(DoHError::InvalidData), - }; - - match self.key.public_key.identifier() { + let odoh_query = ObliviousDoHMessage::deserialize(&mut bytes::Bytes::from(encrypted_query)) + .map_err(|_| DoHError::InvalidData)?; + match self.key.public().identifier() { Ok(key_id) => { - if !key_id.eq(&odoh_query.key_id) { + if !key_id.eq(&odoh_query.key_id()) { return Err(DoHError::StaleKey); } } Err(_) => return Err(DoHError::InvalidData), }; - - let (query, server_secret) = match parse_received_query(&self.key, &encrypted_query).await { + let (query, server_secret) = match odoh_rs::decrypt_query(&odoh_query, &self.key) { Ok((pq, ss)) => (pq, ss), Err(_) => return Err(DoHError::InvalidData), }; @@ -114,22 +70,28 @@ impl ODoHPublicKey { query: query.clone(), secret: server_secret, }; - Ok((query.dns_msg.clone(), context)) + let mut query_bytes = Vec::new(); + query + .serialize(&mut query_bytes) + .map_err(|_| DoHError::InvalidData)?; + Ok((query_bytes, context)) } } impl ODoHQueryContext { pub async fn encrypt_response(self, response_body: Vec) -> Result, DoHError> { - let response_nonce = rand::thread_rng().gen::<[u8; RESPONSE_NONCE_SIZE]>(); - create_response_msg( - &self.secret, - &response_body, - None, - Some(response_nonce.to_vec()), - &self.query, - ) - .await - .map_err(|_| DoHError::InvalidData) + let response_nonce = rand::thread_rng().gen::(); + let response_body_ = + ObliviousDoHMessagePlaintext::deserialize(&mut bytes::Bytes::from(response_body)) + .map_err(|_| DoHError::InvalidData)?; + let encrypted_response = + odoh_rs::encrypt_response(&self.query, &response_body_, self.secret, response_nonce) + .map_err(|_| DoHError::InvalidData)?; + let mut encrypted_response_bytes = Vec::new(); + encrypted_response + .serialize(&mut encrypted_response_bytes) + .map_err(|_| DoHError::InvalidData)?; + Ok(encrypted_response_bytes) } } From b637bb1ec99a564059d2098a6a24d19418449aad Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sat, 12 Jun 2021 10:33:51 +0200 Subject: [PATCH 051/150] Downgrade hpke --- Cargo.toml | 2 +- src/libdoh/Cargo.toml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f6f0d88..10ac75f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "doh-proxy" -version = "0.4.0" +version = "0.4.1" authors = ["Frank Denis "] description = "A DNS-over-HTTPS (DoH) and ODoH (Oblivious DoH) proxy" keywords = ["dns","https","doh","odoh","proxy"] diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index 59cd67b..2da3025 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libdoh" -version = "0.4.0" +version = "0.4.1" authors = ["Frank Denis "] description = "DoH library for the rust-doh app" keywords = ["dns","https","doh","odoh","proxy"] @@ -21,7 +21,7 @@ base64 = "0.13.0" byteorder = "1.4.2" bytes = "1.0.1" futures = "0.3.13" -hpke = "0.6.0" +hpke = "0.5.1" hyper = { version = "0.14.4", default-features = false, features = ["server", "http1", "http2", "stream"] } odoh-rs = "1.0.0-alpha" rand = "0.8.3" From a19c523cf20d24b9120b666a244175dafecfe250 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sat, 12 Jun 2021 10:46:38 +0200 Subject: [PATCH 052/150] Nits --- src/libdoh/src/lib.rs | 26 +++++++++++--------------- src/libdoh/src/odoh.rs | 42 +++++++++++++++++++++++------------------- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/libdoh/src/lib.rs b/src/libdoh/src/lib.rs index e87bc1b..9aff743 100644 --- a/src/libdoh/src/lib.rs +++ b/src/libdoh/src/lib.rs @@ -119,7 +119,7 @@ impl DoH { match Self::parse_content_type(&req) { Ok(DoHType::Standard) => self.serve_doh_get(req).await, Ok(DoHType::Oblivious) => self.serve_odoh_get(req).await, - Err(response) => return Ok(response), + Err(response) => Ok(response), } } @@ -127,7 +127,7 @@ impl DoH { match Self::parse_content_type(&req) { Ok(DoHType::Standard) => self.serve_doh_post(req).await, Ok(DoHType::Oblivious) => self.serve_odoh_post(req).await, - Err(response) => return Ok(response), + Err(response) => Ok(response), } } @@ -187,12 +187,8 @@ impl DoH { } async fn serve_odoh(&self, encrypted_query: Vec) -> Result, http::Error> { - let odoh_public_key = (*self.globals.odoh_rotator).clone().current_key(); - let (query, context) = match (*odoh_public_key) - .clone() - .decrypt_query(encrypted_query) - .await - { + let odoh_public_key = (*self.globals.odoh_rotator).clone().current_public_key(); + let (query, context) = match (*odoh_public_key).clone().decrypt_query(encrypted_query) { Ok((q, context)) => (q.to_vec(), context), Err(e) => return http_error(StatusCode::from(e)), }; @@ -202,7 +198,7 @@ impl DoH { Err(e) => return http_error(StatusCode::from(e)), }; - let encrypted_resp = match context.encrypt_response(resp.packet).await { + let encrypted_resp = match context.encrypt_response(resp.packet) { Ok(resp) => self.build_response(resp, 0u32, DoHType::Oblivious.as_str()), Err(e) => return http_error(StatusCode::from(e)), }; @@ -233,8 +229,8 @@ impl DoH { } async fn serve_odoh_configs(&self) -> Result, http::Error> { - let odoh_public_key = (*self.globals.odoh_rotator).clone().current_key(); - let configs = (*odoh_public_key).clone().config(); + let odoh_public_key = (*self.globals.odoh_rotator).clone().current_public_key(); + let configs = (*odoh_public_key).clone().into_config(); match self.build_response( configs, ODOH_KEY_ROTATION_SECS, @@ -254,9 +250,9 @@ impl DoH { None => return None, Some(accept) => accept, }; - for part in accept.to_str().unwrap_or("").split(",").map(|s| s.trim()) { + for part in accept.to_str().unwrap_or("").split(',').map(|s| s.trim()) { if let Some(found) = part - .split(";") + .split(';') .next() .map(|s| s.trim().to_ascii_lowercase()) { @@ -311,7 +307,7 @@ impl DoH { .status(StatusCode::UNSUPPORTED_MEDIA_TYPE) .body(Body::empty()) .unwrap(); - return Err(response); + Err(response) } } } @@ -391,7 +387,7 @@ impl DoH { .await .map_err(DoHError::Io)?; let packet_len = BigEndian::read_u16(&binlen) as usize; - if packet_len < MIN_DNS_PACKET_LEN || packet_len > MAX_DNS_RESPONSE_LEN { + if !(MIN_DNS_PACKET_LEN..=MAX_DNS_RESPONSE_LEN).contains(&packet_len) { return Err(DoHError::UpstreamIssue); } packet = vec![0u8; packet_len]; diff --git a/src/libdoh/src/odoh.rs b/src/libdoh/src/odoh.rs index ff74d08..04907b2 100644 --- a/src/libdoh/src/odoh.rs +++ b/src/libdoh/src/odoh.rs @@ -14,7 +14,7 @@ use tokio::runtime; #[derive(Clone)] pub struct ODoHPublicKey { - key: ObliviousDoHKeyPair, + key_pair: ObliviousDoHKeyPair, serialized_configs: Vec, } @@ -27,7 +27,7 @@ impl fmt::Debug for ODoHPublicKey { #[derive(Clone, Debug)] pub struct ODoHQueryContext { query: ObliviousDoHMessagePlaintext, - secret: OdohSecret, + server_secret: OdohSecret, } impl ODoHPublicKey { @@ -35,26 +35,26 @@ impl ODoHPublicKey { let key_pair = ObliviousDoHKeyPair::new(&mut rand::thread_rng()); let config = ObliviousDoHConfig::from(key_pair.public().clone()); let mut serialized_configs = Vec::new(); - ObliviousDoHConfigs::from(vec![config.clone()]) + ObliviousDoHConfigs::from(vec![config]) .serialize(&mut serialized_configs) .map_err(|e| DoHError::ODoHConfigError(e.into()))?; Ok(ODoHPublicKey { - key: key_pair, - serialized_configs: serialized_configs, + key_pair, + serialized_configs, }) } - pub fn config(self) -> Vec { + pub fn into_config(self) -> Vec { self.serialized_configs } - pub async fn decrypt_query( + pub fn decrypt_query( self, encrypted_query: Vec, ) -> Result<(Vec, ODoHQueryContext), DoHError> { let odoh_query = ObliviousDoHMessage::deserialize(&mut bytes::Bytes::from(encrypted_query)) .map_err(|_| DoHError::InvalidData)?; - match self.key.public().identifier() { + match self.key_pair.public().identifier() { Ok(key_id) => { if !key_id.eq(&odoh_query.key_id()) { return Err(DoHError::StaleKey); @@ -62,13 +62,13 @@ impl ODoHPublicKey { } Err(_) => return Err(DoHError::InvalidData), }; - let (query, server_secret) = match odoh_rs::decrypt_query(&odoh_query, &self.key) { + let (query, server_secret) = match odoh_rs::decrypt_query(&odoh_query, &self.key_pair) { Ok((pq, ss)) => (pq, ss), Err(_) => return Err(DoHError::InvalidData), }; let context = ODoHQueryContext { query: query.clone(), - secret: server_secret, + server_secret, }; let mut query_bytes = Vec::new(); query @@ -79,14 +79,18 @@ impl ODoHPublicKey { } impl ODoHQueryContext { - pub async fn encrypt_response(self, response_body: Vec) -> Result, DoHError> { + pub fn encrypt_response(self, response_body: Vec) -> Result, DoHError> { let response_nonce = rand::thread_rng().gen::(); let response_body_ = ObliviousDoHMessagePlaintext::deserialize(&mut bytes::Bytes::from(response_body)) .map_err(|_| DoHError::InvalidData)?; - let encrypted_response = - odoh_rs::encrypt_response(&self.query, &response_body_, self.secret, response_nonce) - .map_err(|_| DoHError::InvalidData)?; + let encrypted_response = odoh_rs::encrypt_response( + &self.query, + &response_body_, + self.server_secret, + response_nonce, + ) + .map_err(|_| DoHError::InvalidData)?; let mut encrypted_response_bytes = Vec::new(); encrypted_response .serialize(&mut encrypted_response_bytes) @@ -102,14 +106,14 @@ pub struct ODoHRotator { impl ODoHRotator { pub fn new(runtime_handle: runtime::Handle) -> Result { - let odoh_key = match ODoHPublicKey::new() { + let public_key = match ODoHPublicKey::new() { Ok(key) => Arc::new(ArcSwap::from_pointee(key)), Err(e) => panic!("ODoH key rotation error: {}", e), }; - let current_key = Arc::clone(&odoh_key); + let current_key = Arc::clone(&public_key); - runtime_handle.clone().spawn(async move { + runtime_handle.spawn(async move { loop { tokio::time::sleep(Duration::from_secs(ODOH_KEY_ROTATION_SECS.into())).await; match ODoHPublicKey::new() { @@ -122,11 +126,11 @@ impl ODoHRotator { }); Ok(ODoHRotator { - key: Arc::clone(&odoh_key), + key: Arc::clone(&public_key), }) } - pub fn current_key(&self) -> Arc { + pub fn current_public_key(&self) -> Arc { let key = Arc::clone(&self.key); Arc::clone(&key.load()) } From a988eb42a271e372450479539ba4c058ebedc91c Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sat, 12 Jun 2021 13:32:38 +0200 Subject: [PATCH 053/150] Properly use the odoh-rs API --- src/libdoh/src/lib.rs | 2 -- src/libdoh/src/odoh.rs | 10 ++-------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/libdoh/src/lib.rs b/src/libdoh/src/lib.rs index 9aff743..4bed29d 100644 --- a/src/libdoh/src/lib.rs +++ b/src/libdoh/src/lib.rs @@ -192,12 +192,10 @@ impl DoH { Ok((q, context)) => (q.to_vec(), context), Err(e) => return http_error(StatusCode::from(e)), }; - let resp = match self.proxy(query).await { Ok(resp) => resp, Err(e) => return http_error(StatusCode::from(e)), }; - let encrypted_resp = match context.encrypt_response(resp.packet) { Ok(resp) => self.build_response(resp, 0u32, DoHType::Oblivious.as_str()), Err(e) => return http_error(StatusCode::from(e)), diff --git a/src/libdoh/src/odoh.rs b/src/libdoh/src/odoh.rs index 04907b2..29c16cd 100644 --- a/src/libdoh/src/odoh.rs +++ b/src/libdoh/src/odoh.rs @@ -70,20 +70,14 @@ impl ODoHPublicKey { query: query.clone(), server_secret, }; - let mut query_bytes = Vec::new(); - query - .serialize(&mut query_bytes) - .map_err(|_| DoHError::InvalidData)?; - Ok((query_bytes, context)) + Ok((query.into_msg().to_vec(), context)) } } impl ODoHQueryContext { pub fn encrypt_response(self, response_body: Vec) -> Result, DoHError> { let response_nonce = rand::thread_rng().gen::(); - let response_body_ = - ObliviousDoHMessagePlaintext::deserialize(&mut bytes::Bytes::from(response_body)) - .map_err(|_| DoHError::InvalidData)?; + let response_body_ = ObliviousDoHMessagePlaintext::new(response_body, 0); let encrypted_response = odoh_rs::encrypt_response( &self.query, &response_body_, From 4f1e0f2abe78db0c89e3882e996ac4aa2691b4cc Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sat, 12 Jun 2021 14:00:24 +0200 Subject: [PATCH 054/150] Print ODoH DNS stamps --- Cargo.toml | 2 +- src/config.rs | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 10ac75f..1ba7505 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ tls = ["libdoh/tls"] [dependencies] libdoh = { path = "src/libdoh", version = "0.4.0", default-features = false } clap = "2.33.3" -dnsstamps = "0.1.5" +dnsstamps = "0.1.7" jemallocator = "0.3.2" [package.metadata.deb] diff --git a/src/config.rs b/src/config.rs index 5bab9bf..5d317d9 100644 --- a/src/config.rs +++ b/src/config.rs @@ -207,10 +207,21 @@ pub fn parse_opts(globals: &mut Globals) { builder = builder.with_address(public_address.to_string()); } println!( - "Test DNS stamp to reach [{}]: [{}]", + "Test DNS stamp to reach [{}] over DoH: [{}]\n", hostname, builder.serialize().unwrap() ); - println!("Check out https://dnscrypt.info/stamps/ to compute the actual stamp.\n") + + let builder = + dnsstamps::ODoHTargetBuilder::new(hostname.to_string(), globals.path.to_string()); + println!( + "Test DNS stamp to reach [{}] over Oblivious DoH: [{}]\n", + hostname, + builder.serialize().unwrap() + ); + + println!("Check out https://dnscrypt.info/stamps/ to compute the actual stamps.\n") + } else { + println!("Please provide a fully qualified hostname (-H command-line option) to get test DNS stamps for your server.\n"); } } From f9d2a0fc9421fb8dfd6514bc1d73250e0498b238 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sat, 12 Jun 2021 14:01:07 +0200 Subject: [PATCH 055/150] Bump --- Cargo.toml | 4 ++-- src/libdoh/Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1ba7505..f1cf962 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "doh-proxy" -version = "0.4.1" +version = "0.9.0" authors = ["Frank Denis "] description = "A DNS-over-HTTPS (DoH) and ODoH (Oblivious DoH) proxy" keywords = ["dns","https","doh","odoh","proxy"] @@ -16,7 +16,7 @@ default = ["tls"] tls = ["libdoh/tls"] [dependencies] -libdoh = { path = "src/libdoh", version = "0.4.0", default-features = false } +libdoh = { path = "src/libdoh", version = "0.9.0", default-features = false } clap = "2.33.3" dnsstamps = "0.1.7" jemallocator = "0.3.2" diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index 2da3025..d3acd21 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "libdoh" -version = "0.4.1" +version = "0.9.0" authors = ["Frank Denis "] -description = "DoH library for the rust-doh app" +description = "DoH and Oblivious DoH library for the rust-doh app" keywords = ["dns","https","doh","odoh","proxy"] license = "MIT" homepage = "https://github.com/jedisct1/rust-doh" From 7657d5a2b2614243dc999ff1a4743e9bb45a9659 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Fri, 13 Aug 2021 22:21:28 +0200 Subject: [PATCH 056/150] Require tokio 1.10 --- src/libdoh/Cargo.toml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index d3acd21..6f8c1a6 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -15,17 +15,17 @@ default = ["tls"] tls = ["tokio-rustls"] [dependencies] -anyhow = "1.0.38" -arc-swap = "1.2.0" +anyhow = "1.0.42" +arc-swap = "1.3.0" base64 = "0.13.0" -byteorder = "1.4.2" +byteorder = "1.4.3" bytes = "1.0.1" -futures = "0.3.13" +futures = "0.3.16" hpke = "0.5.1" -hyper = { version = "0.14.4", default-features = false, features = ["server", "http1", "http2", "stream"] } -odoh-rs = "1.0.0-alpha" -rand = "0.8.3" -tokio = { version = "1.6.0", features = ["net", "rt-multi-thread", "parking_lot", "time", "sync"] } +hyper = { version = "0.14.11", default-features = false, features = ["server", "http1", "http2", "stream"] } +odoh-rs = "1.0.0-alpha.1" +rand = "0.8.4" +tokio = { version = "1.10.0", features = ["net", "rt-multi-thread", "parking_lot", "time", "sync"] } tokio-rustls = { version = "0.22.0", features = ["early-data"], optional = true } [profile.release] From 29180617867f914593781e0d9b8d03f5ef6728ad Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Fri, 20 Aug 2021 01:12:14 +0200 Subject: [PATCH 057/150] license -> license-file --- Cargo.toml | 2 +- src/libdoh/Cargo.toml | 2 +- src/libdoh/LICENSE | 21 +++++++++++++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 src/libdoh/LICENSE diff --git a/Cargo.toml b/Cargo.toml index f1cf962..32356f1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "0.9.0" authors = ["Frank Denis "] description = "A DNS-over-HTTPS (DoH) and ODoH (Oblivious DoH) proxy" keywords = ["dns","https","doh","odoh","proxy"] -license = "MIT" +license-file = "LICENSE" homepage = "https://github.com/jedisct1/rust-doh" repository = "https://github.com/jedisct1/rust-doh" categories = ["asynchronous", "network-programming", "command-line-utilities"] diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index 6f8c1a6..bbc9c51 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -4,7 +4,7 @@ version = "0.9.0" authors = ["Frank Denis "] description = "DoH and Oblivious DoH library for the rust-doh app" keywords = ["dns","https","doh","odoh","proxy"] -license = "MIT" +license-file = "LICENSE" homepage = "https://github.com/jedisct1/rust-doh" repository = "https://github.com/jedisct1/rust-doh" categories = ["asynchronous", "network-programming","command-line-utilities"] diff --git a/src/libdoh/LICENSE b/src/libdoh/LICENSE new file mode 100644 index 0000000..010ad6e --- /dev/null +++ b/src/libdoh/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018-2021 Frank Denis + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From a727c4b9fa7c1b165f94726ddff4af8ddddb31c8 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Fri, 20 Aug 2021 01:13:44 +0200 Subject: [PATCH 058/150] Keep the LICENSE file, in addition to its name in Cargo --- Cargo.toml | 2 +- src/libdoh/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 32356f1..f1cf962 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "0.9.0" authors = ["Frank Denis "] description = "A DNS-over-HTTPS (DoH) and ODoH (Oblivious DoH) proxy" keywords = ["dns","https","doh","odoh","proxy"] -license-file = "LICENSE" +license = "MIT" homepage = "https://github.com/jedisct1/rust-doh" repository = "https://github.com/jedisct1/rust-doh" categories = ["asynchronous", "network-programming", "command-line-utilities"] diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index bbc9c51..6f8c1a6 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -4,7 +4,7 @@ version = "0.9.0" authors = ["Frank Denis "] description = "DoH and Oblivious DoH library for the rust-doh app" keywords = ["dns","https","doh","odoh","proxy"] -license-file = "LICENSE" +license = "MIT" homepage = "https://github.com/jedisct1/rust-doh" repository = "https://github.com/jedisct1/rust-doh" categories = ["asynchronous", "network-programming","command-line-utilities"] From 63eac2a622d23c34e880851445f660bf6d98b917 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Mon, 6 Sep 2021 19:24:19 +0200 Subject: [PATCH 059/150] Add the ability to specify an alternative port number --- Cargo.toml | 2 +- README.md | 3 ++- src/config.rs | 17 ++++++++++++++++- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f1cf962..9f1deac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ tls = ["libdoh/tls"] [dependencies] libdoh = { path = "src/libdoh", version = "0.9.0", default-features = false } clap = "2.33.3" -dnsstamps = "0.1.7" +dnsstamps = "0.1.9" jemallocator = "0.3.2" [package.metadata.deb] diff --git a/README.md b/README.md index 14f45d2..db3e19d 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ USAGE: doh-proxy [FLAGS] [OPTIONS] FLAGS: - -O, --allow-odoh-post Allow POST queries over ODoH even with they have been disabed for DoH + -O, --allow-odoh-post Allow POST queries over ODoH even if they have been disabed for DoH -K, --disable-keepalive Disable keepalive -P, --disable-post Disable POST queries -h, --help Prints help information @@ -50,6 +50,7 @@ OPTIONS: -T, --min-ttl Minimum TTL, in seconds [default: 10] -p, --path URI path [default: /dns-query] -g, --public-address External IP address DoH clients will connect to + -j, --public-port External port DoH clients will connect to, if not 443 -u, --server-address Address to connect to [default: 9.9.9.9:53] -t, --timeout Timeout, in seconds [default: 10] -I, --tls-cert-key-path diff --git a/src/config.rs b/src/config.rs index 5d317d9..a65bd19 100644 --- a/src/config.rs +++ b/src/config.rs @@ -35,6 +35,13 @@ pub fn parse_opts(globals: &mut Globals) { .takes_value(true) .help("External IP address DoH clients will connect to"), ) + .arg( + Arg::with_name("public_port") + .short("j") + .long("public-port") + .takes_value(true) + .help("External port DoH clients will connect to, if not 443"), + ) .arg( Arg::with_name("listen_address") .short("l") @@ -206,14 +213,22 @@ pub fn parse_opts(globals: &mut Globals) { if let Some(public_address) = matches.value_of("public_address") { builder = builder.with_address(public_address.to_string()); } + if let Some(public_port) = matches.value_of("public_port") { + let public_port = public_port.parse().expect("Invalid public port"); + builder = builder.with_port(public_port); + } println!( "Test DNS stamp to reach [{}] over DoH: [{}]\n", hostname, builder.serialize().unwrap() ); - let builder = + let mut builder = dnsstamps::ODoHTargetBuilder::new(hostname.to_string(), globals.path.to_string()); + if let Some(public_port) = matches.value_of("public_port") { + let public_port = public_port.parse().expect("Invalid public port"); + builder = builder.with_port(public_port); + } println!( "Test DNS stamp to reach [{}] over Oblivious DoH: [{}]\n", hostname, From b77f10cd9d715bfd67f0e6012cbbe503fc1d8c33 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Mon, 6 Sep 2021 19:28:30 +0200 Subject: [PATCH 060/150] Bump --- Cargo.toml | 2 +- src/libdoh/Cargo.toml | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9f1deac..90454a8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "doh-proxy" -version = "0.9.0" +version = "0.9.1" authors = ["Frank Denis "] description = "A DNS-over-HTTPS (DoH) and ODoH (Oblivious DoH) proxy" keywords = ["dns","https","doh","odoh","proxy"] diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index 6f8c1a6..3233783 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libdoh" -version = "0.9.0" +version = "0.9.1" authors = ["Frank Denis "] description = "DoH and Oblivious DoH library for the rust-doh app" keywords = ["dns","https","doh","odoh","proxy"] @@ -15,17 +15,17 @@ default = ["tls"] tls = ["tokio-rustls"] [dependencies] -anyhow = "1.0.42" -arc-swap = "1.3.0" +anyhow = "1.0.43" +arc-swap = "1.3.2" base64 = "0.13.0" byteorder = "1.4.3" -bytes = "1.0.1" -futures = "0.3.16" +bytes = "1.1.0" +futures = "0.3.17" hpke = "0.5.1" -hyper = { version = "0.14.11", default-features = false, features = ["server", "http1", "http2", "stream"] } +hyper = { version = "0.14.12", default-features = false, features = ["server", "http1", "http2", "stream"] } odoh-rs = "1.0.0-alpha.1" rand = "0.8.4" -tokio = { version = "1.10.0", features = ["net", "rt-multi-thread", "parking_lot", "time", "sync"] } +tokio = { version = "1.11.0", features = ["net", "rt-multi-thread", "parking_lot", "time", "sync"] } tokio-rustls = { version = "0.22.0", features = ["early-data"], optional = true } [profile.release] From 5770f9da33719582c9bfc360990ce5093b7268e5 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Tue, 7 Sep 2021 12:08:28 +0200 Subject: [PATCH 061/150] Remove retired Let's Encrypt certificate --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index db3e19d..bb8cd5b 100644 --- a/README.md +++ b/README.md @@ -188,7 +188,6 @@ This [Go code snippet](https://gist.github.com/d6cb41742a1ceb54d48cc286f3d5c5fa) ### Common certificate hashes * Let's Encrypt R3: - * `3286ff65a65faf32085eea1388c3738ba7e37873c906cce3c4a28b4cc2a58988` and * `444ebd67bb83f8807b3921e938ac9178b882bd50aadb11231f044cf5f08df7ce` * Let's Encrypt E1: * `cc1060d39c8329b62b6fbc7d0d6df9309869b981e7e6392d5cd8fa408f4d80e6` From 379a7abc7eaac8f2832cb836fe4665e144188c93 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Tue, 7 Sep 2021 13:22:55 +0200 Subject: [PATCH 062/150] Add CORS header (only for DoH), for web browsers --- src/libdoh/src/lib.rs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/libdoh/src/lib.rs b/src/libdoh/src/lib.rs index 4bed29d..427acec 100644 --- a/src/libdoh/src/lib.rs +++ b/src/libdoh/src/lib.rs @@ -133,7 +133,9 @@ impl DoH { async fn serve_doh_query(&self, query: Vec) -> Result, http::Error> { let resp = match self.proxy(query).await { - Ok(resp) => self.build_response(resp.packet, resp.ttl, DoHType::Standard.as_str()), + Ok(resp) => { + self.build_response(resp.packet, resp.ttl, DoHType::Standard.as_str(), true) + } Err(e) => return http_error(StatusCode::from(e)), }; match resp { @@ -197,7 +199,7 @@ impl DoH { Err(e) => return http_error(StatusCode::from(e)), }; let encrypted_resp = match context.encrypt_response(resp.packet) { - Ok(resp) => self.build_response(resp, 0u32, DoHType::Oblivious.as_str()), + Ok(resp) => self.build_response(resp, 0u32, DoHType::Oblivious.as_str(), false), Err(e) => return http_error(StatusCode::from(e)), }; @@ -233,6 +235,7 @@ impl DoH { configs, ODOH_KEY_ROTATION_SECS, "application/octet-stream".to_string(), + true, ) { Ok(resp) => Ok(resp), Err(e) => http_error(StatusCode::from(e)), @@ -414,9 +417,10 @@ impl DoH { packet: Vec, ttl: u32, content_type: String, + cors: bool, ) -> Result, DoHError> { let packet_len = packet.len(); - let response = Response::builder() + let mut response_builder = Response::builder() .header(hyper::header::CONTENT_LENGTH, packet_len) .header(hyper::header::CONTENT_TYPE, content_type.as_str()) .header( @@ -426,9 +430,14 @@ impl DoH { ttl, STALE_IF_ERROR_SECS, STALE_WHILE_REVALIDATE_SECS ) .as_str(), - ) + ); + if cors { + response_builder = + response_builder.header(hyper::header::ACCESS_CONTROL_ALLOW_ORIGIN, "*"); + } + let response = response_builder .body(Body::from(packet)) - .unwrap(); + .map_err(|_| DoHError::InvalidData)?; Ok(response) } From e6fe51647d799965c7453e2035c8b902445ade88 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Tue, 7 Sep 2021 13:25:16 +0200 Subject: [PATCH 063/150] Bump --- Cargo.toml | 2 +- src/libdoh/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 90454a8..a52e33c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "doh-proxy" -version = "0.9.1" +version = "0.9.2" authors = ["Frank Denis "] description = "A DNS-over-HTTPS (DoH) and ODoH (Oblivious DoH) proxy" keywords = ["dns","https","doh","odoh","proxy"] diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index 3233783..d5a63a8 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libdoh" -version = "0.9.1" +version = "0.9.2" authors = ["Frank Denis "] description = "DoH and Oblivious DoH library for the rust-doh app" keywords = ["dns","https","doh","odoh","proxy"] From 46be8b96622cc580c914cdf2010684519c756653 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Fri, 29 Oct 2021 20:13:47 +0200 Subject: [PATCH 064/150] Painful update of rustls --- src/libdoh/Cargo.toml | 11 ++++----- src/libdoh/src/tls.rs | 52 +++++++++++++++++++++++++------------------ 2 files changed, 36 insertions(+), 27 deletions(-) diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index d5a63a8..9318afa 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -15,18 +15,19 @@ default = ["tls"] tls = ["tokio-rustls"] [dependencies] -anyhow = "1.0.43" -arc-swap = "1.3.2" +anyhow = "1.0.44" +arc-swap = "1.4.0" base64 = "0.13.0" byteorder = "1.4.3" bytes = "1.1.0" futures = "0.3.17" hpke = "0.5.1" -hyper = { version = "0.14.12", default-features = false, features = ["server", "http1", "http2", "stream"] } +hyper = { version = "0.14.14", default-features = false, features = ["server", "http1", "http2", "stream"] } odoh-rs = "1.0.0-alpha.1" rand = "0.8.4" -tokio = { version = "1.11.0", features = ["net", "rt-multi-thread", "parking_lot", "time", "sync"] } -tokio-rustls = { version = "0.22.0", features = ["early-data"], optional = true } +tokio = { version = "1.13.0", features = ["net", "rt-multi-thread", "parking_lot", "time", "sync"] } +tokio-rustls = { version = "0.23.0", features = ["early-data"], optional = true } +rustls-pemfile = "0.2.1" [profile.release] codegen-units = 1 diff --git a/src/libdoh/src/tls.rs b/src/libdoh/src/tls.rs index 289b9dd..80dfc2b 100644 --- a/src/libdoh/src/tls.rs +++ b/src/libdoh/src/tls.rs @@ -14,7 +14,7 @@ use tokio::{ sync::mpsc::{self, Receiver}, }; use tokio_rustls::{ - rustls::{internal::pemfile, NoClientAuth, ServerConfig}, + rustls::{Certificate, PrivateKey, ServerConfig}, TlsAcceptor, }; @@ -23,7 +23,7 @@ where P: AsRef, P2: AsRef, { - let certs = { + let certs: Vec<_> = { let certs_path_str = certs_path.as_ref().display().to_string(); let mut reader = BufReader::new(File::open(certs_path).map_err(|e| { io::Error::new( @@ -31,18 +31,21 @@ where format!( "Unable to load the certificates [{}]: {}", certs_path_str, - e.to_string() + e ), ) })?); - pemfile::certs(&mut reader).map_err(|_| { + rustls_pemfile::certs(&mut reader).map_err(|_| { io::Error::new( io::ErrorKind::InvalidInput, "Unable to parse the certificates", ) })? - }; - let certs_keys = { + } + .drain(..) + .map(Certificate) + .collect(); + let certs_keys: Vec<_> = { let certs_keys_path_str = certs_keys_path.as_ref().display().to_string(); let encoded_keys = { let mut encoded_keys = vec![]; @@ -53,7 +56,7 @@ where format!( "Unable to load the certificate keys [{}]: {}", certs_keys_path_str, - e.to_string() + e ), ) })? @@ -61,14 +64,14 @@ where encoded_keys }; let mut reader = Cursor::new(encoded_keys); - let pkcs8_keys = pemfile::pkcs8_private_keys(&mut reader).map_err(|_| { + let pkcs8_keys = rustls_pemfile::pkcs8_private_keys(&mut reader).map_err(|_| { io::Error::new( io::ErrorKind::InvalidInput, "Unable to parse the certificates private keys (PKCS8)", ) })?; reader.set_position(0); - let mut rsa_keys = pemfile::rsa_private_keys(&mut reader).map_err(|_| { + let mut rsa_keys = rustls_pemfile::rsa_private_keys(&mut reader).map_err(|_| { io::Error::new( io::ErrorKind::InvalidInput, "Unable to parse the certificates private keys (RSA)", @@ -82,21 +85,26 @@ where "No private keys found - Make sure that they are in PKCS#8/PEM format", )); } - keys + keys.drain(..).map(PrivateKey).collect() }; - let mut server_config = ServerConfig::new(NoClientAuth::new()); - server_config.set_protocols(&[b"h2".to_vec(), b"http/1.1".to_vec()]); - let has_valid_cert_and_key = certs_keys.into_iter().any(|certs_key| { - server_config - .set_single_cert(certs.clone(), certs_key) - .is_ok() - }); - if !has_valid_cert_and_key { - return Err(io::Error::new( - io::ErrorKind::InvalidInput, - "Invalid private key for the given certificate", - )); + + let mut server_config = None; + for certs_key in certs_keys { + let server_config_builder = ServerConfig::builder() + .with_safe_defaults() + .with_no_client_auth(); + if let Ok(found_config) = server_config_builder.with_single_cert(certs.clone(), certs_key) { + server_config = Some(found_config); + break; + } } + let mut server_config = server_config.ok_or_else(|| { + io::Error::new( + io::ErrorKind::InvalidInput, + "Unable to find a valid certificate and key", + ) + })?; + server_config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()]; Ok(TlsAcceptor::from(Arc::new(server_config))) } From d586c500193abf7554d65a11273f28ed1816138e Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Fri, 29 Oct 2021 20:23:41 +0200 Subject: [PATCH 065/150] Uglify --- src/libdoh/src/tls.rs | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/src/libdoh/src/tls.rs b/src/libdoh/src/tls.rs index 80dfc2b..655d1ab 100644 --- a/src/libdoh/src/tls.rs +++ b/src/libdoh/src/tls.rs @@ -30,8 +30,7 @@ where e.kind(), format!( "Unable to load the certificates [{}]: {}", - certs_path_str, - e + certs_path_str, e ), ) })?); @@ -55,8 +54,7 @@ where e.kind(), format!( "Unable to load the certificate keys [{}]: {}", - certs_keys_path_str, - e + certs_keys_path_str, e ), ) })? @@ -88,22 +86,26 @@ where keys.drain(..).map(PrivateKey).collect() }; - let mut server_config = None; - for certs_key in certs_keys { - let server_config_builder = ServerConfig::builder() - .with_safe_defaults() - .with_no_client_auth(); - if let Ok(found_config) = server_config_builder.with_single_cert(certs.clone(), certs_key) { - server_config = Some(found_config); - break; - } - } - let mut server_config = server_config.ok_or_else(|| { - io::Error::new( - io::ErrorKind::InvalidInput, - "Unable to find a valid certificate and key", - ) - })?; + let mut server_config = certs_keys + .into_iter() + .find_map(|certs_key| { + let server_config_builder = ServerConfig::builder() + .with_safe_defaults() + .with_no_client_auth(); + if let Ok(found_config) = + server_config_builder.with_single_cert(certs.clone(), certs_key) + { + Some(found_config) + } else { + None + } + }) + .ok_or_else(|| { + io::Error::new( + io::ErrorKind::InvalidInput, + "Unable to find a valid certificate and key", + ) + })?; server_config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()]; Ok(TlsAcceptor::from(Arc::new(server_config))) } From c6c9d64681fd8fbc46bdce3deccb836f45eba3dd Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sat, 1 Jan 2022 10:29:15 +0100 Subject: [PATCH 066/150] Update clap to v3 --- Cargo.toml | 16 +++++++++--- src/config.rs | 72 +++++++++++++++++++++++++-------------------------- src/utils.rs | 4 +-- 3 files changed, 50 insertions(+), 42 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a52e33c..5fe9b32 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "doh-proxy" version = "0.9.2" authors = ["Frank Denis "] description = "A DNS-over-HTTPS (DoH) and ODoH (Oblivious DoH) proxy" -keywords = ["dns","https","doh","odoh","proxy"] +keywords = ["dns", "https", "doh", "odoh", "proxy"] license = "MIT" homepage = "https://github.com/jedisct1/rust-doh" repository = "https://github.com/jedisct1/rust-doh" @@ -17,7 +17,7 @@ tls = ["libdoh/tls"] [dependencies] libdoh = { path = "src/libdoh", version = "0.9.0", default-features = false } -clap = "2.33.3" +clap = { version = "3.0.0", features = ["std", "cargo", "wrap_help"] } dnsstamps = "0.1.9" jemallocator = "0.3.2" @@ -25,8 +25,16 @@ jemallocator = "0.3.2" extended-description = """\ A fast and secure DoH (DNS-over-HTTPS) and ODoH server written in Rust.""" assets = [ - ["target/release/doh-proxy", "usr/bin/", "755"], - ["README.md", "usr/share/doc/doh-proxy/README.md", "644"] + [ + "target/release/doh-proxy", + "usr/bin/", + "755", + ], + [ + "README.md", + "usr/share/doc/doh-proxy/README.md", + "644", + ], ] section = "network" depends = "$auto" diff --git a/src/config.rs b/src/config.rs index a65bd19..4722516 100644 --- a/src/config.rs +++ b/src/config.rs @@ -22,29 +22,29 @@ pub fn parse_opts(globals: &mut Globals) { let _ = include_str!("../Cargo.toml"); let options = app_from_crate!() .arg( - Arg::with_name("hostname") - .short("H") + Arg::new("hostname") + .short('H') .long("hostname") .takes_value(true) .help("Host name (not IP address) DoH clients will use to connect"), ) .arg( - Arg::with_name("public_address") - .short("g") + Arg::new("public_address") + .short('g') .long("public-address") .takes_value(true) .help("External IP address DoH clients will connect to"), ) .arg( - Arg::with_name("public_port") - .short("j") + Arg::new("public_port") + .short('j') .long("public-port") .takes_value(true) .help("External port DoH clients will connect to, if not 443"), ) .arg( - Arg::with_name("listen_address") - .short("l") + Arg::new("listen_address") + .short('l') .long("listen-address") .takes_value(true) .default_value(LISTEN_ADDRESS) @@ -52,8 +52,8 @@ pub fn parse_opts(globals: &mut Globals) { .help("Address to listen to"), ) .arg( - Arg::with_name("server_address") - .short("u") + Arg::new("server_address") + .short('u') .long("server-address") .takes_value(true) .default_value(SERVER_ADDRESS) @@ -61,84 +61,84 @@ pub fn parse_opts(globals: &mut Globals) { .help("Address to connect to"), ) .arg( - Arg::with_name("local_bind_address") - .short("b") + Arg::new("local_bind_address") + .short('b') .long("local-bind-address") .takes_value(true) .validator(verify_sock_addr) .help("Address to connect from"), ) .arg( - Arg::with_name("path") - .short("p") + Arg::new("path") + .short('p') .long("path") .takes_value(true) .default_value(PATH) .help("URI path"), ) .arg( - Arg::with_name("max_clients") - .short("c") + Arg::new("max_clients") + .short('c') .long("max-clients") .takes_value(true) .default_value(&max_clients) .help("Maximum number of simultaneous clients"), ) .arg( - Arg::with_name("max_concurrent") - .short("C") + Arg::new("max_concurrent") + .short('C') .long("max-concurrent") .takes_value(true) .default_value(&max_concurrent_streams) .help("Maximum number of concurrent requests per client"), ) .arg( - Arg::with_name("timeout") - .short("t") + Arg::new("timeout") + .short('t') .long("timeout") .takes_value(true) .default_value(&timeout_sec) .help("Timeout, in seconds"), ) .arg( - Arg::with_name("min_ttl") - .short("T") + Arg::new("min_ttl") + .short('T') .long("min-ttl") .takes_value(true) .default_value(&min_ttl) .help("Minimum TTL, in seconds"), ) .arg( - Arg::with_name("max_ttl") - .short("X") + Arg::new("max_ttl") + .short('X') .long("max-ttl") .takes_value(true) .default_value(&max_ttl) .help("Maximum TTL, in seconds"), ) .arg( - Arg::with_name("err_ttl") - .short("E") + Arg::new("err_ttl") + .short('E') .long("err-ttl") .takes_value(true) .default_value(&err_ttl) .help("TTL for errors, in seconds"), ) .arg( - Arg::with_name("disable_keepalive") - .short("K") + Arg::new("disable_keepalive") + .short('K') .long("disable-keepalive") .help("Disable keepalive"), ) .arg( - Arg::with_name("disable_post") - .short("P") + Arg::new("disable_post") + .short('P') .long("disable-post") .help("Disable POST queries"), ) .arg( - Arg::with_name("allow_odoh_post") - .short("O") + Arg::new("allow_odoh_post") + .short('O') .long("allow-odoh-post") .help("Allow POST queries over ODoH even if they have been disabed for DoH"), ); @@ -146,8 +146,8 @@ pub fn parse_opts(globals: &mut Globals) { #[cfg(feature = "tls")] let options = options .arg( - Arg::with_name("tls_cert_path") - .short("i") + Arg::new("tls_cert_path") + .short('i') .long("tls-cert-path") .takes_value(true) .help( @@ -155,8 +155,8 @@ pub fn parse_opts(globals: &mut Globals) { ), ) .arg( - Arg::with_name("tls_cert_key_path") - .short("I") + Arg::new("tls_cert_key_path") + .short('I') .long("tls-cert-key-path") .takes_value(true) .help("Path to the PEM-encoded secret keys (only required for built-in TLS)"), diff --git a/src/utils.rs b/src/utils.rs index 0c2137b..0ec0eff 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -2,7 +2,7 @@ use std::net::{SocketAddr, ToSocketAddrs}; -pub(crate) fn verify_sock_addr(arg_val: String) -> Result<(), String> { +pub(crate) fn verify_sock_addr(arg_val: &str) -> Result<(), String> { match arg_val.parse::() { Ok(_addr) => Ok(()), Err(_) => Err(format!( @@ -12,7 +12,7 @@ pub(crate) fn verify_sock_addr(arg_val: String) -> Result<(), String> { } } -pub(crate) fn verify_remote_server(arg_val: String) -> Result<(), String> { +pub(crate) fn verify_remote_server(arg_val: &str) -> Result<(), String> { match arg_val.to_socket_addrs() { Ok(mut addr_iter) => match addr_iter.next() { Some(_) => Ok(()), From 115938f90f03014542f8a2fd0e814dfa4be9aaa5 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sat, 1 Jan 2022 10:29:33 +0100 Subject: [PATCH 067/150] Y++ --- LICENSE | 2 +- src/libdoh/LICENSE | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index 010ad6e..f0d8aed 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2018-2021 Frank Denis +Copyright (c) 2018-2022 Frank Denis Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/libdoh/LICENSE b/src/libdoh/LICENSE index 010ad6e..f0d8aed 100644 --- a/src/libdoh/LICENSE +++ b/src/libdoh/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2018-2021 Frank Denis +Copyright (c) 2018-2022 Frank Denis Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 16ab626cc2eb05843e4ba669ecac176cd1c4e84a Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Fri, 11 Feb 2022 20:32:46 +0100 Subject: [PATCH 068/150] Update rustls-pemfile --- src/libdoh/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index 9318afa..42a9f02 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -27,7 +27,7 @@ odoh-rs = "1.0.0-alpha.1" rand = "0.8.4" tokio = { version = "1.13.0", features = ["net", "rt-multi-thread", "parking_lot", "time", "sync"] } tokio-rustls = { version = "0.23.0", features = ["early-data"], optional = true } -rustls-pemfile = "0.2.1" +rustls-pemfile = "0.3.0" [profile.release] codegen-units = 1 From 054beb390c5ef97adf5ca80beb7669d4b4f9e91c Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sat, 5 Mar 2022 16:04:01 +0100 Subject: [PATCH 069/150] Update deps --- Cargo.toml | 2 +- src/config.rs | 2 +- src/libdoh/Cargo.toml | 14 +++++++------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5fe9b32..21f8857 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ tls = ["libdoh/tls"] [dependencies] libdoh = { path = "src/libdoh", version = "0.9.0", default-features = false } -clap = { version = "3.0.0", features = ["std", "cargo", "wrap_help"] } +clap = { version = "3.1.5", features = ["std", "cargo", "wrap_help"] } dnsstamps = "0.1.9" jemallocator = "0.3.2" diff --git a/src/config.rs b/src/config.rs index 4722516..7e1861a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -20,7 +20,7 @@ pub fn parse_opts(globals: &mut Globals) { let err_ttl = ERR_TTL.to_string(); let _ = include_str!("../Cargo.toml"); - let options = app_from_crate!() + let options = command!() .arg( Arg::new("hostname") .short('H') diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index 42a9f02..3c1aece 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -15,18 +15,18 @@ default = ["tls"] tls = ["tokio-rustls"] [dependencies] -anyhow = "1.0.44" -arc-swap = "1.4.0" +anyhow = "1.0.55" +arc-swap = "1.5.0" base64 = "0.13.0" byteorder = "1.4.3" bytes = "1.1.0" -futures = "0.3.17" +futures = "0.3.21" hpke = "0.5.1" -hyper = { version = "0.14.14", default-features = false, features = ["server", "http1", "http2", "stream"] } +hyper = { version = "0.14.17", default-features = false, features = ["server", "http1", "http2", "stream"] } odoh-rs = "1.0.0-alpha.1" -rand = "0.8.4" -tokio = { version = "1.13.0", features = ["net", "rt-multi-thread", "parking_lot", "time", "sync"] } -tokio-rustls = { version = "0.23.0", features = ["early-data"], optional = true } +rand = "0.8.5" +tokio = { version = "1.17.0", features = ["net", "rt-multi-thread", "parking_lot", "time", "sync"] } +tokio-rustls = { version = "0.23.2", features = ["early-data"], optional = true } rustls-pemfile = "0.3.0" [profile.release] From 74939bdc6c3f3b7de9cb051758d7ff902ecc6027 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sat, 5 Mar 2022 16:04:30 +0100 Subject: [PATCH 070/150] Bump --- Cargo.toml | 4 ++-- src/libdoh/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 21f8857..563d7dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "doh-proxy" -version = "0.9.2" +version = "0.9.3" authors = ["Frank Denis "] description = "A DNS-over-HTTPS (DoH) and ODoH (Oblivious DoH) proxy" keywords = ["dns", "https", "doh", "odoh", "proxy"] @@ -16,7 +16,7 @@ default = ["tls"] tls = ["libdoh/tls"] [dependencies] -libdoh = { path = "src/libdoh", version = "0.9.0", default-features = false } +libdoh = { path = "src/libdoh", version = "0.9.3", default-features = false } clap = { version = "3.1.5", features = ["std", "cargo", "wrap_help"] } dnsstamps = "0.1.9" jemallocator = "0.3.2" diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index 3c1aece..302dffe 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libdoh" -version = "0.9.2" +version = "0.9.3" authors = ["Frank Denis "] description = "DoH and Oblivious DoH library for the rust-doh app" keywords = ["dns","https","doh","odoh","proxy"] From 511b0b43888b5ab322bbf2b7f3aa3e600535c567 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sat, 7 May 2022 15:38:04 +0200 Subject: [PATCH 071/150] Reorder --- src/libdoh/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libdoh/src/lib.rs b/src/libdoh/src/lib.rs index 427acec..a0fc163 100644 --- a/src/libdoh/src/lib.rs +++ b/src/libdoh/src/lib.rs @@ -86,9 +86,9 @@ where #[allow(clippy::type_complexity)] impl hyper::service::Service> for DoH { - type Response = Response; type Error = http::Error; type Future = Pin> + Send>>; + type Response = Response; fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) From e27ab7dee936d04a7be105a18bac666a324459e4 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sat, 14 May 2022 01:33:03 +0200 Subject: [PATCH 072/150] Format --- src/config.rs | 18 ++++++++++-------- src/main.rs | 12 ++++++------ 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/config.rs b/src/config.rs index 7e1861a..278e80e 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,14 +1,13 @@ +use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs}; +#[cfg(feature = "tls")] +use std::path::PathBuf; +use std::time::Duration; + +use clap::Arg; use libdoh::*; use crate::constants::*; -use clap::Arg; -use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs}; -use std::time::Duration; - -#[cfg(feature = "tls")] -use std::path::PathBuf; - pub fn parse_opts(globals: &mut Globals) { use crate::utils::{verify_remote_server, verify_sock_addr}; @@ -237,6 +236,9 @@ pub fn parse_opts(globals: &mut Globals) { println!("Check out https://dnscrypt.info/stamps/ to compute the actual stamps.\n") } else { - println!("Please provide a fully qualified hostname (-H command-line option) to get test DNS stamps for your server.\n"); + println!( + "Please provide a fully qualified hostname (-H command-line option) to get \ + test DNS stamps for your server.\n" + ); } } diff --git a/src/main.rs b/src/main.rs index 582724c..ef0cf88 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,17 +8,17 @@ mod config; mod constants; mod utils; +use std::net::{IpAddr, Ipv4Addr, SocketAddr}; +use std::sync::Arc; +use std::time::Duration; + +use libdoh::odoh::ODoHRotator; +use libdoh::reexports::tokio; use libdoh::*; use crate::config::*; use crate::constants::*; -use libdoh::odoh::ODoHRotator; -use libdoh::reexports::tokio; -use std::net::{IpAddr, Ipv4Addr, SocketAddr}; -use std::sync::Arc; -use std::time::Duration; - fn main() { let mut runtime_builder = tokio::runtime::Builder::new_multi_thread(); runtime_builder.enable_all(); From 533c29ec1e26d17e53d5cc865f735f0198644770 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sat, 14 May 2022 13:02:12 +0200 Subject: [PATCH 073/150] Update rustls-pemfile --- src/libdoh/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index 302dffe..f92f2c3 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -27,7 +27,7 @@ odoh-rs = "1.0.0-alpha.1" rand = "0.8.5" tokio = { version = "1.17.0", features = ["net", "rt-multi-thread", "parking_lot", "time", "sync"] } tokio-rustls = { version = "0.23.2", features = ["early-data"], optional = true } -rustls-pemfile = "0.3.0" +rustls-pemfile = "1.0.0" [profile.release] codegen-units = 1 From db9c8634e3cd0aa585f5ee62b11987b255c4966e Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Wed, 25 May 2022 13:39:09 +0200 Subject: [PATCH 074/150] Replace jemalloc with mimalloc --- Cargo.toml | 2 +- src/main.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 563d7dd..f6ad6f0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ tls = ["libdoh/tls"] libdoh = { path = "src/libdoh", version = "0.9.3", default-features = false } clap = { version = "3.1.5", features = ["std", "cargo", "wrap_help"] } dnsstamps = "0.1.9" -jemallocator = "0.3.2" +mimalloc = { version = "0.1.29", default-features = false } [package.metadata.deb] extended-description = """\ diff --git a/src/main.rs b/src/main.rs index ef0cf88..8f2ccd1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,5 @@ #[global_allocator] -static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; +static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc; #[macro_use] extern crate clap; From ab4c27ef86dab3b874480297f4421c7d86b342e7 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Mon, 6 Jun 2022 09:00:51 +0200 Subject: [PATCH 075/150] Update deps --- Cargo.toml | 2 +- src/libdoh/Cargo.toml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f6ad6f0..17a31ee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ tls = ["libdoh/tls"] [dependencies] libdoh = { path = "src/libdoh", version = "0.9.3", default-features = false } -clap = { version = "3.1.5", features = ["std", "cargo", "wrap_help"] } +clap = { version = "3.1.18", features = ["std", "cargo", "wrap_help"] } dnsstamps = "0.1.9" mimalloc = { version = "0.1.29", default-features = false } diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index f92f2c3..54db144 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -15,18 +15,18 @@ default = ["tls"] tls = ["tokio-rustls"] [dependencies] -anyhow = "1.0.55" +anyhow = "1.0.57" arc-swap = "1.5.0" base64 = "0.13.0" byteorder = "1.4.3" bytes = "1.1.0" futures = "0.3.21" hpke = "0.5.1" -hyper = { version = "0.14.17", default-features = false, features = ["server", "http1", "http2", "stream"] } +hyper = { version = "0.14.19", default-features = false, features = ["server", "http1", "http2", "stream"] } odoh-rs = "1.0.0-alpha.1" rand = "0.8.5" -tokio = { version = "1.17.0", features = ["net", "rt-multi-thread", "parking_lot", "time", "sync"] } -tokio-rustls = { version = "0.23.2", features = ["early-data"], optional = true } +tokio = { version = "1.19.1", features = ["net", "rt-multi-thread", "parking_lot", "time", "sync"] } +tokio-rustls = { version = "0.23.4", features = ["early-data"], optional = true } rustls-pemfile = "1.0.0" [profile.release] From 5b11bc520ea97251b8f88f05bde2f51b1007cf7c Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Mon, 6 Jun 2022 09:01:36 +0200 Subject: [PATCH 076/150] Format --- src/libdoh/src/errors.rs | 3 ++- src/libdoh/src/globals.rs | 7 ++++--- src/libdoh/src/lib.rs | 15 ++++++++------- src/libdoh/src/odoh.rs | 13 +++++++------ src/libdoh/src/tls.rs | 13 +++++++------ 5 files changed, 28 insertions(+), 23 deletions(-) diff --git a/src/libdoh/src/errors.rs b/src/libdoh/src/errors.rs index 82bae1a..aa7b9f0 100644 --- a/src/libdoh/src/errors.rs +++ b/src/libdoh/src/errors.rs @@ -1,6 +1,7 @@ -use hyper::StatusCode; use std::io; +use hyper::StatusCode; + #[derive(Debug)] pub enum DoHError { Incomplete, diff --git a/src/libdoh/src/globals.rs b/src/libdoh/src/globals.rs index 85b9808..fff8959 100644 --- a/src/libdoh/src/globals.rs +++ b/src/libdoh/src/globals.rs @@ -1,12 +1,13 @@ -use crate::odoh::ODoHRotator; use std::net::SocketAddr; +#[cfg(feature = "tls")] +use std::path::PathBuf; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; use std::time::Duration; + use tokio::runtime; -#[cfg(feature = "tls")] -use std::path::PathBuf; +use crate::odoh::ODoHRotator; #[derive(Debug)] pub struct Globals { diff --git a/src/libdoh/src/lib.rs b/src/libdoh/src/lib.rs index a0fc163..3659560 100644 --- a/src/libdoh/src/lib.rs +++ b/src/libdoh/src/lib.rs @@ -6,9 +6,10 @@ pub mod odoh; #[cfg(feature = "tls")] mod tls; -use crate::constants::*; -pub use crate::errors::*; -pub use crate::globals::*; +use std::net::SocketAddr; +use std::pin::Pin; +use std::sync::Arc; +use std::time::Duration; use byteorder::{BigEndian, ByteOrder}; use futures::prelude::*; @@ -16,14 +17,14 @@ use futures::task::{Context, Poll}; use hyper::http; use hyper::server::conn::Http; use hyper::{Body, HeaderMap, Method, Request, Response, StatusCode}; -use std::net::SocketAddr; -use std::pin::Pin; -use std::sync::Arc; -use std::time::Duration; use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; use tokio::net::{TcpListener, TcpSocket, UdpSocket}; use tokio::runtime; +use crate::constants::*; +pub use crate::errors::*; +pub use crate::globals::*; + pub mod reexports { pub use tokio; } diff --git a/src/libdoh/src/odoh.rs b/src/libdoh/src/odoh.rs index 29c16cd..072d89c 100644 --- a/src/libdoh/src/odoh.rs +++ b/src/libdoh/src/odoh.rs @@ -1,17 +1,18 @@ -use crate::constants::ODOH_KEY_ROTATION_SECS; -use crate::errors::DoHError; -use arc_swap::ArcSwap; +use std::fmt; +use std::sync::Arc; +use std::time::Duration; +use arc_swap::ArcSwap; use odoh_rs::{ Deserialize, ObliviousDoHConfig, ObliviousDoHConfigs, ObliviousDoHKeyPair, ObliviousDoHMessage, ObliviousDoHMessagePlaintext, OdohSecret, ResponseNonce, Serialize, }; use rand::Rng; -use std::fmt; -use std::sync::Arc; -use std::time::Duration; use tokio::runtime; +use crate::constants::ODOH_KEY_ROTATION_SECS; +use crate::errors::DoHError; + #[derive(Clone)] pub struct ODoHPublicKey { key_pair: ObliviousDoHKeyPair, diff --git a/src/libdoh/src/tls.rs b/src/libdoh/src/tls.rs index 655d1ab..3a6c4da 100644 --- a/src/libdoh/src/tls.rs +++ b/src/libdoh/src/tls.rs @@ -1,14 +1,11 @@ -use crate::constants::CERTS_WATCH_DELAY_SECS; -use crate::errors::*; -use crate::{DoH, LocalExecutor}; - -use futures::{future::FutureExt, join, select}; -use hyper::server::conn::Http; use std::fs::File; use std::io::{self, BufReader, Cursor, Read}; use std::path::Path; use std::sync::Arc; use std::time::Duration; + +use futures::{future::FutureExt, join, select}; +use hyper::server::conn::Http; use tokio::{ net::TcpListener, sync::mpsc::{self, Receiver}, @@ -18,6 +15,10 @@ use tokio_rustls::{ TlsAcceptor, }; +use crate::constants::CERTS_WATCH_DELAY_SECS; +use crate::errors::*; +use crate::{DoH, LocalExecutor}; + pub fn create_tls_acceptor(certs_path: P, certs_keys_path: P2) -> io::Result where P: AsRef, From 965bca7fde2571e4dd57fc10bd1fa5b57b8c205b Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Mon, 6 Jun 2022 09:02:14 +0200 Subject: [PATCH 077/150] Remove Travis --- .travis.yml | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 32905a5..0000000 --- a/.travis.yml +++ /dev/null @@ -1,4 +0,0 @@ -language: rust -rust: - - nightly - - stable From d12b9deb3583f388ac071ed6dc701efc71a21cb2 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Mon, 6 Jun 2022 09:02:43 +0200 Subject: [PATCH 078/150] Bump --- Cargo.toml | 4 ++-- src/libdoh/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 17a31ee..fdbc183 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "doh-proxy" -version = "0.9.3" +version = "0.9.4" authors = ["Frank Denis "] description = "A DNS-over-HTTPS (DoH) and ODoH (Oblivious DoH) proxy" keywords = ["dns", "https", "doh", "odoh", "proxy"] @@ -16,7 +16,7 @@ default = ["tls"] tls = ["libdoh/tls"] [dependencies] -libdoh = { path = "src/libdoh", version = "0.9.3", default-features = false } +libdoh = { path = "src/libdoh", version = "0.9.4", default-features = false } clap = { version = "3.1.18", features = ["std", "cargo", "wrap_help"] } dnsstamps = "0.1.9" mimalloc = { version = "0.1.29", default-features = false } diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index 54db144..873cbb8 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libdoh" -version = "0.9.3" +version = "0.9.4" authors = ["Frank Denis "] description = "DoH and Oblivious DoH library for the rust-doh app" keywords = ["dns","https","doh","odoh","proxy"] From fd65582aa6048bce4178bf3a15424c71c2846914 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Fri, 24 Jun 2022 23:49:04 +0200 Subject: [PATCH 079/150] Nits --- src/libdoh/Cargo.toml | 4 ++-- src/libdoh/src/tls.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index 873cbb8..c6161b3 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -15,7 +15,7 @@ default = ["tls"] tls = ["tokio-rustls"] [dependencies] -anyhow = "1.0.57" +anyhow = "1.0.58" arc-swap = "1.5.0" base64 = "0.13.0" byteorder = "1.4.3" @@ -25,7 +25,7 @@ hpke = "0.5.1" hyper = { version = "0.14.19", default-features = false, features = ["server", "http1", "http2", "stream"] } odoh-rs = "1.0.0-alpha.1" rand = "0.8.5" -tokio = { version = "1.19.1", features = ["net", "rt-multi-thread", "parking_lot", "time", "sync"] } +tokio = { version = "1.19.2", features = ["net", "rt-multi-thread", "parking_lot", "time", "sync"] } tokio-rustls = { version = "0.23.4", features = ["early-data"], optional = true } rustls-pemfile = "1.0.0" diff --git a/src/libdoh/src/tls.rs b/src/libdoh/src/tls.rs index 3a6c4da..4c1dae0 100644 --- a/src/libdoh/src/tls.rs +++ b/src/libdoh/src/tls.rs @@ -169,6 +169,6 @@ impl DoH { } Ok::<_, DoHError>(()) }; - return join!(https_service, cert_service).0; + join!(https_service, cert_service).0 } } From ff62b6a24bf82a178b304bf604235f37ca2347a0 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sat, 2 Jul 2022 17:44:42 +0200 Subject: [PATCH 080/150] Disable the `parking_lot` feature in `tokio`. Mutexes from the standard library have improved in recent Rust versions. On Linux only, though. --- src/libdoh/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index c6161b3..278bddc 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -25,7 +25,7 @@ hpke = "0.5.1" hyper = { version = "0.14.19", default-features = false, features = ["server", "http1", "http2", "stream"] } odoh-rs = "1.0.0-alpha.1" rand = "0.8.5" -tokio = { version = "1.19.2", features = ["net", "rt-multi-thread", "parking_lot", "time", "sync"] } +tokio = { version = "1.19.2", features = ["net", "rt-multi-thread", "time", "sync"] } tokio-rustls = { version = "0.23.4", features = ["early-data"], optional = true } rustls-pemfile = "1.0.0" From 25d126173098aa6abeb2170030b6e3962eaaf29d Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Thu, 15 Sep 2022 12:41:40 +0200 Subject: [PATCH 081/150] Remove unneeded reference --- src/libdoh/src/dns.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libdoh/src/dns.rs b/src/libdoh/src/dns.rs index fdea9f5..9f5c573 100644 --- a/src/libdoh/src/dns.rs +++ b/src/libdoh/src/dns.rs @@ -180,7 +180,7 @@ fn add_edns_section(packet: &mut Vec, max_payload_size: u16) -> Result<(), E "Packet would be too large to add a new record" ); arcount_inc(packet)?; - packet.extend(&opt_rr); + packet.extend(opt_rr); Ok(()) } From a60ced8782957dd734691bc2ccfa1c7fff5f492b Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Wed, 21 Sep 2022 12:21:00 +0200 Subject: [PATCH 082/150] Update deps --- Cargo.toml | 2 +- src/libdoh/Cargo.toml | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fdbc183..9c2543a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ tls = ["libdoh/tls"] [dependencies] libdoh = { path = "src/libdoh", version = "0.9.4", default-features = false } -clap = { version = "3.1.18", features = ["std", "cargo", "wrap_help"] } +clap = { version = "3.2.22", features = ["std", "cargo", "wrap_help"] } dnsstamps = "0.1.9" mimalloc = { version = "0.1.29", default-features = false } diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index 278bddc..9d3c9eb 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -15,19 +15,19 @@ default = ["tls"] tls = ["tokio-rustls"] [dependencies] -anyhow = "1.0.58" -arc-swap = "1.5.0" +anyhow = "1.0.65" +arc-swap = "1.5.1" base64 = "0.13.0" byteorder = "1.4.3" -bytes = "1.1.0" -futures = "0.3.21" +bytes = "1.2.1" +futures = "0.3.24" hpke = "0.5.1" -hyper = { version = "0.14.19", default-features = false, features = ["server", "http1", "http2", "stream"] } +hyper = { version = "0.14.20", default-features = false, features = ["server", "http1", "http2", "stream"] } odoh-rs = "1.0.0-alpha.1" rand = "0.8.5" -tokio = { version = "1.19.2", features = ["net", "rt-multi-thread", "time", "sync"] } +tokio = { version = "1.21.1", features = ["net", "rt-multi-thread", "time", "sync"] } tokio-rustls = { version = "0.23.4", features = ["early-data"], optional = true } -rustls-pemfile = "1.0.0" +rustls-pemfile = "1.0.1" [profile.release] codegen-units = 1 From 767b3e17b1fff27b8682c3199afc9d22432b275a Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Tue, 11 Oct 2022 22:25:29 +0200 Subject: [PATCH 083/150] Update odoh-rs to the final version --- Cargo.toml | 2 +- src/libdoh/Cargo.toml | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9c2543a..d289d37 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ tls = ["libdoh/tls"] [dependencies] libdoh = { path = "src/libdoh", version = "0.9.4", default-features = false } -clap = { version = "3.2.22", features = ["std", "cargo", "wrap_help"] } +clap = { version = "3", features = ["std", "cargo", "wrap_help"] } dnsstamps = "0.1.9" mimalloc = { version = "0.1.29", default-features = false } diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index 9d3c9eb..cd04489 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libdoh" -version = "0.9.4" +version = "0.9.5" authors = ["Frank Denis "] description = "DoH and Oblivious DoH library for the rust-doh app" keywords = ["dns","https","doh","odoh","proxy"] @@ -21,11 +21,10 @@ base64 = "0.13.0" byteorder = "1.4.3" bytes = "1.2.1" futures = "0.3.24" -hpke = "0.5.1" hyper = { version = "0.14.20", default-features = false, features = ["server", "http1", "http2", "stream"] } -odoh-rs = "1.0.0-alpha.1" +odoh-rs = "1.0.0" rand = "0.8.5" -tokio = { version = "1.21.1", features = ["net", "rt-multi-thread", "time", "sync"] } +tokio = { version = "1.21.2", features = ["net", "rt-multi-thread", "time", "sync"] } tokio-rustls = { version = "0.23.4", features = ["early-data"], optional = true } rustls-pemfile = "1.0.1" From 8b9f9377b3190ee0704b488e2e0e6d8cbf068de3 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Tue, 11 Oct 2022 22:25:59 +0200 Subject: [PATCH 084/150] Update deps --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d289d37..fa976e0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "doh-proxy" -version = "0.9.4" +version = "0.9.5" authors = ["Frank Denis "] description = "A DNS-over-HTTPS (DoH) and ODoH (Oblivious DoH) proxy" keywords = ["dns", "https", "doh", "odoh", "proxy"] @@ -16,7 +16,7 @@ default = ["tls"] tls = ["libdoh/tls"] [dependencies] -libdoh = { path = "src/libdoh", version = "0.9.4", default-features = false } +libdoh = { path = "src/libdoh", version = "0.9.5", default-features = false } clap = { version = "3", features = ["std", "cargo", "wrap_help"] } dnsstamps = "0.1.9" mimalloc = { version = "0.1.29", default-features = false } From 06a3fa0499fe2d06373833637e38fe51486981d9 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sun, 25 Dec 2022 10:21:19 +0100 Subject: [PATCH 085/150] Clarify what -H and -g do Fixes #68 --- README.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bb8cd5b..54f8200 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,14 @@ OPTIONS: Path to the PEM/PKCS#8-encoded certificates (only required for built-in TLS) ``` +Example command-line: + +```sh +doh-proxy -H 'doh.example.com' -u 127.0.0.1:53 -g 233.252.0.5 +``` + +Here, `doh.example.com` is the host name (which should match a name included in the TLS certificate), `127.0.0.1:53` is the address of the DNS resolver, and `233.252.0.5` is the public IP address of the DoH server. + ## HTTP/2 termination The recommended way to use `doh-proxy` is to use a TLS termination proxy (such as [hitch](https://github.com/varnish/hitch) or [relayd](https://bsd.plumbing/about.html)), a CDN or a web server with proxying abilities as a front-end. @@ -142,10 +150,10 @@ upstream_addr = "127.0.0.1:3000" ## Example usage with `nginx` -In an existing `server`, a `/doh` endpoint can be exposed that way: +In an existing `server`, a `/dns-query` endpoint can be exposed that way: ```text -location /doh { +location /dns-query { proxy_pass http://127.0.0.1:3000; } ``` From c82fb339ed781fceffb92f5ab2a19e8cca5cb741 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sun, 25 Dec 2022 11:23:13 +0100 Subject: [PATCH 086/150] Update deps --- src/libdoh/Cargo.toml | 12 ++++++------ src/libdoh/src/errors.rs | 6 +++--- src/libdoh/src/lib.rs | 15 ++++++++++----- src/libdoh/src/odoh.rs | 2 +- src/libdoh/src/tls.rs | 8 +++----- 5 files changed, 23 insertions(+), 20 deletions(-) diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index cd04489..83daff6 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -15,16 +15,16 @@ default = ["tls"] tls = ["tokio-rustls"] [dependencies] -anyhow = "1.0.65" +anyhow = "1.0.68" arc-swap = "1.5.1" -base64 = "0.13.0" +base64 = "0.20.0" byteorder = "1.4.3" -bytes = "1.2.1" -futures = "0.3.24" -hyper = { version = "0.14.20", default-features = false, features = ["server", "http1", "http2", "stream"] } +bytes = "1.3.0" +futures = "0.3.25" +hyper = { version = "0.14.23", default-features = false, features = ["server", "http1", "http2", "stream"] } odoh-rs = "1.0.0" rand = "0.8.5" -tokio = { version = "1.21.2", features = ["net", "rt-multi-thread", "time", "sync"] } +tokio = { version = "1.23.0", features = ["net", "rt-multi-thread", "time", "sync"] } tokio-rustls = { version = "0.23.4", features = ["early-data"], optional = true } rustls-pemfile = "1.0.1" diff --git a/src/libdoh/src/errors.rs b/src/libdoh/src/errors.rs index aa7b9f0..24ae819 100644 --- a/src/libdoh/src/errors.rs +++ b/src/libdoh/src/errors.rs @@ -27,9 +27,9 @@ impl std::fmt::Display for DoHError { DoHError::UpstreamIssue => write!(fmt, "Upstream error"), DoHError::UpstreamTimeout => write!(fmt, "Upstream timeout"), DoHError::StaleKey => write!(fmt, "Stale key material"), - DoHError::Hyper(e) => write!(fmt, "HTTP error: {}", e), - DoHError::Io(e) => write!(fmt, "IO error: {}", e), - DoHError::ODoHConfigError(e) => write!(fmt, "ODoH config error: {}", e), + DoHError::Hyper(e) => write!(fmt, "HTTP error: {e}"), + DoHError::Io(e) => write!(fmt, "IO error: {e}"), + DoHError::ODoHConfigError(e) => write!(fmt, "ODoH config error: {e}"), DoHError::TooManyTcpSessions => write!(fmt, "Too many TCP sessions"), } } diff --git a/src/libdoh/src/lib.rs b/src/libdoh/src/lib.rs index 3659560..3022a7b 100644 --- a/src/libdoh/src/lib.rs +++ b/src/libdoh/src/lib.rs @@ -29,6 +29,12 @@ pub mod reexports { pub use tokio; } +const BASE64_URL_SAFE_NO_PAD: base64::engine::fast_portable::FastPortable = + base64::engine::fast_portable::FastPortable::from( + &base64::alphabet::URL_SAFE, + base64::engine::fast_portable::NO_PAD, + ); + #[derive(Clone, Debug)] struct DnsResponse { packet: Vec, @@ -162,7 +168,7 @@ impl DoH { } } let query = match question_str.and_then(|question_str| { - base64::decode_config(question_str, base64::URL_SAFE_NO_PAD).ok() + base64::decode_engine(question_str, &BASE64_URL_SAFE_NO_PAD).ok() }) { Some(query) => query, _ => return None, @@ -427,8 +433,7 @@ impl DoH { .header( hyper::header::CACHE_CONTROL, format!( - "max-age={}, stale-if-error={}, stale-while-revalidate={}", - ttl, STALE_IF_ERROR_SECS, STALE_WHILE_REVALIDATE_SECS + "max-age={ttl}, stale-if-error={STALE_IF_ERROR_SECS}, stale-while-revalidate={STALE_WHILE_REVALIDATE_SECS}" ) .as_str(), ); @@ -495,9 +500,9 @@ impl DoH { self.globals.tls_cert_path.is_some() && self.globals.tls_cert_key_path.is_some(); } if tls_enabled { - println!("Listening on https://{}{}", listen_address, path); + println!("Listening on https://{listen_address}{path}"); } else { - println!("Listening on http://{}{}", listen_address, path); + println!("Listening on http://{listen_address}{path}"); } let mut server = Http::new(); diff --git a/src/libdoh/src/odoh.rs b/src/libdoh/src/odoh.rs index 072d89c..00bb95f 100644 --- a/src/libdoh/src/odoh.rs +++ b/src/libdoh/src/odoh.rs @@ -115,7 +115,7 @@ impl ODoHRotator { Ok(key) => { current_key.store(Arc::new(key)); } - Err(e) => eprintln!("ODoH key rotation error: {}", e), + Err(e) => eprintln!("ODoH key rotation error: {e}"), }; } }); diff --git a/src/libdoh/src/tls.rs b/src/libdoh/src/tls.rs index 4c1dae0..ccc4585 100644 --- a/src/libdoh/src/tls.rs +++ b/src/libdoh/src/tls.rs @@ -30,8 +30,7 @@ where io::Error::new( e.kind(), format!( - "Unable to load the certificates [{}]: {}", - certs_path_str, e + "Unable to load the certificates [{certs_path_str}]: {e}" ), ) })?); @@ -54,8 +53,7 @@ where io::Error::new( e.kind(), format!( - "Unable to load the certificate keys [{}]: {}", - certs_keys_path_str, e + "Unable to load the certificate keys [{certs_keys_path_str}]: {e}" ), ) })? @@ -163,7 +161,7 @@ impl DoH { break; } } - Err(e) => eprintln!("TLS certificates error: {}", e), + Err(e) => eprintln!("TLS certificates error: {e}"), } tokio::time::sleep(Duration::from_secs(CERTS_WATCH_DELAY_SECS.into())).await; } From 6818fbe8a14920ee9faaac8ab82b45b0582ea60a Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sun, 25 Dec 2022 12:37:48 +0100 Subject: [PATCH 087/150] Update to clap 4 The new API is confusing and very error-prone, with errors being thrown at runtime rather than compile-time. Hopefully nothing got broken in the process. --- Cargo.toml | 4 +- src/config.rs | 124 ++++++++++++++++++++++++++++++++------------------ src/utils.rs | 16 +++---- 3 files changed, 88 insertions(+), 56 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fa976e0..2a09b98 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,9 +17,9 @@ tls = ["libdoh/tls"] [dependencies] libdoh = { path = "src/libdoh", version = "0.9.5", default-features = false } -clap = { version = "3", features = ["std", "cargo", "wrap_help"] } +clap = { version = "4", features = ["std", "cargo", "wrap_help", "string"] } dnsstamps = "0.1.9" -mimalloc = { version = "0.1.29", default-features = false } +mimalloc = { version = "0.1.32", default-features = false } [package.metadata.deb] extended-description = """\ diff --git a/src/config.rs b/src/config.rs index 278e80e..643463a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -3,7 +3,7 @@ use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSoc use std::path::PathBuf; use std::time::Duration; -use clap::Arg; +use clap::{Arg, ArgAction::SetTrue}; use libdoh::*; use crate::constants::*; @@ -24,54 +24,54 @@ pub fn parse_opts(globals: &mut Globals) { Arg::new("hostname") .short('H') .long("hostname") - .takes_value(true) + .num_args(1) .help("Host name (not IP address) DoH clients will use to connect"), ) .arg( Arg::new("public_address") .short('g') .long("public-address") - .takes_value(true) + .num_args(1) .help("External IP address DoH clients will connect to"), ) .arg( Arg::new("public_port") .short('j') .long("public-port") - .takes_value(true) + .num_args(1) .help("External port DoH clients will connect to, if not 443"), ) .arg( Arg::new("listen_address") .short('l') .long("listen-address") - .takes_value(true) + .num_args(1) .default_value(LISTEN_ADDRESS) - .validator(verify_sock_addr) + .value_parser(verify_sock_addr) .help("Address to listen to"), ) .arg( Arg::new("server_address") .short('u') .long("server-address") - .takes_value(true) + .num_args(1) .default_value(SERVER_ADDRESS) - .validator(verify_remote_server) + .value_parser(verify_remote_server) .help("Address to connect to"), ) .arg( Arg::new("local_bind_address") .short('b') .long("local-bind-address") - .takes_value(true) - .validator(verify_sock_addr) + .num_args(1) + .value_parser(verify_sock_addr) .help("Address to connect from"), ) .arg( Arg::new("path") .short('p') .long("path") - .takes_value(true) + .num_args(1) .default_value(PATH) .help("URI path"), ) @@ -79,65 +79,68 @@ pub fn parse_opts(globals: &mut Globals) { Arg::new("max_clients") .short('c') .long("max-clients") - .takes_value(true) - .default_value(&max_clients) + .num_args(1) + .default_value(max_clients) .help("Maximum number of simultaneous clients"), ) .arg( Arg::new("max_concurrent") .short('C') .long("max-concurrent") - .takes_value(true) - .default_value(&max_concurrent_streams) + .num_args(1) + .default_value(max_concurrent_streams) .help("Maximum number of concurrent requests per client"), ) .arg( Arg::new("timeout") .short('t') .long("timeout") - .takes_value(true) - .default_value(&timeout_sec) + .num_args(1) + .default_value(timeout_sec) .help("Timeout, in seconds"), ) .arg( Arg::new("min_ttl") .short('T') .long("min-ttl") - .takes_value(true) - .default_value(&min_ttl) + .num_args(1) + .default_value(min_ttl) .help("Minimum TTL, in seconds"), ) .arg( Arg::new("max_ttl") .short('X') .long("max-ttl") - .takes_value(true) - .default_value(&max_ttl) + .num_args(1) + .default_value(max_ttl) .help("Maximum TTL, in seconds"), ) .arg( Arg::new("err_ttl") .short('E') .long("err-ttl") - .takes_value(true) - .default_value(&err_ttl) + .num_args(1) + .default_value(err_ttl) .help("TTL for errors, in seconds"), ) .arg( Arg::new("disable_keepalive") .short('K') + .action(SetTrue) .long("disable-keepalive") .help("Disable keepalive"), ) .arg( Arg::new("disable_post") .short('P') + .action(SetTrue) .long("disable-post") .help("Disable POST queries"), ) .arg( Arg::new("allow_odoh_post") .short('O') + .action(SetTrue) .long("allow-odoh-post") .help("Allow POST queries over ODoH even if they have been disabed for DoH"), ); @@ -148,7 +151,7 @@ pub fn parse_opts(globals: &mut Globals) { Arg::new("tls_cert_path") .short('i') .long("tls-cert-path") - .takes_value(true) + .num_args(1) .help( "Path to the PEM/PKCS#8-encoded certificates (only required for built-in TLS)", ), @@ -157,21 +160,24 @@ pub fn parse_opts(globals: &mut Globals) { Arg::new("tls_cert_key_path") .short('I') .long("tls-cert-key-path") - .takes_value(true) + .num_args(1) .help("Path to the PEM-encoded secret keys (only required for built-in TLS)"), ); let matches = options.get_matches(); - globals.listen_address = matches.value_of("listen_address").unwrap().parse().unwrap(); - + globals.listen_address = matches + .get_one::("listen_address") + .unwrap() + .parse() + .unwrap(); globals.server_address = matches - .value_of("server_address") + .get_one::("server_address") .unwrap() .to_socket_addrs() .unwrap() .next() .unwrap(); - globals.local_bind_address = match matches.value_of("local_bind_address") { + globals.local_bind_address = match matches.get_one::("local_bind_address") { Some(address) => address.parse().unwrap(), None => match globals.server_address { SocketAddr::V4(_) => SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0)), @@ -183,36 +189,64 @@ pub fn parse_opts(globals: &mut Globals) { )), }, }; - globals.path = matches.value_of("path").unwrap().to_string(); + globals.path = matches.get_one::("path").unwrap().to_string(); if !globals.path.starts_with('/') { globals.path = format!("/{}", globals.path); } - globals.max_clients = matches.value_of("max_clients").unwrap().parse().unwrap(); - globals.timeout = Duration::from_secs(matches.value_of("timeout").unwrap().parse().unwrap()); - globals.max_concurrent_streams = matches.value_of("max_concurrent").unwrap().parse().unwrap(); - globals.min_ttl = matches.value_of("min_ttl").unwrap().parse().unwrap(); - globals.max_ttl = matches.value_of("max_ttl").unwrap().parse().unwrap(); - globals.err_ttl = matches.value_of("err_ttl").unwrap().parse().unwrap(); - globals.keepalive = !matches.is_present("disable_keepalive"); - globals.disable_post = matches.is_present("disable_post"); - globals.allow_odoh_post = matches.is_present("allow_odoh_post"); + globals.max_clients = matches + .get_one::("max_clients") + .unwrap() + .parse() + .unwrap(); + globals.timeout = Duration::from_secs( + matches + .get_one::("timeout") + .unwrap() + .parse() + .unwrap(), + ); + globals.max_concurrent_streams = matches + .get_one::("max_concurrent") + .unwrap() + .parse() + .unwrap(); + globals.min_ttl = matches + .get_one::("min_ttl") + .unwrap() + .parse() + .unwrap(); + globals.max_ttl = matches + .get_one::("max_ttl") + .unwrap() + .parse() + .unwrap(); + globals.err_ttl = matches + .get_one::("err_ttl") + .unwrap() + .parse() + .unwrap(); + globals.keepalive = !matches.get_flag("disable_keepalive"); + globals.disable_post = matches.get_flag("disable_post"); + globals.allow_odoh_post = matches.get_flag("allow_odoh_post"); #[cfg(feature = "tls")] { - globals.tls_cert_path = matches.value_of("tls_cert_path").map(PathBuf::from); + globals.tls_cert_path = matches + .get_one::("tls_cert_path") + .map(PathBuf::from); globals.tls_cert_key_path = matches - .value_of("tls_cert_key_path") + .get_one::("tls_cert_key_path") .map(PathBuf::from) .or_else(|| globals.tls_cert_path.clone()); } - if let Some(hostname) = matches.value_of("hostname") { + if let Some(hostname) = matches.get_one::("hostname") { let mut builder = dnsstamps::DoHBuilder::new(hostname.to_string(), globals.path.to_string()); - if let Some(public_address) = matches.value_of("public_address") { + if let Some(public_address) = matches.get_one::("public_address") { builder = builder.with_address(public_address.to_string()); } - if let Some(public_port) = matches.value_of("public_port") { + if let Some(public_port) = matches.get_one::("public_port") { let public_port = public_port.parse().expect("Invalid public port"); builder = builder.with_port(public_port); } @@ -224,7 +258,7 @@ pub fn parse_opts(globals: &mut Globals) { let mut builder = dnsstamps::ODoHTargetBuilder::new(hostname.to_string(), globals.path.to_string()); - if let Some(public_port) = matches.value_of("public_port") { + if let Some(public_port) = matches.get_one::("public_port") { let public_port = public_port.parse().expect("Invalid public port"); builder = builder.with_port(public_port); } diff --git a/src/utils.rs b/src/utils.rs index 0ec0eff..5440def 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -2,25 +2,23 @@ use std::net::{SocketAddr, ToSocketAddrs}; -pub(crate) fn verify_sock_addr(arg_val: &str) -> Result<(), String> { +pub(crate) fn verify_sock_addr(arg_val: &str) -> Result { match arg_val.parse::() { - Ok(_addr) => Ok(()), + Ok(_addr) => Ok(arg_val.to_string()), Err(_) => Err(format!( - "Could not parse \"{}\" as a valid socket address (with port).", - arg_val + "Could not parse \"{arg_val}\" as a valid socket address (with port)." )), } } -pub(crate) fn verify_remote_server(arg_val: &str) -> Result<(), String> { +pub(crate) fn verify_remote_server(arg_val: &str) -> Result { match arg_val.to_socket_addrs() { Ok(mut addr_iter) => match addr_iter.next() { - Some(_) => Ok(()), + Some(_) => Ok(arg_val.to_string()), None => Err(format!( - "Could not parse \"{}\" as a valid remote uri", - arg_val + "Could not parse \"{arg_val}\" as a valid remote uri" )), }, - Err(err) => Err(format!("{}", err)), + Err(err) => Err(format!("{err}")), } } From 4b887d6705745524ddcfb0887ffc78ca4b878279 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Mon, 2 Jan 2023 20:05:19 +0100 Subject: [PATCH 088/150] Update arc-swap --- src/libdoh/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index 83daff6..1e4e0db 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -16,7 +16,7 @@ tls = ["tokio-rustls"] [dependencies] anyhow = "1.0.68" -arc-swap = "1.5.1" +arc-swap = "1.6.0" base64 = "0.20.0" byteorder = "1.4.3" bytes = "1.3.0" From a1fc5bbffc6cb2e3f757519db79dd724fd301a14 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Mon, 2 Jan 2023 20:17:20 +0100 Subject: [PATCH 089/150] CI: try to package a build for Windows --- .github/workflows/release.yml | 58 ++++++++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index efeb24a..6d31928 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,15 +27,41 @@ jobs: run: rustup default | grep stable - name: Install cargo-deb - run: cargo install --debug cargo-deb + run: cargo install cargo-deb - - name: Release build + - name: Install cargo-zigbuild + run: cargo install cargo-zigbuild + + - name: Release build Linux-x86-64 run: | - env RUSTFLAGS="-C link-arg=-s" cargo build --release + rustup target add x86_64-unknown-linux-musl + env RUSTFLAGS="-C link-arg=-s" cargo zigbuild --release --target x86_64-unknown-linux-musl mkdir doh-proxy - mv target/release/doh-proxy doh-proxy/ + mv target/x86_64-unknown-linux-musl/release/doh-proxy doh-proxy/ cp README.md localhost.pem doh-proxy/ tar cJpf doh-proxy_${{ steps.get_version.outputs.VERSION }}_linux-x86_64.tar.bz2 doh-proxy + rm -fr doh-proxy + + - name: Release build Linux-aarch64 + run: | + rustup target add + env RUSTFLAGS="-C link-arg=-s" cargo zigbuild --release --target aarch64-unknown-linux-musl + mkdir doh-proxy + mv target/aarch64-unknown-linux-musl/release/doh-proxy doh-proxy/ + cp README.md localhost.pem doh-proxy/ + tar cJpf doh-proxy_${{ steps.get_version.outputs.VERSION }}_linux-aarch64.tar.bz2 doh-proxy + rm -fr doh-proxy + + - name: Release build Windows-x86_64 + run: | + rustup target add x86_64-pc-windows-gnu + env RUSTFLAGS="-C link-arg=-s" cargo zigbuild --release --target x86_64-pc-windows-gnu + mkdir doh-proxy + mv target/x86_64-pc-windows-gnu/release/doh-proxy doh-proxy/ + cp README.md localhost.pem doh-proxy/ + zip -9 doh-proxy_${{ steps.get_version.outputs.VERSION }}_windows-x86_64.zip doh-proxy + rm -fr doh-proxy + - name: Debian package run: | cargo deb @@ -62,7 +88,7 @@ jobs: asset_path: "target/debian/doh-proxy_${{ steps.get_version.outputs.VERSION }}_amd64.deb" asset_content_type: application/x-debian-package - - name: Upload tarball + - name: Upload tarball for linux-x86_64 id: upload-release-asset-tarball uses: actions/upload-release-asset@v1 env: @@ -72,3 +98,25 @@ jobs: asset_name: "doh-proxy_${{ steps.get_version.outputs.VERSION }}_linux-x86_64.tar.bz2" asset_path: "doh-proxy_${{ steps.get_version.outputs.VERSION }}_linux-x86_64.tar.bz2" asset_content_type: application/x-tar + + - name: Upload tarball for linux-aarch64 + id: upload-release-asset-tarball + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_name: "doh-proxy_${{ steps.get_version.outputs.VERSION }}_linux-aarch64.tar.bz2" + asset_path: "doh-proxy_${{ steps.get_version.outputs.VERSION }}_linux-aarch64.tar.bz2" + asset_content_type: application/x-tar + + - name: Upload tarball for windows-x86_64 + id: upload-release-asset-tarball + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_name: "doh-proxy_${{ steps.get_version.outputs.VERSION }}_windows-x86_64.zip" + asset_path: "doh-proxy_${{ steps.get_version.outputs.VERSION }}_windows-x86_64.zip" + asset_content_type: application/zip From 3e59f425581bfa893281febae46d9477d26e801c Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Mon, 2 Jan 2023 20:17:50 +0100 Subject: [PATCH 090/150] Bump --- src/libdoh/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index 1e4e0db..fe209ca 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libdoh" -version = "0.9.5" +version = "0.9.6" authors = ["Frank Denis "] description = "DoH and Oblivious DoH library for the rust-doh app" keywords = ["dns","https","doh","odoh","proxy"] From fdcc797fcb533667b10d60b39ab36c669da5616a Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Mon, 2 Jan 2023 20:19:04 +0100 Subject: [PATCH 091/150] Bump --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2a09b98..e6be0d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "doh-proxy" -version = "0.9.5" +version = "0.9.6" authors = ["Frank Denis "] description = "A DNS-over-HTTPS (DoH) and ODoH (Oblivious DoH) proxy" keywords = ["dns", "https", "doh", "odoh", "proxy"] @@ -16,7 +16,7 @@ default = ["tls"] tls = ["libdoh/tls"] [dependencies] -libdoh = { path = "src/libdoh", version = "0.9.5", default-features = false } +libdoh = { path = "src/libdoh", version = "0.9.6", default-features = false } clap = { version = "4", features = ["std", "cargo", "wrap_help", "string"] } dnsstamps = "0.1.9" mimalloc = { version = "0.1.32", default-features = false } From eede3f4ab31e3459a68b0f9ec50b5a97b9796cdc Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Mon, 2 Jan 2023 20:19:56 +0100 Subject: [PATCH 092/150] 2023 --- LICENSE | 2 +- src/libdoh/LICENSE | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index f0d8aed..06c6cdb 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2018-2022 Frank Denis +Copyright (c) 2018-2023 Frank Denis Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/libdoh/LICENSE b/src/libdoh/LICENSE index f0d8aed..06c6cdb 100644 --- a/src/libdoh/LICENSE +++ b/src/libdoh/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2018-2022 Frank Denis +Copyright (c) 2018-2023 Frank Denis Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 6f5213838bcd0d7e4f3b3e3967e5a9e2bddcd3d8 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Mon, 2 Jan 2023 20:21:24 +0100 Subject: [PATCH 093/150] Actions require unique names --- .github/workflows/release.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6d31928..b9f0df8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -89,7 +89,7 @@ jobs: asset_content_type: application/x-debian-package - name: Upload tarball for linux-x86_64 - id: upload-release-asset-tarball + id: upload-release-asset-tarball-linux-x86_64 uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -100,7 +100,7 @@ jobs: asset_content_type: application/x-tar - name: Upload tarball for linux-aarch64 - id: upload-release-asset-tarball + id: upload-release-asset-tarball-linux-aarch64 uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -111,7 +111,7 @@ jobs: asset_content_type: application/x-tar - name: Upload tarball for windows-x86_64 - id: upload-release-asset-tarball + id: upload-release-asset-tarball-windows-x86_64 uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From a3739570455b3965400ed9a0ce90a40f915623e6 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Mon, 2 Jan 2023 20:37:09 +0100 Subject: [PATCH 094/150] Install Zig --- .github/workflows/release.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b9f0df8..baab355 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,6 +16,8 @@ jobs: - uses: actions/checkout@master + - uses: goto-bus-stop/setup-zig@v2 + - uses: hecrj/setup-rust-action@master with: rust-version: stable From a92f4a77ae7869aab16abec3b0f68c2ddc01fce5 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Mon, 2 Jan 2023 20:47:01 +0100 Subject: [PATCH 095/150] Rust requires every single target to be installed individually --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index baab355..16cbcb3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -46,7 +46,7 @@ jobs: - name: Release build Linux-aarch64 run: | - rustup target add + rustup target add aarch64-unknown-linux-musl env RUSTFLAGS="-C link-arg=-s" cargo zigbuild --release --target aarch64-unknown-linux-musl mkdir doh-proxy mv target/aarch64-unknown-linux-musl/release/doh-proxy doh-proxy/ From fc61c79a9fb7e09fcade07d092d33ef8ac8435b3 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Mon, 2 Jan 2023 21:00:07 +0100 Subject: [PATCH 096/150] Windows requires a .exe suffix --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 16cbcb3..4be3298 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -59,7 +59,7 @@ jobs: rustup target add x86_64-pc-windows-gnu env RUSTFLAGS="-C link-arg=-s" cargo zigbuild --release --target x86_64-pc-windows-gnu mkdir doh-proxy - mv target/x86_64-pc-windows-gnu/release/doh-proxy doh-proxy/ + mv target/x86_64-pc-windows-gnu/release/doh-proxy.exe doh-proxy/ cp README.md localhost.pem doh-proxy/ zip -9 doh-proxy_${{ steps.get_version.outputs.VERSION }}_windows-x86_64.zip doh-proxy rm -fr doh-proxy From d277c0a8061c853a63dfda8564192e341daa251f Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Mon, 2 Jan 2023 21:14:00 +0100 Subject: [PATCH 097/150] Update upload-release actions --- .github/workflows/release.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4be3298..f5c5741 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -70,7 +70,7 @@ jobs: - name: Create release id: create_release - uses: actions/create-release@v1 + uses: actions/create-release@v2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: @@ -81,7 +81,7 @@ jobs: - name: Upload Debian package id: upload-release-asset-debian - uses: actions/upload-release-asset@v1 + uses: actions/upload-release-asset@v2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: @@ -92,7 +92,7 @@ jobs: - name: Upload tarball for linux-x86_64 id: upload-release-asset-tarball-linux-x86_64 - uses: actions/upload-release-asset@v1 + uses: actions/upload-release-asset@v2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: @@ -103,7 +103,7 @@ jobs: - name: Upload tarball for linux-aarch64 id: upload-release-asset-tarball-linux-aarch64 - uses: actions/upload-release-asset@v1 + uses: actions/upload-release-asset@v2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: @@ -114,7 +114,7 @@ jobs: - name: Upload tarball for windows-x86_64 id: upload-release-asset-tarball-windows-x86_64 - uses: actions/upload-release-asset@v1 + uses: actions/upload-release-asset@v2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: From f5c07a205b04320c35a0aed27538b1d4a5195fe3 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Mon, 2 Jan 2023 21:15:00 +0100 Subject: [PATCH 098/150] Ah --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f5c5741..60b24ea 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -70,7 +70,7 @@ jobs: - name: Create release id: create_release - uses: actions/create-release@v2 + uses: actions/create-release@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: From d573a20c86077d8c752690ef58720e284ca29077 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Mon, 2 Jan 2023 21:48:23 +0100 Subject: [PATCH 099/150] Use v3 for the checkout action --- .github/workflows/release.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 60b24ea..a7dc6fe 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,7 +14,7 @@ jobs: id: get_version run: echo ::set-output name=VERSION::${GITHUB_REF#refs/tags/} - - uses: actions/checkout@master + - uses: actions/checkout@v3 - uses: goto-bus-stop/setup-zig@v2 @@ -81,7 +81,7 @@ jobs: - name: Upload Debian package id: upload-release-asset-debian - uses: actions/upload-release-asset@v2 + uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: @@ -92,7 +92,7 @@ jobs: - name: Upload tarball for linux-x86_64 id: upload-release-asset-tarball-linux-x86_64 - uses: actions/upload-release-asset@v2 + uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: @@ -103,7 +103,7 @@ jobs: - name: Upload tarball for linux-aarch64 id: upload-release-asset-tarball-linux-aarch64 - uses: actions/upload-release-asset@v2 + uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: @@ -114,7 +114,7 @@ jobs: - name: Upload tarball for windows-x86_64 id: upload-release-asset-tarball-windows-x86_64 - uses: actions/upload-release-asset@v2 + uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: From e92fddb1656fd5ed08ce91c59adb30556723b392 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Mon, 2 Jan 2023 22:29:51 +0100 Subject: [PATCH 100/150] Use zip -9 -r --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a7dc6fe..1974711 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -61,7 +61,7 @@ jobs: mkdir doh-proxy mv target/x86_64-pc-windows-gnu/release/doh-proxy.exe doh-proxy/ cp README.md localhost.pem doh-proxy/ - zip -9 doh-proxy_${{ steps.get_version.outputs.VERSION }}_windows-x86_64.zip doh-proxy + zip -9 -r doh-proxy_${{ steps.get_version.outputs.VERSION }}_windows-x86_64.zip doh-proxy rm -fr doh-proxy - name: Debian package From 3f1bbcd8dcfbe1bf5166018e3642e96ba411c1eb Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Tue, 3 Jan 2023 11:01:39 +0100 Subject: [PATCH 101/150] link-args=-s -> -C strip=symbols --- .github/workflows/release.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1974711..5e3e3f0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -37,7 +37,7 @@ jobs: - name: Release build Linux-x86-64 run: | rustup target add x86_64-unknown-linux-musl - env RUSTFLAGS="-C link-arg=-s" cargo zigbuild --release --target x86_64-unknown-linux-musl + env RUSTFLAGS="-C strip=symbols" cargo zigbuild --release --target x86_64-unknown-linux-musl mkdir doh-proxy mv target/x86_64-unknown-linux-musl/release/doh-proxy doh-proxy/ cp README.md localhost.pem doh-proxy/ @@ -47,7 +47,7 @@ jobs: - name: Release build Linux-aarch64 run: | rustup target add aarch64-unknown-linux-musl - env RUSTFLAGS="-C link-arg=-s" cargo zigbuild --release --target aarch64-unknown-linux-musl + env RUSTFLAGS="-C strip=symbols" cargo zigbuild --release --target aarch64-unknown-linux-musl mkdir doh-proxy mv target/aarch64-unknown-linux-musl/release/doh-proxy doh-proxy/ cp README.md localhost.pem doh-proxy/ @@ -57,7 +57,7 @@ jobs: - name: Release build Windows-x86_64 run: | rustup target add x86_64-pc-windows-gnu - env RUSTFLAGS="-C link-arg=-s" cargo zigbuild --release --target x86_64-pc-windows-gnu + env RUSTFLAGS="-C strip=symbols" cargo zigbuild --release --target x86_64-pc-windows-gnu mkdir doh-proxy mv target/x86_64-pc-windows-gnu/release/doh-proxy.exe doh-proxy/ cp README.md localhost.pem doh-proxy/ From b81cc3e5d2a4886713c817646cb2083431ea489f Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Tue, 3 Jan 2023 11:06:31 +0100 Subject: [PATCH 102/150] Remove glibc dependency for the Debian package Fixes #93 --- .github/workflows/release.yml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5e3e3f0..f554931 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -66,7 +66,17 @@ jobs: - name: Debian package run: | - cargo deb + rustup target add x86_64-unknown-linux-musl + env \ + RUSTFLAGS="-C lto=yes -C strip=symbols" \ + __CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS=nightly \ + CARGO_UNSTABLE_TARGET_APPLIES_TO_HOST=true \ + CARGO_TARGET_APPLIES_TO_HOST=false \ + RANLIB="zig ranlib" \ + CARGO_LINKER="zig cc --target=x86_64-linux-musl" \ + CC="zig cc --target=x86_64-linux-musl" \ + CXX="zig c++ --target=x86_64-linux-musl" \ + cargo deb --target=x86_64-unknown-linux-musl - name: Create release id: create_release From 37dc663b6edf448959dff488ae442ecac256b6a4 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Tue, 3 Jan 2023 11:53:35 +0100 Subject: [PATCH 103/150] Simpler incantation to build Debian packages --- .github/workflows/release.yml | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f554931..079e40b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -64,19 +64,12 @@ jobs: zip -9 -r doh-proxy_${{ steps.get_version.outputs.VERSION }}_windows-x86_64.zip doh-proxy rm -fr doh-proxy - - name: Debian package + - name: Debian packages run: | rustup target add x86_64-unknown-linux-musl - env \ - RUSTFLAGS="-C lto=yes -C strip=symbols" \ - __CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS=nightly \ - CARGO_UNSTABLE_TARGET_APPLIES_TO_HOST=true \ - CARGO_TARGET_APPLIES_TO_HOST=false \ - RANLIB="zig ranlib" \ - CARGO_LINKER="zig cc --target=x86_64-linux-musl" \ - CC="zig cc --target=x86_64-linux-musl" \ - CXX="zig c++ --target=x86_64-linux-musl" \ - cargo deb --target=x86_64-unknown-linux-musl + env RUSTFLAGS="-C strip=symbols" cargo deb --no-strip --cargo-build=zigbuild --target=x86_64-unknown-linux-musl + rustup target add aarch64-unknown-linux-musl + env RUSTFLAGS="-C strip=symbols" cargo deb --no-strip --cargo-build=zigbuild --target=aarch64-unknown-linux-musl - name: Create release id: create_release @@ -89,7 +82,7 @@ jobs: draft: true prerelease: false - - name: Upload Debian package + - name: Upload Debian package for x86_64 id: upload-release-asset-debian uses: actions/upload-release-asset@v1 env: @@ -97,9 +90,22 @@ jobs: with: upload_url: ${{ steps.create_release.outputs.upload_url }} asset_name: "doh-proxy_${{ steps.get_version.outputs.VERSION }}_amd64.deb" + asset_path: "target/x86_64-unknown-linux-musl/debian/doh-proxy_${{ steps.get_version.outputs.VERSION }}_amd64.deb" asset_path: "target/debian/doh-proxy_${{ steps.get_version.outputs.VERSION }}_amd64.deb" asset_content_type: application/x-debian-package + - name: Upload Debian package for aarch64 + id: upload-release-asset-debian + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_name: "doh-proxy_${{ steps.get_version.outputs.VERSION }}_aarch64.deb" + asset_path: "target/aarch64-unknown-linux-musl/debian/doh-proxy_${{ steps.get_version.outputs.VERSION }}_aarch64.deb" + asset_path: "target/debian/doh-proxy_${{ steps.get_version.outputs.VERSION }}_aarch64.deb" + asset_content_type: application/x-debian-package + - name: Upload tarball for linux-x86_64 id: upload-release-asset-tarball-linux-x86_64 uses: actions/upload-release-asset@v1 From c9e084b2b4b6a0565597aa9fa2c9137a7946ec14 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Mon, 9 Jan 2023 21:34:33 +0100 Subject: [PATCH 104/150] Title --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 54f8200..2542d56 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# doh-proxy +# DoH Server (and ODoH server) A fast and secure DoH (DNS-over-HTTPS) and ODoH (Oblivious DoH) server. From fbf82068d1c53b2c9a6e4d77ca1cc77cdb5566b4 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Thu, 12 Jan 2023 01:14:33 +0100 Subject: [PATCH 105/150] Only retrieve clap arguments as String, don't expect it to be smart Fixes #94 --- src/config.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/config.rs b/src/config.rs index 643463a..287cf8a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -232,10 +232,10 @@ pub fn parse_opts(globals: &mut Globals) { #[cfg(feature = "tls")] { globals.tls_cert_path = matches - .get_one::("tls_cert_path") + .get_one::("tls_cert_path") .map(PathBuf::from); globals.tls_cert_key_path = matches - .get_one::("tls_cert_key_path") + .get_one::("tls_cert_key_path") .map(PathBuf::from) .or_else(|| globals.tls_cert_path.clone()); } From 1c28a28b7891a5095b825ceaf9c5df931046d2f6 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Thu, 12 Jan 2023 01:16:47 +0100 Subject: [PATCH 106/150] Bump --- Cargo.toml | 6 +++--- src/libdoh/Cargo.toml | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e6be0d1..af348d8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "doh-proxy" -version = "0.9.6" +version = "0.9.7" authors = ["Frank Denis "] description = "A DNS-over-HTTPS (DoH) and ODoH (Oblivious DoH) proxy" keywords = ["dns", "https", "doh", "odoh", "proxy"] @@ -16,10 +16,10 @@ default = ["tls"] tls = ["libdoh/tls"] [dependencies] -libdoh = { path = "src/libdoh", version = "0.9.6", default-features = false } +libdoh = { path = "src/libdoh", version = "0.9.7", default-features = false } clap = { version = "4", features = ["std", "cargo", "wrap_help", "string"] } dnsstamps = "0.1.9" -mimalloc = { version = "0.1.32", default-features = false } +mimalloc = { version = "0.1.34", default-features = false } [package.metadata.deb] extended-description = """\ diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index fe209ca..b0c2757 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libdoh" -version = "0.9.6" +version = "0.9.7" authors = ["Frank Denis "] description = "DoH and Oblivious DoH library for the rust-doh app" keywords = ["dns","https","doh","odoh","proxy"] @@ -24,9 +24,9 @@ futures = "0.3.25" hyper = { version = "0.14.23", default-features = false, features = ["server", "http1", "http2", "stream"] } odoh-rs = "1.0.0" rand = "0.8.5" -tokio = { version = "1.23.0", features = ["net", "rt-multi-thread", "time", "sync"] } +tokio = { version = "1.24.1", features = ["net", "rt-multi-thread", "time", "sync"] } tokio-rustls = { version = "0.23.4", features = ["early-data"], optional = true } -rustls-pemfile = "1.0.1" +rustls-pemfile = "1.0.2" [profile.release] codegen-units = 1 From 85280f45251d40a07dacfd12876700ac5b161c89 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Thu, 12 Jan 2023 01:25:34 +0100 Subject: [PATCH 107/150] Try to build Debian packages --- .github/workflows/release.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 079e40b..ab6f4da 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -91,7 +91,6 @@ jobs: upload_url: ${{ steps.create_release.outputs.upload_url }} asset_name: "doh-proxy_${{ steps.get_version.outputs.VERSION }}_amd64.deb" asset_path: "target/x86_64-unknown-linux-musl/debian/doh-proxy_${{ steps.get_version.outputs.VERSION }}_amd64.deb" - asset_path: "target/debian/doh-proxy_${{ steps.get_version.outputs.VERSION }}_amd64.deb" asset_content_type: application/x-debian-package - name: Upload Debian package for aarch64 @@ -103,7 +102,6 @@ jobs: upload_url: ${{ steps.create_release.outputs.upload_url }} asset_name: "doh-proxy_${{ steps.get_version.outputs.VERSION }}_aarch64.deb" asset_path: "target/aarch64-unknown-linux-musl/debian/doh-proxy_${{ steps.get_version.outputs.VERSION }}_aarch64.deb" - asset_path: "target/debian/doh-proxy_${{ steps.get_version.outputs.VERSION }}_aarch64.deb" asset_content_type: application/x-debian-package - name: Upload tarball for linux-x86_64 From 8cba04338e2b6575acd0522b340bcfa28e74d2a4 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Thu, 12 Jan 2023 01:27:37 +0100 Subject: [PATCH 108/150] Debian again... --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ab6f4da..331ae0b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -83,7 +83,7 @@ jobs: prerelease: false - name: Upload Debian package for x86_64 - id: upload-release-asset-debian + id: upload-release-asset-debian-x86_64 uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -94,7 +94,7 @@ jobs: asset_content_type: application/x-debian-package - name: Upload Debian package for aarch64 - id: upload-release-asset-debian + id: upload-release-asset-debian-aarch64 uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From d5fd8231ffd43a1f94dfc9677f8a5516652651d1 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Thu, 12 Jan 2023 01:38:53 +0100 Subject: [PATCH 109/150] Sorry, Debian-aarch64 users --- .github/workflows/release.yml | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 331ae0b..f07e45d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -93,17 +93,6 @@ jobs: asset_path: "target/x86_64-unknown-linux-musl/debian/doh-proxy_${{ steps.get_version.outputs.VERSION }}_amd64.deb" asset_content_type: application/x-debian-package - - name: Upload Debian package for aarch64 - id: upload-release-asset-debian-aarch64 - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_name: "doh-proxy_${{ steps.get_version.outputs.VERSION }}_aarch64.deb" - asset_path: "target/aarch64-unknown-linux-musl/debian/doh-proxy_${{ steps.get_version.outputs.VERSION }}_aarch64.deb" - asset_content_type: application/x-debian-package - - name: Upload tarball for linux-x86_64 id: upload-release-asset-tarball-linux-x86_64 uses: actions/upload-release-asset@v1 From 47330ebcade881a1da7a26ee219869427e11e4bf Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Tue, 31 Jan 2023 00:00:21 +0100 Subject: [PATCH 110/150] Update deps --- src/libdoh/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index b0c2757..909f861 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -20,11 +20,11 @@ arc-swap = "1.6.0" base64 = "0.20.0" byteorder = "1.4.3" bytes = "1.3.0" -futures = "0.3.25" +futures = "0.3.26" hyper = { version = "0.14.23", default-features = false, features = ["server", "http1", "http2", "stream"] } -odoh-rs = "1.0.0" +odoh-rs = "1.0.1" rand = "0.8.5" -tokio = { version = "1.24.1", features = ["net", "rt-multi-thread", "time", "sync"] } +tokio = { version = "1.25.0", features = ["net", "rt-multi-thread", "time", "sync"] } tokio-rustls = { version = "0.23.4", features = ["early-data"], optional = true } rustls-pemfile = "1.0.2" From 11d8f4cb31ae0f2eaa9ba7841d4068509e066517 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Wed, 1 Feb 2023 20:28:37 +0100 Subject: [PATCH 111/150] Add a logo --- README.md | 2 +- logo.png | Bin 0 -> 59408 bytes 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 logo.png diff --git a/README.md b/README.md index 2542d56..971fdc2 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# DoH Server (and ODoH server) +# ![DoH server (and ODoH - Oblivious DoH server)](logo.png) A fast and secure DoH (DNS-over-HTTPS) and ODoH (Oblivious DoH) server. diff --git a/logo.png b/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..21e85fda91ef3ecd99154959ad386a4cd7af1e6c GIT binary patch literal 59408 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 DMnP+? literal 0 HcmV?d00001 From b5d525abcd871b3522239b38efcd571089be17c3 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Thu, 9 Feb 2023 16:58:31 +0100 Subject: [PATCH 112/150] Update deps --- src/libdoh/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index 909f861..50a1841 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -15,13 +15,13 @@ default = ["tls"] tls = ["tokio-rustls"] [dependencies] -anyhow = "1.0.68" +anyhow = "1.0.69" arc-swap = "1.6.0" base64 = "0.20.0" byteorder = "1.4.3" -bytes = "1.3.0" +bytes = "1.4.0" futures = "0.3.26" -hyper = { version = "0.14.23", default-features = false, features = ["server", "http1", "http2", "stream"] } +hyper = { version = "0.14.24", default-features = false, features = ["server", "http1", "http2", "stream"] } odoh-rs = "1.0.1" rand = "0.8.5" tokio = { version = "1.25.0", features = ["net", "rt-multi-thread", "time", "sync"] } From 651224d900d600ea7d0e07e6767d0e48709e3d8e Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Thu, 9 Feb 2023 16:58:58 +0100 Subject: [PATCH 113/150] Format --- src/libdoh/src/lib.rs | 3 ++- src/libdoh/src/tls.rs | 8 ++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/libdoh/src/lib.rs b/src/libdoh/src/lib.rs index 3022a7b..06ee661 100644 --- a/src/libdoh/src/lib.rs +++ b/src/libdoh/src/lib.rs @@ -433,7 +433,8 @@ impl DoH { .header( hyper::header::CACHE_CONTROL, format!( - "max-age={ttl}, stale-if-error={STALE_IF_ERROR_SECS}, stale-while-revalidate={STALE_WHILE_REVALIDATE_SECS}" + "max-age={ttl}, stale-if-error={STALE_IF_ERROR_SECS}, \ + stale-while-revalidate={STALE_WHILE_REVALIDATE_SECS}" ) .as_str(), ); diff --git a/src/libdoh/src/tls.rs b/src/libdoh/src/tls.rs index ccc4585..7047f99 100644 --- a/src/libdoh/src/tls.rs +++ b/src/libdoh/src/tls.rs @@ -29,9 +29,7 @@ where let mut reader = BufReader::new(File::open(certs_path).map_err(|e| { io::Error::new( e.kind(), - format!( - "Unable to load the certificates [{certs_path_str}]: {e}" - ), + format!("Unable to load the certificates [{certs_path_str}]: {e}"), ) })?); rustls_pemfile::certs(&mut reader).map_err(|_| { @@ -52,9 +50,7 @@ where .map_err(|e| { io::Error::new( e.kind(), - format!( - "Unable to load the certificate keys [{certs_keys_path_str}]: {e}" - ), + format!("Unable to load the certificate keys [{certs_keys_path_str}]: {e}"), ) })? .read_to_end(&mut encoded_keys)?; From 920d31b5027b6ad2fc66a053d99b6e3c9c3c0bab Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Thu, 9 Feb 2023 17:16:34 +0100 Subject: [PATCH 114/150] Update relayd URL --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 971fdc2..625eac8 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ Here, `doh.example.com` is the host name (which should match a name included in ## HTTP/2 termination -The recommended way to use `doh-proxy` is to use a TLS termination proxy (such as [hitch](https://github.com/varnish/hitch) or [relayd](https://bsd.plumbing/about.html)), a CDN or a web server with proxying abilities as a front-end. +The recommended way to use `doh-proxy` is to use a TLS termination proxy (such as [hitch](https://github.com/varnish/hitch) or [relayd](https://man.openbsd.org/relayd.8)), a CDN or a web server with proxying abilities as a front-end. That way, the DoH service can be exposed as a virtual host, sharing the same IP addresses as existing websites. From 1386b7d13a8381da1d8c8d450a29ae05f757a378 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Thu, 9 Feb 2023 17:18:27 +0100 Subject: [PATCH 115/150] Mention HTTP/3 --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 625eac8..801902d 100644 --- a/README.md +++ b/README.md @@ -68,13 +68,13 @@ doh-proxy -H 'doh.example.com' -u 127.0.0.1:53 -g 233.252.0.5 Here, `doh.example.com` is the host name (which should match a name included in the TLS certificate), `127.0.0.1:53` is the address of the DNS resolver, and `233.252.0.5` is the public IP address of the DoH server. -## HTTP/2 termination +## HTTP/2 and HTTP/3 termination The recommended way to use `doh-proxy` is to use a TLS termination proxy (such as [hitch](https://github.com/varnish/hitch) or [relayd](https://man.openbsd.org/relayd.8)), a CDN or a web server with proxying abilities as a front-end. That way, the DoH service can be exposed as a virtual host, sharing the same IP addresses as existing websites. -If `doh-proxy` and the HTTP/2 front-end run on the same host, using the HTTP protocol to communicate between both is fine. +If `doh-proxy` and the HTTP/2 (/ HTTP/3) front-end run on the same host, using the HTTP protocol to communicate between both is fine. If both are on distinct networks, such as when using a CDN, `doh-proxy` can handle HTTPS requests, provided that it was compiled with the `tls` feature. @@ -136,7 +136,7 @@ This can be achieved with the `--allow-odoh-post` command-line switch. * When using DoH, DNS stamps should include a resolver IP address in order to remove a dependency on non-encrypted, non-authenticated, easy-to-block resolvers. * Unlike DNSCrypt where users must explicitly trust a DNS server's public key, the security of DoH relies on traditional public Certificate Authorities. Additional root certificates (required by governments, security software, enterprise gateways) installed on a client immediately make DoH vulnerable to MITM. In order to prevent this, DNS stamps should include the hash of the parent certificate. * TLS certificates are tied to host names. But domains expire, get reassigned and switch hands all the time. If a domain originally used for a DoH service gets a new, possibly malicious owner, clients still configured to use the service will blindly keep trusting it if the CA is the same. As a mitigation, the CA should sign an intermediate certificate (the only one present in the stamp), itself used to sign the name used by the DoH server. While commercial CAs offer this, Let's Encrypt currently doesn't. -* Make sure that the front-end supports HTTP/2 and TLS 1.3. +* Make sure that the front-end supports HTTP/2 and TLS 1.3, and optionally HTTP/3. * Internal DoH servers still require TLS certificates. So, if you are planning to deploy an internal server, you need to set up an internal CA, or add self-signed certificates to every single client. ## Example usage with `encrypted-dns-server` From 1c5c83803a325d8fececa77ea20b2a1d45f0bee9 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Thu, 9 Feb 2023 17:21:29 +0100 Subject: [PATCH 116/150] Remove optional requirement --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 801902d..52b45df 100644 --- a/README.md +++ b/README.md @@ -136,7 +136,7 @@ This can be achieved with the `--allow-odoh-post` command-line switch. * When using DoH, DNS stamps should include a resolver IP address in order to remove a dependency on non-encrypted, non-authenticated, easy-to-block resolvers. * Unlike DNSCrypt where users must explicitly trust a DNS server's public key, the security of DoH relies on traditional public Certificate Authorities. Additional root certificates (required by governments, security software, enterprise gateways) installed on a client immediately make DoH vulnerable to MITM. In order to prevent this, DNS stamps should include the hash of the parent certificate. * TLS certificates are tied to host names. But domains expire, get reassigned and switch hands all the time. If a domain originally used for a DoH service gets a new, possibly malicious owner, clients still configured to use the service will blindly keep trusting it if the CA is the same. As a mitigation, the CA should sign an intermediate certificate (the only one present in the stamp), itself used to sign the name used by the DoH server. While commercial CAs offer this, Let's Encrypt currently doesn't. -* Make sure that the front-end supports HTTP/2 and TLS 1.3, and optionally HTTP/3. +* Make sure that the front-end supports HTTP/2 and TLS 1.3. * Internal DoH servers still require TLS certificates. So, if you are planning to deploy an internal server, you need to set up an internal CA, or add self-signed certificates to every single client. ## Example usage with `encrypted-dns-server` From c54b3303fc739bc3d451ca2d214557512883a329 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sun, 19 Feb 2023 21:02:28 +0100 Subject: [PATCH 117/150] Update base64, accept padding on decoding --- src/libdoh/src/lib.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/libdoh/src/lib.rs b/src/libdoh/src/lib.rs index 06ee661..4b6eea8 100644 --- a/src/libdoh/src/lib.rs +++ b/src/libdoh/src/lib.rs @@ -11,6 +11,7 @@ use std::pin::Pin; use std::sync::Arc; use std::time::Duration; +use base64::engine::Engine; use byteorder::{BigEndian, ByteOrder}; use futures::prelude::*; use futures::task::{Context, Poll}; @@ -29,10 +30,12 @@ pub mod reexports { pub use tokio; } -const BASE64_URL_SAFE_NO_PAD: base64::engine::fast_portable::FastPortable = - base64::engine::fast_portable::FastPortable::from( +const BASE64_URL_SAFE_NO_PAD: base64::engine::GeneralPurpose = + base64::engine::general_purpose::GeneralPurpose::new( &base64::alphabet::URL_SAFE, - base64::engine::fast_portable::NO_PAD, + base64::engine::general_purpose::GeneralPurposeConfig::new() + .with_encode_padding(false) + .with_decode_padding_mode(base64::engine::DecodePaddingMode::Indifferent), ); #[derive(Clone, Debug)] @@ -167,9 +170,9 @@ impl DoH { return None; } } - let query = match question_str.and_then(|question_str| { - base64::decode_engine(question_str, &BASE64_URL_SAFE_NO_PAD).ok() - }) { + let query = match question_str + .and_then(|question_str| BASE64_URL_SAFE_NO_PAD.decode(question_str).ok()) + { Some(query) => query, _ => return None, }; From 908e7d64dba40fdc32ef2be34a92bba07b6673a2 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sun, 19 Feb 2023 21:05:34 +0100 Subject: [PATCH 118/150] Update base64 --- src/libdoh/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index 50a1841..8c6fece 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -17,7 +17,7 @@ tls = ["tokio-rustls"] [dependencies] anyhow = "1.0.69" arc-swap = "1.6.0" -base64 = "0.20.0" +base64 = "0.21.0" byteorder = "1.4.3" bytes = "1.4.0" futures = "0.3.26" From 18297228c74ef8cf0aec5cc0edb01377445b9964 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sun, 19 Feb 2023 21:10:14 +0100 Subject: [PATCH 119/150] Bump --- Cargo.toml | 4 ++-- src/libdoh/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index af348d8..3c13e6f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "doh-proxy" -version = "0.9.7" +version = "0.9.8" authors = ["Frank Denis "] description = "A DNS-over-HTTPS (DoH) and ODoH (Oblivious DoH) proxy" keywords = ["dns", "https", "doh", "odoh", "proxy"] @@ -16,7 +16,7 @@ default = ["tls"] tls = ["libdoh/tls"] [dependencies] -libdoh = { path = "src/libdoh", version = "0.9.7", default-features = false } +libdoh = { path = "src/libdoh", version = "0.9.8", default-features = false } clap = { version = "4", features = ["std", "cargo", "wrap_help", "string"] } dnsstamps = "0.1.9" mimalloc = { version = "0.1.34", default-features = false } diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index 8c6fece..63ce489 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libdoh" -version = "0.9.7" +version = "0.9.8" authors = ["Frank Denis "] description = "DoH and Oblivious DoH library for the rust-doh app" keywords = ["dns","https","doh","odoh","proxy"] From f64770bdd7132b38d546a5f9328afe7fbb3f74ea Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sun, 19 Feb 2023 21:44:31 +0100 Subject: [PATCH 120/150] Install zig 0.10.1 --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f07e45d..dcf2991 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,6 +17,7 @@ jobs: - uses: actions/checkout@v3 - uses: goto-bus-stop/setup-zig@v2 + version: 0.10.1 - uses: hecrj/setup-rust-action@master with: From 6580f6ffb52ff3452bee6f5b596a59cc350fb14d Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sun, 19 Feb 2023 21:50:08 +0100 Subject: [PATCH 121/150] Fix CI --- .github/workflows/release.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index dcf2991..a3c8be0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,7 +17,8 @@ jobs: - uses: actions/checkout@v3 - uses: goto-bus-stop/setup-zig@v2 - version: 0.10.1 + with: + version: 0.10.1 - uses: hecrj/setup-rust-action@master with: From ffa082851576f518625b7174b437bc05af9ada52 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Thu, 2 Mar 2023 19:05:11 +0100 Subject: [PATCH 122/150] Update tokio --- src/libdoh/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index 63ce489..f60a1c4 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -24,7 +24,7 @@ futures = "0.3.26" hyper = { version = "0.14.24", default-features = false, features = ["server", "http1", "http2", "stream"] } odoh-rs = "1.0.1" rand = "0.8.5" -tokio = { version = "1.25.0", features = ["net", "rt-multi-thread", "time", "sync"] } +tokio = { version = "1.26.0", features = ["net", "rt-multi-thread", "time", "sync"] } tokio-rustls = { version = "0.23.4", features = ["early-data"], optional = true } rustls-pemfile = "1.0.2" From 678bd04bed08dcf097f7eeef3b0172fd27018d26 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Thu, 13 Apr 2023 17:12:29 +0200 Subject: [PATCH 123/150] Update deps --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 3c13e6f..0f880cc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ tls = ["libdoh/tls"] libdoh = { path = "src/libdoh", version = "0.9.8", default-features = false } clap = { version = "4", features = ["std", "cargo", "wrap_help", "string"] } dnsstamps = "0.1.9" -mimalloc = { version = "0.1.34", default-features = false } +mimalloc = { version = "0.1.36", default-features = false } [package.metadata.deb] extended-description = """\ From 6f9f63e754dd700ed8215c2cb45d43ffad96f097 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Thu, 13 Apr 2023 17:13:03 +0200 Subject: [PATCH 124/150] Update deps, especially hyper --- src/libdoh/Cargo.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index f60a1c4..0ee20e8 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -15,16 +15,16 @@ default = ["tls"] tls = ["tokio-rustls"] [dependencies] -anyhow = "1.0.69" +anyhow = "1.0.70" arc-swap = "1.6.0" base64 = "0.21.0" byteorder = "1.4.3" bytes = "1.4.0" -futures = "0.3.26" -hyper = { version = "0.14.24", default-features = false, features = ["server", "http1", "http2", "stream"] } +futures = "0.3.28" +hyper = { version = "0.14.25", default-features = false, features = ["server", "http1", "http2", "stream"] } odoh-rs = "1.0.1" rand = "0.8.5" -tokio = { version = "1.26.0", features = ["net", "rt-multi-thread", "time", "sync"] } +tokio = { version = "1.27.0", features = ["net", "rt-multi-thread", "time", "sync"] } tokio-rustls = { version = "0.23.4", features = ["early-data"], optional = true } rustls-pemfile = "1.0.2" From 19040f1e884e4b9c381f79bcd189ad71c03675c7 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Fri, 14 Apr 2023 09:45:20 +0200 Subject: [PATCH 125/150] Nits --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 52b45df..91e0783 100644 --- a/README.md +++ b/README.md @@ -136,7 +136,7 @@ This can be achieved with the `--allow-odoh-post` command-line switch. * When using DoH, DNS stamps should include a resolver IP address in order to remove a dependency on non-encrypted, non-authenticated, easy-to-block resolvers. * Unlike DNSCrypt where users must explicitly trust a DNS server's public key, the security of DoH relies on traditional public Certificate Authorities. Additional root certificates (required by governments, security software, enterprise gateways) installed on a client immediately make DoH vulnerable to MITM. In order to prevent this, DNS stamps should include the hash of the parent certificate. * TLS certificates are tied to host names. But domains expire, get reassigned and switch hands all the time. If a domain originally used for a DoH service gets a new, possibly malicious owner, clients still configured to use the service will blindly keep trusting it if the CA is the same. As a mitigation, the CA should sign an intermediate certificate (the only one present in the stamp), itself used to sign the name used by the DoH server. While commercial CAs offer this, Let's Encrypt currently doesn't. -* Make sure that the front-end supports HTTP/2 and TLS 1.3. +* Make sure that the front-end supports at least HTTP/2 and TLS 1.3. * Internal DoH servers still require TLS certificates. So, if you are planning to deploy an internal server, you need to set up an internal CA, or add self-signed certificates to every single client. ## Example usage with `encrypted-dns-server` From e8df0458ac79b2b1d72c974b66641931c891b1d2 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Fri, 14 Apr 2023 12:38:08 +0200 Subject: [PATCH 126/150] Bump hyper. Again. --- src/libdoh/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index 0ee20e8..603218b 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -21,7 +21,7 @@ base64 = "0.21.0" byteorder = "1.4.3" bytes = "1.4.0" futures = "0.3.28" -hyper = { version = "0.14.25", default-features = false, features = ["server", "http1", "http2", "stream"] } +hyper = { version = "0.14.26", default-features = false, features = ["server", "http1", "http2", "stream"] } odoh-rs = "1.0.1" rand = "0.8.5" tokio = { version = "1.27.0", features = ["net", "rt-multi-thread", "time", "sync"] } From e5f6f2a5d65f29af0e97597041ce6f8604e01d87 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Fri, 14 Apr 2023 12:44:12 +0200 Subject: [PATCH 127/150] Bump --- Cargo.toml | 4 ++-- src/libdoh/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0f880cc..e37d98a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "doh-proxy" -version = "0.9.8" +version = "0.9.9" authors = ["Frank Denis "] description = "A DNS-over-HTTPS (DoH) and ODoH (Oblivious DoH) proxy" keywords = ["dns", "https", "doh", "odoh", "proxy"] @@ -16,7 +16,7 @@ default = ["tls"] tls = ["libdoh/tls"] [dependencies] -libdoh = { path = "src/libdoh", version = "0.9.8", default-features = false } +libdoh = { path = "src/libdoh", version = "0.9.9", default-features = false } clap = { version = "4", features = ["std", "cargo", "wrap_help", "string"] } dnsstamps = "0.1.9" mimalloc = { version = "0.1.36", default-features = false } diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index 603218b..ef65f30 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libdoh" -version = "0.9.8" +version = "0.9.9" authors = ["Frank Denis "] description = "DoH and Oblivious DoH library for the rust-doh app" keywords = ["dns","https","doh","odoh","proxy"] From 9e2853da86990d782a50e2fe882eda79626a8891 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Wed, 3 May 2023 17:35:23 +0200 Subject: [PATCH 128/150] Update deps --- Cargo.toml | 2 +- src/libdoh/Cargo.toml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e37d98a..2772885 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ tls = ["libdoh/tls"] libdoh = { path = "src/libdoh", version = "0.9.9", default-features = false } clap = { version = "4", features = ["std", "cargo", "wrap_help", "string"] } dnsstamps = "0.1.9" -mimalloc = { version = "0.1.36", default-features = false } +mimalloc = { version = "0.1.37", default-features = false } [package.metadata.deb] extended-description = """\ diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index ef65f30..5c85b08 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -15,7 +15,7 @@ default = ["tls"] tls = ["tokio-rustls"] [dependencies] -anyhow = "1.0.70" +anyhow = "1.0.71" arc-swap = "1.6.0" base64 = "0.21.0" byteorder = "1.4.3" @@ -24,8 +24,8 @@ futures = "0.3.28" hyper = { version = "0.14.26", default-features = false, features = ["server", "http1", "http2", "stream"] } odoh-rs = "1.0.1" rand = "0.8.5" -tokio = { version = "1.27.0", features = ["net", "rt-multi-thread", "time", "sync"] } -tokio-rustls = { version = "0.23.4", features = ["early-data"], optional = true } +tokio = { version = "1.28.0", features = ["net", "rt-multi-thread", "time", "sync"] } +tokio-rustls = { version = "0.24.0", features = ["early-data"], optional = true } rustls-pemfile = "1.0.2" [profile.release] From 78c47830ff15e31d99810ea3478da9ce82fe60d2 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sat, 15 Jul 2023 21:18:46 +0200 Subject: [PATCH 129/150] Update deps --- Cargo.toml | 2 +- src/libdoh/Cargo.toml | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2772885..1ac4cb5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "doh-proxy" -version = "0.9.9" +version = "0.9.10" authors = ["Frank Denis "] description = "A DNS-over-HTTPS (DoH) and ODoH (Oblivious DoH) proxy" keywords = ["dns", "https", "doh", "odoh", "proxy"] diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index 5c85b08..66fc1c3 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libdoh" -version = "0.9.9" +version = "0.9.10" authors = ["Frank Denis "] description = "DoH and Oblivious DoH library for the rust-doh app" keywords = ["dns","https","doh","odoh","proxy"] @@ -17,16 +17,16 @@ tls = ["tokio-rustls"] [dependencies] anyhow = "1.0.71" arc-swap = "1.6.0" -base64 = "0.21.0" +base64 = "0.21.2" byteorder = "1.4.3" bytes = "1.4.0" futures = "0.3.28" -hyper = { version = "0.14.26", default-features = false, features = ["server", "http1", "http2", "stream"] } -odoh-rs = "1.0.1" +hyper = { version = "0.14.27", default-features = false, features = ["server", "http1", "http2", "stream"] } +odoh-rs = "1.0.2" rand = "0.8.5" -tokio = { version = "1.28.0", features = ["net", "rt-multi-thread", "time", "sync"] } -tokio-rustls = { version = "0.24.0", features = ["early-data"], optional = true } -rustls-pemfile = "1.0.2" +tokio = { version = "1.29.1", features = ["net", "rt-multi-thread", "time", "sync"] } +tokio-rustls = { version = "0.24.1", features = ["early-data"], optional = true } +rustls-pemfile = "1.0.3" [profile.release] codegen-units = 1 From c92308ccbb48ebea146da4fd83800d0d4d6d5315 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sat, 2 Sep 2023 00:20:06 +0200 Subject: [PATCH 130/150] Update deps --- Cargo.toml | 2 +- src/libdoh/Cargo.toml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1ac4cb5..c7c46ff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ tls = ["libdoh/tls"] libdoh = { path = "src/libdoh", version = "0.9.9", default-features = false } clap = { version = "4", features = ["std", "cargo", "wrap_help", "string"] } dnsstamps = "0.1.9" -mimalloc = { version = "0.1.37", default-features = false } +mimalloc = { version = "0.1.38", default-features = false } [package.metadata.deb] extended-description = """\ diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index 66fc1c3..55a26b6 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -15,16 +15,16 @@ default = ["tls"] tls = ["tokio-rustls"] [dependencies] -anyhow = "1.0.71" +anyhow = "1.0.75" arc-swap = "1.6.0" -base64 = "0.21.2" +base64 = "0.21.3" byteorder = "1.4.3" bytes = "1.4.0" futures = "0.3.28" hyper = { version = "0.14.27", default-features = false, features = ["server", "http1", "http2", "stream"] } odoh-rs = "1.0.2" rand = "0.8.5" -tokio = { version = "1.29.1", features = ["net", "rt-multi-thread", "time", "sync"] } +tokio = { version = "1.32.0", features = ["net", "rt-multi-thread", "time", "sync"] } tokio-rustls = { version = "0.24.1", features = ["early-data"], optional = true } rustls-pemfile = "1.0.3" From 1165fab90c0f0beee93e82e8bd019e090d97908b Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Wed, 6 Mar 2024 18:25:38 +0100 Subject: [PATCH 131/150] Update a few deps --- Cargo.toml | 2 +- src/libdoh/Cargo.toml | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c7c46ff..d38b674 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ tls = ["libdoh/tls"] libdoh = { path = "src/libdoh", version = "0.9.9", default-features = false } clap = { version = "4", features = ["std", "cargo", "wrap_help", "string"] } dnsstamps = "0.1.9" -mimalloc = { version = "0.1.38", default-features = false } +mimalloc = { version = "0.1.39", default-features = false } [package.metadata.deb] extended-description = """\ diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index 55a26b6..f7c2b46 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -15,18 +15,18 @@ default = ["tls"] tls = ["tokio-rustls"] [dependencies] -anyhow = "1.0.75" -arc-swap = "1.6.0" -base64 = "0.21.3" -byteorder = "1.4.3" -bytes = "1.4.0" -futures = "0.3.28" -hyper = { version = "0.14.27", default-features = false, features = ["server", "http1", "http2", "stream"] } +anyhow = "1.0.80" +arc-swap = "1.7.0" +base64 = "0.22.0" +byteorder = "1.5.0" +bytes = "1.5.0" +futures = "0.3.30" +hyper = { version = "^0.14.27", default-features = false, features = ["server", "http1", "http2", "stream"] } odoh-rs = "1.0.2" rand = "0.8.5" -tokio = { version = "1.32.0", features = ["net", "rt-multi-thread", "time", "sync"] } -tokio-rustls = { version = "0.24.1", features = ["early-data"], optional = true } -rustls-pemfile = "1.0.3" +tokio = { version = "1.36.0", features = ["net", "rt-multi-thread", "time", "sync"] } +tokio-rustls = { version = "^0.24.1", features = ["early-data"], optional = true } +rustls-pemfile = "^1.0.4" [profile.release] codegen-units = 1 From 66c66c7a28c4f1f7596cd697619c2482822458cb Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sun, 5 May 2024 18:01:19 +0200 Subject: [PATCH 132/150] Update mimalloc --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index d38b674..9d4cfde 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ tls = ["libdoh/tls"] libdoh = { path = "src/libdoh", version = "0.9.9", default-features = false } clap = { version = "4", features = ["std", "cargo", "wrap_help", "string"] } dnsstamps = "0.1.9" -mimalloc = { version = "0.1.39", default-features = false } +mimalloc = { version = "0.1.41", default-features = false } [package.metadata.deb] extended-description = """\ From 02b3a67a0087131dc95c71a4cc33426b914b4a2d Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Mon, 6 May 2024 12:22:21 +0200 Subject: [PATCH 133/150] Update hyper to 0.14.28 --- src/libdoh/Cargo.toml | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index f7c2b46..b84c89c 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -3,11 +3,11 @@ name = "libdoh" version = "0.9.10" authors = ["Frank Denis "] description = "DoH and Oblivious DoH library for the rust-doh app" -keywords = ["dns","https","doh","odoh","proxy"] +keywords = ["dns", "https", "doh", "odoh", "proxy"] license = "MIT" homepage = "https://github.com/jedisct1/rust-doh" repository = "https://github.com/jedisct1/rust-doh" -categories = ["asynchronous", "network-programming","command-line-utilities"] +categories = ["asynchronous", "network-programming", "command-line-utilities"] edition = "2018" [features] @@ -21,11 +21,24 @@ base64 = "0.22.0" byteorder = "1.5.0" bytes = "1.5.0" futures = "0.3.30" -hyper = { version = "^0.14.27", default-features = false, features = ["server", "http1", "http2", "stream"] } +hyper = { version = "^0.14.28", default-features = false, features = [ + "server", + "http1", + "http2", + "stream", + "runtime", +] } odoh-rs = "1.0.2" rand = "0.8.5" -tokio = { version = "1.36.0", features = ["net", "rt-multi-thread", "time", "sync"] } -tokio-rustls = { version = "^0.24.1", features = ["early-data"], optional = true } +tokio = { version = "1.36.0", features = [ + "net", + "rt-multi-thread", + "time", + "sync", +] } +tokio-rustls = { version = "^0.24.1", features = [ + "early-data", +], optional = true } rustls-pemfile = "^1.0.4" [profile.release] From bd85572368859bfbeec3517c488f6feb24cafe76 Mon Sep 17 00:00:00 2001 From: demarcush <146051763+demarcush@users.noreply.github.com> Date: Tue, 14 May 2024 03:44:17 +0000 Subject: [PATCH 134/150] Update common hashes --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 91e0783..21fe75a 100644 --- a/README.md +++ b/README.md @@ -199,6 +199,8 @@ This [Go code snippet](https://gist.github.com/d6cb41742a1ceb54d48cc286f3d5c5fa) * `444ebd67bb83f8807b3921e938ac9178b882bd50aadb11231f044cf5f08df7ce` * Let's Encrypt E1: * `cc1060d39c8329b62b6fbc7d0d6df9309869b981e7e6392d5cd8fa408f4d80e6` +* ZeroSSL: + * `9a3a34f727deb9bca51003d9ce9c39f8f27dd9c5242901c2bab1a44e635a0219` ## Clients From 3511672d499551e4de8a76c91fbf1bd2b316eba1 Mon Sep 17 00:00:00 2001 From: demarcush <146051763+demarcush@users.noreply.github.com> Date: Tue, 2 Jul 2024 20:47:53 +0000 Subject: [PATCH 135/150] Add Let's Encrypt R10 --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 21fe75a..6b0c9f6 100644 --- a/README.md +++ b/README.md @@ -195,10 +195,12 @@ This [Go code snippet](https://gist.github.com/d6cb41742a1ceb54d48cc286f3d5c5fa) ### Common certificate hashes -* Let's Encrypt R3: - * `444ebd67bb83f8807b3921e938ac9178b882bd50aadb11231f044cf5f08df7ce` * Let's Encrypt E1: * `cc1060d39c8329b62b6fbc7d0d6df9309869b981e7e6392d5cd8fa408f4d80e6` +* Let's Encrypt R3: + * `444ebd67bb83f8807b3921e938ac9178b882bd50aadb11231f044cf5f08df7ce` +* Let's Encrypt R10: + * `e644ba6963e335fe765cb9976b12b10eb54294b42477764ccb3a3acca3acb2fc` * ZeroSSL: * `9a3a34f727deb9bca51003d9ce9c39f8f27dd9c5242901c2bab1a44e635a0219` From 7bb8293c2873488fcc2add905c4cb9cb2e9b7522 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Wed, 3 Jul 2024 12:33:29 +0200 Subject: [PATCH 136/150] package.metadata.generate-rpm --- Cargo.toml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 9d4cfde..5ba2356 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,12 @@ clap = { version = "4", features = ["std", "cargo", "wrap_help", "string"] } dnsstamps = "0.1.9" mimalloc = { version = "0.1.41", default-features = false } +[package.metadata.generate-rpm] +assets = [ + { source = "target/release/doh-proxy", dest = "/usr/bin/doh-proxy", mode = "755" }, + { source = "README.md", dest = "/usr/share/doc/doh-proxy/README.md", mode = "644", doc = true }, +] + [package.metadata.deb] extended-description = """\ A fast and secure DoH (DNS-over-HTTPS) and ODoH server written in Rust.""" From bafbdc0926e2f8246b90226faff0dcac62c004d9 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Wed, 3 Jul 2024 13:27:29 +0200 Subject: [PATCH 137/150] Try creating RPM packages Fixes #98 --- .github/workflows/release.yml | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a3c8be0..811b41d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -33,6 +33,9 @@ jobs: - name: Install cargo-deb run: cargo install cargo-deb + - name: Install cargo-generate-rpm + run: cargo install cargo-generate-rpm + - name: Install cargo-zigbuild run: cargo install cargo-zigbuild @@ -73,6 +76,16 @@ jobs: rustup target add aarch64-unknown-linux-musl env RUSTFLAGS="-C strip=symbols" cargo deb --no-strip --cargo-build=zigbuild --target=aarch64-unknown-linux-musl + - name: RPM packages + run: | + rustup target add x86_64-unknown-linux-gnu + env RUSTFLAGS="-C strip=symbols" cargo-zigbuild build --target=x86_64-unknown-linux-gnu.2.17 --release + mv target/x86_64-unknown-linux-musl/release/doh-proxy target/release/ + cargo generate-rpm --target x86_64-unknown-linux-gnu + rustup target add aarch64-unknown-linux-gnu + env RUSTFLAGS="-C strip=symbols" cargo-zigbuild build --target=aarch64-unknown-linux-gnu.2.17 --release + cargo generate-rpm --target aarch64-unknown-linux-gnu + - name: Create release id: create_release uses: actions/create-release@v1 @@ -95,6 +108,28 @@ jobs: asset_path: "target/x86_64-unknown-linux-musl/debian/doh-proxy_${{ steps.get_version.outputs.VERSION }}_amd64.deb" asset_content_type: application/x-debian-package + - name: Upload RPM package for x86_64 + id: upload-release-asset-rpm-x86_64 + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_name: "doh-proxy-${{ steps.get_version.outputs.VERSION }}-1.x86_64.rpm" + asset_path: "target/x86_64-unknown-linux-gnu/generate-rpm/doh-proxy-${{ steps.get_version.outputs.VERSION }}-1.x86_64.rpm" + asset_content_type: application/x-redhat-package-manager + + - name: Upload RPM package for aarch64 + id: upload-release-asset-rpm-aarch64 + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_name: "doh-proxy-${{ steps.get_version.outputs.VERSION }}-1.aarch64.rpm" + asset_path: "target/aarch64-unknown-linux-gnu/generate-rpm/doh-proxy-${{ steps.get_version.outputs.VERSION }}-1.aarch64.rpm" + asset_content_type: application/x-redhat-package-manager + - name: Upload tarball for linux-x86_64 id: upload-release-asset-tarball-linux-x86_64 uses: actions/upload-release-asset@v1 From e73964fa1df37c616ad349b39ea5037d4e8adfd2 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Wed, 3 Jul 2024 13:52:56 +0200 Subject: [PATCH 138/150] Update deps --- Cargo.toml | 4 ++-- src/libdoh/Cargo.toml | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5ba2356..d7e7ca0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "doh-proxy" -version = "0.9.10" +version = "0.9.11" authors = ["Frank Denis "] description = "A DNS-over-HTTPS (DoH) and ODoH (Oblivious DoH) proxy" keywords = ["dns", "https", "doh", "odoh", "proxy"] @@ -19,7 +19,7 @@ tls = ["libdoh/tls"] libdoh = { path = "src/libdoh", version = "0.9.9", default-features = false } clap = { version = "4", features = ["std", "cargo", "wrap_help", "string"] } dnsstamps = "0.1.9" -mimalloc = { version = "0.1.41", default-features = false } +mimalloc = { version = "0.1.43", default-features = false } [package.metadata.generate-rpm] assets = [ diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index b84c89c..7b72764 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libdoh" -version = "0.9.10" +version = "0.9.11" authors = ["Frank Denis "] description = "DoH and Oblivious DoH library for the rust-doh app" keywords = ["dns", "https", "doh", "odoh", "proxy"] @@ -15,22 +15,22 @@ default = ["tls"] tls = ["tokio-rustls"] [dependencies] -anyhow = "1.0.80" -arc-swap = "1.7.0" -base64 = "0.22.0" +anyhow = "1.0.86" +arc-swap = "1.7.1" +base64 = "0.22.1" byteorder = "1.5.0" -bytes = "1.5.0" +bytes = "1.6.0" futures = "0.3.30" -hyper = { version = "^0.14.28", default-features = false, features = [ +hyper = { version = "^0.14.29", default-features = false, features = [ "server", "http1", "http2", "stream", "runtime", ] } -odoh-rs = "1.0.2" +odoh-rs = "1.0.3" rand = "0.8.5" -tokio = { version = "1.36.0", features = [ +tokio = { version = "1.38.0", features = [ "net", "rt-multi-thread", "time", From c79501aea30bbfe4780c91599a1361bb67f81614 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Wed, 3 Jul 2024 14:03:51 +0200 Subject: [PATCH 139/150] Use Zig 0.13 --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 811b41d..3e6c4ce 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,7 +18,7 @@ jobs: - uses: goto-bus-stop/setup-zig@v2 with: - version: 0.10.1 + version: 0.13 - uses: hecrj/setup-rust-action@master with: From d6635eebb717798529f73bb94b566b6cfd7e5282 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Wed, 3 Jul 2024 14:16:01 +0200 Subject: [PATCH 140/150] up --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3e6c4ce..5716724 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -104,8 +104,8 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_name: "doh-proxy_${{ steps.get_version.outputs.VERSION }}_amd64.deb" - asset_path: "target/x86_64-unknown-linux-musl/debian/doh-proxy_${{ steps.get_version.outputs.VERSION }}_amd64.deb" + asset_name: "doh-proxy_${{ steps.get_version.outputs.VERSION }}-1_amd64.deb" + asset_path: "target/x86_64-unknown-linux-musl/debian/doh-proxy_${{ steps.get_version.outputs.VERSION }}-1_amd64.deb" asset_content_type: application/x-debian-package - name: Upload RPM package for x86_64 From 34f614e938587a8576fa7392e6f96d40eeacd06c Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Wed, 3 Jul 2024 14:17:17 +0200 Subject: [PATCH 141/150] 0.13 -> 0.13.0 --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5716724..e71dd32 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,7 +18,7 @@ jobs: - uses: goto-bus-stop/setup-zig@v2 with: - version: 0.13 + version: 0.13.0 - uses: hecrj/setup-rust-action@master with: From 890a74276f33e380c513d56aa3b90d4b6157a2d7 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Wed, 3 Jul 2024 14:26:44 +0200 Subject: [PATCH 142/150] Downgrade to Zig 0.12.0 --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e71dd32..c226877 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,7 +18,7 @@ jobs: - uses: goto-bus-stop/setup-zig@v2 with: - version: 0.13.0 + version: 0.12.0 - uses: hecrj/setup-rust-action@master with: From 1a0a0566c4e9e93b73ecdc8d400949d8f7a94635 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Wed, 3 Jul 2024 14:38:16 +0200 Subject: [PATCH 143/150] Back to Zig 0.10.1 --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c226877..df1c604 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,7 +18,7 @@ jobs: - uses: goto-bus-stop/setup-zig@v2 with: - version: 0.12.0 + version: 0.10.1 - uses: hecrj/setup-rust-action@master with: From bf443c33b965619866c52300f4383d00d75d6248 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Mon, 4 Nov 2024 00:11:49 +0100 Subject: [PATCH 144/150] Switch to mlugg/setup-zig@v1 --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index df1c604..48f03a0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,7 +16,7 @@ jobs: - uses: actions/checkout@v3 - - uses: goto-bus-stop/setup-zig@v2 + - uses: mlugg/setup-zig@v1 with: version: 0.10.1 From 40b0b029729ca23b54a80870f49b04c908f69026 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Tue, 31 Dec 2024 14:54:55 +0100 Subject: [PATCH 145/150] Add issues.yml --- .github/workflows/issues.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .github/workflows/issues.yml diff --git a/.github/workflows/issues.yml b/.github/workflows/issues.yml new file mode 100644 index 0000000..c5bf530 --- /dev/null +++ b/.github/workflows/issues.yml @@ -0,0 +1,17 @@ +name: Close inactive issues +on: + schedule: + - cron: "30 1 * * *" + +jobs: + close-issues: + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + steps: + - uses: actions/stale@v9 + with: + stale-issue-message: "This issue is stale because it has been open for 30 days with no activity." + close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale." + repo-token: ${{ secrets.GITHUB_TOKEN }} From 9e4a931bceff7d794f1fb341599ae48ea2cae2a6 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Thu, 20 Feb 2025 20:32:42 +0100 Subject: [PATCH 146/150] Nits --- src/config.rs | 61 ++++++++++++++++++++++-------------------- src/libdoh/src/lib.rs | 5 +--- src/libdoh/src/odoh.rs | 2 +- src/libdoh/src/tls.rs | 9 +++---- 4 files changed, 37 insertions(+), 40 deletions(-) diff --git a/src/config.rs b/src/config.rs index 287cf8a..6d69671 100644 --- a/src/config.rs +++ b/src/config.rs @@ -240,39 +240,42 @@ pub fn parse_opts(globals: &mut Globals) { .or_else(|| globals.tls_cert_path.clone()); } - if let Some(hostname) = matches.get_one::("hostname") { - let mut builder = - dnsstamps::DoHBuilder::new(hostname.to_string(), globals.path.to_string()); - if let Some(public_address) = matches.get_one::("public_address") { - builder = builder.with_address(public_address.to_string()); - } - if let Some(public_port) = matches.get_one::("public_port") { - let public_port = public_port.parse().expect("Invalid public port"); - builder = builder.with_port(public_port); - } - println!( - "Test DNS stamp to reach [{}] over DoH: [{}]\n", - hostname, - builder.serialize().unwrap() - ); + match matches.get_one::("hostname") { + Some(hostname) => { + let mut builder = + dnsstamps::DoHBuilder::new(hostname.to_string(), globals.path.to_string()); + if let Some(public_address) = matches.get_one::("public_address") { + builder = builder.with_address(public_address.to_string()); + } + if let Some(public_port) = matches.get_one::("public_port") { + let public_port = public_port.parse().expect("Invalid public port"); + builder = builder.with_port(public_port); + } + println!( + "Test DNS stamp to reach [{}] over DoH: [{}]\n", + hostname, + builder.serialize().unwrap() + ); - let mut builder = - dnsstamps::ODoHTargetBuilder::new(hostname.to_string(), globals.path.to_string()); - if let Some(public_port) = matches.get_one::("public_port") { - let public_port = public_port.parse().expect("Invalid public port"); - builder = builder.with_port(public_port); - } - println!( - "Test DNS stamp to reach [{}] over Oblivious DoH: [{}]\n", - hostname, - builder.serialize().unwrap() - ); + let mut builder = + dnsstamps::ODoHTargetBuilder::new(hostname.to_string(), globals.path.to_string()); + if let Some(public_port) = matches.get_one::("public_port") { + let public_port = public_port.parse().expect("Invalid public port"); + builder = builder.with_port(public_port); + } + println!( + "Test DNS stamp to reach [{}] over Oblivious DoH: [{}]\n", + hostname, + builder.serialize().unwrap() + ); - println!("Check out https://dnscrypt.info/stamps/ to compute the actual stamps.\n") - } else { - println!( + println!("Check out https://dnscrypt.info/stamps/ to compute the actual stamps.\n") + } + _ => { + println!( "Please provide a fully qualified hostname (-H command-line option) to get \ test DNS stamps for your server.\n" ); + } } } diff --git a/src/libdoh/src/lib.rs b/src/libdoh/src/lib.rs index 4b6eea8..e6dd729 100644 --- a/src/libdoh/src/lib.rs +++ b/src/libdoh/src/lib.rs @@ -257,10 +257,7 @@ impl DoH { content_types: &[&'static str], ) -> Option<&'static str> { let accept = headers.get(hyper::header::ACCEPT); - let accept = match accept { - None => return None, - Some(accept) => accept, - }; + let accept = accept?; for part in accept.to_str().unwrap_or("").split(',').map(|s| s.trim()) { if let Some(found) = part .split(';') diff --git a/src/libdoh/src/odoh.rs b/src/libdoh/src/odoh.rs index 00bb95f..3f2c29e 100644 --- a/src/libdoh/src/odoh.rs +++ b/src/libdoh/src/odoh.rs @@ -77,7 +77,7 @@ impl ODoHPublicKey { impl ODoHQueryContext { pub fn encrypt_response(self, response_body: Vec) -> Result, DoHError> { - let response_nonce = rand::thread_rng().gen::(); + let response_nonce = rand::thread_rng().r#gen::(); let response_body_ = ObliviousDoHMessagePlaintext::new(response_body, 0); let encrypted_response = odoh_rs::encrypt_response( &self.query, diff --git a/src/libdoh/src/tls.rs b/src/libdoh/src/tls.rs index 7047f99..7c5509f 100644 --- a/src/libdoh/src/tls.rs +++ b/src/libdoh/src/tls.rs @@ -87,12 +87,9 @@ where let server_config_builder = ServerConfig::builder() .with_safe_defaults() .with_no_client_auth(); - if let Ok(found_config) = - server_config_builder.with_single_cert(certs.clone(), certs_key) - { - Some(found_config) - } else { - None + match server_config_builder.with_single_cert(certs.clone(), certs_key) { + Ok(found_config) => Some(found_config), + _ => None, } }) .ok_or_else(|| { From 672d1a11f18b078e83be9c317777427aeb6158cc Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Thu, 20 Feb 2025 20:33:01 +0100 Subject: [PATCH 147/150] 2025 --- LICENSE | 2 +- src/libdoh/LICENSE | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index 06c6cdb..fe0d515 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2018-2023 Frank Denis +Copyright (c) 2018-2025 Frank Denis Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/libdoh/LICENSE b/src/libdoh/LICENSE index 06c6cdb..fe0d515 100644 --- a/src/libdoh/LICENSE +++ b/src/libdoh/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2018-2023 Frank Denis +Copyright (c) 2018-2025 Frank Denis Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 2254632d3373ace147527dc333ecdbb5aba3e660 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Thu, 20 Feb 2025 20:37:23 +0100 Subject: [PATCH 148/150] Update deps --- Cargo.toml | 2 +- src/libdoh/Cargo.toml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d7e7ca0..d82bfbf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ tls = ["libdoh/tls"] [dependencies] libdoh = { path = "src/libdoh", version = "0.9.9", default-features = false } clap = { version = "4", features = ["std", "cargo", "wrap_help", "string"] } -dnsstamps = "0.1.9" +dnsstamps = "0.1.10" mimalloc = { version = "0.1.43", default-features = false } [package.metadata.generate-rpm] diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index 7b72764..503a52d 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -15,12 +15,12 @@ default = ["tls"] tls = ["tokio-rustls"] [dependencies] -anyhow = "1.0.86" +anyhow = "1.0.96" arc-swap = "1.7.1" base64 = "0.22.1" byteorder = "1.5.0" -bytes = "1.6.0" -futures = "0.3.30" +bytes = "1.10.0" +futures = "0.3.31" hyper = { version = "^0.14.29", default-features = false, features = [ "server", "http1", @@ -29,8 +29,8 @@ hyper = { version = "^0.14.29", default-features = false, features = [ "runtime", ] } odoh-rs = "1.0.3" -rand = "0.8.5" -tokio = { version = "1.38.0", features = [ +rand = "^0.8.5" +tokio = { version = "1.43.0", features = [ "net", "rt-multi-thread", "time", From 25fa6946e69ec8d36e98b598be11be3b1a777d3e Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Thu, 20 Mar 2025 00:37:34 +0100 Subject: [PATCH 149/150] tar cJpf -> tar cjpf in order to build bz2 archives Fixes #103 --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 48f03a0..0a91737 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -46,7 +46,7 @@ jobs: mkdir doh-proxy mv target/x86_64-unknown-linux-musl/release/doh-proxy doh-proxy/ cp README.md localhost.pem doh-proxy/ - tar cJpf doh-proxy_${{ steps.get_version.outputs.VERSION }}_linux-x86_64.tar.bz2 doh-proxy + tar cjpf doh-proxy_${{ steps.get_version.outputs.VERSION }}_linux-x86_64.tar.bz2 doh-proxy rm -fr doh-proxy - name: Release build Linux-aarch64 @@ -56,7 +56,7 @@ jobs: mkdir doh-proxy mv target/aarch64-unknown-linux-musl/release/doh-proxy doh-proxy/ cp README.md localhost.pem doh-proxy/ - tar cJpf doh-proxy_${{ steps.get_version.outputs.VERSION }}_linux-aarch64.tar.bz2 doh-proxy + tar cjpf doh-proxy_${{ steps.get_version.outputs.VERSION }}_linux-aarch64.tar.bz2 doh-proxy rm -fr doh-proxy - name: Release build Windows-x86_64 From f0242354d39445891160244a58f740ed99a98a8d Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Thu, 20 Mar 2025 00:41:43 +0100 Subject: [PATCH 150/150] Update deps --- Cargo.toml | 2 +- src/libdoh/Cargo.toml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d82bfbf..2be4b3d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ tls = ["libdoh/tls"] libdoh = { path = "src/libdoh", version = "0.9.9", default-features = false } clap = { version = "4", features = ["std", "cargo", "wrap_help", "string"] } dnsstamps = "0.1.10" -mimalloc = { version = "0.1.43", default-features = false } +mimalloc = { version = "0.1.44", default-features = false } [package.metadata.generate-rpm] assets = [ diff --git a/src/libdoh/Cargo.toml b/src/libdoh/Cargo.toml index 503a52d..69fe04d 100644 --- a/src/libdoh/Cargo.toml +++ b/src/libdoh/Cargo.toml @@ -15,13 +15,13 @@ default = ["tls"] tls = ["tokio-rustls"] [dependencies] -anyhow = "1.0.96" +anyhow = "1.0.97" arc-swap = "1.7.1" base64 = "0.22.1" byteorder = "1.5.0" -bytes = "1.10.0" +bytes = "1.10.1" futures = "0.3.31" -hyper = { version = "^0.14.29", default-features = false, features = [ +hyper = { version = "^0.14.32", default-features = false, features = [ "server", "http1", "http2", @@ -30,7 +30,7 @@ hyper = { version = "^0.14.29", default-features = false, features = [ ] } odoh-rs = "1.0.3" rand = "^0.8.5" -tokio = { version = "1.43.0", features = [ +tokio = { version = "1.44.1", features = [ "net", "rt-multi-thread", "time",