2022-05-13 23:30:16 +00:00
|
|
|
use std::hash::Hasher;
|
|
|
|
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
|
|
|
|
use std::sync::Arc;
|
2019-10-13 20:34:46 +00:00
|
|
|
|
|
|
|
use byteorder::{BigEndian, ByteOrder};
|
2019-11-08 12:06:21 +00:00
|
|
|
use ipext::IpExt;
|
2019-10-20 09:18:45 +00:00
|
|
|
use siphasher::sip128::Hasher128;
|
2019-10-13 20:34:46 +00:00
|
|
|
use tokio::net::UdpSocket;
|
|
|
|
|
2022-05-13 23:30:16 +00:00
|
|
|
use crate::errors::*;
|
|
|
|
use crate::*;
|
|
|
|
|
2019-10-13 20:34:46 +00:00
|
|
|
pub const ANONYMIZED_DNSCRYPT_QUERY_MAGIC: [u8; 10] =
|
|
|
|
[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00];
|
|
|
|
|
|
|
|
pub const ANONYMIZED_DNSCRYPT_OVERHEAD: usize = 16 + 2;
|
|
|
|
|
2019-10-20 09:18:45 +00:00
|
|
|
pub const RELAYED_CERT_CACHE_SIZE: usize = 1000;
|
|
|
|
pub const RELAYED_CERT_CACHE_TTL: u32 = 600;
|
|
|
|
|
2019-10-13 20:34:46 +00:00
|
|
|
pub async fn handle_anonymized_dns(
|
|
|
|
globals: Arc<Globals>,
|
|
|
|
client_ctx: ClientCtx,
|
2019-10-20 09:18:45 +00:00
|
|
|
relayed_packet: &[u8],
|
2019-10-13 20:34:46 +00:00
|
|
|
) -> Result<(), Error> {
|
|
|
|
ensure!(
|
2019-10-20 09:18:45 +00:00
|
|
|
relayed_packet.len() > ANONYMIZED_DNSCRYPT_OVERHEAD,
|
2019-10-13 20:34:46 +00:00
|
|
|
"Short packet"
|
|
|
|
);
|
2019-10-20 09:18:45 +00:00
|
|
|
let ip_bin = &relayed_packet[..16];
|
2019-10-13 20:34:46 +00:00
|
|
|
let ip_v6 = Ipv6Addr::new(
|
|
|
|
BigEndian::read_u16(&ip_bin[0..2]),
|
|
|
|
BigEndian::read_u16(&ip_bin[2..4]),
|
|
|
|
BigEndian::read_u16(&ip_bin[4..6]),
|
|
|
|
BigEndian::read_u16(&ip_bin[6..8]),
|
|
|
|
BigEndian::read_u16(&ip_bin[8..10]),
|
|
|
|
BigEndian::read_u16(&ip_bin[10..12]),
|
|
|
|
BigEndian::read_u16(&ip_bin[12..14]),
|
|
|
|
BigEndian::read_u16(&ip_bin[14..16]),
|
|
|
|
);
|
|
|
|
let ip = match ip_v6.to_ipv4() {
|
|
|
|
Some(ip_v4) => IpAddr::V4(ip_v4),
|
|
|
|
None => IpAddr::V6(ip_v6),
|
|
|
|
};
|
|
|
|
#[cfg(feature = "metrics")]
|
|
|
|
globals.varz.anonymized_queries.inc();
|
|
|
|
|
2019-11-08 12:06:21 +00:00
|
|
|
ensure!(IpExt::is_global(&ip), "Forbidden upstream address");
|
2019-10-14 09:10:55 +00:00
|
|
|
ensure!(
|
|
|
|
!globals.anonymized_dns_blacklisted_ips.contains(&ip),
|
|
|
|
"Blacklisted upstream IP"
|
|
|
|
);
|
2019-10-20 09:18:45 +00:00
|
|
|
let port = BigEndian::read_u16(&relayed_packet[16..18]);
|
2019-10-14 09:10:55 +00:00
|
|
|
ensure!(
|
2019-10-17 20:44:43 +00:00
|
|
|
(globals.anonymized_dns_allow_non_reserved_ports && port >= 1024)
|
|
|
|
|| globals.anonymized_dns_allowed_ports.contains(&port),
|
2019-10-14 09:10:55 +00:00
|
|
|
"Forbidden upstream port"
|
|
|
|
);
|
2019-10-13 20:34:46 +00:00
|
|
|
let upstream_address = SocketAddr::new(ip, port);
|
|
|
|
ensure!(
|
|
|
|
!globals.listen_addrs.contains(&upstream_address)
|
2019-10-19 09:36:16 +00:00
|
|
|
&& globals.external_addr != Some(upstream_address),
|
2019-10-13 20:34:46 +00:00
|
|
|
"Would be relaying to self"
|
|
|
|
);
|
2019-10-20 09:18:45 +00:00
|
|
|
let encrypted_packet = &relayed_packet[ANONYMIZED_DNSCRYPT_OVERHEAD..];
|
2019-10-13 20:34:46 +00:00
|
|
|
let encrypted_packet_len = encrypted_packet.len();
|
|
|
|
ensure!(
|
2019-10-20 16:05:26 +00:00
|
|
|
encrypted_packet_len >= ANONYMIZED_DNSCRYPT_QUERY_MAGIC.len() + DNS_HEADER_SIZE
|
2019-10-13 20:34:46 +00:00
|
|
|
&& encrypted_packet_len <= DNSCRYPT_UDP_QUERY_MAX_SIZE,
|
|
|
|
"Unexpected encapsulated query length"
|
|
|
|
);
|
|
|
|
ensure!(
|
|
|
|
encrypted_packet_len > 8 && [0u8, 0, 0, 0, 0, 0, 0, 1] != encrypted_packet[..8],
|
|
|
|
"Protocol confusion with QUIC"
|
|
|
|
);
|
2019-10-14 09:41:37 +00:00
|
|
|
debug_assert!(DNSCRYPT_UDP_QUERY_MIN_SIZE > ANONYMIZED_DNSCRYPT_QUERY_MAGIC.len());
|
|
|
|
ensure!(
|
|
|
|
encrypted_packet[..ANONYMIZED_DNSCRYPT_QUERY_MAGIC.len()]
|
|
|
|
!= ANONYMIZED_DNSCRYPT_QUERY_MAGIC,
|
|
|
|
"Loop detected"
|
|
|
|
);
|
2020-12-23 20:23:35 +00:00
|
|
|
let ext_socket = match globals.external_addr {
|
2019-10-19 09:36:16 +00:00
|
|
|
Some(x) => UdpSocket::bind(x).await?,
|
|
|
|
None => match upstream_address {
|
|
|
|
SocketAddr::V4(_) => {
|
|
|
|
UdpSocket::bind(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0))).await?
|
|
|
|
}
|
|
|
|
SocketAddr::V6(s) => {
|
|
|
|
UdpSocket::bind(SocketAddr::V6(SocketAddrV6::new(
|
|
|
|
Ipv6Addr::UNSPECIFIED,
|
|
|
|
0,
|
|
|
|
s.flowinfo(),
|
|
|
|
s.scope_id(),
|
|
|
|
)))
|
|
|
|
.await?
|
|
|
|
}
|
|
|
|
},
|
|
|
|
};
|
2019-10-13 20:34:46 +00:00
|
|
|
ext_socket.connect(&upstream_address).await?;
|
2021-06-19 23:03:06 +00:00
|
|
|
ext_socket.send(encrypted_packet).await?;
|
2019-10-13 20:34:46 +00:00
|
|
|
let mut response = vec![0u8; DNSCRYPT_UDP_RESPONSE_MAX_SIZE];
|
2019-10-20 09:18:45 +00:00
|
|
|
let (response_len, is_certificate_response) = loop {
|
2019-10-13 20:34:46 +00:00
|
|
|
let fut = ext_socket.recv_from(&mut response[..]);
|
|
|
|
let (response_len, response_addr) = fut.await?;
|
2019-10-20 09:18:45 +00:00
|
|
|
if response_addr != upstream_address {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if is_encrypted_response(&response, response_len) {
|
|
|
|
break (response_len, false);
|
|
|
|
}
|
2021-06-19 23:03:06 +00:00
|
|
|
if is_certificate_response(&response, encrypted_packet) {
|
2019-10-20 09:18:45 +00:00
|
|
|
break (response_len, true);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
response.truncate(response_len);
|
|
|
|
if is_certificate_response {
|
|
|
|
let mut hasher = globals.hasher;
|
|
|
|
hasher.write(&relayed_packet[..ANONYMIZED_DNSCRYPT_OVERHEAD]);
|
2021-06-19 23:03:06 +00:00
|
|
|
hasher.write(&dns::qname(encrypted_packet)?);
|
2019-10-20 09:18:45 +00:00
|
|
|
let packet_hash = hasher.finish128().as_u128();
|
|
|
|
let cached_response = {
|
|
|
|
match globals.cert_cache.lock().get(&packet_hash) {
|
|
|
|
None => None,
|
|
|
|
Some(response) if !(*response).has_expired() => {
|
|
|
|
trace!("Relayed certificate cached");
|
|
|
|
let mut cached_response = (*response).clone();
|
|
|
|
cached_response.set_tid(dns::tid(encrypted_packet));
|
|
|
|
Some(cached_response.into_response())
|
|
|
|
}
|
|
|
|
Some(_) => {
|
|
|
|
trace!("Relayed certificate expired");
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
match cached_response {
|
|
|
|
None => {
|
|
|
|
globals.cert_cache.lock().insert(
|
|
|
|
packet_hash,
|
|
|
|
CachedResponse::new(&globals.cert_cache, response.clone()),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
Some(cached_response) => response = cached_response,
|
2019-10-13 20:34:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "metrics")]
|
|
|
|
globals.varz.anonymized_responses.inc();
|
|
|
|
|
|
|
|
respond_to_query(client_ctx, response).await
|
|
|
|
}
|
2019-10-19 11:39:21 +00:00
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn is_encrypted_response(response: &[u8], response_len: usize) -> bool {
|
|
|
|
(DNSCRYPT_UDP_RESPONSE_MIN_SIZE..=DNSCRYPT_UDP_RESPONSE_MAX_SIZE).contains(&response_len)
|
|
|
|
&& response[..DNSCRYPT_RESPONSE_MAGIC_SIZE] == DNSCRYPT_RESPONSE_MAGIC
|
|
|
|
}
|
|
|
|
|
2019-10-20 16:05:26 +00:00
|
|
|
fn is_certificate_response(response: &[u8], query: &[u8]) -> bool {
|
|
|
|
let prefix = b"2.dnscrypt-cert.";
|
|
|
|
if !((DNS_HEADER_SIZE + prefix.len() + 4..=DNS_MAX_PACKET_SIZE).contains(&query.len())
|
|
|
|
&& (DNS_HEADER_SIZE + prefix.len() + 4..=DNS_MAX_PACKET_SIZE).contains(&response.len())
|
2019-10-19 23:22:36 +00:00
|
|
|
&& dns::tid(response) == dns::tid(query)
|
|
|
|
&& dns::is_response(response)
|
2019-10-20 09:40:50 +00:00
|
|
|
&& !dns::is_response(query))
|
|
|
|
{
|
|
|
|
debug!("Unexpected relayed cert response");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
let qname = match (dns::qname(query), dns::qname(response)) {
|
|
|
|
(Ok(response_qname), Ok(query_qname)) if response_qname == query_qname => query_qname,
|
|
|
|
_ => {
|
|
|
|
debug!("Relayed cert qname response didn't match the query qname");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
if qname.len() <= prefix.len() || &qname[..prefix.len()] != prefix {
|
|
|
|
debug!("Relayed cert qname response didn't start with the standard prefix");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
true
|
2019-10-19 11:39:21 +00:00
|
|
|
}
|