use async_trait::async_trait;
use crate::error::DkimError;
#[async_trait]
pub trait DkimResolver: Send + Sync {
async fn lookup_txt(&self, domain: &str) -> Result<Vec<String>, DkimError>;
}
#[cfg(feature = "hickory")]
pub mod hickory {
use super::*;
use hickory_resolver::TokioResolver;
use hickory_resolver::proto::rr::RData;
pub struct HickoryDkimResolver {
inner: TokioResolver,
}
impl HickoryDkimResolver {
pub fn new(resolver: TokioResolver) -> Self {
Self { inner: resolver }
}
}
fn is_no_records<E: std::fmt::Display>(e: &E) -> bool {
let s = e.to_string();
s.contains("no record")
|| s.contains("NXDOMAIN")
|| s.contains("no records found")
|| s.contains("NoRecordsFound")
}
#[async_trait]
impl DkimResolver for HickoryDkimResolver {
async fn lookup_txt(&self, domain: &str) -> Result<Vec<String>, DkimError> {
match self.inner.txt_lookup(domain).await {
Ok(resp) => {
let mut out = Vec::new();
for record in resp.answers() {
if let RData::TXT(txt) = &record.data {
out.push(txt.to_string());
}
}
Ok(out)
}
Err(e) if is_no_records(&e) => Ok(Vec::new()),
Err(e) => Err(DkimError::DnsTempError(e.to_string())),
}
}
}
}