use log::{debug, warn};
use crate::dns_key::DNSKey;
use crate::dns_parser;
use crate::errors::DnsResult;
#[allow(dead_code)]
pub const DNS_RCODE_NOERROR: u8 = 0; #[allow(dead_code)]
pub const DNS_RCODE_FORMERR: u8 = 1; pub const DNS_RCODE_SERVFAIL: u8 = 2; pub const DNS_RCODE_NXDOMAIN: u8 = 3; pub const DNS_RCODE_NOTIMP: u8 = 4; pub const DNS_RCODE_REFUSED: u8 = 5; pub const DNS_RCODE_BADVERS: u8 = 16;
pub fn validate_and_create_key(packet: &[u8], client_addr: &str) -> DnsResult<DNSKey> {
if dns_parser::is_response(packet) {
return Err(crate::errors::DnsError::InvalidPacket(
"Client packet is a response, not a query".to_string(),
));
}
if let Err(e) = dns_parser::validate_dns_packet(packet) {
debug!("Invalid DNS packet from {client_addr}: {e}");
debug!("Dropping invalid DNS packet without response");
return Err(e);
}
match DNSKey::from_packet(packet) {
Ok(key) => Ok(key),
Err(e) => {
debug!("Failed to create DNSKey from packet from {client_addr}: {e}");
debug!("Error details: {e:?}");
Err(e)
}
}
}
pub fn log_received_packet(packet_size: usize, client_addr: &str, protocol: &str) {
log::debug!("Received {packet_size} bytes from {protocol} client {client_addr}");
}
pub fn create_dns_response(query_data: &[u8], rcode: u8, log_msg: Option<&str>) -> Vec<u8> {
if let Some(msg) = log_msg {
log::debug!("{msg}");
}
let mut response = query_data.to_vec();
if let Err(e) = dns_parser::set_qr(&mut response, true) {
log::error!("Failed to set QR bit: {e}");
}
if let Err(e) = dns_parser::set_rcode(&mut response, rcode) {
log::error!("Failed to set RCODE: {e}");
}
if let Err(e) = dns_parser::set_ad(&mut response, false) {
log::error!("Failed to clear AD bit: {e}");
}
response
}
#[allow(dead_code)]
pub fn create_response_with_rcode(query_data: &[u8], rcode: u8) -> Vec<u8> {
create_dns_response(query_data, rcode, None)
}
pub fn create_badvers_response(query_data: &[u8]) -> Vec<u8> {
let stripped_query = match dns_parser::recover_question_from_response(query_data) {
Ok(query) => query,
Err(e) => {
log::error!("Failed to strip additional records for BADVERS response: {e}");
query_data.to_vec()
}
};
let mut response = create_dns_response(&stripped_query, DNS_RCODE_BADVERS, None);
let opt_record: Vec<u8> = vec![
0, 0, 41, 0x04, 0xd0, 1, 0, 0, 0, 0, 0, ];
let arcount_offset = 10;
if response.len() > arcount_offset + 2 {
dns_parser::set_arcount(&mut response, 1).unwrap_or_else(|e| {
log::error!("Failed to update ARCOUNT: {e}");
});
response.extend_from_slice(&opt_record);
}
response
}
pub fn set_response_flags(response_data: &mut [u8], authoritative_dns: bool) {
if let Err(e) = dns_parser::set_aa(response_data, authoritative_dns) {
log::error!("Failed to set AA bit: {e}");
}
if let Err(e) = dns_parser::set_ra(response_data, !authoritative_dns) {
log::error!("Failed to set RA bit: {e}");
}
}
pub trait DnsQueryProcessor {
#[allow(async_fn_in_trait)]
async fn process_dns_query(
&self,
query_data: &[u8],
client_addr: &str,
protocol: &str,
query_manager: &std::sync::Arc<crate::query_manager::QueryManager>,
upstream_servers: &[String],
server_timeout: u64,
dns_packet_len_max: usize,
stats: Option<std::sync::Arc<crate::stats::SharedStats>>,
load_balancing_strategy: crate::load_balancer::LoadBalancingStrategy,
) -> Option<(Vec<u8>, u16)> {
log_received_packet(query_data.len(), client_addr, protocol);
let dns_key = match validate_and_create_key(query_data, client_addr) {
Ok(key) => key,
Err(e) => {
if let crate::errors::DnsError::UnsupportedEdnsVersion { version } = e {
debug!(
"Unsupported EDNS version {version} from {client_addr}, returning BADVERS"
);
let response = create_badvers_response(query_data);
let client_query_id = crate::dns_parser::tid(query_data);
return Some((response, client_query_id));
}
return None; }
};
let client_query_id = crate::dns_parser::tid(query_data);
let client_ip = extract_client_ip(client_addr);
let resolver = prepare_resolver(
query_manager,
upstream_servers,
server_timeout,
dns_packet_len_max,
stats,
load_balancing_strategy,
&client_ip,
);
submit_query_and_get_response(
query_manager,
dns_key,
query_data,
resolver,
client_addr,
client_query_id,
)
.await
.map(|response_data| (response_data, client_query_id))
}
}
fn extract_client_ip(client_addr: &str) -> String {
if let Ok(sa) = client_addr.parse::<std::net::SocketAddr>() {
return sa.ip().to_string();
}
if let Ok(ip) = client_addr.parse::<std::net::IpAddr>() {
return ip.to_string();
}
warn!("Unable to parse client address: {client_addr}, using 'unknown'");
"unknown".to_string()
}
fn prepare_resolver(
query_manager: &std::sync::Arc<crate::query_manager::QueryManager>,
upstream_servers: &[String],
server_timeout: u64,
dns_packet_len_max: usize,
stats: Option<std::sync::Arc<crate::stats::SharedStats>>,
load_balancing_strategy: crate::load_balancer::LoadBalancingStrategy,
client_ip: &str,
) -> impl Fn(Vec<u8>) -> futures::future::BoxFuture<'static, crate::errors::DnsResult<Vec<u8>>>
+ Send
+ Sync
+ 'static {
let enable_ecs = query_manager.get_enable_ecs();
let ecs_prefix_v4 = query_manager.get_ecs_prefix_v4();
let ecs_prefix_v6 = query_manager.get_ecs_prefix_v6();
let spoof_protection = query_manager.get_spoof_protection();
crate::resolver::create_resolver_with_client_ip(
upstream_servers.to_vec(),
server_timeout,
dns_packet_len_max,
stats,
load_balancing_strategy,
client_ip.to_string(),
enable_ecs,
ecs_prefix_v4,
ecs_prefix_v6,
spoof_protection,
)
}
async fn submit_query_and_get_response(
query_manager: &std::sync::Arc<crate::query_manager::QueryManager>,
dns_key: DNSKey,
query_data: &[u8],
resolver: impl Fn(Vec<u8>) -> futures::future::BoxFuture<'static, crate::errors::DnsResult<Vec<u8>>>
+ Send
+ Sync
+ 'static,
client_addr: &str,
client_query_id: u16,
) -> Option<Vec<u8>> {
match query_manager
.submit_query_with_client(dns_key, query_data.to_vec(), resolver, client_addr)
.await
{
Ok(mut receiver) => {
match receiver.recv().await {
Ok(response) => {
let client_had_ecs = dns_parser::extract_edns_client_subnet(query_data)
.ok()
.flatten()
.is_some();
process_dns_response(response, client_query_id, client_addr, client_had_ecs)
}
Err(e) => {
log::debug!("Failed to receive response from query manager: {e}");
log::debug!("Error details: {e:?}");
None
}
}
}
Err(e) => {
log::debug!("Failed to submit query to query manager: {e}");
log::debug!("Error details: {e:?}");
None
}
}
}
fn process_dns_response(
response: crate::query_manager::DnsResponse,
client_query_id: u16,
client_addr: &str,
client_had_ecs: bool,
) -> Option<Vec<u8>> {
if let Some(error_msg) = response.error {
log::debug!("Error in DNS response: {error_msg}");
log::debug!("Not sending error response to client {client_addr}");
return None;
}
let mut response_data = response.data;
if let Err(e) = crate::dns_parser::set_tid(&mut response_data, client_query_id) {
log::debug!("Failed to set transaction ID in response: {e}");
None
} else {
if !client_had_ecs
&& let Err(e) = crate::dns_parser::remove_edns_client_subnet(&mut response_data)
{
log::debug!("Failed to remove ECS from client response: {e}");
return None;
}
log::debug!("Replaced response transaction ID with client query ID: {client_query_id}");
Some(response_data)
}
}
impl DnsQueryProcessor for () {}
#[cfg(test)]
mod tests {
use super::*;
use crate::dns_parser;
fn create_test_query_with_edns_version(version: u8) -> Vec<u8> {
let query = vec![
0x12, 0x34, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x07, b'e', b'x', b'a', b'm', b'p', b'l', b'e', 0x03, b'c', b'o', b'm', 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x29, 0x04, 0xd0, 0x00, version, 0x00, 0x00, 0x00, 0x00, ];
query
}
#[test]
fn test_create_badvers_response() {
let query = create_test_query_with_edns_version(1);
let response = create_badvers_response(&query);
assert!(dns_parser::is_response(&response));
assert_eq!(dns_parser::rcode(&response), 0);
assert_eq!(dns_parser::arcount(&response), 1);
let edns_version = dns_parser::extract_edns_version(&response).unwrap();
assert_eq!(edns_version, Some(0));
let response_len = response.len();
assert!(response_len >= 11);
let opt_start = response_len - 11;
assert_eq!(response[opt_start], 0); assert_eq!(response[opt_start + 1], 0); assert_eq!(response[opt_start + 2], 41); assert_eq!(response[opt_start + 5], 1); assert_eq!(response[opt_start + 6], 0); }
#[test]
fn test_unsupported_edns_version_validation() {
let query_v0 = create_test_query_with_edns_version(0);
let result = validate_and_create_key(&query_v0, "127.0.0.1:12345");
assert!(result.is_ok());
let query_v1 = create_test_query_with_edns_version(1);
let result = validate_and_create_key(&query_v1, "127.0.0.1:12345");
assert!(result.is_err());
match result {
Err(crate::errors::DnsError::UnsupportedEdnsVersion { version }) => {
assert_eq!(version, 1);
}
_ => panic!("Expected UnsupportedEdnsVersion error"),
}
let query_v2 = create_test_query_with_edns_version(2);
let result = validate_and_create_key(&query_v2, "127.0.0.1:12345");
assert!(result.is_err());
match result {
Err(crate::errors::DnsError::UnsupportedEdnsVersion { version }) => {
assert_eq!(version, 2);
}
_ => panic!("Expected UnsupportedEdnsVersion error"),
}
}
#[test]
fn test_response_packet_is_not_accepted_as_a_query() {
let mut packet = create_test_query_with_edns_version(0);
dns_parser::set_qr(&mut packet, true).unwrap();
assert!(validate_and_create_key(&packet, "127.0.0.1:12345").is_err());
let mut packet = create_test_query_with_edns_version(1);
dns_parser::set_qr(&mut packet, true).unwrap();
assert!(matches!(
validate_and_create_key(&packet, "127.0.0.1:12345"),
Err(crate::errors::DnsError::InvalidPacket(_))
));
}
#[test]
fn test_synthetic_response_clears_ad() {
let mut query = create_test_query_with_edns_version(0);
query[3] |= 0x20;
let response = create_dns_response(&query, DNS_RCODE_REFUSED, None);
assert!(!dns_parser::is_authenticated_data_requested(&response));
}
#[tokio::test]
async fn test_disabled_spoof_protection_skips_tcp_fallback() {
use tokio::io::{AsyncReadExt, AsyncWriteExt};
let udp = tokio::net::UdpSocket::bind("127.0.0.1:0").await.unwrap();
let upstream_addr = udp.local_addr().unwrap();
let tcp = tokio::net::TcpListener::bind(upstream_addr).await.unwrap();
tokio::spawn(async move {
let mut packet = vec![0u8; 4096];
let (length, client) = udp.recv_from(&mut packet).await.unwrap();
packet.truncate(length);
dns_parser::set_qr(&mut packet, true).unwrap();
let wrong_tid = dns_parser::tid(&packet).wrapping_add(1);
dns_parser::set_tid(&mut packet, wrong_tid).unwrap();
udp.send_to(&packet, client).await.unwrap();
});
tokio::spawn(async move {
let (mut stream, _) = tcp.accept().await.unwrap();
let mut length = [0u8; 2];
stream.read_exact(&mut length).await.unwrap();
let mut packet = vec![0u8; usize::from(u16::from_be_bytes(length))];
stream.read_exact(&mut packet).await.unwrap();
dns_parser::set_qr(&mut packet, true).unwrap();
stream.write_all(&length).await.unwrap();
stream.write_all(&packet).await.unwrap();
});
let query_manager = std::sync::Arc::new(crate::query_manager::QueryManager::for_tests(
2, 1232, false,
));
let response = ()
.process_dns_query(
&create_test_query_with_edns_version(0),
"127.0.0.1:5353",
"UDP",
&query_manager,
&[upstream_addr.to_string()],
2,
1232,
Some(std::sync::Arc::new(crate::stats::SharedStats::new())),
crate::load_balancer::LoadBalancingStrategy::Random,
)
.await;
assert!(response.is_none());
}
}