diff options
author | openshift-merge-bot[bot] <148852131+openshift-merge-bot[bot]@users.noreply.github.com> | 2024-09-25 20:39:08 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-09-25 20:39:08 +0000 |
commit | c283c98b2b912f5a9c8681309afb6a625e8ecc20 (patch) | |
tree | c10989da045e8814440ba5efa2a049d9cae48ff4 | |
parent | 56d105fb55568da0215f73135a10d9365321015c (diff) | |
parent | 22293ef96905b288f4b6087e5cf62212a1f6a543 (diff) |
Merge pull request #518 from Luap99/resolv-conf
serve: parse resolv.conf ourselves
-rw-r--r-- | Cargo.lock | 16 | ||||
-rw-r--r-- | Cargo.toml | 1 | ||||
-rw-r--r-- | src/dns/coredns.rs | 22 | ||||
-rw-r--r-- | src/error.rs | 10 | ||||
-rw-r--r-- | src/server/serve.rs | 115 |
5 files changed, 121 insertions, 43 deletions
@@ -17,7 +17,6 @@ dependencies = [ "libc", "log", "nix", - "resolv-conf", "syslog", "tokio", ] @@ -684,12 +683,6 @@ dependencies = [ ] [[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - -[[package]] name = "quote" version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -739,15 +732,6 @@ dependencies = [ ] [[package]] -name = "resolv-conf" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" -dependencies = [ - "quick-error", -] - -[[package]] name = "rustc-demangle" version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -30,7 +30,6 @@ hickory-proto = { version = "0.24.1", features = ["tokio-runtime"] } hickory-client = "0.24.1" futures-util = { version = "0.3.30", default-features = false } tokio = { version = "1.40.0", features = ["macros", "rt-multi-thread", "net", "signal"] } -resolv-conf = "0.7.0" nix = { version = "0.29.0", features = ["fs", "signal"] } libc = "0.2.159" arc-swap = "1.7.1" diff --git a/src/dns/coredns.rs b/src/dns/coredns.rs index 71ca70e..d5b817a 100644 --- a/src/dns/coredns.rs +++ b/src/dns/coredns.rs @@ -17,8 +17,6 @@ use hickory_proto::{ DnsStreamHandle, }; use log::{debug, error, trace, warn}; -use resolv_conf; -use resolv_conf::ScopedIp; use std::io::Error; use std::net::{IpAddr, SocketAddr}; use std::sync::Arc; @@ -40,10 +38,10 @@ pub struct CoreDns { #[derive(Clone)] struct CoreDnsData { - network_name: String, // raw network name - backend: &'static ArcSwap<DNSBackend>, // server's data store - no_proxy: bool, // do not forward to external resolvers - nameservers: Arc<Mutex<Vec<ScopedIp>>>, // host nameservers from resolv.conf + network_name: String, // raw network name + backend: &'static ArcSwap<DNSBackend>, // server's data store + no_proxy: bool, // do not forward to external resolvers + nameservers: Arc<Mutex<Vec<IpAddr>>>, // host nameservers from resolv.conf } enum Protocol { @@ -59,7 +57,7 @@ impl CoreDns { backend: &'static ArcSwap<DNSBackend>, rx: flume::Receiver<()>, no_proxy: bool, - nameservers: Arc<Mutex<Vec<ScopedIp>>>, + nameservers: Arc<Mutex<Vec<IpAddr>>>, ) -> Self { CoreDns { rx, @@ -219,18 +217,18 @@ impl CoreDns { "Forwarding dns request for {} type: {}", &request_name_string, record_type ); - let mut nameservers: Vec<ScopedIp> = Vec::new(); + let mut nameservers: Vec<IpAddr> = Vec::new(); // Add resolvers configured for container if let Some(Some(dns_servers)) = backend.ctr_dns_server.get(&src_address.ip()) { for dns_server in dns_servers.iter() { - nameservers.push(ScopedIp::from(*dns_server)); + nameservers.push(*dns_server); } // Add network scoped resolvers only if container specific resolvers were not configured } else if let Some(network_dns_servers) = backend.get_network_scoped_resolvers(&src_address.ip()) { for dns_server in network_dns_servers.iter() { - nameservers.push(ScopedIp::from(*dns_server)); + nameservers.push(*dns_server); } } // Use host resolvers if no custom resolvers are set for the container. @@ -257,7 +255,7 @@ impl CoreDns { } async fn forward_to_servers( - nameservers: Vec<ScopedIp>, + nameservers: Vec<IpAddr>, mut sender: BufDnsStreamHandle, src_address: SocketAddr, req: Message, @@ -265,7 +263,7 @@ impl CoreDns { ) { // forward dns request to hosts's /etc/resolv.conf for nameserver in &nameservers { - let addr = SocketAddr::new(nameserver.into(), 53); + let addr = SocketAddr::new(*nameserver, 53); let (client, handle) = match proto { Protocol::Udp => { let stream = UdpClientStream::<UdpSocket>::new(addr); diff --git a/src/error.rs b/src/error.rs index 6c81f53..fa256cb 100644 --- a/src/error.rs +++ b/src/error.rs @@ -8,7 +8,7 @@ pub enum AardvarkError { IOError(std::io::Error), Chain(String, Box<Self>), List(AardvarkErrorList), - ResolvConfParseError(resolv_conf::ParseError), + AddrParseError(std::net::AddrParseError), } impl AardvarkError { @@ -59,7 +59,7 @@ impl fmt::Display for AardvarkError { Self::Message(s) => write!(f, "{s}"), Self::Chain(s, e) => write!(f, "{s}: {e}"), Self::IOError(e) => write!(f, "IO error: {e}"), - Self::ResolvConfParseError(e) => write!(f, "parse resolv.conf: {e}"), + Self::AddrParseError(e) => write!(f, "parse address: {e}"), Self::List(list) => { // some extra code to only add \n when it contains multiple errors let mut iter = list.0.iter(); @@ -87,9 +87,9 @@ impl From<nix::Error> for AardvarkError { } } -impl From<resolv_conf::ParseError> for AardvarkError { - fn from(err: resolv_conf::ParseError) -> Self { - Self::ResolvConfParseError(err) +impl From<std::net::AddrParseError> for AardvarkError { + fn from(err: std::net::AddrParseError) -> Self { + Self::AddrParseError(err) } } diff --git a/src/server/serve.rs b/src/server/serve.rs index 6f30ed5..261ba6b 100644 --- a/src/server/serve.rs +++ b/src/server/serve.rs @@ -10,7 +10,6 @@ use arc_swap::ArcSwap; use log::{debug, error, info}; use nix::unistd; use nix::unistd::dup2; -use resolv_conf::ScopedIp; use std::collections::HashMap; use std::collections::HashSet; use std::env; @@ -126,7 +125,7 @@ async fn stop_and_start_threads<Ip>( listen_ips: HashMap<String, Vec<Ip>>, thread_handles: &mut ThreadHandleMap<Ip>, no_proxy: bool, - nameservers: Arc<Mutex<Vec<ScopedIp>>>, + nameservers: Arc<Mutex<Vec<IpAddr>>>, ) -> AardvarkResult<()> where Ip: Eq + Hash + Copy + Into<IpAddr> + Send + 'static, @@ -248,7 +247,7 @@ async fn start_dns_server( backend: &'static ArcSwap<DNSBackend>, rx: flume::Receiver<()>, no_proxy: bool, - nameservers: Arc<Mutex<Vec<ScopedIp>>>, + nameservers: Arc<Mutex<Vec<IpAddr>>>, ) -> AardvarkResult<()> { let server = CoreDns::new(name, backend, rx, no_proxy, nameservers); server @@ -263,7 +262,7 @@ async fn read_config_and_spawn( filter_search_domain: &str, handles_v4: &mut ThreadHandleMap<Ipv4Addr>, handles_v6: &mut ThreadHandleMap<Ipv6Addr>, - nameservers: Arc<Mutex<Vec<ScopedIp>>>, + nameservers: Arc<Mutex<Vec<IpAddr>>>, no_proxy: bool, ) -> AardvarkResult<()> { let (conf, listen_ip_v4, listen_ip_v6) = @@ -314,6 +313,7 @@ async fn read_config_and_spawn( Vec::new() } }; + debug!("Using the following upstream servers: {upstream_resolvers:?}"); { // use new scope to only lock for a short time @@ -373,10 +373,107 @@ fn daemonize() -> Result<(), Error> { } // read /etc/resolv.conf and return all nameservers -fn get_upstream_resolvers() -> AardvarkResult<Vec<ScopedIp>> { +fn get_upstream_resolvers() -> AardvarkResult<Vec<IpAddr>> { let mut f = File::open("/etc/resolv.conf").wrap("open resolv.conf")?; - let mut buf = Vec::with_capacity(4096); - f.read_to_end(&mut buf).wrap("read resolv.conf")?; - let conf = resolv_conf::Config::parse(buf)?; - Ok(conf.nameservers) + let mut buf = String::with_capacity(4096); + f.read_to_string(&mut buf).wrap("read resolv.conf")?; + + parse_resolv_conf(&buf) +} + +fn parse_resolv_conf(content: &str) -> AardvarkResult<Vec<IpAddr>> { + let mut nameservers: Vec<IpAddr> = Vec::new(); + for line in content.split('\n') { + // split of comments + let line = match line.split_once(|s| s == '#' || s == ';') { + Some((f, _)) => f, + None => line, + }; + let mut line_parts = line.split_whitespace(); + match line_parts.next() { + Some(first) => { + if first == "nameserver" { + if let Some(ip) = line_parts.next() { + // split of zone, we do not support the link local zone currently with ipv6 addresses + let ip = match ip.split_once("%s") { + Some((f, _)) => f, + None => ip, + }; + nameservers.push(ip.parse().wrap(ip)?); + } + } + } + None => continue, + } + } + Ok(nameservers) +} + +#[cfg(test)] +mod tests { + use super::*; + + const IP_1_1_1_1: IpAddr = IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1)); + const IP_1_1_1_2: IpAddr = IpAddr::V4(Ipv4Addr::new(1, 1, 1, 2)); + const IP_1_1_1_3: IpAddr = IpAddr::V4(Ipv4Addr::new(1, 1, 1, 3)); + + #[test] + fn test_parse_resolv_conf() { + let res = parse_resolv_conf("nameserver 1.1.1.1").expect("failed to parse"); + assert_eq!(res, vec![IP_1_1_1_1]); + } + + #[test] + fn test_parse_resolv_conf_multiple() { + let res = parse_resolv_conf( + "nameserver 1.1.1.1 +nameserver 1.1.1.2 +nameserver 1.1.1.3", + ) + .expect("failed to parse"); + assert_eq!(res, vec![IP_1_1_1_1, IP_1_1_1_2, IP_1_1_1_3]); + } + + #[test] + fn test_parse_resolv_conf_search_and_options() { + let res = parse_resolv_conf( + "nameserver 1.1.1.1 +nameserver 1.1.1.2 +nameserver 1.1.1.3 +search test.podman +options rotate", + ) + .expect("failed to parse"); + assert_eq!(res, vec![IP_1_1_1_1, IP_1_1_1_2, IP_1_1_1_3]); + } + #[test] + fn test_parse_resolv_conf_with_comment() { + let res = parse_resolv_conf( + "# mytest + nameserver 1.1.1.1 # space +nameserver 1.1.1.2#nospace + #leading spaces +nameserver 1.1.1.3", + ) + .expect("failed to parse"); + assert_eq!(res, vec![IP_1_1_1_1, IP_1_1_1_2, IP_1_1_1_3]); + } + + #[test] + fn test_parse_resolv_conf_with_invalid_content() { + let res = parse_resolv_conf( + "hey I am not known +nameserver 1.1.1.1 +nameserver 1.1.1.2 somestuff here +abc +nameserver 1.1.1.3", + ) + .expect("failed to parse"); + assert_eq!(res, vec![IP_1_1_1_1, IP_1_1_1_2, IP_1_1_1_3]); + } + + #[test] + fn test_parse_resolv_conf_with_invalid_ip() { + parse_resolv_conf("nameserver abc").expect_err("invalid ip must error"); + } } |