Handle truncated responses when len(query)<len(response)

pull/5/head
Frank Denis 5 years ago
parent f673f8497b
commit 0505f886e5

@ -330,3 +330,19 @@ pub fn serve_certificates<'t>(
}
Ok(Some(packet))
}
pub fn serve_truncated(client_packet: Vec<u8>) -> Result<Vec<u8>, Error> {
ensure!(client_packet.len() >= DNS_HEADER_SIZE, "Short packet");
ensure!(qdcount(&client_packet) == 1, "No question");
ensure!(
!is_response(&client_packet),
"Question expected, but got a response instead"
);
let offset = skip_name(&client_packet, DNS_HEADER_SIZE)?;
let mut packet = client_packet;
ensure!(packet.len() - offset >= 4, "Short packet");
packet.truncate(offset + 4);
authoritative_response(&mut packet);
truncate(&mut packet);
Ok(packet)
}

@ -8,12 +8,37 @@ use rand::prelude::*;
use std::ffi::CStr;
use std::ptr;
pub const DNSCRYPT_CLIENT_MAGIC_SIZE: usize = 8;
pub const DNSCRYPT_CLIENT_PK_SIZE: usize = 32;
pub const DNSCRYPT_CLIENT_NONCE_SIZE: usize =
crypto_box_curve25519xchacha20poly1305_HALFNONCEBYTES as usize;
pub const DNSCRYPT_FULL_NONCE_SIZE: usize =
crypto_box_curve25519xchacha20poly1305_NONCEBYTES as usize;
pub const DNSCRYPT_MAC_SIZE: usize = crypto_box_curve25519xchacha20poly1305_MACBYTES as usize;
pub const DNSCRYPT_QUERY_MAGIC_SIZE: usize = 8;
pub const DNSCRYPT_QUERY_PK_SIZE: usize = 32;
pub const DNSCRYPT_QUERY_NONCE_SIZE: usize = DNSCRYPT_FULL_NONCE_SIZE / 2;
pub const DNSCRYPT_QUERY_HEADER_SIZE: usize =
DNSCRYPT_QUERY_MAGIC_SIZE + DNSCRYPT_QUERY_PK_SIZE + DNSCRYPT_QUERY_NONCE_SIZE;
pub const DNSCRYPT_QUERY_MIN_PADDING_SIZE: usize = 1;
pub const DNSCRYPT_QUERY_MIN_OVERHEAD: usize =
DNSCRYPT_QUERY_HEADER_SIZE + DNSCRYPT_MAC_SIZE + DNSCRYPT_QUERY_MIN_PADDING_SIZE;
pub const DNSCRYPT_RESPONSE_MAGIC_SIZE: usize = 8;
pub const DNSCRYPT_RESPONSE_NONCE_SIZE: usize = DNSCRYPT_FULL_NONCE_SIZE;
pub const DNSCRYPT_RESPONSE_HEADER_SIZE: usize =
DNSCRYPT_RESPONSE_MAGIC_SIZE + DNSCRYPT_RESPONSE_NONCE_SIZE;
pub const DNSCRYPT_RESPONSE_MIN_PADDING_SIZE: usize = 1;
pub const DNSCRYPT_RESPONSE_MIN_OVERHEAD: usize =
DNSCRYPT_RESPONSE_HEADER_SIZE + DNSCRYPT_MAC_SIZE + DNSCRYPT_RESPONSE_MIN_PADDING_SIZE;
pub const DNSCRYPT_UDP_QUERY_MIN_SIZE: usize = DNSCRYPT_QUERY_MIN_OVERHEAD + DNS_HEADER_SIZE;
pub const DNSCRYPT_UDP_QUERY_MAX_SIZE: usize = DNS_MAX_PACKET_SIZE;
pub const DNSCRYPT_TCP_QUERY_MIN_SIZE: usize = DNSCRYPT_QUERY_MIN_OVERHEAD + DNS_HEADER_SIZE;
pub const DNSCRYPT_TCP_QUERY_MAX_SIZE: usize = DNSCRYPT_QUERY_MIN_OVERHEAD + DNS_MAX_PACKET_SIZE;
pub const DNSCRYPT_UDP_RESPONSE_MIN_SIZE: usize = DNSCRYPT_RESPONSE_MIN_OVERHEAD + DNS_HEADER_SIZE;
pub const DNSCRYPT_UDP_RESPONSE_MAX_SIZE: usize = DNS_MAX_PACKET_SIZE;
pub const DNSCRYPT_TCP_RESPONSE_MIN_SIZE: usize = DNSCRYPT_RESPONSE_MIN_OVERHEAD + DNS_HEADER_SIZE;
pub const DNSCRYPT_TCP_RESPONSE_MAX_SIZE: usize =
DNSCRYPT_RESPONSE_MIN_OVERHEAD + DNS_MAX_PACKET_SIZE;
pub fn decrypt(
wrapped_packet: &[u8],
@ -21,19 +46,18 @@ pub fn decrypt(
) -> Result<(SharedKey, [u8; DNSCRYPT_FULL_NONCE_SIZE as usize], Vec<u8>), Error> {
ensure!(
wrapped_packet.len()
>= DNSCRYPT_CLIENT_MAGIC_SIZE
+ DNSCRYPT_CLIENT_PK_SIZE
+ DNSCRYPT_CLIENT_NONCE_SIZE
>= DNSCRYPT_QUERY_MAGIC_SIZE
+ DNSCRYPT_QUERY_PK_SIZE
+ DNSCRYPT_QUERY_NONCE_SIZE
+ DNS_HEADER_SIZE,
"Short packet"
);
let client_magic = &wrapped_packet[..DNSCRYPT_CLIENT_MAGIC_SIZE];
let client_magic = &wrapped_packet[..DNSCRYPT_QUERY_MAGIC_SIZE];
let client_pk = &wrapped_packet
[DNSCRYPT_CLIENT_MAGIC_SIZE..DNSCRYPT_CLIENT_MAGIC_SIZE + DNSCRYPT_CLIENT_PK_SIZE];
let client_nonce = &wrapped_packet[DNSCRYPT_CLIENT_MAGIC_SIZE + DNSCRYPT_CLIENT_PK_SIZE
..DNSCRYPT_CLIENT_MAGIC_SIZE + DNSCRYPT_CLIENT_PK_SIZE + DNSCRYPT_CLIENT_NONCE_SIZE];
let encrypted_packet = &wrapped_packet
[DNSCRYPT_CLIENT_MAGIC_SIZE + DNSCRYPT_CLIENT_PK_SIZE + DNSCRYPT_CLIENT_NONCE_SIZE..];
[DNSCRYPT_QUERY_MAGIC_SIZE..DNSCRYPT_QUERY_MAGIC_SIZE + DNSCRYPT_QUERY_PK_SIZE];
let client_nonce = &wrapped_packet[DNSCRYPT_QUERY_MAGIC_SIZE + DNSCRYPT_QUERY_PK_SIZE
..DNSCRYPT_QUERY_MAGIC_SIZE + DNSCRYPT_QUERY_PK_SIZE + DNSCRYPT_QUERY_NONCE_SIZE];
let encrypted_packet = &wrapped_packet[DNSCRYPT_QUERY_HEADER_SIZE..];
let encrypted_packet_len = encrypted_packet.len();
let dnscrypt_encryption_params = dnscrypt_encryption_params_set
@ -42,11 +66,11 @@ pub fn decrypt(
.ok_or_else(|| format_err!("Client magic not found"))?;
let mut nonce = [0u8; DNSCRYPT_FULL_NONCE_SIZE as usize];
&mut nonce[..DNSCRYPT_CLIENT_NONCE_SIZE].copy_from_slice(client_nonce);
&mut nonce[..DNSCRYPT_QUERY_NONCE_SIZE].copy_from_slice(client_nonce);
let resolver_kp = dnscrypt_encryption_params.resolver_kp();
let shared_key = resolver_kp.compute_shared_key(client_pk)?;
let packet = shared_key.decrypt(&nonce, encrypted_packet)?;
rand::thread_rng().fill_bytes(&mut nonce[DNSCRYPT_CLIENT_NONCE_SIZE..]);
rand::thread_rng().fill_bytes(&mut nonce[DNSCRYPT_QUERY_NONCE_SIZE..]);
Ok((shared_key, nonce, packet))
}

@ -52,9 +52,6 @@ use tokio::runtime::Runtime;
use tokio::sync::oneshot;
use tokio_net::driver::Handle;
const DNSCRYPT_QUERY_MIN_SIZE: usize = 12;
const DNSCRYPT_QUERY_MAX_SIZE: usize = 512;
#[derive(Debug)]
struct UdpClientCtx {
net_udp_socket: std::net::UdpSocket,
@ -72,30 +69,57 @@ enum ClientCtx {
Tcp(TcpClientCtx),
}
fn maybe_truncate_response(
client_ctx: &ClientCtx,
packet: Vec<u8>,
response: Vec<u8>,
original_packet_len: usize,
) -> Result<Vec<u8>, Error> {
if let ClientCtx::Udp(_) = client_ctx {
let encrypted_response_min_len =
response.len() + DNSCRYPT_RESPONSE_HEADER_SIZE + DNSCRYPT_RESPONSE_MIN_PADDING_SIZE;
if encrypted_response_min_len > original_packet_len
|| encrypted_response_min_len > DNSCRYPT_UDP_RESPONSE_MAX_SIZE
{
return Ok(dns::serve_truncated(packet)?);
}
}
Ok(response)
}
async fn respond_to_query(
client_ctx: ClientCtx,
packet: Vec<u8>,
response: Vec<u8>,
original_packet_len: usize,
shared_key: Option<SharedKey>,
nonce: Option<[u8; DNSCRYPT_FULL_NONCE_SIZE]>,
) -> Result<(), Error> {
ensure!(dns::is_response(&packet), "Packet is not a response");
let packet = match &shared_key {
None => packet,
Some(shared_key) => dnscrypt::encrypt(packet, shared_key, nonce.as_ref().unwrap())?,
ensure!(dns::is_response(&response), "Packet is not a response");
let response = match &shared_key {
None => response,
Some(shared_key) => dnscrypt::encrypt(
maybe_truncate_response(&client_ctx, packet, response, original_packet_len)?,
shared_key,
nonce.as_ref().unwrap(),
)?,
};
match client_ctx {
ClientCtx::Udp(client_ctx) => {
let net_udp_socket = client_ctx.net_udp_socket;
net_udp_socket.send_to(&packet, client_ctx.client_addr)?;
net_udp_socket.send_to(&response, client_ctx.client_addr)?;
}
ClientCtx::Tcp(client_ctx) => {
let packet_len = packet.len();
ensure!(packet_len <= DNS_MAX_PACKET_SIZE, "Packet too large");
let response_len = response.len();
ensure!(
response_len <= DNSCRYPT_TCP_RESPONSE_MAX_SIZE,
"Packet too large"
);
let mut client_connection = client_ctx.client_connection;
let mut binlen = [0u8, 0];
BigEndian::write_u16(&mut binlen[..], packet_len as u16);
BigEndian::write_u16(&mut binlen[..], response_len as u16);
client_connection.write_all(&binlen).await?;
client_connection.write_all(&packet).await?;
client_connection.write_all(&response).await?;
client_connection.flush();
}
}
@ -107,17 +131,26 @@ async fn handle_client_query(
client_ctx: ClientCtx,
encrypted_packet: Vec<u8>,
) -> Result<(), Error> {
let original_packet_len = encrypted_packet.len();
let (shared_key, nonce, mut packet) =
match dnscrypt::decrypt(&encrypted_packet, &globals.dnscrypt_encryption_params_set) {
Ok(x) => x,
Err(_) => {
Err(e) => {
let packet = encrypted_packet;
if let Some(synth_packet) = serve_certificates(
&packet,
&globals.provider_name,
&globals.dnscrypt_encryption_params_set,
)? {
return respond_to_query(client_ctx, synth_packet, None, None).await;
return respond_to_query(
client_ctx,
packet,
synth_packet,
original_packet_len,
None,
None,
)
.await;
}
bail!("Unencrypted query");
}
@ -180,7 +213,15 @@ async fn handle_client_query(
);
}
dns::set_tid(&mut response, original_tid);
respond_to_query(client_ctx, response, Some(shared_key), Some(nonce)).await
respond_to_query(
client_ctx,
packet,
response,
original_packet_len,
Some(shared_key),
Some(nonce),
)
.await
}
async fn tcp_acceptor(globals: Arc<Globals>, tcp_listener: TcpListener) -> Result<(), Error> {
@ -212,7 +253,7 @@ async fn tcp_acceptor(globals: Arc<Globals>, tcp_listener: TcpListener) -> Resul
client_connection.read_exact(&mut binlen).await?;
let packet_len = BigEndian::read_u16(&binlen) as usize;
ensure!(
(DNSCRYPT_QUERY_MIN_SIZE..=DNSCRYPT_QUERY_MAX_SIZE).contains(&packet_len),
(DNSCRYPT_TCP_QUERY_MIN_SIZE..=DNSCRYPT_TCP_QUERY_MAX_SIZE).contains(&packet_len),
"Unexpected query size"
);
let mut packet = vec![0u8; packet_len];
@ -240,7 +281,7 @@ async fn udp_acceptor(
let concurrent_connections = globals.udp_concurrent_connections.clone();
let active_connections = globals.udp_active_connections.clone();
loop {
let mut packet = vec![0u8; DNSCRYPT_QUERY_MAX_SIZE];
let mut packet = vec![0u8; DNSCRYPT_UDP_QUERY_MAX_SIZE];
let (packet_len, client_addr) = tokio_udp_socket.recv_from(&mut packet).await?;
let net_udp_socket = net_udp_socket.try_clone()?;
packet.truncate(packet_len);

Loading…
Cancel
Save