use std::net::{Ipv4Addr, Ipv6Addr}; use std::sync::Arc; use std::time::Duration; use anyhow::{anyhow, bail, Result}; use tokio::select; use tokio::sync::watch; use tracing::*; use crate::dns_config::*; use crate::provider::DnsProvider; pub async fn dns_updater_task( mut rx_dns_config: watch::Receiver>, provider: Box, allowed_domains: Vec, mut must_exit: watch::Receiver, ) { info!("DNS updater will start in 5 seconds"); tokio::time::sleep(Duration::from_secs(5)).await; info!("DNS updater starting"); let mut config = Arc::new(DnsConfig::new()); while !*must_exit.borrow() { select!( c = rx_dns_config.changed() => { if c.is_err() { break; } } _ = must_exit.changed() => continue, ); let new_config: Arc = rx_dns_config.borrow().clone(); for (k, v) in new_config.entries.iter() { if config.entries.get(k) != Some(v) { let fulldomain = format!("{}.{}", k.subdomain, k.domain); if !allowed_domains.iter().any(|d| fulldomain.ends_with(d)) { error!( "Got an entry for domain {} which is not in allowed list", k.domain ); continue; } info!("Updating {} {}", k, v); if let Err(e) = update_dns_entry(k, v, provider.as_ref()).await { error!("Unable to update entry {} {}: {}", k, v, e); } } } config = new_config; } } async fn update_dns_entry( key: &DnsEntryKey, value: &DnsEntryValue, provider: &dyn DnsProvider, ) -> Result<()> { if value.targets.is_empty() { bail!("zero targets (internal error)"); } match key.record_type { DnsRecordType::A => { let mut targets = vec![]; for tgt in value.targets.iter() { targets.push( tgt.parse::() .map_err(|_| anyhow!("Invalid ipv4 address: {}", tgt))?, ); } provider .update_a(&key.domain, &key.subdomain, &targets) .await?; } DnsRecordType::AAAA => { let mut targets = vec![]; for tgt in value.targets.iter() { targets.push( tgt.parse::() .map_err(|_| anyhow!("Invalid ipv6 address: {}", tgt))?, ); } provider .update_aaaa(&key.domain, &key.subdomain, &targets) .await?; } DnsRecordType::CNAME => { let mut targets = value.targets.iter().cloned().collect::>(); if targets.len() > 1 { targets.sort(); warn!("Several CNAME targets for {}: {:?}. Taking first one in alphabetical order. Consider switching to a single global target instead.", key, targets); } provider .update_cname(&key.domain, &key.subdomain, &targets[0]) .await?; } } Ok(()) }