use std::{io::Cursor, mem::size_of, net::{Ipv4Addr, Ipv6Addr}};
use async_recursion::async_recursion;
use tokio::io::{AsyncReadExt as IoAsyncReadExt, AsyncWriteExt as IOAsyncWriteExt};
use tokio_byteorder::{BigEndian, AsyncReadBytesExt as BoAsyncReadExt, AsyncWriteBytesExt as BoAsyncWriteExt};
use crate::{internal_error, internal_error_map, error::{CDnsErrorType, map_read_err, CDnsResult}};
pub use crate::common::*;
pub async
fn pkt2name(pkt: &mut Cursor<&[u8]>) -> CDnsResult<Vec<u8>>
{
let qname_s = pkt.position() as usize;
loop
{
let cnt = BoAsyncReadExt::read_u8(pkt).await.map_err(map_read_err)?;
if cnt == 0
{
break;
}
else
{
for _ in 0..cnt
{
let c = BoAsyncReadExt::read_u8(pkt).await.map_err(map_read_err)?;
if c == 0
{
internal_error!(
CDnsErrorType::DnsResponse,
"incorrectly encoded QNAME in response, found '0' at offset: '{}'",
pkt.position()
);
}
}
}
}
let cur_pos = pkt.position() as usize;
if (cur_pos - qname_s) <= 1
{
internal_error!(
CDnsErrorType::DnsResponse,
"read name is too short"
);
}
return Ok(pkt.get_ref()[qname_s..cur_pos].to_vec());
}
#[async_recursion]
pub async
fn name2str(pkt: &mut Cursor<&[u8]>, mut opt_rdlen: Option<u16>) -> CDnsResult<String>
{
let cur_in_pos = pkt.position();
let mut comp: u8;
let mut output: Vec<String> = Vec::with_capacity(6);
loop
{
if let Some(rdlen) = opt_rdlen
{
if pkt.position() - cur_in_pos >= rdlen as u64
{
return Ok(output.join("."));
}
}
comp = BoAsyncReadExt::read_u8(pkt).await.map_err(map_read_err)?;
let msb = comp & 0xC0;
if msb == 0xC0
{
if opt_rdlen.is_none() == true
{
opt_rdlen = Some(2);
}
let offset1: u16 = ((comp & !0xC0) as u16) << 8;let offset2: u16 = BoAsyncReadExt::read_u8(pkt).await.map_err(map_read_err)? as u16;
let offset = offset1 | offset2;
if offset as usize >= pkt.get_ref().len()
{
internal_error!(
CDnsErrorType::DnsResponse,
"incoreclty formated packet: offset: '{}' > len: '{}'",
offset, pkt.get_ref().len()
);
}
let cur_pos = pkt.position();
pkt.set_position(offset as u64);
output.push(name2str(pkt, None).await?);
pkt.set_position(cur_pos);
}
else if msb == 0x00
{
if comp == 0
{
if let Some(rdlen) = opt_rdlen
{
let dif = pkt.position() - cur_in_pos;
if rdlen as u64 != dif
{
internal_error!(CDnsErrorType::DnsResponse, "incorrect rdlen: '{}', exp: '{}'", rdlen, dif);
}
}
return Ok(output.join("."));
}
else
{
let mut tmp: String = String::with_capacity(comp as usize);
for _ in 0..comp
{
let c = BoAsyncReadExt::read_u8(pkt).await.map_err(map_read_err)?;
if c == 0
{
internal_error!(CDnsErrorType::DnsResponse, "protocol violation, incorrectly encoded QNAME in response");
}
tmp.push(c as char);
}
output.push(tmp);
}
}
else
{
internal_error!(CDnsErrorType::DnsResponse, "incorrect compression: {:x}", msb);
}
} }
impl DnsRdata
{
async
fn async_convert(pkt: &mut Cursor<&[u8]>, rlen: u16, qtype: &QType) -> CDnsResult<Self>
{
match &qtype
{
QType::A =>
{
if rlen != 4
{
internal_error!(CDnsErrorType::DnsResponse, "ipv4 len expected: '4', got: '{}'", rlen);
}
let ip = BoAsyncReadExt::read_u32::<BigEndian>(pkt).await.map_err(map_read_err)?;
return Ok(Self::A{ ip: Ipv4Addr::from(ip) });
},
QType::AAAA =>
{
if rlen != 16
{
internal_error!(CDnsErrorType::DnsResponse, "ipv6 len expected: '16', got: '{}'", rlen);
}
let ip = BoAsyncReadExt::read_u128::<BigEndian>(pkt).await.map_err(map_read_err)?;
return Ok(Self::AAAA{ ip: Ipv6Addr::from(ip) });
},
QType::MX =>
{
return Ok(
Self::MX
{
preference: BoAsyncReadExt::read_u16::<BigEndian>(pkt).await.map_err(map_read_err)?,
exchange: name2str(pkt, Some(rlen - 2)).await?,
}
);
},
QType::CNAME =>
{
return Ok(
Self::CNAME{ fqdn: name2str(pkt, Some(rlen)).await? }
);
},
QType::PTR =>
{
return Ok(
Self::PTR{ fqdn: name2str(pkt, Some(rlen)).await? }
);
},
QType::NS =>
{
return Ok(
Self::NS{ fqdn: name2str(pkt, Some(rlen)).await? }
);
},
QType::SOA =>
{
return Ok(
Self::SOA
{
soa:
DnsSoa
{
pnm: name2str(pkt, None).await?,
ram: name2str(pkt, None).await?,
serial: BoAsyncReadExt::read_u32::<BigEndian>(pkt).await.map_err(map_read_err)?,
interv_refr: BoAsyncReadExt::read_u32::<BigEndian>(pkt).await.map_err(map_read_err)?,
interv_retry: BoAsyncReadExt::read_u32::<BigEndian>(pkt).await.map_err(map_read_err)?,
expire_limit: BoAsyncReadExt::read_u32::<BigEndian>(pkt).await.map_err(map_read_err)?,
min_ttl: BoAsyncReadExt::read_u32::<BigEndian>(pkt).await.map_err(map_read_err)?,
}
}
);
},
_ =>
{
let ipos = pkt.position();
let npos: u64 = pkt.position() + rlen as u64;
let buf_size = npos - ipos;
let mut data: Vec<u8> = vec![0_u8; buf_size as usize];IoAsyncReadExt::read_exact(pkt, &mut data).await.map_err(map_read_err)?;
pkt.set_position(npos);
return Ok(Self::UNKNOWN{ data: data });
}
}
}
}
impl DnsResponsePayload
{
async
fn async_new(pkt: &mut Cursor<&[u8]>) -> CDnsResult<Self>
{
let name = name2str(pkt, None).await?;
let qtype = QType::u16_to_type(BoAsyncReadExt::read_u16::<BigEndian>(pkt).await.map_err(map_read_err)?)?;
let qclass = QClass::u16_to_class(BoAsyncReadExt::read_u16::<BigEndian>(pkt).await.map_err(map_read_err)?)?;
let ttl = BoAsyncReadExt::read_i32::<BigEndian>(pkt).await.map_err(map_read_err)?;
let rdlength = BoAsyncReadExt::read_u16::<BigEndian>(pkt).await.map_err(map_read_err)?;
let rdata = DnsRdata::async_convert(pkt, rdlength, &qtype).await?;
return Ok(
DnsResponsePayload
{
name: name,
dtype: qtype,
class: qclass,
ttl: ttl,
rdlength: rdlength,
rdata: rdata,
}
);
}
}
impl DnsRequestAnswer
{
pub async
fn async_try_from(value: &[u8]) -> CDnsResult<Self>
{
return DnsRequestAnswer::async_parse(value).await;
}
}
impl DnsRequestAnswer
{
async
fn async_parse(ans: &[u8]) -> CDnsResult<Self>
{
let mut pkt = Cursor::new(ans);
let id = BoAsyncReadExt::read_u16::<BigEndian>(&mut pkt).await.map_err(map_read_err)?;
let status = BoAsyncReadExt::read_u16::<BigEndian>(&mut pkt).await.map_err(map_read_err)?;
let header =
DnsHeader
{
id: id,
status: StatusBits::from_bits(status).ok_or_else(||
internal_error_map!(CDnsErrorType::DnsResponse, "unknown status bits: '{}'", status)
)?,
qdcount: BoAsyncReadExt::read_u16::<BigEndian>(&mut pkt).await.map_err(map_read_err)?,
ancount: BoAsyncReadExt::read_u16::<BigEndian>(&mut pkt).await.map_err(map_read_err)?,
nscount: BoAsyncReadExt::read_u16::<BigEndian>(&mut pkt).await.map_err(map_read_err)?,
arcount: BoAsyncReadExt::read_u16::<BigEndian>(&mut pkt).await.map_err(map_read_err)?,
};
if header.status.contains(StatusBits::QR_RESP) == false
{
internal_error!(
CDnsErrorType::DnsResponse,
"incorret QR flag in STATUS register of response, expected 1 got 0"
);
}
let request =
DnsRequestPayload
{
qname: pkt2name(&mut pkt).await?,
qtype: QType::u16_to_qtype(BoAsyncReadExt::read_u16::<BigEndian>(&mut pkt).await
.map_err(map_read_err)?)?,
qclass: QClass::u16_to_qclass(BoAsyncReadExt::read_u16::<BigEndian>(&mut pkt).await
.map_err(map_read_err)?)?,
};
let mut an_list: Vec<DnsResponsePayload> = Vec::with_capacity(header.ancount as usize);
let mut rr_list: Vec<DnsResponsePayload> = Vec::with_capacity(header.arcount as usize);
let mut ns_list: Vec<DnsResponsePayload> = Vec::with_capacity(header.nscount as usize);
for _ in 0..header.ancount
{
an_list.push(DnsResponsePayload::async_new(&mut pkt).await?);
}
for _ in 0..header.nscount
{
ns_list.push(DnsResponsePayload::async_new(&mut pkt).await?);
}
for _ in 0..header.arcount
{
rr_list.push(DnsResponsePayload::async_new(&mut pkt).await?);
}
return Ok(
DnsRequestAnswer
{
header: header,
request: request,
response: an_list,
additional: rr_list,
authoratives: ns_list,
}
);
}
}
impl DnsRequestHeader
{
pub async
fn async_to_bytes(&self) -> CDnsResult<Vec<u8>>
{
let pkt_size: usize = size_of::<DnsHeader>() + self.payload.qname.len() + 4;
let mut pkt = Cursor::new(vec![0_u8; pkt_size]);
BoAsyncWriteExt::write_u16::<BigEndian>(&mut pkt, self.header.id)
.await
.map_err(map_read_err)?;
BoAsyncWriteExt::write_u16::<BigEndian>(&mut pkt, self.header.status.bits())
.await
.map_err(map_read_err)?;
BoAsyncWriteExt::write_u16::<BigEndian>(&mut pkt, self.header.qdcount)
.await
.map_err(map_read_err)?;
BoAsyncWriteExt::write_u16::<BigEndian>(&mut pkt, self.header.ancount)
.await
.map_err(map_read_err)?;
BoAsyncWriteExt::write_u16::<BigEndian>(&mut pkt, self.header.nscount)
.await
.map_err(map_read_err)?;
BoAsyncWriteExt::write_u16::<BigEndian>(&mut pkt, self.header.arcount)
.await
.map_err(map_read_err)?;
IOAsyncWriteExt::write(&mut pkt, self.payload.qname.as_slice())
.await
.map_err(map_read_err)?;
BoAsyncWriteExt::write_u16::<BigEndian>(&mut pkt, self.payload.qtype.into())
.await
.map_err(map_read_err)?;
BoAsyncWriteExt::write_u16::<BigEndian>(&mut pkt, self.payload.qclass.into())
.await
.map_err(map_read_err)?;
return Ok(pkt.into_inner());
}
}
#[cfg(test)]
mod tests
{
use std::{io::Cursor, net::Ipv4Addr};
use crate::{a_sync::{common::{name2str, pkt2name, DnsHeader, DnsRequestAnswer, DnsRequestHeader, DnsRequestPayload, QClass}, QDnsName}, DnsRdata, DnsSoa, QType};
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
async fn test_pkt2name()
{
use std::time::Instant;
fn prepare(d: &[u8]) -> Cursor<&[u8]>
{
return Cursor::new(d);
}
let t1 = b"\x03\x64\x6e\x73\x06\x67\x6f\x6f\x67\x6c\x65\x00";
let mut cur1 = prepare(t1);
let now = Instant::now();
let r1 = pkt2name(&mut cur1).await;
let elapsed = now.elapsed();
println!("Elapsed: {:.2?}", elapsed);
assert_eq!(r1.is_ok(), true);
assert_eq!(r1.as_ref().unwrap(), t1);
let t2 =
b"\x0f\x6d\x61\x64\x30\x37\x73\x30\x39\x2d\x69\x6e\x2d\x78\x30\x65\x05\x31\x65\x31\
\x30\x30\x03\x6e\x65\x74\x00";
let mut cur2 = prepare(t2);
let now = Instant::now();
let r2 = pkt2name(&mut cur2).await;
let elapsed = now.elapsed();
println!("Elapsed: {:.2?}", elapsed);
assert_eq!(r2.is_ok(), true);
assert_eq!(r2.as_ref().unwrap(), t2);
let t3 =
b"\x0f\x6d\x61\x64\x30\x37\x73\x30\x39\x2d\x69\x6e\x2d\x78\x30\x65\x05\x31\x65\x31\
\x30\x30\x03\x6e\x65\x74";
let r3 = pkt2name(&mut prepare(t3)).await;
assert_eq!(r3.is_ok(), false);
let t4 =
b"\x10\x6d\x61\x64\x30\x37\x73\x30\x39\x2d\x69\x6e\x2d\x78\x30\x65\x05\x31\x65\x31\
\x30\x30\x03\x6e\x65\x74";
let r4 = pkt2name(&mut prepare(t4)).await;
assert_eq!(r4.is_ok(), false);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
async fn test_qname2str()
{
use std::time::Instant;
fn prepare(d: &[u8]) -> Cursor<&[u8]>
{
return Cursor::new(d);
}
let t1 = b"\x03\x64\x6e\x73\x06\x67\x6f\x6f\x67\x6c\x65\x00";
let mut cur1 = prepare(t1);
let now = Instant::now();
let r1 = name2str(&mut cur1, None).await;
let elapsed = now.elapsed();
println!("Elapsed: {:.2?}", elapsed);
assert_eq!(r1.is_ok(), true);
assert_eq!(r1.as_ref().unwrap(), "dns.google");
let t2 =
b"\x0f\x6d\x61\x64\x30\x37\x73\x30\x39\x2d\x69\x6e\x2d\x78\x30\x65\x05\x31\x65\x31\
\x30\x30\x03\x6e\x65\x74\x00";
let mut cur2 = prepare(t2);
let now = Instant::now();
let r2 = name2str(&mut cur2, None).await;
let elapsed = now.elapsed();
println!("Elapsed: {:.2?}", elapsed);
assert_eq!(r2.is_ok(), true);
assert_eq!(r2.as_ref().unwrap(), "mad07s09-in-x0e.1e100.net");
let t3 =
b"\x0f\x6d\x61\x64\x30\x37\x73\x30\x39\x2d\x69\x6e\x2d\x78\x30\x65\x05\x31\x65\x31\
\x30\x30\x03\x6e\x65\x74";
let r3 = name2str(&mut prepare(t3), None).await;
assert_eq!(r3.is_ok(), false);
let t4 =
b"\x10\x6d\x61\x64\x30\x37\x73\x30\x39\x2d\x69\x6e\x2d\x78\x30\x65\x05\x31\x65\x31\
\x30\x30\x03\x6e\x65\x74";
let r4 = name2str(&mut prepare(t4), None).await;
assert_eq!(r4.is_ok(), false);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
async fn test_response_parser()
{
use std::time::Instant;
let pkt =
b"\x15\xc8\x81\x80\x00\x01\x00\x01\x00\x00\x00\x02\x01\x38\x01\x38\
\x01\x38\x01\x38\x07\x69\x6e\x2d\x61\x64\x64\x72\x04\x61\x72\x70\
\x61\x00\x00\x0c\x00\x01\xc0\x0c\x00\x0c\x00\x01\x00\x01\x35\xf0\
\x00\x0c\x03\x64\x6e\x73\x06\x67\x6f\x6f\x67\x6c\x65\x00\xc0\x32\
\x00\x01\x00\x01\x00\x00\x00\xe8\x00\x04\x08\x08\x08\x08\xc0\x32\
\x00\x01\x00\x01\x00\x00\x00\xe8\x00\x04\x08\x08\x04\x04";
let dummy =
DnsRequestHeader
{
header: DnsHeader {id: 0x15c8, ..Default::default()},
payload: DnsRequestPayload
{
qname: b"\x01\x38\x01\x38\x01\x38\x01\x38\x07\x69\x6e\x2d\x61\x64\x64\x72\x04\x61\x72\x70\x61\x00".to_vec(),
qtype: QType::PTR,
qclass: QClass::IN,
}
};
let now = Instant::now();
let ans = DnsRequestAnswer::async_try_from(pkt.as_slice()).await;
let elapsed = now.elapsed();
println!("Elapsed: {:.2?}", elapsed);
assert_eq!(ans.is_ok(), true, "{}", ans.err().unwrap());
let ans = ans.unwrap();
let verif = ans.verify(&dummy);
assert_eq!(verif.is_ok(), true, "{}", verif.err().unwrap());
assert_eq!(ans.header.id, 0x15c8);
assert_eq!(ans.header.ancount, 1);
assert_eq!(ans.header.arcount, 2);
assert_eq!(ans.header.nscount, 0);
assert_eq!(ans.header.qdcount, 1);
assert_eq!(ans.response[0].rdata, DnsRdata::PTR{ fqdn: "dns.google".to_string() });
assert_eq!(ans.response[0].name, "8.8.8.8.in-addr.arpa".to_string());
assert_eq!(ans.response[0].dtype, QType::PTR);
assert_eq!(ans.response[0].class, QClass::IN);
assert_eq!(ans.response[0].ttl, 79344);
assert_eq!(ans.response[0].rdlength, 12);
assert_eq!(ans.additional[0].rdata, DnsRdata::A{ ip: "8.8.8.8".parse().unwrap() });
assert_eq!(ans.additional[0].name, "dns.google".to_string());
assert_eq!(ans.additional[0].ttl, 232);
assert_eq!(ans.additional[0].dtype, QType::A);
assert_eq!(ans.additional[0].class, QClass::IN);
assert_eq!(ans.additional[1].rdata, DnsRdata::A{ ip: "8.8.4.4".parse().unwrap() });
assert_eq!(ans.additional[1].name, "dns.google".to_string());
assert_eq!(ans.additional[1].ttl, 232);
assert_eq!(ans.additional[1].dtype, QType::A);
assert_eq!(ans.additional[1].class, QClass::IN);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
async fn test_response2()
{
use std::time::Instant;
let pkt =
b"\x4b\x38\x81\x80\x00\x01\x00\x05\x00\x00\x00\x00\x05\x67\x6d\x61\
\x69\x6c\x03\x63\x6f\x6d\x00\x00\x0f\x00\x01\xc0\x0c\x00\x0f\x00\
\x01\x00\x00\x0b\x55\x00\x1b\x00\x05\x0d\x67\x6d\x61\x69\x6c\x2d\
\x73\x6d\x74\x70\x2d\x69\x6e\x01\x6c\x06\x67\x6f\x6f\x67\x6c\x65\
\xc0\x12\xc0\x0c\x00\x0f\x00\x01\x00\x00\x0b\x55\x00\x09\x00\x0a\
\x04\x61\x6c\x74\x31\xc0\x29\xc0\x0c\x00\x0f\x00\x01\x00\x00\x0b\
\x55\x00\x09\x00\x1e\x04\x61\x6c\x74\x33\xc0\x29\xc0\x0c\x00\x0f\
\x00\x01\x00\x00\x0b\x55\x00\x09\x00\x14\x04\x61\x6c\x74\x32\xc0\
\x29\xc0\x0c\x00\x0f\x00\x01\x00\x00\x0b\x55\x00\x09\x00\x28\x04\
\x61\x6c\x74\x34\xc0\x29";
let dummy =
DnsRequestHeader
{
header: DnsHeader {id: 0x4b38, ..Default::default()},
payload: DnsRequestPayload
{
qname: b"\x05\x67\x6d\x61\x69\x6c\x03\x63\x6f\x6d\x00".to_vec(),
qtype: QType::MX,
qclass: QClass::IN,
}
};
let now = Instant::now();
let ans = DnsRequestAnswer::async_try_from(pkt.as_slice()).await;
let elapsed = now.elapsed();
println!("Elapsed: {:.2?}", elapsed);
assert_eq!(ans.is_ok(), true, "{}", ans.err().unwrap());
let ans = ans.unwrap();
let verif = ans.verify(&dummy);
assert_eq!(verif.is_ok(), true, "{}", verif.err().unwrap());
assert_eq!(ans.header.id, 0x4b38);
assert_eq!(ans.header.ancount, 5);
assert_eq!(ans.header.arcount, 0);
assert_eq!(ans.header.nscount, 0);
assert_eq!(ans.header.qdcount, 1);
assert_eq!(ans.response[0].rdata, DnsRdata::MX{ preference: 5, exchange: "gmail-smtp-in.l.google.com".to_string() });
assert_eq!(ans.response[0].name, "gmail.com".to_string());
assert_eq!(ans.response[0].dtype, QType::MX);
assert_eq!(ans.response[0].class, QClass::IN);
assert_eq!(ans.response[0].ttl, 2901);
assert_eq!(ans.response[0].rdlength, 27);
assert_eq!(ans.response[1].rdata, DnsRdata::MX{ preference: 10, exchange: "alt1.gmail-smtp-in.l.google.com".to_string() });
assert_eq!(ans.response[1].name, "gmail.com".to_string());
assert_eq!(ans.response[1].dtype, QType::MX);
assert_eq!(ans.response[1].class, QClass::IN);
assert_eq!(ans.response[1].ttl, 2901);
assert_eq!(ans.response[1].rdlength, 9);
assert_eq!(ans.response[2].rdata, DnsRdata::MX{ preference: 30, exchange: "alt3.gmail-smtp-in.l.google.com".to_string() });
assert_eq!(ans.response[2].name, "gmail.com".to_string());
assert_eq!(ans.response[2].dtype, QType::MX);
assert_eq!(ans.response[2].class, QClass::IN);
assert_eq!(ans.response[2].ttl, 2901);
assert_eq!(ans.response[2].rdlength, 9);
assert_eq!(ans.response[3].rdata, DnsRdata::MX{ preference: 20, exchange: "alt2.gmail-smtp-in.l.google.com".to_string() });
assert_eq!(ans.response[3].name, "gmail.com".to_string());
assert_eq!(ans.response[3].dtype, QType::MX);
assert_eq!(ans.response[3].class, QClass::IN);
assert_eq!(ans.response[3].ttl, 2901);
assert_eq!(ans.response[3].rdlength, 9);
assert_eq!(ans.response[4].rdata, DnsRdata::MX{ preference: 40, exchange: "alt4.gmail-smtp-in.l.google.com".to_string() });
assert_eq!(ans.response[4].name, "gmail.com".to_string());
assert_eq!(ans.response[4].dtype, QType::MX);
assert_eq!(ans.response[4].class, QClass::IN);
assert_eq!(ans.response[4].ttl, 2901);
assert_eq!(ans.response[4].rdlength, 9);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
async fn test_response3()
{
use std::time::Instant;
let pkt =
b"\xd0\x79\x81\x80\x00\x01\x00\x17\x00\x00\x00\x00\x06\x72\
\x65\x6c\x6b\x6f\x6d\x02\x73\x6b\x00\x00\xff\x00\x01\xc0\x0c\x00\
\x06\x00\x01\x00\x00\x0e\x10\x00\x30\x04\x6e\x73\x32\x31\x07\x63\
\x6c\x6f\x75\x64\x6e\x73\x03\x6e\x65\x74\x00\x07\x73\x75\x70\x70\
\x6f\x72\x74\xc0\x2c\x78\x77\xe2\xf1\x00\x00\x1c\x20\x00\x00\x07\
\x08\x00\x12\x75\x00\x00\x00\x0e\x10\xc0\x0c\x00\x2e\x00\x01\x00\
\x00\x0e\x10\x00\x5d\x00\x06\x0d\x02\x00\x00\x0e\x10\x61\xf2\xaf\
\x39\x61\xcb\x22\x39\x28\xeb\x06\x72\x65\x6c\x6b\x6f\x6d\x02\x73\
\x6b\x00\x2f\x79\xed\x73\xd0\x75\xc8\xa9\xa2\x2a\x08\x3c\x78\xb9\
\xfb\x43\xc5\x8e\x62\x4c\xbc\x36\xeb\x22\x96\x45\x59\x36\x1f\x69\
\x3a\x7a\x5e\x67\xd0\x54\x1e\xf0\xf2\x11\x1a\x72\x00\x56\x89\x26\
\x79\xf4\x06\x1a\x0c\x59\x41\x32\x60\x68\x75\x05\x3d\x90\xed\x1e\
\x0f\xfc\xc0\x0c\x00\x02\x00\x01\x00\x00\x0e\x10\x00\x02\xc0\x27\
\xc0\x0c\x00\x02\x00\x01\x00\x00\x0e\x10\x00\x08\x05\x70\x6e\x73\
\x32\x33\xc0\x2c\xc0\x0c\x00\x02\x00\x01\x00\x00\x0e\x10\x00\x07\
\x04\x6e\x73\x32\x33\xc0\x2c\xc0\x0c\x00\x02\x00\x01\x00\x00\x0e\
\x10\x00\x08\x05\x70\x6e\x73\x32\x32\xc0\x2c\xc0\x0c\x00\x02\x00\
\x01\x00\x00\x0e\x10\x00\x08\x05\x70\x6e\x73\x32\x34\xc0\x2c\xc0\
\x0c\x00\x02\x00\x01\x00\x00\x0e\x10\x00\x07\x04\x6e\x73\x32\x34\
\xc0\x2c\xc0\x0c\x00\x02\x00\x01\x00\x00\x0e\x10\x00\x08\x05\x70\
\x6e\x73\x32\x31\xc0\x2c\xc0\x0c\x00\x02\x00\x01\x00\x00\x0e\x10\
\x00\x07\x04\x6e\x73\x32\x32\xc0\x2c\xc0\x0c\x00\x2e\x00\x01\x00\
\x00\x0e\x10\x00\x5d\x00\x02\x0d\x02\x00\x00\x0e\x10\x61\xf2\xaf\
\x39\x61\xcb\x22\x39\x28\xeb\x06\x72\x65\x6c\x6b\x6f\x6d\x02\x73\
\x6b\x00\xd1\xc5\x6c\xf1\xfb\xd0\x75\xf1\x38\x20\x28\x80\x4c\xe0\
\x59\xa5\xa8\xab\x84\x79\xd8\x37\x48\xa7\xa5\x3f\x08\x9b\x4c\xca\
\x40\x2b\xcb\x2c\xda\xcc\xc2\x18\xad\x07\x9e\xf8\x4e\x17\x8d\xb1\
\x2b\x2d\xa2\xa6\x17\xdb\x55\x30\xbc\xa2\xb9\xa0\x01\x71\x01\xe5\
\xdc\x4f\xc0\x0c\x00\x01\x00\x01\x00\x00\x0e\x10\x00\x04\xd9\x14\
\x70\xd0\xc0\x0c\x00\x2e\x00\x01\x00\x00\x0e\x10\x00\x5d\x00\x01\
\x0d\x02\x00\x00\x0e\x10\x61\xf2\xaf\x39\x61\xcb\x22\x39\x28\xeb\
\x06\x72\x65\x6c\x6b\x6f\x6d\x02\x73\x6b\x00\xf8\x57\x68\xf0\xad\
\x9e\xfb\x3a\x0f\x66\xbd\xcc\x48\xe7\x29\x0a\xf4\xd8\xf6\xbe\xbc\
\x04\x76\x02\x27\x64\xf2\xc9\x42\x6d\x75\x54\x83\x0a\x11\xda\x0a\
\x02\x6b\x8c\xf1\x65\xc4\x21\x44\xea\x89\x09\x01\xc8\xa1\xe2\x11\
\x8f\xed\x67\x39\x69\x33\xdd\x97\x22\x1a\xd3\xc0\x0c\x00\x0f\x00\
\x01\x00\x00\x0e\x10\x00\x11\x00\x0a\x04\x6d\x61\x69\x6c\x04\x6e\
\x69\x78\x64\x03\x6f\x72\x67\x00\xc0\x0c\x00\x2e\x00\x01\x00\x00\
\x0e\x10\x00\x5d\x00\x0f\x0d\x02\x00\x00\x0e\x10\x61\xf2\xaf\x39\
\x61\xcb\x22\x39\x28\xeb\x06\x72\x65\x6c\x6b\x6f\x6d\x02\x73\x6b\
\x00\xd4\xdd\x07\xd9\xb6\xb2\xba\x57\xa9\x1d\x3b\xaa\x6c\x55\xc4\
\x3d\x73\x79\xea\x74\xfe\xd7\x23\x0c\xb4\xab\x8f\x4b\x1f\xd9\x8a\
\xb2\x4a\x5c\xad\x3e\x8e\x4a\x85\xbb\xbd\x75\xf2\x47\x2c\xa8\x89\
\x21\x75\x89\xb1\x12\xc4\xd2\xf7\x40\x06\x52\x57\x83\x8a\xaa\x7b\
\x75\xc0\x0c\x00\x10\x00\x01\x00\x00\x0e\x10\x00\x34\x33\x76\x3d\
\x73\x70\x66\x31\x20\x2b\x6d\x78\x20\x2b\x61\x3a\x6d\x61\x69\x6c\
\x2e\x6e\x69\x78\x64\x2e\x6f\x72\x67\x20\x69\x70\x34\x3a\x32\x31\
\x37\x2e\x32\x30\x2e\x31\x31\x32\x2e\x32\x30\x38\x20\x2d\x61\x6c\
\x6c\xc0\x0c\x00\x2e\x00\x01\x00\x00\x0e\x10\x00\x5d\x00\x10\x0d\
\x02\x00\x00\x0e\x10\x61\xf2\xaf\x39\x61\xcb\x22\x39\x28\xeb\x06\
\x72\x65\x6c\x6b\x6f\x6d\x02\x73\x6b\x00\xd1\x6c\xd1\xf4\x3b\xe0\
\x44\xba\xfe\xe9\xdb\x82\xbd\x89\x5f\xa1\x07\x72\xdd\x47\xad\x4e\
\x91\xd5\xc3\xfe\x3e\x39\x74\xdb\x50\x50\x19\x6c\x3f\x6c\xb7\xa8\
\x01\x03\x6a\xf5\xa7\xf3\x9b\xf7\x76\xd4\xff\xa3\xd5\x43\xfc\xec\
\xa9\x89\x24\xf8\xd2\xb6\x76\xd4\x20\xbc\xc0\x0c\x00\x30\x00\x01\
\x00\x00\x0e\x10\x00\x44\x01\x01\x03\x0d\xf3\x87\xe2\x7c\x2b\x82\
\x40\x72\x7c\xfd\xc9\x2b\xe8\x22\xd6\xa9\x40\xc0\xab\x03\x25\x7d\
\x92\xae\xf3\x17\x71\x82\x67\xc6\xcd\xb6\x4b\x11\x62\xc6\xfa\x06\
\xec\x4c\x9f\xd9\xe6\xaf\x5c\x3d\xe4\x32\xde\x11\x1b\x09\x13\xe3\
\xd0\xba\x66\xd1\xbc\x32\xdb\x13\xd7\x1d\xc0\x0c\x00\x30\x00\x01\
\x00\x00\x0e\x10\x00\x44\x01\x00\x03\x0d\xd4\x43\xde\x96\xe5\xea\
\x0a\xf9\x5d\x4f\x72\x88\x9c\xd9\x9c\xf7\xa6\x3f\x12\xd7\xf3\xea\
\x8a\x6b\x44\x4c\x79\x23\x81\x94\x43\xa3\xbd\x9e\xb8\xde\xfe\x8c\
\xe6\x21\xe3\x8a\x71\xba\x05\xd2\x0f\x98\x5b\xfc\x7e\x72\x8c\xe9\
\x9a\xc0\x49\x00\xca\xd5\x62\x93\x7f\x03\xc0\x0c\x00\x2e\x00\x01\
\x00\x00\x0e\x10\x00\x5d\x00\x30\x0d\x02\x00\x00\x0e\x10\x61\xf2\
\xaf\x39\x61\xcb\x22\x39\x28\xeb\x06\x72\x65\x6c\x6b\x6f\x6d\x02\
\x73\x6b\x00\xc0\x93\x23\x1d\xcb\x1e\x79\xfe\x7c\x40\x3e\xd4\x33\
\x5f\xed\x69\x8e\x7d\x75\xff\x73\x6b\x24\x71\x8f\x50\xf8\xe0\x49\
\xce\x5f\x62\x0c\x8c\xb3\x06\x8f\x26\xea\x20\xa0\xe3\x71\xe0\xa1\
\x8b\xe0\x4a\x2f\x1d\x4b\x79\x2c\x52\x6b\xa4\x43\xb5\x70\x27\x01\
\xb0\x63\x47\xc0\x0c\x00\x2e\x00\x01\x00\x00\x0e\x10\x00\x5d\x00\
\x30\x0d\x02\x00\x00\x0e\x10\x61\xf2\xaf\x39\x61\xcb\x22\x39\x97\
\x18\x06\x72\x65\x6c\x6b\x6f\x6d\x02\x73\x6b\x00\x41\x87\x75\x1d\
\x30\x44\xd1\x94\x40\xd4\xe6\x40\x98\x62\x94\x53\xad\x53\xe2\xed\
\xc0\xc0\xb7\xa3\x20\x15\xae\x59\xbb\x97\x45\xfb\x0e\xbf\x70\xf3\
\xb1\x24\x79\xe8\x85\x6c\x2a\x66\x10\xb6\x75\x99\x7b\x77\x78\x65\
\xa6\x67\x8d\x59\xa6\x14\xf7\xe6\x77\xab\x53\x9c\xc0\x0c\x00\x33\
\x00\x01\x00\x00\x02\x58\x00\x0d\x01\x00\x00\x0a\x08\x90\xc7\xf1\
\x74\x0b\x0c\xfb\x34\xc0\x0c\x00\x2e\x00\x01\x00\x00\x02\x58\x00\
\x5d\x00\x33\x0d\x02\x00\x00\x00\x00\x61\xf2\xaf\x39\x61\xcb\x22\
\x39\x28\xeb\x06\x72\x65\x6c\x6b\x6f\x6d\x02\x73\x6b\x00\xc4\x4d\
\x00\x48\x9c\x86\x49\xac\x8d\x03\x28\x23\xac\xec\xf5\x5b\xb6\xe5\
\x2f\xf6\xae\xaa\x01\x5a\x66\x52\xf7\x43\xc3\xb1\xe5\xef\xe5\xbf\
\x5f\x71\x5d\xa1\x57\x64\x66\x5e\xa1\x6f\x96\xa8\xcd\x48\x85\xe4\
\x20\xe2\xfb\xb0\xc1\x00\x47\x72\xc8\x72\x98\xc7\x41\xd9";
let dummy =
DnsRequestHeader
{
header: DnsHeader {id: 0xd079, ..Default::default()},
payload: DnsRequestPayload
{
qname: b"\x06\x72\x65\x6c\x6b\x6f\x6d\x02\x73\x6b\x00".to_vec(),
qtype: QType::ALL,
qclass: QClass::IN,
}
};
let now = Instant::now();
let ans = DnsRequestAnswer::async_try_from(pkt.as_slice()).await;
let elapsed = now.elapsed();
println!("Elapsed: {:.2?}", elapsed);
assert_eq!(ans.is_ok(), true, "{}", ans.err().unwrap());
let ans = ans.unwrap();
let verif = ans.verify(&dummy);
assert_eq!(verif.is_ok(), true, "{}", verif.err().unwrap());
assert_eq!(ans.header.id, 0xd079);
assert_eq!(ans.header.ancount, 23);
assert_eq!(ans.header.arcount, 0);
assert_eq!(ans.header.nscount, 0);
assert_eq!(ans.header.qdcount, 1);
let ansord = &ans.response[0];
assert_eq!(
ansord.rdata,
DnsRdata::SOA
{
soa:
DnsSoa
{
pnm: "ns21.cloudns.net".to_string(),
ram: "support.cloudns.net".to_string(),
serial: 2021122801,
interv_refr: 7200,
interv_retry: 1800,
expire_limit: 1209600,
min_ttl: 3600
}
}
);
assert_eq!(ansord.name, "relkom.sk".to_string());
assert_eq!(ansord.dtype, QType::SOA);
assert_eq!(ansord.class, QClass::IN);
assert_eq!(ansord.ttl, 3600);
assert_eq!(ansord.rdlength, 48);
let a1 =
b"\x00\x06\x0d\x02\
\x00\x00\x0e\x10\x61\xf2\xaf\x39\x61\xcb\x22\x39\x28\xeb\x06\x72\
\x65\x6c\x6b\x6f\x6d\x02\x73\x6b\x00\x2f\x79\xed\x73\xd0\x75\xc8\
\xa9\xa2\x2a\x08\x3c\x78\xb9\xfb\x43\xc5\x8e\x62\x4c\xbc\x36\xeb\
\x22\x96\x45\x59\x36\x1f\x69\x3a\x7a\x5e\x67\xd0\x54\x1e\xf0\xf2\
\x11\x1a\x72\x00\x56\x89\x26\x79\xf4\x06\x1a\x0c\x59\x41\x32\x60\
\x68\x75\x05\x3d\x90\xed\x1e\x0f\xfc";
let ansord = &ans.response[1];
assert_eq!(ansord.rdata, DnsRdata::UNKNOWN{ data: a1.to_vec() });
assert_eq!(ansord.name, "relkom.sk".to_string());
assert_eq!(ansord.dtype, QType::RRSIG);
assert_eq!(ansord.class, QClass::IN);
assert_eq!(ansord.ttl, 3600);
assert_eq!(ansord.rdlength, 93);
let ansord = &ans.response[2];
assert_eq!(ansord.rdata, DnsRdata::NS{ fqdn: "ns21.cloudns.net".to_string() });
assert_eq!(ansord.name, "relkom.sk".to_string());
assert_eq!(ansord.dtype, QType::NS);
assert_eq!(ansord.class, QClass::IN);
assert_eq!(ansord.ttl, 3600);
assert_eq!(ansord.rdlength, 2);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
async fn test_request()
{
use std::time::Instant;
use std::net::IpAddr;
let ipp = IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8));
let test = QDnsName::from(&ipp);
let now = Instant::now();
let req = DnsRequestHeader::construct_lookup(test, QType::PTR);
let elapsed = now.elapsed();
println!("Elapsed: {:.2?}", elapsed);
assert_eq!(req.is_ok(), true, "{}", req.err().unwrap());
let pkt = req.unwrap().async_to_bytes().await;
assert_eq!(pkt.is_ok(), true);
let pkt = pkt.unwrap();
let ctrl =
b"\x15\xc8\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x01\x38\x01\x38\
\x01\x38\x01\x38\x07\x69\x6e\x2d\x61\x64\x64\x72\x04\x61\x72\x70\
\x61\x00\x00\x0c\x00\x01";
assert_eq!(&pkt[2..], &ctrl[2..]);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
async fn test_request_1()
{
use std::time::Instant;
use std::net::IpAddr;
let ipp = IpAddr::V4("100.150.111.80".parse().unwrap());
let test = QDnsName::from(&ipp);
let now = Instant::now();
let req = DnsRequestHeader::construct_lookup(test, QType::PTR);
let elapsed = now.elapsed();
println!("Elapsed: {:.2?}", elapsed);
assert_eq!(req.is_ok(), true, "{}", req.err().unwrap());
let pkt = req.unwrap().async_to_bytes().await;
assert_eq!(pkt.is_ok(), true);
let pkt = pkt.unwrap();
let ctrl =
b"\x74\xa1\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x02\x38\x30\x03\
\x31\x31\x31\x03\x31\x35\x30\x03\x31\x30\x30\x07\x69\x6e\x2d\x61\
\x64\x64\x72\x04\x61\x72\x70\x61\x00\x00\x0c\x00\x01";
assert_eq!(&pkt[2..], &ctrl[2..]);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
async fn test_request6()
{
use std::time::Instant;
use std::net::IpAddr;
let ipp = IpAddr::V6("2a00:1450:4003:802::200e".parse().unwrap());
let test = QDnsName::from(&ipp);
let now = Instant::now();
let req = DnsRequestHeader::construct_lookup(test, QType::PTR);
let elapsed = now.elapsed();
println!("Elapsed: {:.2?}", elapsed);
assert_eq!(req.is_ok(), true, "{}", req.err().unwrap());
let pkt = req.unwrap().async_to_bytes().await;
assert_eq!(pkt.is_ok(), true);
let pkt = pkt.unwrap();
let ctrl =
b"\xee\xec\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x01\x65\x01\x30\
\x01\x30\x01\x32\x01\x30\x01\x30\x01\x30\x01\x30\x01\x30\x01\x30\
\x01\x30\x01\x30\x01\x30\x01\x30\x01\x30\x01\x30\x01\x32\x01\x30\
\x01\x38\x01\x30\x01\x33\x01\x30\x01\x30\x01\x34\x01\x30\x01\x35\
\x01\x34\x01\x31\x01\x30\x01\x30\x01\x61\x01\x32\x03\x69\x70\x36\
\x04\x61\x72\x70\x61\x00\x00\x0c\x00\x01";
assert_eq!(&pkt[2..], &ctrl[2..]);
}
}