hashiverse_lib/transport/bootstrap_provider/
dnssec_bootstrap_provider.rs1use crate::tools::{config, tools};
14use crate::transport::bootstrap_provider::bootstrap_provider::BootstrapProvider;
15use hickory_resolver::{
16 config::{LookupIpStrategy, ResolverConfig, CLOUDFLARE, GOOGLE},
17 net::runtime::TokioRuntimeProvider,
18 Resolver, TokioResolver,
19};
20use log::warn;
21use std::collections::HashSet;
22
23pub struct DnssecBootstrapProvider {
24 resolvers: Vec<TokioResolver>,
25}
26
27impl Default for DnssecBootstrapProvider {
28 fn default() -> Self {
29 Self::new()
30 }
31}
32
33impl DnssecBootstrapProvider {
34 pub fn new() -> Self {
35 let resolver_configs = [
36 ResolverConfig::https(&CLOUDFLARE),
37 ResolverConfig::https(&GOOGLE),
38 ];
40
41 let resolvers = resolver_configs
42 .into_iter()
43 .filter_map(|resolver_config| {
44 let mut builder = Resolver::builder_with_config(resolver_config, TokioRuntimeProvider::default());
45 builder.options_mut().validate = true;
46 builder.options_mut().ip_strategy = LookupIpStrategy::Ipv4Only;
47 match builder.build() {
48 Ok(resolver) => Some(resolver),
49 Err(e) => {
50 warn!("DNSSEC bootstrap resolver construction failed: {}", e);
51 None
52 }
53 }
54 })
55 .collect();
56
57 Self { resolvers }
58 }
59}
60
61#[async_trait::async_trait]
62impl BootstrapProvider for DnssecBootstrapProvider {
63 async fn get_bootstrap_addresses(&self) -> Vec<String> {
64 let mut addresses = HashSet::new();
65 for domain in config::BOOTSTRAP_DOMAINS {
66 for resolver in &self.resolvers {
67 match resolver.lookup_ip(*domain).await {
68 Ok(response) => {
69 for ip in response.iter() {
70 addresses.insert(format!("{}:443", ip));
71 }
72 }
73 Err(e) => {
74 warn!("DNSSEC bootstrap lookup failed for {}: {}", domain, e);
75 }
76 }
77 }
78 }
79 let mut addresses: Vec<String> = addresses.drain().collect();
80 tools::shuffle(&mut addresses);
81 addresses
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
88
89 #[tokio::test]
92 #[ignore]
93 async fn dnssec_bootstrap_resolves_live_domains() {
94 let dnssec_bootstrap_provider = DnssecBootstrapProvider::new();
95 assert_eq!(dnssec_bootstrap_provider.resolvers.len(), 2, "every configured resolver should build");
96 let bootstrap_addresses = dnssec_bootstrap_provider.get_bootstrap_addresses().await;
97 assert!(!bootstrap_addresses.is_empty(), "expected at least one DNSSEC-validated bootstrap address");
98 assert!(bootstrap_addresses.iter().all(|bootstrap_address| bootstrap_address.ends_with(":443")));
99 }
100}