Made dns more useful

This commit is contained in:
Slatian
2023-02-24 20:17:25 +01:00
parent b900ec3b1c
commit 42fd9c0bca
4 changed files with 254 additions and 96 deletions

View File

@ -5,13 +5,17 @@
* It does not aim to be reusable for any other purpose,
* the trust_dns_resolver library already does that.
*/
use trust_dns_proto::op::response_code::ResponseCode;
use trust_dns_proto::rr::{
record_type::RecordType,
RData,
record_type::RecordType,
};
use trust_dns_resolver::{
error::ResolveError,
error::ResolveErrorKind,
lookup::Lookup,
TokioAsyncResolver,
error::*,
};
use std::net::IpAddr;
@ -21,12 +25,20 @@ use std::net::IpAddr;
#[derive(serde::Deserialize, serde::Serialize, Default, Clone)]
pub struct DnsLookupResult {
a: Vec<IpAddr>,
aaaa: Vec<IpAddr>,
cname: Vec<String>,
mx: Vec<MxRecord>,
ns: Vec<String>,
soa: Vec<SoaRecord>,
a: Option<Vec<IpAddr>>,
aaaa: Option<Vec<IpAddr>>,
aname: Option<Vec<String>>,
cname: Option<Vec<String>>,
mx: Option<Vec<MxRecord>>,
ns: Option<Vec<String>>,
soa: Option<Vec<SoaRecord>>,
txt: Option<Vec<String>>,
srv: Option<Vec<SrvRecord>>,
caa: Option<Vec<String>>,
other_error: bool,
dns_error: bool,
nxdomain: bool,
timeout: bool,
}
#[derive(serde::Deserialize, serde::Serialize, Clone)]
@ -46,6 +58,14 @@ pub struct SoaRecord {
minimum: u32,
}
#[derive(serde::Deserialize, serde::Serialize, Clone)]
pub struct SrvRecord {
priority: u16,
weight: u16,
port: u16,
target: String,
}
/* Lookup Functions*/
pub async fn reverse_lookup(
@ -75,6 +95,114 @@ pub async fn reverse_lookup(
}
}
pub fn opush<T>(opt_vec: &mut Option<Vec<T>>, data: T) {
if opt_vec.is_none() {
*opt_vec = Some(Default::default());
}
match opt_vec {
Some(vec) => vec.push(data),
None => {},
}
}
pub fn set_default_if_none<T>(opt_vec: &mut Option<Vec<T>>) {
match opt_vec {
Some(_) => {},
None => {
*opt_vec = Some(Default::default());
}
}
}
pub fn add_record_to_lookup_result(result: &mut DnsLookupResult, record: &RData){
match record {
RData::AAAA(address) => opush(&mut result.aaaa, std::net::IpAddr::V6(*address)),
RData::ANAME(aname) => opush(&mut result.aname, aname.to_string()),
RData::A(address) => opush(&mut result.a, std::net::IpAddr::V4(*address)),
RData::CAA(caa) => opush(&mut result.caa, caa.to_string()),
RData::CNAME(cname) => opush(&mut result.cname, cname.to_string()),
RData::MX(mx) => opush(&mut result.mx, MxRecord{
preference: mx.preference(),
exchange: mx.exchange().to_string(),
}),
RData::NS(ns) => opush(&mut result.ns, ns.to_string()),
RData::SOA(soa) => opush(&mut result.soa, SoaRecord{
mname: soa.mname().to_string(),
rname: soa.rname().to_string(),
serial: soa.serial(),
refresh: soa.refresh(),
retry: soa.retry(),
expire: soa.expire(),
minimum: soa.minimum(),
}),
RData::SRV(srv) => opush(&mut result.srv, SrvRecord{
priority: srv.priority(),
weight: srv.weight(),
port: srv.port(),
target: srv.target().to_string(),
}),
RData::TXT(txt) => {
for text in txt.txt_data().iter() {
opush(
&mut result.txt,
String::from_utf8_lossy(text).into_owned()
);
}
},
_ => { println!("Tried to add an unkown DNS record to results: {record}"); },
}
}
pub fn integrate_lookup_result(dig_result: &mut DnsLookupResult, lookup_result: Result<Lookup, ResolveError>) {
match lookup_result {
Ok(lookup) => {
match lookup.query().query_type() {
RecordType::AAAA => set_default_if_none(&mut dig_result.aaaa),
RecordType::ANAME => set_default_if_none(&mut dig_result.aname),
RecordType::A => set_default_if_none(&mut dig_result.a),
RecordType::CAA => set_default_if_none(&mut dig_result.caa),
RecordType::CNAME => set_default_if_none(&mut dig_result.cname),
RecordType::MX => set_default_if_none(&mut dig_result.mx),
RecordType::NS => set_default_if_none(&mut dig_result.ns),
RecordType::SOA => set_default_if_none(&mut dig_result.soa),
RecordType::SRV => set_default_if_none(&mut dig_result.srv),
RecordType::TXT => set_default_if_none(&mut dig_result.txt),
_ => { /* This should not happen */ },
};
for record in lookup.iter() {
add_record_to_lookup_result(dig_result, record);
}
},
Err(e) => {
match e.kind() {
ResolveErrorKind::Message(..) |
ResolveErrorKind::Msg(..) |
ResolveErrorKind::NoConnections |
ResolveErrorKind::Io(..) |
ResolveErrorKind::Proto(..) => {
dig_result.other_error = true;
println!("There was an error while doing a DNS Lookup: {e}");
},
ResolveErrorKind::Timeout => {
dig_result.timeout = true;
println!("There was a timeout while doing a DNS Lookup.");
},
ResolveErrorKind::NoRecordsFound{response_code, ..} => {
match response_code {
ResponseCode::NXDomain => dig_result.nxdomain = true,
ResponseCode::NoError => {},
_ => {
println!("The DNS Server returned an error while doing a DNS Lookup: {response_code}");
dig_result.dns_error = true;
},
}
}
_ => { /*Ignore for now*/ },
}
}
}
}
// This function takes a resolver, a domain name and returns a DnsLookupResult.
// If do_full_lookup is false only the A and AAAA (CNAMEs planned for the future)
// records will be fetched.
@ -83,97 +211,34 @@ pub async fn lookup(
name: &String,
do_full_lookup: bool,
) -> DnsLookupResult {
let ipv4_lookup_res = resolver.ipv4_lookup(name);
let ipv6_lookup_res = resolver.ipv6_lookup(name);
let ipv4_lookup_res = resolver.lookup(name, RecordType::A);
let ipv6_lookup_res = resolver.lookup(name, RecordType::AAAA);
let cname_lookup_res = resolver.lookup(name, RecordType::CNAME);
let aname_lookup_res = resolver.lookup(name, RecordType::ANAME);
// initlize an empty lookup result
let mut dig_result: DnsLookupResult = Default::default();
match ipv4_lookup_res.await {
Ok(lookup) => {
for address in lookup {
dig_result.a.push(std::net::IpAddr::V4(address));
}
}
Err(e) => {
println!("There was an error while looking A up {name}: {e}");
}
}
integrate_lookup_result(&mut dig_result, ipv4_lookup_res.await);
integrate_lookup_result(&mut dig_result, ipv6_lookup_res.await);
integrate_lookup_result(&mut dig_result, cname_lookup_res.await);
integrate_lookup_result(&mut dig_result, aname_lookup_res.await);
match ipv6_lookup_res.await {
Ok(lookup) => {
for address in lookup {
dig_result.aaaa.push(std::net::IpAddr::V6(address));
}
}
Err(e) => {
println!("There was an error while looking AAAA up {name}: {e}");
}
}
//Don't do an extented lookup if the domain seemingly doesn't exist
if do_full_lookup && !dig_result.nxdomain {
let mx_lookup_res = resolver.lookup(name, RecordType::MX);
let ns_lookup_res = resolver.lookup(name, RecordType::NS);
let soa_lookup_res = resolver.lookup(name, RecordType::SOA);
let caa_lookup_res = resolver.lookup(name, RecordType::CAA);
let srv_lookup_res = resolver.lookup(name, RecordType::SRV);
let txt_lookup_res = resolver.lookup(name, RecordType::TXT);
match cname_lookup_res.await {
Ok(lookup) => {
for record in lookup {
match record {
RData::CNAME(cname) => dig_result.cname.push(cname.to_string()),
_ => {},
}
}
}
Err(e) => {
println!("There was an error while looking CNAME up {name}: {e}");
}
}
if do_full_lookup {
let mx_lookup_res = resolver.mx_lookup(name);
let ns_lookup_res = resolver.ns_lookup(name);
let soa_lookup_res = resolver.soa_lookup(name);
match mx_lookup_res.await {
Ok(lookup) => {
for mx in lookup {
dig_result.mx.push(MxRecord{
preference: mx.preference(),
exchange: mx.exchange().to_string(),
});
}
}
Err(e) => {
println!("There was an error while looking MX up {name}: {e}");
}
}
match ns_lookup_res.await {
Ok(lookup) => {
for ns in lookup {
dig_result.ns.push(ns.to_string());
}
}
Err(e) => {
println!("There was an error while looking NS up {name}: {e}");
}
}
match soa_lookup_res.await {
Ok(lookup) => {
for soa in lookup {
dig_result.soa.push(SoaRecord{
mname: soa.mname().to_string(),
rname: soa.rname().to_string(),
serial: soa.serial(),
refresh: soa.refresh(),
retry: soa.retry(),
expire: soa.expire(),
minimum: soa.minimum(),
});
}
}
Err(e) => {
println!("There was an error while looking MX up {name}: {e}");
}
}
integrate_lookup_result(&mut dig_result, mx_lookup_res.await);
integrate_lookup_result(&mut dig_result, ns_lookup_res.await);
integrate_lookup_result(&mut dig_result, soa_lookup_res.await);
integrate_lookup_result(&mut dig_result, caa_lookup_res.await);
integrate_lookup_result(&mut dig_result, srv_lookup_res.await);
integrate_lookup_result(&mut dig_result, txt_lookup_res.await);
}