#[cfg(feature = "tokio")]
use hickory_resolver::net::runtime::TokioRuntimeProvider;
use hickory_resolver::{
proto::{
op::Query,
rr::{Name, Record, RecordType}
},
recursor::{DnssecConfig, DnssecPolicy, Recursor, RecursorError, RecursorOptions}
};
use log::{debug, error};
use openssl::sha::sha256;
use std::{sync::LazyLock, time::Instant};
#[cfg(feature = "tokio")]
type DnsRuntimeProvider = TokioRuntimeProvider;
static DNS_CLIENT: LazyLock<Recursor<DnsRuntimeProvider>> = LazyLock::new(|| {
let roots = [
"2001:503:ba3e::2:30".parse().unwrap(),
"198.41.0.4".parse().unwrap(),
"2801:1b8:10::b".parse().unwrap(),
"170.247.170.2".parse().unwrap(),
"2001:500:2::c".parse().unwrap(),
"192.33.4.12".parse().unwrap(),
"2001:500:2d::d".parse().unwrap(),
"199.7.91.13".parse().unwrap(),
"2001:500:2f::f".parse().unwrap(),
"192.5.5.241".parse().unwrap(),
"2001:7fe::53".parse().unwrap(),
"192.36.148.17".parse().unwrap(),
"2001:503:c27::2:30".parse().unwrap(),
"192.58.128.30".parse().unwrap(),
"2001:7fd::1".parse().unwrap(),
"193.0.14.129".parse().unwrap(),
"2001:500:9f::42".parse().unwrap(),
"199.7.83.42".parse().unwrap(),
"2001:dc3::35".parse().unwrap(),
"202.12.27.33".parse().unwrap()
];
Recursor::new(
&roots,
DnssecPolicy::ValidateWithStaticKey(DnssecConfig::default()),
None,
RecursorOptions::default(),
DnsRuntimeProvider::new()
)
.expect("Failed to initialise DNS client")
});
pub(crate) async fn query_record<F, T>(
name: &Name,
ty: RecordType,
filter_map: F
) -> Result<Vec<T>, RecursorError>
where
F: Fn(Record) -> Option<T>
{
let query = Query::query(name.clone(), ty);
debug!("Querying {ty} DNS record for {name}");
let instant = Instant::now();
let res = DNS_CLIENT.resolve(query, instant, true).await;
let elapsed = instant.elapsed().as_secs_f32();
match res {
Ok(msg) => {
debug!(
"Received {} DNS records in {elapsed} seconds",
msg.answers.len()
);
Ok(msg.answers.into_iter().filter_map(filter_map).collect())
},
Err(err) if err.is_no_records_found() || err.is_nx_domain() => {
debug!("Received 0 DNS records in {elapsed} seconds");
Ok(Vec::new())
},
Err(err) => {
error!(
"Error querying {ty} DNS record for {name} after {elapsed} seconds: {err}"
);
Err(err)
}
}
}
pub(crate) fn sha256_truncated(bytes: &[u8]) -> String {
let hash = sha256(bytes);
let hex = hash
.into_iter()
.take(28)
.map(|byte| format!("{byte:02x}"))
.collect::<Vec<_>>();
hex.join("")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sha256_truncated() {
assert_eq!(
sha256_truncated(b"git"),
"9a881b9b9f23849475296a8cd768ea1965bc3152df7118e60c145975"
);
}
}