update dependencies & rust edition; add better logging

This commit is contained in:
2026-03-23 01:37:32 -06:00
parent 25a950f758
commit 1fb709f7b1
5 changed files with 1736 additions and 859 deletions

View File

@@ -1,38 +1,60 @@
use hickory_resolver::Resolver;
use hickory_resolver::config::{ResolverConfig, ResolverOpts};
use hickory_resolver::name_server::TokioConnectionProvider;
use log::info;
use std::error::Error;
use std::net::IpAddr;
use log::info;
use trust_dns_proto::rr::record_type::RecordType;
use trust_dns_proto::rr::RecordData;
use trust_dns_resolver::config::{ResolverConfig, ResolverOpts};
use trust_dns_resolver::TokioAsyncResolver;
pub struct DnsResolver {
resolver: Resolver<TokioConnectionProvider>,
}
pub async fn is_addr_current(domain: &str, ip_addr: IpAddr) -> Result<bool, Box<dyn Error>> {
let response = TokioAsyncResolver::tokio(ResolverConfig::cloudflare(), ResolverOpts::default())
.lookup(
domain,
match ip_addr {
IpAddr::V4(_) => RecordType::A,
IpAddr::V6(_) => RecordType::AAAA,
},
)
.await?;
let mut record_ip: Option<IpAddr> = None;
for record in response.into_iter() {
record_ip = record.into_rdata().ip_addr();
if !record_ip.is_none() && record_ip == Some(ip_addr) {
return Ok(true);
impl DnsResolver {
pub fn build() -> Self {
Self {
resolver: Resolver::builder_with_config(
ResolverConfig::cloudflare(),
TokioConnectionProvider::default(),
)
.with_options(ResolverOpts::default())
.build(),
}
}
info!(
"dynamic ip drift detected: {} -> {}",
record_ip.unwrap(),
ip_addr
);
pub async fn is_addr_current(
&self,
domain: &str,
ip_addr: IpAddr,
) -> Result<bool, Box<dyn Error>> {
let dns_ip = match ip_addr {
IpAddr::V4(_) => self
.resolver
.ipv4_lookup(domain)
.await?
.iter()
.map(|r| IpAddr::V4(r.0))
.next(),
IpAddr::V6(_) => self
.resolver
.ipv6_lookup(domain)
.await?
.iter()
.map(|r| IpAddr::V6(r.0))
.next(),
};
Ok(false)
if dns_ip == Some(ip_addr) {
return Ok(true);
}
info!(
"dynamic ip drift detected: {} -> {}",
dns_ip.map_or("unknown".to_string(), |ip| ip.to_string()),
ip_addr
);
Ok(false)
}
}
#[cfg(test)]
@@ -42,10 +64,11 @@ mod unit {
#[tokio::test]
async fn test_is_addr_current() {
let resolver = super::DnsResolver::build();
let domain = "rskio.com";
let ip_addr = IpAddr::from_str("0.0.0.0").unwrap();
assert_eq!(
super::is_addr_current(domain, ip_addr).await.unwrap(),
resolver.is_addr_current(domain, ip_addr).await.unwrap(),
false
);
}

View File

@@ -7,7 +7,9 @@ use log::info;
use reqwest::get;
use std::net::IpAddr;
use std::time::SystemTime;
use tokio::time::{sleep, Duration};
use tokio::time::{Duration, sleep};
const WEEK_SECS: u64 = 604_800; // 7 * 24 * 60 * 60
#[derive(Parser)]
#[clap(
@@ -41,19 +43,36 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
);
let mut next_status_time = SystemTime::now();
let mut await_dns_sync = false;
let resolver = dns::DnsResolver::build();
loop {
let public_ip = get_public_ip().await?;
// print the current public ip each week
if SystemTime::now() > next_status_time {
info!("current public address is: {}", public_ip);
next_status_time += Duration::from_secs(7 * 24 * 60 * 60);
next_status_time += Duration::from_secs(WEEK_SECS);
}
// update record if current public ip drifts from dns record
if !dns::is_addr_current(&args.domain_name, public_ip).await? {
route53::update_record(&args.dns_zone_id, &args.domain_name, public_ip).await?;
match resolver
.is_addr_current(&args.domain_name, public_ip)
.await?
{
true => {
if await_dns_sync {
info! {"dns updates propogated successfully"}
await_dns_sync = false
}
}
false => {
if !await_dns_sync {
route53::update_record(&args.dns_zone_id, &args.domain_name, public_ip).await?;
await_dns_sync = true;
} else {
info! {"awaiting dns propagation delay"};
}
}
}
sleep(Duration::from_secs(args.seconds)).await;

View File

@@ -2,14 +2,14 @@ use std::error::Error;
use std::fmt;
use std::net::IpAddr;
use aws_config::meta::region::RegionProviderChain;
use aws_config::BehaviorVersion;
use aws_sdk_route53 as r53;
use aws_sdk_route53::types::{
Change, ChangeAction, ChangeBatch, ResourceRecord, ResourceRecordSet,
};
use log::info;
use thiserror::Error;
use tokio::time::{sleep, Duration};
use tokio::time::{Duration, sleep};
pub async fn update_record(
dns_zone_id: &str,
@@ -24,20 +24,14 @@ pub async fn update_record(
let resource_record_set: Option<ResourceRecordSet> =
get_single_record_set(&client, &dns_zone_id, &domain_name, &record_type).await?;
match resource_record_set.is_none() {
true => return Err(Box::new(Route53UpdateError::NoRecordAvailable)),
false => {
match resource_record_set {
None => Err(Box::new(Route53UpdateError::NoRecordAvailable)),
Some(rrs) => {
info!(
"requesting update to route53 record for {} {} -> {}",
record_type, domain_name, public_ip
);
return Ok(submit_single_change_request(
&client,
resource_record_set.unwrap(),
&public_ip,
&dns_zone_id,
)
.await?);
return Ok(submit_single_change_request(&client, rrs, &public_ip, &dns_zone_id).await?);
}
}
}
@@ -45,10 +39,7 @@ pub async fn update_record(
pub async fn get_client() -> Result<aws_sdk_route53::Client, Box<dyn Error>> {
// get aws r53 client
Ok(r53::Client::new(
&aws_config::from_env()
.region(RegionProviderChain::default_provider())
.load()
.await,
&aws_config::load_defaults(BehaviorVersion::latest()).await,
))
}