use domain::base::iana::rtype::Rtype;
use domain::base::name::Dname;
use domain::base::question::Question;
use domain::resolv::stub::{
conf::{ResolvConf, ResolvOptions, ServerConf, Transport},
StubResolver,
};
const USAGE: &str = "Usage: wrong-dns [@ipv4-server|@[ipv6-server]] RTYPE...
Port is always udp/53. Default server is CloudFlare 1.1.1.3";
struct Args {
server: String,
rtypes: Vec<Rtype>,
}
#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let args = parse_args()?;
let server = args.server;
let mut server = ServerConf::new(format!("{server}:53").parse()?, Transport::Udp);
server.request_timeout = core::time::Duration::from_millis(100);
let options = ResolvOptions {
attempts: 0,
..Default::default()
};
let resolv = StubResolver::from_conf(ResolvConf {
servers: vec![server],
options,
});
for rtype in args.rtypes {
print!("{rtype}: ");
let q = naughty_question(&rtype);
let res = resolv.query(q).await;
match res {
Err(e) if e.kind() == tokio::io::ErrorKind::TimedOut => {
println!("server ignored us, perhaps: {e:?}");
}
Err(e) => Err(e)?,
Ok(answer) => {
println!("{:?}", answer.as_octets())
}
};
}
Ok(())
}
fn naughty_question(qtype: &Rtype) -> Question<Dname<Vec<u8>>> {
let name = Dname::vec_from_str("example.").unwrap();
let mut bytes = name.into_octets();
bytes.push(0);
Question::new_in(
unsafe { Dname::from_octets_unchecked(bytes) },
qtype.clone(),
)
}
#[test]
fn it_is_different() {
assert!(
!Question::from((Dname::vec_from_str("example.").unwrap(), Rtype::Any))
.eq(&naughty_question(&Rtype::Any))
);
}
fn parse_args() -> Result<Args, Box<dyn std::error::Error>> {
use lexopt::prelude::*;
let mut server = "1.1.1.3".into();
let mut rtypes = vec![];
let mut parser = lexopt::Parser::from_env();
while let Some(arg) = parser.next()? {
match arg {
Short('h') | Long("help") => {
eprintln!("{}", USAGE);
std::process::exit(0);
}
Value(val) => {
let val = val.parse::<String>()?;
if let Some(ip) = val.strip_prefix('@') {
server = ip.to_string();
} else {
rtypes.push(val.parse()?);
}
}
_ => return Err(arg.unexpected().into()),
}
}
if rtypes.is_empty() {
eprintln!("{}", USAGE);
std::process::exit(1);
}
Ok(Args { server, rtypes })
}