Return various HTTP error codes on invalid queries

Fixes #23
This commit is contained in:
Frank Denis 2019-06-04 18:34:32 +02:00
parent de99e6a8b2
commit 217bb90320

View file

@ -13,6 +13,7 @@ use hyper;
use hyper::server::conn::Http; use hyper::server::conn::Http;
use hyper::service::Service; use hyper::service::Service;
use hyper::{Body, Method, Request, Response, StatusCode}; use hyper::{Body, Method, Request, Response, StatusCode};
use std::io;
#[cfg(feature = "tls")] #[cfg(feature = "tls")]
use native_tls::{self, Identity}; use native_tls::{self, Identity};
@ -103,20 +104,41 @@ struct DoH {
#[derive(Debug)] #[derive(Debug)]
enum Error { enum Error {
Incomplete, Incomplete,
InvalidData,
TooLarge, TooLarge,
UpstreamIssue,
Hyper(hyper::Error), Hyper(hyper::Error),
Io(io::Error),
} }
impl std::fmt::Display for Error { impl std::fmt::Display for Error {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
std::fmt::Debug::fmt(self, fmt) std::fmt::Debug::fmt(self, fmt)
} }
} }
impl std::error::Error for Error { impl std::error::Error for Error {
fn description(&self) -> &str { fn description(&self) -> &str {
match *self { match *self {
Error::Incomplete => "Incomplete", Error::Incomplete => "Incomplete",
Error::TooLarge => "TooLarge", Error::InvalidData => "Invalid data",
Error::TooLarge => "Too large",
Error::UpstreamIssue => "Upstream error",
Error::Hyper(_) => self.description(), Error::Hyper(_) => self.description(),
Error::Io(_) => self.description(),
}
}
}
impl From<Error> for StatusCode {
fn from(e: Error) -> StatusCode {
match e {
Error::Incomplete => StatusCode::UNPROCESSABLE_ENTITY,
Error::InvalidData => StatusCode::BAD_REQUEST,
Error::TooLarge => StatusCode::PAYLOAD_TOO_LARGE,
Error::UpstreamIssue => StatusCode::BAD_GATEWAY,
Error::Hyper(_) => StatusCode::SERVICE_UNAVAILABLE,
Error::Io(_) => StatusCode::INTERNAL_SERVER_ERROR,
} }
} }
} }
@ -157,6 +179,38 @@ impl DoH {
.unwrap(); .unwrap();
return Box::new(future::ok(response)); return Box::new(future::ok(response));
} }
let headers = req.headers();
let accept = match headers.get("accept") {
None => {
let response = Response::builder()
.status(StatusCode::NOT_ACCEPTABLE)
.body(Body::empty())
.unwrap();
return Box::new(future::ok(response));
}
Some(accept) => accept.to_str(),
};
let accept = match accept {
Err(_) => {
let response = Response::builder()
.status(StatusCode::BAD_REQUEST)
.body(Body::empty())
.unwrap();
return Box::new(future::ok(response));
}
Ok(accept) => accept.to_lowercase(),
};
let found = accept
.split(',')
.take(10)
.any(|part| part.trim() == "application/dns-message");
if !found {
let response = Response::builder()
.status(StatusCode::UNSUPPORTED_MEDIA_TYPE)
.body(Body::empty())
.unwrap();
return Box::new(future::ok(response));
}
match *req.method() { match *req.method() {
Method::POST => { Method::POST => {
if self.inner.disable_post { if self.inner.disable_post {
@ -166,8 +220,14 @@ impl DoH {
.unwrap(); .unwrap();
return Box::new(future::ok(response)); return Box::new(future::ok(response));
} }
let fut = self.read_body_and_proxy(req.into_body()); let fut = self.read_body_and_proxy(req.into_body()).or_else(|e| {
Box::new(fut.map_err(|_| Error::Incomplete)) let response = Response::builder()
.status(StatusCode::from(e))
.body(Body::empty())
.unwrap();
future::ok(response)
});
Box::new(fut)
} }
Method::GET => { Method::GET => {
let query = req.uri().query().unwrap_or(""); let query = req.uri().query().unwrap_or("");
@ -192,8 +252,14 @@ impl DoH {
return Box::new(future::ok(response)); return Box::new(future::ok(response));
} }
}; };
let fut = self.proxy(question); let fut = self.proxy(question).or_else(|e| {
Box::new(fut.map_err(|_| Error::Incomplete)) let response = Response::builder()
.status(StatusCode::from(e))
.body(Body::empty())
.unwrap();
future::ok(response)
});
Box::new(fut)
} }
_ => { _ => {
let response = Response::builder() let response = Response::builder()
@ -208,13 +274,9 @@ impl DoH {
fn proxy( fn proxy(
&self, &self,
mut query: Vec<u8>, mut query: Vec<u8>,
) -> Box<dyn Future<Item = Response<Body>, Error = ()> + Send> { ) -> Box<dyn Future<Item = Response<Body>, Error = Error> + Send> {
if query.len() < MIN_DNS_PACKET_LEN { if query.len() < MIN_DNS_PACKET_LEN {
let response = Response::builder() return Box::new(future::err(Error::Incomplete));
.status(StatusCode::BAD_REQUEST)
.body(Body::empty())
.unwrap();
return Box::new(future::ok(response));
} }
let _ = dns::set_edns_max_payload_size(&mut query, MAX_DNS_RESPONSE_LEN as u16); let _ = dns::set_edns_max_payload_size(&mut query, MAX_DNS_RESPONSE_LEN as u16);
let inner = &self.inner; let inner = &self.inner;
@ -223,18 +285,18 @@ impl DoH {
let (min_ttl, max_ttl, err_ttl) = (inner.min_ttl, inner.max_ttl, inner.err_ttl); let (min_ttl, max_ttl, err_ttl) = (inner.min_ttl, inner.max_ttl, inner.err_ttl);
let fut = socket let fut = socket
.send_dgram(query, &inner.server_address) .send_dgram(query, &inner.server_address)
.map_err(|_| ()) .map_err(Error::Io)
.and_then(move |(socket, _)| { .and_then(move |(socket, _)| {
let packet = vec![0; MAX_DNS_RESPONSE_LEN]; let packet = vec![0; MAX_DNS_RESPONSE_LEN];
socket.recv_dgram(packet).map_err(|_| {}) socket.recv_dgram(packet).map_err(Error::Io)
}) })
.and_then(move |(_socket, mut packet, len, response_server_address)| { .and_then(move |(_socket, mut packet, len, response_server_address)| {
if len < MIN_DNS_PACKET_LEN || expected_server_address != response_server_address { if len < MIN_DNS_PACKET_LEN || expected_server_address != response_server_address {
return future::err(()); return future::err(Error::UpstreamIssue);
} }
packet.truncate(len); packet.truncate(len);
let ttl = match dns::min_ttl(&packet, min_ttl, max_ttl, err_ttl) { let ttl = match dns::min_ttl(&packet, min_ttl, max_ttl, err_ttl) {
Err(_) => return future::err(()), Err(_) => return future::err(Error::UpstreamIssue),
Ok(ttl) => ttl, Ok(ttl) => ttl,
}; };
let packet_len = packet.len(); let packet_len = packet.len();
@ -256,7 +318,7 @@ impl DoH {
fn read_body_and_proxy( fn read_body_and_proxy(
&self, &self,
body: Body, body: Body,
) -> Box<dyn Future<Item = Response<Body>, Error = ()> + Send> { ) -> Box<dyn Future<Item = Response<Body>, Error = Error> + Send> {
let mut sum_size = 0; let mut sum_size = 0;
let inner = self.clone(); let inner = self.clone();
let fut = body let fut = body
@ -270,7 +332,6 @@ impl DoH {
} }
}) })
.concat2() .concat2()
.map_err(move |_err| ())
.map(move |chunk| chunk.to_vec()) .map(move |chunk| chunk.to_vec())
.and_then(move |query| inner.proxy(query)); .and_then(move |query| inner.proxy(query));
Box::new(fut) Box::new(fut)