Skip to content

Commit

Permalink
Update edition to 2021 and use cargo fmt
Browse files Browse the repository at this point in the history
Signed-off-by: Andrew Gunnerson <[email protected]>
  • Loading branch information
chenxiaolong committed Oct 14, 2023
1 parent db39fe8 commit 4a5cf43
Show file tree
Hide file tree
Showing 7 changed files with 87 additions and 109 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ repository = "https://github.com/chenxiaolong/ddns-updater"
readme = "README.md"
license = "GPL-3.0"
version = "0.1.8"
edition = "2018"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ The resulting executable will be in `target/release/ddns-updater` or `target\rel

If an issue occurs, the best way to troubleshoot is to set `log_level` to `debug` or `trace` in the config file. The `debug` level includes information like the detected IP addresses, while the `trace` level will also print out the raw DNS update request and response. Note that the `trace` output is not safe to paste online because it includes a dump of the TSIG key.

To enable trace logging for everything, including the underlying trust-dns library, set the `RUST_LOG` environment variable to `trace`.
To enable trace logging for everything, including the underlying hickory-dns library, set the `RUST_LOG` environment variable to `trace`.

## Limitations

Expand Down
2 changes: 1 addition & 1 deletion config.sample.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ name_server = ""
#
# Due to a bug, using `tcp` will currently result in a 5 second delay after the
# DDNS update response is received. See:
# https://github.com/bluejekyll/trust-dns/issues/1607
# https://github.com/hickory-dns/hickory-dns/issues/1607
#protocol = "udp"

# The interface to query IP addresses for populating A/AAAA records. If no
Expand Down
17 changes: 3 additions & 14 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,8 @@
use {
std::{
fmt,
path::Path,
time::Duration,
},
hickory_client::rr::{rdata::tsig::TsigAlgorithm, Name},
serde::Deserialize,
serde_with::{
base64::Base64,
serde_as,
DisplayFromStr,
},
hickory_client::rr::{
rdata::tsig::TsigAlgorithm,
Name,
},
serde_with::{base64::Base64, serde_as, DisplayFromStr},
std::{fmt, path::Path, time::Duration},
};

const DEFAULT_TTL: u32 = 300;
Expand Down
50 changes: 22 additions & 28 deletions src/dns.rs
Original file line number Diff line number Diff line change
@@ -1,49 +1,46 @@
use {
std::{
future::Future,
net::{IpAddr, SocketAddr},
pin::Pin,
time::Duration,
},
crate::config::Protocol,
hickory_client::{
client::{AsyncClient, Client, SyncClient},
error::ClientResult,
op::{Edns, Message, MessageType, OpCode, Query, UpdateMessage},
proto::{
error::ProtoError,
rr::{
dnssec::tsig::TSigner,
DNSClass, Name, RData, Record, RecordType,
},
rr::{dnssec::tsig::TSigner, DNSClass, Name, RData, Record, RecordType},
xfer::DnsExchangeSend,
},
tcp::TcpClientConnection,
udp::UdpClientConnection,
},
crate::config::Protocol,
std::{
future::Future,
net::{IpAddr, SocketAddr},
pin::Pin,
time::Duration,
},
};

/// `trust_dns_client::client::NewFutureObj` is not public
/// `hickory_client::client::NewFutureObj` is not public
type NewFutureObj<H> = Pin<
Box<
dyn Future<
Output = Result<
(
H,
Box<dyn Future<Output = Result<(), ProtoError>> + 'static + Send + Unpin>,
),
ProtoError,
>,
>
+ 'static
+ Send,
Output = Result<
(
H,
Box<dyn Future<Output = Result<(), ProtoError>> + 'static + Send + Unpin>,
),
ProtoError,
>,
>
+ 'static
+ Send,
>,
>;

/// Small wrapper to avoid callers needing to distinguish between TCP/UDP.
pub enum DnsClient {
Tcp(SyncClient::<TcpClientConnection>),
Udp(SyncClient::<UdpClientConnection>),
Tcp(SyncClient<TcpClientConnection>),
Udp(SyncClient<UdpClientConnection>),
}

impl DnsClient {
Expand Down Expand Up @@ -87,9 +84,6 @@ pub fn replace_addrs_message(
ttl: u32,
addrs: &[IpAddr],
) -> Message {
// trust_dns_client::client::AsyncClient::MAX_PAYLOAD_LEN is not public
const MAX_PAYLOAD_LEN: u16 = 1232;

let mut zone = Query::new();
zone.set_name(zone_origin.clone())
.set_query_class(DNSClass::IN)
Expand Down Expand Up @@ -121,7 +115,7 @@ pub fn replace_addrs_message(
message
.extensions_mut()
.get_or_insert_with(Edns::new)
.set_max_payload(MAX_PAYLOAD_LEN)
.set_max_payload(hickory_client::proto::op::update_message::MAX_PAYLOAD_LEN)
.set_version(0);

message
Expand Down
17 changes: 8 additions & 9 deletions src/iface.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use {
netif::Interface,
std::{
io,
net::{IpAddr, SocketAddr, TcpStream},
},
netif::Interface,
};

#[derive(Debug, thiserror::Error)]
Expand All @@ -24,19 +24,17 @@ pub struct Interfaces {

impl Interfaces {
pub fn new() -> Result<Self> {
let ifaces = netif::up()
.map_err(Error::QueryInterface)?
.collect();
let ifaces = netif::up().map_err(Error::QueryInterface)?.collect();

Ok(Self {
ifaces,
})
Ok(Self { ifaces })
}

pub fn get_addrs_by_name(&self, name: &str) -> Option<Vec<IpAddr>> {
let mut found = false;

let addrs = self.ifaces.iter()
let addrs = self
.ifaces
.iter()
.filter(|iface| iface.name() == name)
.inspect(|_| found = true)
.map(|iface| *iface.address())
Expand All @@ -52,7 +50,8 @@ impl Interfaces {
pub fn get_iface_by_tcp_source_ip(&self, server: SocketAddr) -> Result<&str> {
let source_ip = Self::get_tcp_source_ip(server)?;

self.ifaces.iter()
self.ifaces
.iter()
.find(|iface| *iface.address() == source_ip)
.map(Interface::name)
.ok_or(Error::InterfaceNotFound(source_ip))
Expand Down
106 changes: 51 additions & 55 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,27 @@ mod iface;
mod ip;

use {
std::{
borrow::Cow,
ffi::OsString,
net::{SocketAddr, ToSocketAddrs},
path::PathBuf,
str::FromStr,
sync::atomic::{AtomicBool, Ordering},
},
crate::dns::DnsClient,
clap::Parser,
log::{debug, error, info, trace, warn},
hickory_client::{
client::Client,
error::ClientError,
op::{ResponseCode, UpdateMessage},
proto::{
error::ProtoError,
rr::{
dnssec::tsig::TSigner,
DNSClass, Name, RecordType,
},
rr::{dnssec::tsig::TSigner, DNSClass, Name, RecordType},
},
},
iface::Interfaces,
crate::dns::DnsClient,
log::{debug, error, info, trace, warn},
std::{
borrow::Cow,
ffi::OsString,
net::{SocketAddr, ToSocketAddrs},
path::PathBuf,
str::FromStr,
sync::atomic::{AtomicBool, Ordering},
},
};

// Same as nsupdate
Expand Down Expand Up @@ -72,52 +69,58 @@ fn update_dns(server: SocketAddr, config: &config::Config) -> Result<()> {
let iface = match &config.global.interface {
Some(s) => s.as_str(),
None => {
debug!("Autodetecting interface from source IP of TCP connection to {}", server);
debug!("Autodetecting interface from source IP of TCP connection to {server}");
ifaces.get_iface_by_tcp_source_ip(server)?
}
};
debug!("Interface: {:?}", iface);
debug!("Interface: {iface:?}");

let addrs = ifaces.get_addrs_by_name(iface)
let addrs = ifaces
.get_addrs_by_name(iface)
.ok_or_else(|| Error::InterfaceNotFound(iface.to_string()))?;
let valid_addrs = addrs.into_iter()
let valid_addrs = addrs
.into_iter()
.filter(|ip| ip::is_suitable_ip(*ip))
.collect::<Vec<_>>();
debug!("Addresses: {:?}", valid_addrs);
debug!("Addresses: {valid_addrs:?}");

let name = match &config.global.hostname {
Some(n) => Cow::Borrowed(n),
None => {
let hostname = gethostname::gethostname();
let hostname_str = hostname.to_str()
let hostname_str = hostname
.to_str()
.ok_or_else(|| Error::InvalidHostnameUtf8(hostname.clone()))?;
let name = Name::from_str(hostname_str)
.map_err(|e| Error::InvalidHostnameDns(hostname.clone(), e))?;
Cow::Owned(name)
}
};
debug!("Hostname: {}", name);
debug!("Hostname: {name}");

let tsig = TSigner::new(
config.tsig.secret.clone(),
config.tsig.algorithm.into(),
name.clone().into_owned(),
DEFAULT_FUDGE,
).unwrap();
)
.unwrap();

let client = DnsClient::new(
server,
config.global.protocol,
config.global.timeout.to_duration(),
tsig,
).map_err(|e| Error::DnsClient(server, e))?;
)
.map_err(|e| Error::DnsClient(server, e))?;

let zone = match &config.global.zone {
Some(n) => Cow::Borrowed(n),
None => {
debug!("Querying SOA for: {}", name);
debug!("Querying SOA for: {name}");

let response = client.query(&name, DNSClass::IN, RecordType::SOA)
let response = client
.query(&name, DNSClass::IN, RecordType::SOA)
.map_err(|e| Error::DnsClient(server, e))?;
let authority = response.name_servers();
if authority.is_empty() {
Expand All @@ -127,31 +130,26 @@ fn update_dns(server: SocketAddr, config: &config::Config) -> Result<()> {
Cow::Owned(authority[0].name().clone())
}
};
debug!("Zone: {}", zone);
debug!("Zone: {zone}");

let request = dns::replace_addrs_message(
&zone,
&name,
config.global.ttl.0,
&valid_addrs,
);
trace!("Update request: {:?}", request);
let request = dns::replace_addrs_message(&zone, &name, config.global.ttl.0, &valid_addrs);
trace!("Update request: {request:?}");

for record in request.updates() {
info!("Record update: {}", record);
info!("Record update: {record}");
}

let responses = client.send(request);
let mut errored = false;

for response in responses {
let r = response.map_err(|e| Error::DnsClient(server, e))?;
trace!("Update response: {:?}", r);
trace!("Update response: {r:?}");

let code = r.response_code();

if code != ResponseCode::NoError {
warn!("Received error response: {0:?} ({0})", code);
warn!("Received error response: {code:?} ({code})");
errored = true;
}
}
Expand All @@ -165,38 +163,36 @@ fn update_dns(server: SocketAddr, config: &config::Config) -> Result<()> {

fn main_wrapper() -> Result<()> {
let opts: Opts = Opts::parse();
let config = config::load_config(&opts.config)
.map_err(|e| Error::Config(opts.config.clone(), e))?;

env_logger::Builder::from_env(
env_logger::Env::default()
.default_filter_or(format!(
"{}={}",
env!("CARGO_PKG_NAME").replace('-', "_"),
config.global.log_level,
))
)
.format_timestamp(None)
.init();
let config =
config::load_config(&opts.config).map_err(|e| Error::Config(opts.config.clone(), e))?;

env_logger::Builder::from_env(env_logger::Env::default().default_filter_or(format!(
"{}={}",
env!("CARGO_PKG_NAME").replace('-', "_"),
config.global.log_level,
)))
.format_timestamp(None)
.init();
LOGGING_INITIALIZED.store(true, Ordering::SeqCst);

trace!("Loaded config: {:?}", config);
trace!("Loaded config: {config:?}");

let server_with_port = match &config.global.name_server {
h if h.contains(':') => Cow::Borrowed(h.as_str()),
h => Cow::Owned(format!("{}:{}", h, config.global.protocol.default_port())),
h => Cow::Owned(format!("{h}:{}", config.global.protocol.default_port())),
};
debug!("Name server: {:?}", server_with_port);
debug!("Name server: {server_with_port:?}");

let servers = server_with_port.to_socket_addrs()
let servers = server_with_port
.to_socket_addrs()
.map_err(|e| Error::ResolveDnsHost(server_with_port.to_string(), e))?
.collect::<Vec<_>>();
debug!("Resolved name servers: {:?}", servers);
debug!("Resolved name servers: {servers:?}");

let mut last_error = None;

for server in servers {
debug!("Attempting to use name server: {}", server);
debug!("Attempting to use name server: {server}");

match update_dns(server, &config) {
Ok(_) => break,
Expand Down

0 comments on commit 4a5cf43

Please sign in to comment.