#![cfg(feature = "discovery")]
use std::net::SocketAddr;
use hickory_resolver::TokioAsyncResolver;
use hickory_resolver::config::{ResolverConfig, ResolverOpts};
use super::Backend;
pub struct DnsSdBackend {
zone: String,
resolver: TokioAsyncResolver,
}
impl DnsSdBackend {
pub fn new(zone: impl Into<String>) -> Result<Self, std::io::Error> {
let resolver = match hickory_resolver::system_conf::read_system_conf() {
Ok((cfg, opts)) => TokioAsyncResolver::tokio(cfg, opts),
Err(e) => {
tracing::warn!(error = %e, "system DNS config unavailable; using defaults");
TokioAsyncResolver::tokio(ResolverConfig::default(), ResolverOpts::default())
}
};
Ok(Self {
zone: zone.into(),
resolver,
})
}
fn service_fqdn(&self) -> String {
format!("_epics-ca._tcp.{}", self.zone)
}
}
#[async_trait::async_trait]
impl Backend for DnsSdBackend {
async fn discover(&self) -> Vec<SocketAddr> {
let svc = self.service_fqdn();
let ptr = match self.resolver.srv_lookup(&svc).await {
Ok(r) => r,
Err(e) => {
tracing::warn!(zone = %self.zone, error = %e,
"DNS-SD: PTR/SRV lookup failed");
return Vec::new();
}
};
let mut out = Vec::new();
for srv in ptr.iter() {
for ip in ptr.ip_iter() {
let addr = SocketAddr::new(ip, srv.port());
if !out.contains(&addr) {
out.push(addr);
}
}
}
if out.is_empty() {
tracing::debug!(zone = %self.zone, "DNS-SD: no instances found");
} else {
tracing::info!(zone = %self.zone, count = out.len(),
"DNS-SD discovered IOCs");
}
out
}
}