use std::{borrow::Cow, net::IpAddr, str::FromStr};
use hickory_proto::{ProtoError, rr::Name};
use system_configuration::{
core_foundation::{
array::CFArray,
base::{FromVoid, ItemRef, TCFType},
dictionary::CFDictionary,
string::CFString,
},
dynamic_store::SCDynamicStoreBuilder,
};
use crate::config::{NameServerConfig, ResolverConfig, ResolverOpts};
pub fn read_system_conf() -> Result<(ResolverConfig, ResolverOpts), ProtoError> {
let sc = SCDynamicStoreBuilder::new("hickory-resolver").build();
let dns_cfg = sc
.ok_or("failed to access System Configuration dynamic store")?
.get("State:/Network/Global/DNS")
.ok_or("no DNS information in System Configuration")?
.downcast_into::<CFDictionary>()
.ok_or("DNS object in System Configuration is not a CFDictionary")?;
let nameservers_cf = dns_cfg
.find(CFString::from_static_string("ServerAddresses").as_CFTypeRef())
.ok_or("no ServerAddresses key in DNS info")?;
let nameservers_cf: ItemRef<'_, CFArray<CFString>> =
unsafe { CFArray::from_void(*nameservers_cf) };
let mut nameservers = Vec::with_capacity(nameservers_cf.len() as usize);
for n in &*nameservers_cf {
let addr = IpAddr::from_str(&Cow::from(&*n))
.map_err(|e| format!("failed to parse nameserver address: {e}"))?;
nameservers.push(NameServerConfig::udp_and_tcp(addr));
}
let search_domains_cf =
dns_cfg.find(CFString::from_static_string("SearchDomains").as_CFTypeRef());
let search_domains = if let Some(search_domains_cf) = search_domains_cf {
let search_domains_cf: ItemRef<'_, CFArray<CFString>> =
unsafe { CFArray::from_void(*search_domains_cf) };
let mut search_domains = Vec::with_capacity(search_domains_cf.len() as usize);
for s in &*search_domains_cf {
search_domains.push(Name::from_str(&Cow::from(&*s))?);
}
search_domains
} else {
vec![]
};
Ok((
ResolverConfig::from_parts(None, search_domains, nameservers),
ResolverOpts::default(),
))
}