use std::net::SocketAddr;
use console::style;
use structopt::StructOpt;
use trust_dns_resolver::config::{
NameServerConfig, NameServerConfigGroup, Protocol, ResolverConfig, ResolverOpts,
};
use trust_dns_resolver::proto::rr::RecordType;
use trust_dns_resolver::TokioAsyncResolver;
#[derive(Debug, StructOpt)]
#[structopt(name = "resolve")]
struct Opts {
domainname: String,
#[structopt(short = "t", long = "type", default_value = "A")]
ty: RecordType,
#[structopt(short = "e", long = "happy", conflicts_with("ty"))]
happy: bool,
#[structopt(short = "s", long = "system")]
system: bool,
#[structopt(long)]
google: bool,
#[structopt(long)]
cloudflare: bool,
#[structopt(long)]
quad9: bool,
#[structopt(short = "n", long, require_delimiter = true)]
nameserver: Vec<SocketAddr>,
#[structopt(long)]
ipv4: bool,
#[structopt(long)]
ipv6: bool,
#[structopt(long)]
udp: bool,
#[structopt(long)]
tcp: bool,
#[structopt(long)]
debug: bool,
#[structopt(long)]
info: bool,
#[structopt(long)]
warn: bool,
#[structopt(long)]
error: bool,
}
#[tokio::main]
pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
let opts: Opts = Opts::from_args();
let log_level = if opts.debug {
log::LevelFilter::Debug
} else if opts.info {
log::LevelFilter::Info
} else if opts.warn {
log::LevelFilter::Warn
} else if opts.error {
log::LevelFilter::Error
} else {
log::LevelFilter::Off
};
env_logger::builder()
.filter_module("trust_dns_resolver", log_level)
.filter_module("trust_dns_proto", log_level)
.filter_module("trust_dns_proto", log_level)
.write_style(env_logger::WriteStyle::Auto)
.format_indent(Some(4))
.init();
let (sys_config, sys_options): (Option<ResolverConfig>, Option<ResolverOpts>) = if opts.system {
let (config, options) = trust_dns_resolver::system_conf::read_system_conf()?;
(Some(config), Some(options))
} else {
(None, None)
};
let mut name_servers = NameServerConfigGroup::new();
for socket_addr in &opts.nameserver {
name_servers.push(NameServerConfig {
socket_addr: *socket_addr,
protocol: Protocol::Tcp,
tls_dns_name: None,
trust_nx_responses: false,
#[cfg(feature = "dns-over-rustls")]
tls_config: None,
});
name_servers.push(NameServerConfig {
socket_addr: *socket_addr,
protocol: Protocol::Udp,
tls_dns_name: None,
trust_nx_responses: false,
#[cfg(feature = "dns-over-rustls")]
tls_config: None,
});
}
if opts.google {
name_servers.merge(NameServerConfigGroup::google());
}
if opts.cloudflare {
name_servers.merge(NameServerConfigGroup::cloudflare());
}
if opts.quad9 {
name_servers.merge(NameServerConfigGroup::quad9());
}
if name_servers.is_empty() && sys_config.is_none() {
name_servers.merge(NameServerConfigGroup::google());
}
let ipv4 = opts.ipv4 || !opts.ipv6;
let ipv6 = opts.ipv6 || !opts.ipv4;
let udp = opts.udp || !opts.tcp;
let tcp = opts.tcp || !opts.udp;
name_servers
.retain(|ns| (ipv4 && ns.socket_addr.is_ipv4()) || (ipv6 && ns.socket_addr.is_ipv6()));
name_servers.retain(|ns| {
(udp && ns.protocol == Protocol::Udp) || (tcp && ns.protocol == Protocol::Tcp)
});
let mut config = sys_config.unwrap_or_else(ResolverConfig::new);
for ns in name_servers.iter() {
config.add_name_server(ns.clone());
}
let name_servers = config.name_servers().iter().map(|n| format!("{}", n)).fold(
String::new(),
|mut names, n| {
if !names.is_empty() {
names.push_str(", ")
}
names.push_str(&n);
names
},
);
let name = &opts.domainname;
let ty = opts.ty;
let mut options = sys_options.unwrap_or_default();
if opts.happy {
options.ip_strategy = trust_dns_resolver::config::LookupIpStrategy::Ipv4AndIpv6;
}
let resolver = TokioAsyncResolver::tokio(config, options)?;
println!(
"Querying for {name} {ty} from {ns}",
name = style(name).yellow(),
ty = style(ty).yellow(),
ns = style(name_servers).blue()
);
let lookup = if opts.happy {
let lookup = resolver.lookup_ip(name.to_string()).await?;
lookup.into()
} else {
resolver
.lookup(name.to_string(), ty, Default::default())
.await?
};
println!(
"{} for query {}",
style("Success").green(),
style(lookup.query()).blue()
);
for r in lookup.record_iter() {
println!(
"\t{name} {ttl} {class} {ty} {rdata}",
name = style(r.name()).blue(),
ttl = style(r.ttl()).blue(),
class = style(r.dns_class()).blue(),
ty = style(r.record_type()).blue(),
rdata = style(r.rdata()).yellow()
);
}
Ok(())
}