diff --git a/Cargo.lock b/Cargo.lock index f5b1831..fc7541b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -139,7 +139,7 @@ dependencies = [ [[package]] name = "cloudflare-ddns" -version = "2.0.9" +version = "2.0.10" dependencies = [ "chrono", "idna", diff --git a/Cargo.toml b/Cargo.toml index 89a8065..f2e6e11 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cloudflare-ddns" -version = "2.0.9" +version = "2.0.10" edition = "2021" description = "Access your home network remotely via a custom domain name without a static IP" license = "GPL-3.0" diff --git a/SECURITY.md b/SECURITY.md index 8b71857..fba5fa2 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -47,7 +47,7 @@ This project handles **Cloudflare API tokens** that grant DNS editing privileges - The Docker image runs as a **static binary from scratch** with zero runtime dependencies, which minimizes the attack surface. - Use `security_opt: no-new-privileges:true` in Docker Compose deployments. -- Pin image tags to a specific version (e.g., `timothyjmiller/cloudflare-ddns:v2.0.9`) rather than using `latest` in production. +- Pin image tags to a specific version (e.g., `timothyjmiller/cloudflare-ddns:v2.0.10`) rather than using `latest` in production. ### Network Security diff --git a/src/updater.rs b/src/updater.rs index 059b8ef..cf64fd1 100644 --- a/src/updater.rs +++ b/src/updater.rs @@ -26,7 +26,11 @@ pub async fn update_once( let mut notify = false; // NEW: track meaningful events if config.legacy_mode { - all_ok = update_legacy(config, cf_cache, ppfmt, noop_reported, detection_client).await; + let (ok, legacy_msgs, legacy_notify) = + update_legacy(config, cf_cache, ppfmt, noop_reported, detection_client).await; + all_ok = ok; + messages = legacy_msgs; + notify = legacy_notify; } else { // Detect IPs for each provider let mut detected_ips: HashMap> = HashMap::new(); @@ -251,10 +255,10 @@ async fn update_legacy( ppfmt: &PP, noop_reported: &mut HashSet, detection_client: &Client, -) -> bool { +) -> (bool, Vec, bool) { let legacy = match &config.legacy_config { Some(l) => l, - None => return false, + None => return (false, Vec::new(), false), }; let ddns = LegacyDdnsClient { @@ -341,16 +345,17 @@ async fn update_legacy( } } - ddns.update_ips( - &ips, - &legacy.cloudflare, - legacy.ttl, - legacy.purge_unknown_records, - noop_reported, - ) - .await; + let (msgs, should_notify) = ddns + .update_ips( + &ips, + &legacy.cloudflare, + legacy.ttl, + legacy.purge_unknown_records, + noop_reported, + ) + .await; - true + (true, msgs, should_notify) } /// Delete records on stop (for env var mode). @@ -499,11 +504,19 @@ impl LegacyDdnsClient { ttl: i64, purge_unknown_records: bool, noop_reported: &mut HashSet, - ) { + ) -> (Vec, bool) { + let mut messages = Vec::new(); + let mut notify = false; for ip in ips.values() { - self.commit_record(ip, config, ttl, purge_unknown_records, noop_reported) + let (msgs, changed) = self + .commit_record(ip, config, ttl, purge_unknown_records, noop_reported) .await; + messages.extend(msgs); + if changed { + notify = true; + } } + (messages, notify) } async fn commit_record( @@ -513,7 +526,9 @@ impl LegacyDdnsClient { ttl: i64, purge_unknown_records: bool, noop_reported: &mut HashSet, - ) { + ) -> (Vec, bool) { + let mut messages = Vec::new(); + let mut changed = false; for entry in config { let zone_resp: Option> = self .cf_api( @@ -592,6 +607,7 @@ impl LegacyDdnsClient { if let Some(ref id) = identifier { if modified { noop_reported.remove(&noop_key); + changed = true; if self.dry_run { println!("[DRY RUN] Would update record {fqdn} -> {}", ip.ip); } else { @@ -602,6 +618,10 @@ impl LegacyDdnsClient { .cf_api(&update_endpoint, "PUT", entry, Some(&record)) .await; } + messages.push(Message::new_ok(&format!( + "Updated {fqdn} -> {}", + ip.ip + ))); } else if noop_reported.insert(noop_key) { if self.dry_run { println!("[DRY RUN] Record {fqdn} is up to date"); @@ -611,6 +631,7 @@ impl LegacyDdnsClient { } } else { noop_reported.remove(&noop_key); + changed = true; if self.dry_run { println!("[DRY RUN] Would add new record {fqdn} -> {}", ip.ip); } else { @@ -620,6 +641,10 @@ impl LegacyDdnsClient { .cf_api(&create_endpoint, "POST", entry, Some(&record)) .await; } + messages.push(Message::new_ok(&format!( + "Created {fqdn} -> {}", + ip.ip + ))); } if purge_unknown_records { @@ -638,6 +663,7 @@ impl LegacyDdnsClient { } } } + (messages, changed) } }