• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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