Compare commits
2 commits
8306293913
...
74cf34edff
Author | SHA1 | Date | |
---|---|---|---|
74cf34edff | |||
ae0dbf3bd9 |
3 changed files with 90 additions and 81 deletions
16
Cargo.lock
generated
16
Cargo.lock
generated
|
@ -185,9 +185,9 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
|
|||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.25"
|
||||
version = "1.2.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0fc897dc1e865cc67c0e05a836d9d3f1df3cbe442aa4a9473b18e12624a4951"
|
||||
checksum = "956a5e21988b87f372569b66183b78babf23ebc2e744b733e4350a752c4dafac"
|
||||
dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
|
@ -834,9 +834,9 @@ checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa"
|
|||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.15.0"
|
||||
version = "1.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9"
|
||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
|
@ -1027,9 +1027,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tracing-attributes"
|
||||
version = "0.1.28"
|
||||
version = "0.1.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
|
||||
checksum = "1b1ffbcf9c6f6b99d386e7444eb608ba646ae452a36b39737deb9663b610f662"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -1038,9 +1038,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.33"
|
||||
version = "0.1.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
|
||||
checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"valuable",
|
||||
|
|
20
flake.lock
generated
20
flake.lock
generated
|
@ -37,11 +37,11 @@
|
|||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1749008870,
|
||||
"narHash": "sha256-5QEAcgawS2tOJrLr+U5DtzlShSCEpeg7PZDC7txvLQs=",
|
||||
"rev": "c2a03962b8e24e669fb37b7df10e7c79531ff1a4",
|
||||
"lastModified": 1749238035,
|
||||
"narHash": "sha256-+w+VydE8NSQXu8RSNLhn0fVMaEoFPgUbNYa8m1rewuM=",
|
||||
"rev": "d3d2d80a2191a73d1e86456a751b83aa13085d7d",
|
||||
"type": "tarball",
|
||||
"url": "https://releases.nixos.org/nixos/unstable/nixos-25.11pre810143.c2a03962b8e2/nixexprs.tar.xz?rev=c2a03962b8e24e669fb37b7df10e7c79531ff1a4"
|
||||
"url": "https://releases.nixos.org/nixos/unstable/nixos-25.11pre811203.d3d2d80a2191/nixexprs.tar.xz?rev=d3d2d80a2191a73d1e86456a751b83aa13085d7d"
|
||||
},
|
||||
"original": {
|
||||
"type": "tarball",
|
||||
|
@ -65,11 +65,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1749091064,
|
||||
"narHash": "sha256-TGtYjzRX0sueFhwYsnNNFF5TTKnpnloznpIghLzxeXo=",
|
||||
"lastModified": 1749177458,
|
||||
"narHash": "sha256-9HNq3EHZIvvxXQyEn0sYOywcESF1Xqw2Q8J1ZewcXuk=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "12419593ce78f2e8e1e89a373c6515885e218acb",
|
||||
"rev": "d58933b88cef7a05e9677e94352fd6fedba402cd",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -100,11 +100,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1748243702,
|
||||
"narHash": "sha256-9YzfeN8CB6SzNPyPm2XjRRqSixDopTapaRsnTpXUEY8=",
|
||||
"lastModified": 1749194973,
|
||||
"narHash": "sha256-eEy8cuS0mZ2j/r/FE0/LYBSBcIs/MKOIVakwHVuqTfk=",
|
||||
"owner": "numtide",
|
||||
"repo": "treefmt-nix",
|
||||
"rev": "1f3f7b784643d488ba4bf315638b2b0a4c5fb007",
|
||||
"rev": "a05be418a1af1198ca0f63facb13c985db4cb3c5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
135
src/main.rs
135
src/main.rs
|
@ -128,7 +128,7 @@ impl SavedIPs {
|
|||
.chain(self.ipv6.map(IpAddr::V6))
|
||||
}
|
||||
|
||||
fn from_str(data: &str) -> miette::Result<Self> {
|
||||
fn from_str(data: &str) -> Result<Self> {
|
||||
match data.parse::<IpAddr>() {
|
||||
// Old format
|
||||
Ok(IpAddr::V4(ipv4)) => Ok(Self {
|
||||
|
@ -145,7 +145,7 @@ impl SavedIPs {
|
|||
}
|
||||
|
||||
impl AppState<'static> {
|
||||
fn from_args(args: &Opts, config: &config::Config) -> miette::Result<Self> {
|
||||
fn from_args(args: &Opts, config: &config::Config) -> Result<Self> {
|
||||
let Opts {
|
||||
verbosity: _,
|
||||
data_dir,
|
||||
|
@ -180,7 +180,7 @@ impl AppState<'static> {
|
|||
// Load keyfile
|
||||
key_file: key_file
|
||||
.as_deref()
|
||||
.map(|path| -> miette::Result<_> {
|
||||
.map(|path| -> Result<_> {
|
||||
std::fs::File::open(path)
|
||||
.into_diagnostic()
|
||||
.wrap_err_with(|| {
|
||||
|
@ -255,6 +255,18 @@ impl std::str::FromStr for Ipv6Prefix {
|
|||
}
|
||||
}
|
||||
|
||||
fn load_password(path: &Path) -> Result<Box<[u8]>> {
|
||||
let pass = std::fs::read_to_string(path).into_diagnostic()?;
|
||||
|
||||
let pass: Box<[u8]> = URL_SAFE_NO_PAD
|
||||
.decode(pass.trim().as_bytes())
|
||||
.into_diagnostic()
|
||||
.wrap_err_with(|| format!("failed to decode password from {}", path.display()))?
|
||||
.into();
|
||||
|
||||
Ok(pass)
|
||||
}
|
||||
|
||||
#[tracing::instrument(err)]
|
||||
fn main() -> Result<()> {
|
||||
// set panic hook to pretty print with miette's formatter
|
||||
|
@ -310,18 +322,8 @@ fn main() -> Result<()> {
|
|||
let password_hash = config
|
||||
.password
|
||||
.password_file
|
||||
.map(|path| -> miette::Result<_> {
|
||||
let path = path.as_path();
|
||||
let pass = std::fs::read_to_string(path).into_diagnostic()?;
|
||||
|
||||
let pass: Box<[u8]> = URL_SAFE_NO_PAD
|
||||
.decode(pass.trim().as_bytes())
|
||||
.into_diagnostic()
|
||||
.wrap_err_with(|| format!("failed to decode password from {}", path.display()))?
|
||||
.into();
|
||||
|
||||
Ok(pass)
|
||||
})
|
||||
.as_deref()
|
||||
.map(load_password)
|
||||
.transpose()
|
||||
.wrap_err("failed to load password hash")?;
|
||||
|
||||
|
@ -336,63 +338,70 @@ fn main() -> Result<()> {
|
|||
.into_diagnostic()
|
||||
.wrap_err("failed to start the tokio runtime")?;
|
||||
|
||||
rt.block_on(async {
|
||||
// Update DNS record with previous IPs (if available)
|
||||
let ips = state.last_ips.lock().await.clone();
|
||||
rt.block_on(async_main(state, config, password_hash))
|
||||
.wrap_err("failed to run main loop")
|
||||
}
|
||||
|
||||
let mut actions = ips
|
||||
.ips()
|
||||
.filter(|ip| config.records.ip_type.valid_for_type(*ip))
|
||||
.flat_map(|ip| nsupdate::Action::from_records(ip, state.ttl, state.records))
|
||||
.peekable();
|
||||
#[tracing::instrument(err, skip(state, pass))]
|
||||
async fn async_main(
|
||||
state: AppState<'static>,
|
||||
config: Config,
|
||||
pass: Option<Box<[u8]>>,
|
||||
) -> Result<()> {
|
||||
// Update DNS record with previous IPs (if available)
|
||||
let ips = state.last_ips.lock().await.clone();
|
||||
|
||||
if actions.peek().is_some() {
|
||||
match nsupdate::nsupdate(state.key_file, actions).await {
|
||||
Ok(status) => {
|
||||
if !status.success() {
|
||||
error!("nsupdate failed: code {status}");
|
||||
bail!("nsupdate returned with code {status}");
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
error!("Failed to update records with previous IP: {err}");
|
||||
return Err(err)
|
||||
.into_diagnostic()
|
||||
.wrap_err("failed to update records with previous IP");
|
||||
let mut actions = ips
|
||||
.ips()
|
||||
.filter(|ip| config.records.ip_type.valid_for_type(*ip))
|
||||
.flat_map(|ip| nsupdate::Action::from_records(ip, state.ttl, state.records))
|
||||
.peekable();
|
||||
|
||||
if actions.peek().is_some() {
|
||||
match nsupdate::nsupdate(state.key_file, actions).await {
|
||||
Ok(status) => {
|
||||
if !status.success() {
|
||||
error!("nsupdate failed: code {status}");
|
||||
bail!("nsupdate returned with code {status}");
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
error!("Failed to update records with previous IP: {err}");
|
||||
return Err(err)
|
||||
.into_diagnostic()
|
||||
.wrap_err("failed to update records with previous IP");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create services
|
||||
let app = Router::new().route("/update", get(update_records));
|
||||
// if a password is provided, validate it
|
||||
let app = if let Some(pass) = password_hash {
|
||||
app.layer(auth::layer(
|
||||
Box::leak(pass),
|
||||
Box::leak(config.password.salt),
|
||||
))
|
||||
} else {
|
||||
app
|
||||
}
|
||||
.layer(config.records.ip_source.into_extension())
|
||||
.with_state(state);
|
||||
// Create services
|
||||
let app = Router::new().route("/update", get(update_records));
|
||||
// if a password is provided, validate it
|
||||
let app = if let Some(pass) = pass {
|
||||
app.layer(auth::layer(
|
||||
Box::leak(pass),
|
||||
Box::leak(config.password.salt),
|
||||
))
|
||||
} else {
|
||||
app
|
||||
}
|
||||
.layer(config.records.ip_source.into_extension())
|
||||
.with_state(state);
|
||||
|
||||
let config::Server { address } = config.server;
|
||||
let config::Server { address } = config.server;
|
||||
|
||||
// Start services
|
||||
info!("starting listener on {address}");
|
||||
let listener = tokio::net::TcpListener::bind(address)
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
info!("listening on {address}");
|
||||
axum::serve(
|
||||
listener,
|
||||
app.into_make_service_with_connect_info::<SocketAddr>(),
|
||||
)
|
||||
// Start services
|
||||
info!("starting listener on {address}");
|
||||
let listener = tokio::net::TcpListener::bind(address)
|
||||
.await
|
||||
.into_diagnostic()
|
||||
})
|
||||
.wrap_err("failed to run main loop")
|
||||
.into_diagnostic()?;
|
||||
info!("listening on {address}");
|
||||
axum::serve(
|
||||
listener,
|
||||
app.into_make_service_with_connect_info::<SocketAddr>(),
|
||||
)
|
||||
.await
|
||||
.into_diagnostic()
|
||||
}
|
||||
|
||||
/// Serde deserialization decorator to map empty Strings to None,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue