aboutsummaryrefslogtreecommitdiff
path: root/src/dns_updater.rs
diff options
context:
space:
mode:
authorAlex Auvolat <alex@adnab.me>2022-12-07 15:35:12 +0100
committerAlex Auvolat <alex@adnab.me>2022-12-07 16:09:38 +0100
commited2653ae7dba9c072dcca1aed03b7cda0d910c85 (patch)
tree84452e729d4dbe8c27e3b7b7d4b3160833cfa40e /src/dns_updater.rs
downloadD53-ed2653ae7dba9c072dcca1aed03b7cda0d910c85.tar.gz
D53-ed2653ae7dba9c072dcca1aed03b7cda0d910c85.zip
First version of D53 that does something
First working version
Diffstat (limited to 'src/dns_updater.rs')
-rw-r--r--src/dns_updater.rs98
1 files changed, 98 insertions, 0 deletions
diff --git a/src/dns_updater.rs b/src/dns_updater.rs
new file mode 100644
index 0000000..06ba408
--- /dev/null
+++ b/src/dns_updater.rs
@@ -0,0 +1,98 @@
+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(())
+}