From 7036af5cabbcd89c1841cd97be53cf8b729207d0 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Tue, 6 Feb 2018 13:49:05 +0100 Subject: [PATCH] up --- Cargo.toml | 8 +++++++- README.md | 11 +++------- src/main.rs | 59 ++++++++++++++++++++++++++++------------------------- 3 files changed, 41 insertions(+), 37 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c15382a..5dcb74a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,13 @@ [package] name = "doh-proxy" -version = "0.1.0" +version = "0.1.1" authors = ["Frank Denis "] +description = "A DNS-over-HTTPS (DoH) proxy" +keywords = ["dns","https","doh","proxy"] +license = "MIT" +homepage = "https://github.com/jedisct1/rust-doh" +repository = "https://github.com/jedisct1/rust-doh" +categories = ["asynchronous", "network-programming","command-line-utilities"] [dependencies] base64 = "~0.9" diff --git a/README.md b/README.md index b836078..0914eb5 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # doh-proxy -A DNS-over-HTTP server proxy in Rust. +A DNS-over-HTTP server proxy in Rust. Add a webserver and you get DNS-over-HTTPS, which is actually DNS-over-HTTP/2. ## Usage -``` +```text doh-proxy A DNS-over-HTTP server proxy @@ -19,14 +19,9 @@ OPTIONS: -l, --listen_address Address to listen to [default: 127.0.0.1:3000] -b, --local_bind_address Address to connect from [default: 0.0.0.0:0] -c, --max_clients Maximum number of simultaneous clients [default: 512] + -p, --path URI path [default: /dns-query] -u, --server_address Address to connect to [default: 9.9.9.9:53] -t, --timeout Timeout, in seconds [default: 10] ``` -## Limitations - -Only support `POST` queries. `GET` queries are too noisy in log files, including when they are not yours. - Serves HTTP requests only. DoH is mostly useful to leverage an existing webserver, so just configure your webserver to proxy connections to this. - -Path is `/dns-query`. diff --git a/src/main.rs b/src/main.rs index bceadd8..2cd2dd6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -33,6 +33,7 @@ const MAX_CLIENTS: u32 = 512; const MAX_DNS_QUESTION_LEN: usize = 512; const MAX_DNS_RESPONSE_LEN: usize = 4096; const MIN_DNS_PACKET_LEN: usize = 17; +const PATH: &str = "/dns-query"; const SERVER_ADDRESS: &str = "9.9.9.9:53"; const TIMEOUT_SEC: u64 = 10; const MAX_TTL: u32 = 86400 * 7; @@ -45,6 +46,7 @@ struct DoH { listen_address: SocketAddr, local_bind_address: SocketAddr, server_address: SocketAddr, + path: String, max_clients: u32, timeout: Duration, } @@ -58,18 +60,12 @@ impl Service for DoH { fn call(&self, req: Request) -> Self::Future { let mut response = Response::new(); match (req.method(), req.path()) { - (&Method::Post, "/dns-query") => { - let fut = self.body_read(req.body(), self.handle.clone()) - .map(|(body, ttl)| { - let body_len = body.len(); - response.set_body(body); - response - .with_header(ContentLength(body_len as u64)) - .with_header(ContentType( - "application/dns-udpwireformat".parse().unwrap(), - )) - .with_header(CacheControl(vec![CacheDirective::MaxAge(ttl)])) - }); + (&Method::Post, path) => { + if path != self.path { + response.set_status(StatusCode::NotFound); + return Box::new(future::ok(response)); + } + let fut = self.read_body_and_proxy(req.body(), self.handle.clone()); return Box::new(fut.map_err(|_| hyper::Error::Incomplete)); } (&Method::Get, "/dns-query") => { @@ -98,21 +94,9 @@ impl Service for DoH { return Box::new(future::ok(response)); } }; - let fut = Self::proxy(question, self.handle.clone()).map(|(body, ttl)| { - let body_len = body.len(); - response.set_body(body); - response - .with_header(ContentLength(body_len as u64)) - .with_header(ContentType( - "application/dns-udpwireformat".parse().unwrap(), - )) - .with_header(CacheControl(vec![CacheDirective::MaxAge(ttl)])) - }); + let fut = Self::proxy(question, self.handle.clone()); return Box::new(fut.map_err(|_| hyper::Error::Incomplete)); } - (&Method::Post, _) => { - response.set_status(StatusCode::NotFound); - } _ => { response.set_status(StatusCode::NotAcceptable); } @@ -123,7 +107,7 @@ impl Service for DoH { impl DoH { #[async] - fn proxy(query: Vec, handle: Handle) -> Result<(Vec, u32), ()> { + fn proxy(query: Vec, handle: Handle) -> Result { let local_addr = LOCAL_BIND_ADDRESS.parse().unwrap(); let socket = UdpSocket::bind(&local_addr, &handle).unwrap(); let remote_addr = SERVER_ADDRESS.parse().unwrap(); @@ -136,11 +120,21 @@ impl DoH { } packet.truncate(len); let min_ttl = dns::min_ttl(&packet, MIN_TTL, MAX_TTL, ERR_TTL).map_err(|_| {})?; - Ok((packet, min_ttl)) + Ok((packet, min_ttl)).map(|(body, ttl)| { + let body_len = body.len(); + let mut response = Response::new(); + response.set_body(body); + response + .with_header(ContentLength(body_len as u64)) + .with_header(ContentType( + "application/dns-udpwireformat".parse().unwrap(), + )) + .with_header(CacheControl(vec![CacheDirective::MaxAge(ttl)])) + }) } #[async] - fn body_read(&self, body: Body, handle: Handle) -> Result<(Vec, u32), ()> { + fn read_body_and_proxy(&self, body: Body, handle: Handle) -> Result { let query = await!( body.concat2() .map_err(|_err| ()) @@ -162,6 +156,7 @@ fn main() { listen_address: LISTEN_ADDRESS.parse().unwrap(), local_bind_address: LOCAL_BIND_ADDRESS.parse().unwrap(), server_address: SERVER_ADDRESS.parse().unwrap(), + path: PATH.to_string(), max_clients: MAX_CLIENTS, timeout: Duration::from_secs(TIMEOUT_SEC), }; @@ -229,6 +224,14 @@ fn parse_opts(doh: &mut DoH) { .default_value(LOCAL_BIND_ADDRESS) .help("Address to connect from"), ) + .arg( + Arg::with_name("path") + .short("p") + .long("path") + .takes_value(true) + .default_value(PATH) + .help("URI path"), + ) .arg( Arg::with_name("max_clients") .short("c")