// Copyright 2024 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /// This module provides a reverse-dns function that caches the domain /// name (FQDNs) and IpAddr from DNS answer records. /// /// This manager exists for two reasons: /// /// 1. RFC2817 Compliance (b/37055721): Requires converting IP address to /// hostname for HTTP CONNECT requests. /// /// 2. Proxy bypass/exclusion list requires matching on host name /// patterns. /// use crate::dns; use etherparse::{PacketHeaders, PayloadSlice, TransportHeader}; use log::debug; use std::collections::HashMap; use std::net::IpAddr; use std::sync::Mutex; /// DNS Manager of IP addresses to FQDN pub struct DnsManager { map: Mutex>, } impl Default for DnsManager { fn default() -> Self { Self::new() } } impl DnsManager { const DNS_PORT: u16 = 53; /// Creates a new `DnsManager`. pub fn new() -> Self { DnsManager { map: Mutex::new(HashMap::new()) } } /// Add potential DNS entries to the cache. pub fn add_from_packet_headers(&self, headers: &PacketHeaders) { // Check if the packet contains a UDP header // with source port from DNS server // and DNS answers with A/AAAA records if let Some(TransportHeader::Udp(udp_header)) = &headers.transport { // with source port from DNS server if udp_header.source_port == Self::DNS_PORT { if let PayloadSlice::Udp(payload) = headers.payload { // Add any A/AAAA domain names if let Ok(answers) = dns::parse_answers(payload) { for (ip_addr, name) in answers { self.map.lock().unwrap().insert(ip_addr, name.clone()); debug!("Added {} ({}) to DNS cache", name, ip_addr); } } } } } } /// Adds potential DNS entries from an Ethernet slice. pub fn add_from_ethernet_slice(&self, packet: &[u8]) { let headers = PacketHeaders::from_ethernet_slice(packet).unwrap(); self.add_from_packet_headers(&headers); } /// Return a FQDN from a prior DNS response for ip address pub fn get(&self, ip_addr: &IpAddr) -> Option { self.map.lock().unwrap().get(ip_addr).cloned() } /// Returns the number of entries in the cache. pub fn len(&self) -> usize { self.map.lock().unwrap().len() } /// Checks if the cache is empty. pub fn is_empty(&self) -> bool { self.map.lock().unwrap().len() == 0 } }