This builds, I guess.
This commit is contained in:
@@ -1,35 +1,28 @@
|
||||
use error_stack::report;
|
||||
use hickory_proto::error::ProtoError;
|
||||
use hickory_resolver::config::{NameServerConfig, Protocol, ResolverConfig, ResolverOpts};
|
||||
use hickory_resolver::{Name, TokioAsyncResolver};
|
||||
use proptest::arbitrary::Arbitrary;
|
||||
use proptest::strategy::{BoxedStrategy, Just, Strategy};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::net::SocketAddr;
|
||||
use thiserror::Error;
|
||||
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
|
||||
|
||||
#[derive(Debug, PartialEq, Deserialize, Serialize)]
|
||||
pub struct DnsConfig {
|
||||
built_in: Option<BuiltinDnsOption>,
|
||||
local_domain: Option<String>,
|
||||
pub local_domain: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
search_domains: Vec<String>,
|
||||
pub search_domains: Vec<String>,
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
name_servers: Vec<ServerConfig>,
|
||||
pub name_servers: Vec<NameServerConfig>,
|
||||
#[serde(default)]
|
||||
timeout_in_seconds: Option<u16>,
|
||||
pub retry_attempts: Option<u16>,
|
||||
#[serde(default)]
|
||||
retry_attempts: Option<u16>,
|
||||
pub cache_size: Option<u32>,
|
||||
#[serde(default)]
|
||||
cache_size: Option<u32>,
|
||||
pub max_concurrent_requests_for_query: Option<u16>,
|
||||
#[serde(default)]
|
||||
use_hosts_file: Option<bool>,
|
||||
pub preserve_intermediates: Option<bool>,
|
||||
#[serde(default)]
|
||||
max_concurrent_requests_for_query: Option<u16>,
|
||||
pub shuffle_dns_servers: Option<bool>,
|
||||
#[serde(default)]
|
||||
preserve_intermediates: Option<bool>,
|
||||
#[serde(default)]
|
||||
shuffle_dns_servers: Option<bool>,
|
||||
pub allow_mdns: Option<bool>,
|
||||
}
|
||||
|
||||
impl Default for DnsConfig {
|
||||
@@ -39,13 +32,12 @@ impl Default for DnsConfig {
|
||||
local_domain: None,
|
||||
search_domains: vec![],
|
||||
name_servers: vec![],
|
||||
timeout_in_seconds: None,
|
||||
retry_attempts: None,
|
||||
cache_size: None,
|
||||
use_hosts_file: None,
|
||||
max_concurrent_requests_for_query: None,
|
||||
preserve_intermediates: None,
|
||||
shuffle_dns_servers: None,
|
||||
allow_mdns: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -62,13 +54,12 @@ impl Arbitrary for DnsConfig {
|
||||
local_domain: None,
|
||||
search_domains: vec![],
|
||||
name_servers: vec![],
|
||||
timeout_in_seconds: None,
|
||||
retry_attempts: None,
|
||||
cache_size: None,
|
||||
use_hosts_file: None,
|
||||
max_concurrent_requests_for_query: None,
|
||||
preserve_intermediates: None,
|
||||
shuffle_dns_servers: None,
|
||||
allow_mdns: None,
|
||||
})
|
||||
.boxed()
|
||||
} else {
|
||||
@@ -79,51 +70,47 @@ impl Arbitrary for DnsConfig {
|
||||
let search_domains = proptest::collection::vec(domain_name_strat(), 0..10);
|
||||
let min_servers = if built_in.is_some() { 0 } else { 1 };
|
||||
let name_servers =
|
||||
proptest::collection::vec(ServerConfig::arbitrary(), min_servers..6);
|
||||
let timeout_in_seconds = proptest::option::of(u16::arbitrary());
|
||||
proptest::collection::vec(NameServerConfig::arbitrary(), min_servers..6);
|
||||
let retry_attempts = proptest::option::of(u16::arbitrary());
|
||||
let cache_size = proptest::option::of(u32::arbitrary());
|
||||
let use_hosts_file = proptest::option::of(bool::arbitrary());
|
||||
let max_concurrent_requests_for_query = proptest::option::of(u16::arbitrary());
|
||||
let preserve_intermediates = proptest::option::of(bool::arbitrary());
|
||||
let shuffle_dns_servers = proptest::option::of(bool::arbitrary());
|
||||
let allow_mdns = proptest::option::of(bool::arbitrary());
|
||||
|
||||
(
|
||||
local_domain,
|
||||
search_domains,
|
||||
name_servers,
|
||||
timeout_in_seconds,
|
||||
retry_attempts,
|
||||
cache_size,
|
||||
use_hosts_file,
|
||||
max_concurrent_requests_for_query,
|
||||
preserve_intermediates,
|
||||
shuffle_dns_servers,
|
||||
allow_mdns,
|
||||
)
|
||||
.prop_map(
|
||||
move |(
|
||||
local_domain,
|
||||
search_domains,
|
||||
name_servers,
|
||||
timeout_in_seconds,
|
||||
retry_attempts,
|
||||
cache_size,
|
||||
use_hosts_file,
|
||||
max_concurrent_requests_for_query,
|
||||
preserve_intermediates,
|
||||
shuffle_dns_servers,
|
||||
allow_mdns,
|
||||
)| DnsConfig {
|
||||
built_in,
|
||||
local_domain,
|
||||
search_domains,
|
||||
name_servers,
|
||||
timeout_in_seconds,
|
||||
retry_attempts,
|
||||
cache_size,
|
||||
use_hosts_file,
|
||||
max_concurrent_requests_for_query,
|
||||
preserve_intermediates,
|
||||
shuffle_dns_servers,
|
||||
allow_mdns,
|
||||
},
|
||||
)
|
||||
})
|
||||
@@ -172,34 +159,34 @@ impl Arbitrary for BuiltinDnsOption {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Deserialize, Serialize)]
|
||||
struct ServerConfig {
|
||||
address: SocketAddr,
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
pub struct NameServerConfig {
|
||||
pub address: SocketAddr,
|
||||
#[serde(default)]
|
||||
trust_negatives: bool,
|
||||
pub timeout_in_seconds: Option<u64>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
bind_address: Option<SocketAddr>,
|
||||
pub bind_address: Option<SocketAddr>,
|
||||
}
|
||||
|
||||
impl Arbitrary for ServerConfig {
|
||||
impl Arbitrary for NameServerConfig {
|
||||
type Parameters = ();
|
||||
type Strategy = BoxedStrategy<Self>;
|
||||
|
||||
fn arbitrary_with((): Self::Parameters) -> Self::Strategy {
|
||||
(
|
||||
SocketAddr::arbitrary(),
|
||||
bool::arbitrary(),
|
||||
proptest::option::of(u64::arbitrary()),
|
||||
proptest::option::of(SocketAddr::arbitrary()),
|
||||
)
|
||||
.prop_map(|(mut address, trust_negatives, mut bind_address)| {
|
||||
.prop_map(|(mut address, timeout_in_seconds, mut bind_address)| {
|
||||
clear_flow_and_scope_info(&mut address);
|
||||
if let Some(bind_address) = bind_address.as_mut() {
|
||||
clear_flow_and_scope_info(bind_address);
|
||||
}
|
||||
|
||||
ServerConfig {
|
||||
NameServerConfig {
|
||||
address,
|
||||
trust_negatives,
|
||||
timeout_in_seconds,
|
||||
bind_address,
|
||||
}
|
||||
})
|
||||
@@ -214,86 +201,6 @@ fn clear_flow_and_scope_info(address: &mut SocketAddr) {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ResolverConfigError {
|
||||
#[error("Bad local domain name '{name}' provided: {error}")]
|
||||
BadDomainName { name: String, error: ProtoError },
|
||||
#[error("Bad domain name for search '{name}' provided: {error}")]
|
||||
BadSearchName { name: String, error: ProtoError },
|
||||
#[error("No DNS hosts found to search")]
|
||||
NoHosts,
|
||||
}
|
||||
|
||||
impl DnsConfig {
|
||||
/// Convert this resolver configuration into an actual ResolverConfig, or say
|
||||
/// why it's bad.
|
||||
pub fn resolver(&self) -> error_stack::Result<TokioAsyncResolver, ResolverConfigError> {
|
||||
let mut config = match &self.built_in {
|
||||
None => ResolverConfig::new(),
|
||||
Some(BuiltinDnsOption::Cloudflare) => ResolverConfig::cloudflare(),
|
||||
Some(BuiltinDnsOption::Google) => ResolverConfig::google(),
|
||||
Some(BuiltinDnsOption::Quad9) => ResolverConfig::quad9(),
|
||||
};
|
||||
|
||||
if let Some(local_domain) = &self.local_domain {
|
||||
let name = Name::from_utf8(local_domain).map_err(|error| {
|
||||
report!(ResolverConfigError::BadDomainName {
|
||||
name: local_domain.clone(),
|
||||
error,
|
||||
})
|
||||
})?;
|
||||
config.set_domain(name);
|
||||
}
|
||||
|
||||
for name in self.search_domains.iter() {
|
||||
let name = Name::from_utf8(name).map_err(|error| {
|
||||
report!(ResolverConfigError::BadSearchName {
|
||||
name: name.clone(),
|
||||
error,
|
||||
})
|
||||
})?;
|
||||
config.add_search(name);
|
||||
}
|
||||
|
||||
for ns in self.name_servers.iter() {
|
||||
let mut nsconfig = NameServerConfig::new(ns.address, Protocol::Udp);
|
||||
|
||||
nsconfig.trust_negative_responses = ns.trust_negatives;
|
||||
nsconfig.bind_addr = ns.bind_address;
|
||||
|
||||
config.add_name_server(nsconfig);
|
||||
}
|
||||
|
||||
if config.name_servers().is_empty() {
|
||||
return Err(report!(ResolverConfigError::NoHosts));
|
||||
}
|
||||
|
||||
let mut options = ResolverOpts::default();
|
||||
|
||||
if let Some(seconds) = self.timeout_in_seconds {
|
||||
options.timeout = tokio::time::Duration::from_secs(seconds as u64);
|
||||
}
|
||||
if let Some(retries) = self.retry_attempts {
|
||||
options.attempts = retries as usize;
|
||||
}
|
||||
if let Some(cache_size) = self.cache_size {
|
||||
options.cache_size = cache_size as usize;
|
||||
}
|
||||
options.use_hosts_file = self.use_hosts_file.unwrap_or(true);
|
||||
if let Some(max) = self.max_concurrent_requests_for_query {
|
||||
options.num_concurrent_reqs = max as usize;
|
||||
}
|
||||
if let Some(preserve) = self.preserve_intermediates {
|
||||
options.preserve_intermediates = preserve;
|
||||
}
|
||||
if let Some(shuffle) = self.shuffle_dns_servers {
|
||||
options.shuffle_dns_servers = shuffle;
|
||||
}
|
||||
|
||||
Ok(TokioAsyncResolver::tokio(config, options))
|
||||
}
|
||||
}
|
||||
|
||||
proptest::proptest! {
|
||||
#[test]
|
||||
fn valid_configs_parse(config in DnsConfig::arbitrary_with(false)) {
|
||||
@@ -302,3 +209,98 @@ proptest::proptest! {
|
||||
assert_eq!(config, reversed);
|
||||
}
|
||||
}
|
||||
|
||||
impl DnsConfig {
|
||||
/// Return the configurations for all of the name servers that the user
|
||||
/// has selected.
|
||||
pub fn name_servers(&self) -> Vec<NameServerConfig> {
|
||||
let mut results = self.name_servers.clone();
|
||||
|
||||
match self.built_in {
|
||||
None => {}
|
||||
Some(BuiltinDnsOption::Cloudflare) => {
|
||||
results.push(NameServerConfig {
|
||||
address: SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(1, 1, 1, 1), 53)),
|
||||
timeout_in_seconds: None,
|
||||
bind_address: None,
|
||||
});
|
||||
results.push(NameServerConfig {
|
||||
address: SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(1, 0, 0, 1), 53)),
|
||||
timeout_in_seconds: None,
|
||||
bind_address: None,
|
||||
});
|
||||
results.push(NameServerConfig {
|
||||
address: SocketAddr::V6(SocketAddrV6::new(
|
||||
Ipv6Addr::new(0x2606, 0x4700, 0x4700, 0, 0, 0, 0, 0x1111),
|
||||
53,
|
||||
0,
|
||||
0,
|
||||
)),
|
||||
timeout_in_seconds: None,
|
||||
bind_address: None,
|
||||
});
|
||||
results.push(NameServerConfig {
|
||||
address: SocketAddr::V6(SocketAddrV6::new(
|
||||
Ipv6Addr::new(0x2606, 0x4700, 0x4700, 0, 0, 0, 0, 0x1001),
|
||||
53,
|
||||
0,
|
||||
0,
|
||||
)),
|
||||
timeout_in_seconds: None,
|
||||
bind_address: None,
|
||||
});
|
||||
}
|
||||
Some(BuiltinDnsOption::Google) => {
|
||||
results.push(NameServerConfig {
|
||||
address: SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(8, 8, 8, 8), 53)),
|
||||
timeout_in_seconds: None,
|
||||
bind_address: None,
|
||||
});
|
||||
results.push(NameServerConfig {
|
||||
address: SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(8, 8, 4, 4), 53)),
|
||||
timeout_in_seconds: None,
|
||||
bind_address: None,
|
||||
});
|
||||
results.push(NameServerConfig {
|
||||
address: SocketAddr::V6(SocketAddrV6::new(
|
||||
Ipv6Addr::new(0x2001, 0x4860, 0x4860, 0, 0, 0, 0, 0x8888),
|
||||
53,
|
||||
0,
|
||||
0,
|
||||
)),
|
||||
timeout_in_seconds: None,
|
||||
bind_address: None,
|
||||
});
|
||||
results.push(NameServerConfig {
|
||||
address: SocketAddr::V6(SocketAddrV6::new(
|
||||
Ipv6Addr::new(0x2001, 0x4860, 0x4860, 0, 0, 0, 0, 0x8844),
|
||||
53,
|
||||
0,
|
||||
0,
|
||||
)),
|
||||
timeout_in_seconds: None,
|
||||
bind_address: None,
|
||||
});
|
||||
}
|
||||
Some(BuiltinDnsOption::Quad9) => {
|
||||
results.push(NameServerConfig {
|
||||
address: SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(9, 9, 9, 9), 53)),
|
||||
timeout_in_seconds: None,
|
||||
bind_address: None,
|
||||
});
|
||||
results.push(NameServerConfig {
|
||||
address: SocketAddr::V6(SocketAddrV6::new(
|
||||
Ipv6Addr::new(0x2620, 0, 0, 0, 0, 0, 0xfe, 0xfe),
|
||||
53,
|
||||
0,
|
||||
0,
|
||||
)),
|
||||
timeout_in_seconds: None,
|
||||
bind_address: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
results
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user