From fd65582aa6048bce4178bf3a15424c71c2846914 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Fri, 24 Jun 2022 23:49:04 +0200 Subject: [PATCH 01/72] 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 02/72] 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 03/72] 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 04/72] 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 05/72] 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 06/72] 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 07/72] 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 08/72] 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 09/72] 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 10/72] 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 11/72] 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 12/72] 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 13/72] 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 14/72] 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 15/72] 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 16/72] 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 17/72] 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 18/72] 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 19/72] 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 20/72] 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 21/72] 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 22/72] 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 23/72] 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 24/72] 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 25/72] 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 26/72] 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 27/72] 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 28/72] 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 29/72] 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 30/72] 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 31/72] 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 32/72] 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 33/72] 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 34/72] 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 35/72] 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 36/72] 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 37/72] 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 38/72] 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 39/72] 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 40/72] 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 41/72] 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 42/72] 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 43/72] 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 44/72] 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 45/72] 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 46/72] 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 47/72] 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 48/72] 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 49/72] 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 50/72] 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 51/72] 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 52/72] 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 53/72] 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 54/72] 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 55/72] 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 56/72] 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 57/72] 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 58/72] 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 59/72] 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 60/72] 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 61/72] 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 62/72] 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 63/72] 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 64/72] 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 65/72] 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 66/72] 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 67/72] 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 68/72] 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 69/72] 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 70/72] 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 71/72] 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 72/72] 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",