diff --git a/src/dns.rs b/src/dns.rs index 3df2722..c3194ec 100644 --- a/src/dns.rs +++ b/src/dns.rs @@ -22,6 +22,7 @@ const DNS_TYPE_HINFO: u16 = 13; const DNS_CLASS_INET: u16 = 1; const DNS_RCODE_SERVFAIL: u8 = 2; +const DNS_RCODE_NXDOMAIN: u8 = 3; const DNS_RCODE_REFUSED: u8 = 5; #[inline] @@ -54,6 +55,16 @@ pub fn set_rcode_refused(packet: &mut [u8]) { set_rcode(packet, DNS_RCODE_REFUSED) } +#[inline] +pub fn rcode_nxdomain(packet: &[u8]) -> bool { + rcode(packet) == DNS_RCODE_NXDOMAIN +} + +#[inline] +pub fn set_rcode_nxdomain(packet: &mut [u8]) { + set_rcode(packet, DNS_RCODE_NXDOMAIN) +} + #[inline] pub fn qdcount(packet: &[u8]) -> u16 { BigEndian::read_u16(&packet[4..]) diff --git a/src/main.rs b/src/main.rs index e319569..ac89f6a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -132,6 +132,7 @@ pub async fn respond_to_query(client_ctx: ClientCtx, response: Vec) -> Resul } async fn encrypt_and_respond_to_query( + globals: Arc, client_ctx: ClientCtx, packet: Vec, response: Vec, @@ -153,6 +154,10 @@ async fn encrypt_and_respond_to_query( max_response_size, )?, }; + globals.varz.client_queries_resolved.inc(); + if dns::rcode_nxdomain(&response) { + globals.varz.client_queries_rcode_nxdomain.inc(); + } respond_to_query(client_ctx, response).await } @@ -191,6 +196,7 @@ async fn handle_client_query( &dnscrypt_encryption_params_set, )? { return encrypt_and_respond_to_query( + globals, client_ctx, packet, synth_packet, @@ -211,6 +217,7 @@ async fn handle_client_query( ); let response = resolver::get_cached_response_or_resolve(&globals, &mut packet).await?; encrypt_and_respond_to_query( + globals, client_ctx, packet, response, diff --git a/src/resolver.rs b/src/resolver.rs index ee230d9..1fec08c 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -123,7 +123,12 @@ pub async fn resolve( response = resolve_tcp(globals, packet, &packet_qname, tid).await?; } #[cfg(feature = "metrics")] - globals.varz.upstream_received.inc(); + { + globals.varz.upstream_received.inc(); + if dns::rcode_nxdomain(&response) { + globals.varz.upstream_rcode_nxdomain.inc(); + } + } if dns::rcode_servfail(&response) || dns::rcode_refused(&response) { trace!("SERVFAIL/REFUSED: {}", dns::rcode(&response)); if let Some(cached_response) = cached_response { diff --git a/src/varz.rs b/src/varz.rs index 9dc017a..1167ee1 100644 --- a/src/varz.rs +++ b/src/varz.rs @@ -17,12 +17,15 @@ pub struct Inner { pub client_queries_offline: IntCounter, pub client_queries_errors: IntCounter, pub client_queries_blocked: IntCounter, + pub client_queries_resolved: IntCounter, + pub client_queries_rcode_nxdomain: IntCounter, pub inflight_udp_queries: IntGauge, pub inflight_tcp_queries: IntGauge, pub upstream_errors: IntCounter, pub upstream_sent: IntCounter, pub upstream_received: IntCounter, pub upstream_response_sizes: Histogram, + pub upstream_rcode_nxdomain: IntCounter, } pub type Varz = Arc; @@ -106,6 +109,18 @@ impl Inner { labels! {"handler" => "all",} )) .unwrap(), + client_queries_resolved: register_int_counter!(opts!( + "encrypted_dns_client_queries_resolved", + "Number of blocked client resolved", + labels! {"handler" => "all",} + )) + .unwrap(), + client_queries_rcode_nxdomain: register_int_counter!(opts!( + "encrypted_dns_client_queries_rcode_nxdomain", + "Number of responses with an NXDOMAIN error code", + labels! {"handler" => "all",} + )) + .unwrap(), inflight_udp_queries: register_int_gauge!(opts!( "encrypted_dns_inflight_udp_queries", "Number of UDP queries currently waiting for a response", @@ -142,6 +157,12 @@ impl Inner { vec![64.0, 128.0, 192.0, 256.0, 512.0, 1024.0, 2048.0] )) .unwrap(), + upstream_rcode_nxdomain: register_int_counter!(opts!( + "encrypted_dns_upstream_rcode_nxdomain", + "Number of upstream responses with an NXDOMAIN error code", + labels! {"handler" => "all",} + )) + .unwrap(), } } }