blob: fb84d9ac43ee7bacce5994281188b7a91b62b4f0 [file]
// 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<HashMap<IpAddr, String>>,
}
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<String> {
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
}
}