1 // Copyright 2024 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 /// This module provides a reverse-dns function that caches the domain 16 /// name (FQDNs) and IpAddr from DNS answer records. 17 /// 18 /// This manager exists for two reasons: 19 /// 20 /// 1. RFC2817 Compliance (b/37055721): Requires converting IP address to 21 /// hostname for HTTP CONNECT requests. 22 /// 23 /// 2. Proxy bypass/exclusion list requires matching on host name 24 /// patterns. 25 /// 26 use crate::dns; 27 use etherparse::{PacketHeaders, PayloadSlice, TransportHeader}; 28 use log::debug; 29 use std::collections::HashMap; 30 use std::net::IpAddr; 31 use std::sync::Mutex; 32 33 /// DNS Manager of IP addresses to FQDN 34 pub struct DnsManager { 35 map: Mutex<HashMap<IpAddr, String>>, 36 } 37 38 impl Default for DnsManager { default() -> Self39 fn default() -> Self { 40 Self::new() 41 } 42 } 43 44 impl DnsManager { 45 const DNS_PORT: u16 = 53; 46 47 /// Creates a new `DnsManager`. new() -> Self48 pub fn new() -> Self { 49 DnsManager { map: Mutex::new(HashMap::new()) } 50 } 51 52 /// Add potential DNS entries to the cache. add_from_packet_headers(&self, headers: &PacketHeaders)53 pub fn add_from_packet_headers(&self, headers: &PacketHeaders) { 54 // Check if the packet contains a UDP header 55 // with source port from DNS server 56 // and DNS answers with A/AAAA records 57 if let Some(TransportHeader::Udp(udp_header)) = &headers.transport { 58 // with source port from DNS server 59 if udp_header.source_port == Self::DNS_PORT { 60 if let PayloadSlice::Udp(payload) = headers.payload { 61 // Add any A/AAAA domain names 62 if let Ok(answers) = dns::parse_answers(payload) { 63 for (ip_addr, name) in answers { 64 self.map.lock().unwrap().insert(ip_addr, name.clone()); 65 debug!("Added {} ({}) to DNS cache", name, ip_addr); 66 } 67 } 68 } 69 } 70 } 71 } 72 73 /// Adds potential DNS entries from an Ethernet slice. add_from_ethernet_slice(&self, packet: &[u8])74 pub fn add_from_ethernet_slice(&self, packet: &[u8]) { 75 let headers = PacketHeaders::from_ethernet_slice(packet).unwrap(); 76 self.add_from_packet_headers(&headers); 77 } 78 79 /// Return a FQDN from a prior DNS response for ip address get(&self, ip_addr: &IpAddr) -> Option<String>80 pub fn get(&self, ip_addr: &IpAddr) -> Option<String> { 81 self.map.lock().unwrap().get(ip_addr).cloned() 82 } 83 84 /// Returns the number of entries in the cache. len(&self) -> usize85 pub fn len(&self) -> usize { 86 self.map.lock().unwrap().len() 87 } 88 89 /// Checks if the cache is empty. is_empty(&self) -> bool90 pub fn is_empty(&self) -> bool { 91 self.map.lock().unwrap().len() == 0 92 } 93 } 94