mod protocol;
pub(crate) mod providers;
pub use providers::{default_providers, provider_names};
use crate::error::ProviderError;
use crate::provider::Provider;
use crate::types::{IpVersion, Protocol};
use protocol::{build_query, parse_response, DnsClass, RecordType};
use std::future::Future;
use std::net::{IpAddr, SocketAddr};
use std::pin::Pin;
use std::str::FromStr;
use tokio::net::UdpSocket;
#[derive(Debug, Clone, Copy)]
pub enum DnsRecordType {
Address,
Txt,
}
#[derive(Debug, Clone)]
pub struct DnsProvider {
name: String,
query_domain: String,
resolver_addr: SocketAddr,
resolver_addr_v6: Option<SocketAddr>,
record_type: DnsRecordType,
dns_class: DnsClass,
supports_v4: bool,
supports_v6: bool,
}
impl DnsProvider {
pub fn new(
name: impl Into<String>,
query_domain: impl Into<String>,
resolver_addr: SocketAddr,
record_type: DnsRecordType,
) -> Self {
Self {
name: name.into(),
query_domain: query_domain.into(),
resolver_addr,
resolver_addr_v6: None,
record_type,
dns_class: DnsClass::In,
supports_v4: true,
supports_v6: false,
}
}
pub fn with_class(mut self, class: DnsClass) -> Self {
self.dns_class = class;
self
}
pub fn with_v6(mut self, supports: bool) -> Self {
self.supports_v6 = supports;
self
}
pub fn with_v6_resolver(mut self, addr: SocketAddr) -> Self {
self.resolver_addr_v6 = Some(addr);
self.supports_v6 = true;
self
}
async fn query(&self, version: IpVersion) -> Result<IpAddr, ProviderError> {
let resolver = match version {
IpVersion::V6 => self.resolver_addr_v6.unwrap_or(self.resolver_addr),
_ => self.resolver_addr,
};
let record_type = match self.record_type {
DnsRecordType::Address => match version {
IpVersion::V6 => RecordType::Aaaa,
_ => RecordType::A,
},
DnsRecordType::Txt => RecordType::Txt,
};
let query = build_query(&self.query_domain, record_type, self.dns_class)
.map_err(|e| ProviderError::new(&self.name, e))?;
let bind_addr = if resolver.is_ipv6() {
"[::]:0"
} else {
"0.0.0.0:0"
};
let socket = UdpSocket::bind(bind_addr)
.await
.map_err(|e| ProviderError::new(&self.name, e))?;
socket
.send_to(&query, resolver)
.await
.map_err(|e| ProviderError::new(&self.name, e))?;
let mut buf = [0u8; 1232]; let len = socket
.recv(&mut buf)
.await
.map_err(|e| ProviderError::new(&self.name, e))?;
let results = parse_response(&buf[..len], record_type)
.map_err(|e| ProviderError::message(&self.name, e))?;
for result in results {
for part in result.split_whitespace() {
let ip_str = part.split('/').next().unwrap_or(part);
if let Ok(ip) = IpAddr::from_str(ip_str) {
match version {
IpVersion::V4 if ip.is_ipv4() => return Ok(ip),
IpVersion::V6 if ip.is_ipv6() => return Ok(ip),
IpVersion::Any => return Ok(ip),
_ => continue,
}
}
}
}
Err(ProviderError::message(
&self.name,
"no valid IP in DNS response",
))
}
}
impl Provider for DnsProvider {
fn name(&self) -> &str {
&self.name
}
fn protocol(&self) -> Protocol {
Protocol::Dns
}
fn supports_v4(&self) -> bool {
self.supports_v4
}
fn supports_v6(&self) -> bool {
self.supports_v6
}
fn get_ip(
&self,
version: IpVersion,
) -> Pin<Box<dyn Future<Output = Result<IpAddr, ProviderError>> + Send + '_>> {
Box::pin(self.query(version))
}
}