use crate::clients::Exchanger;
use crate::Message;
use crate::clients::stats::StatsBuilder;
use std::net::SocketAddr;
use std::net::ToSocketAddrs;
use std::net::UdpSocket;
use std::time::Duration;
pub const GOOGLE_IPV4_PRIMARY: &str = "8.8.8.8:53";
pub const GOOGLE_IPV4_SECONDARY: &str = "8.8.4.4:53";
pub const GOOGLE_IPV6_PRIMARY: &str = "2001:4860:4860::8888:53";
pub const GOOGLE_IPV6_SECONDARY: &str = "2001:4860:4860::8844:53";
pub const GOOGLE: [&str; 4] = [
GOOGLE_IPV4_PRIMARY,
GOOGLE_IPV4_SECONDARY,
GOOGLE_IPV6_PRIMARY,
GOOGLE_IPV6_SECONDARY,
];
pub struct Client {
servers: Vec<SocketAddr>,
read_timeout: Option<Duration>,
}
impl Default for Client {
fn default() -> Self {
Client {
servers: Vec::default(),
read_timeout: Some(Duration::new(5, 0)),
}
}
}
impl Client {
pub fn new<A: ToSocketAddrs>(servers: A) -> Result<Self, crate::Error> {
let servers: Vec<_> = servers.to_socket_addrs()?.collect();
if servers.is_empty() {
return Err(crate::Error::InvalidArgument(
"at least one DNS server is required".to_string(),
));
}
Ok(Self {
servers,
..Default::default()
})
}
pub fn set_read_timeout(&mut self, timeout: Option<Duration>) {
self.read_timeout = timeout;
}
}
impl Exchanger for Client {
fn exchange(&self, query: &Message) -> Result<Message, crate::Error> {
let socket = UdpSocket::bind("0.0.0.0:0")?;
socket.set_read_timeout(self.read_timeout)?;
let server = self.servers.first().ok_or_else(|| {
crate::Error::InvalidArgument("at least one DNS server is required".to_string())
})?;
log::trace!("UDP target={server}");
socket.connect(server)?;
log::trace!(
"UDP connected local={} peer={}",
socket.local_addr()?,
socket.peer_addr()?
);
let req = query.to_vec()?;
let stats = StatsBuilder::start(req.len());
log::trace!("UDP sending {} bytes to {}", req.len(), socket.peer_addr()?);
socket.send(&req)?;
let mut buf = [0; 4096];
let len = socket.recv(&mut buf)?;
log::trace!("UDP received {len} bytes from {}", socket.peer_addr()?);
let mut resp = Message::from_slice(&buf[0..len])?;
resp.stats = Some(stats.end(socket.peer_addr()?, len));
Ok(resp)
}
}
#[cfg(feature = "async-udp")]
pub use crate::clients::udp_async::AsyncClient;