99 lines
3.1 KiB
Rust
99 lines
3.1 KiB
Rust
|
use std::net::{Ipv4Addr, Ipv6Addr};
|
||
|
use std::sync::Arc;
|
||
|
|
||
|
use anyhow::{anyhow, bail, Result};
|
||
|
use log::*;
|
||
|
use tokio::select;
|
||
|
use tokio::sync::watch;
|
||
|
|
||
|
use crate::dns_config::*;
|
||
|
use crate::provider::DnsProvider;
|
||
|
|
||
|
pub async fn dns_updater_task(
|
||
|
mut rx_dns_config: watch::Receiver<Arc<DnsConfig>>,
|
||
|
provider: Box<dyn DnsProvider>,
|
||
|
allowed_domains: Vec<String>,
|
||
|
mut must_exit: watch::Receiver<bool>,
|
||
|
) {
|
||
|
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<DnsConfig> = 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::<Ipv4Addr>()
|
||
|
.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::<Ipv6Addr>()
|
||
|
.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::<Vec<_>>();
|
||
|
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(())
|
||
|
}
|