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 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}");
}
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 mut response = create_dns_response(query_data, 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 {
let arcount = dns_parser::arcount(&response);
dns_parser::set_arcount(&mut response, arcount + 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();
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,
)
}
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) => {
process_dns_response(response, client_query_id, client_addr)
}
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,
) -> 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 {
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!(dns_parser::arcount(&response) > 0);
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"),
}
}
}