use std::{borrow::Cow, cmp::Ordering, fmt::{self}, io::{Cursor, Read, Write}, net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}, path::Path, sync::{LazyLock, OnceLock}, time::Duration};
use std::borrow::Borrow;
use std::hash::{Hash, Hasher};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use crate::{cfg_host_parser::HostnameEntry, common, error::*, internal_error_map, query_private::QDnsReq};
use crate::internal_error;
pub const RESOLV_CFG_PATH: &'static str = "/etc/resolv.conf";
pub const HOST_CFG_PATH: &'static str = "/etc/hosts";
pub static RESOLV_CFG_PATH_P: LazyLock<&'static Path> = LazyLock::new(|| {Path::new(RESOLV_CFG_PATH)});
pub static HOST_CFG_PATH_P: LazyLock<&'static Path> = LazyLock::new(|| {Path::new(HOST_CFG_PATH)});
pub static GLOBALS: OnceLock<CDdnsGlobals> = OnceLock::new();
pub const NSSWITCH_CFG_PATH: &'static str = "/etc/nsswitch.conf";
const IN_ADDR_ARPA: &[u8] = b"\x07in-addr\x04arpa\x00";
const IN_ADDR6_ARPA: &[u8] = b"\x03ip6\x04arpa\x00";
pub const DEF_USERAGENT: &'static str = concat!("CDNS-RS/", env!("CARGO_PKG_VERSION"));
pub const DEF_TLS_PORT: u16 = 853;
pub const DEF_HTTPS_PORT: u16 = 443;
pub const IPV4_BIND_ALL: IpAddr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0));
pub const IPV6_BIND_ALL: IpAddr = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0));
#[derive(Debug)]
pub struct CDdnsGlobals
{
pub tcp_timeout: Option<Duration>,
}
impl CDdnsGlobals
{
pub
fn init_once(globals: CDdnsGlobals)
{
GLOBALS.get_or_init(|| globals);
return;
}
pub
fn get_tcp_conn_timeout() -> Option<Duration>
{
return GLOBALS.get().map_or(None, |f| f.tcp_timeout);
}
}
#[inline]
pub
fn bind_all(ip_addr: IpAddr) -> SocketAddr
{
match ip_addr
{
IpAddr::V4(_) =>
SocketAddr::from((IPV4_BIND_ALL, 0)),
IpAddr::V6(_) =>
SocketAddr::from((IPV6_BIND_ALL, 0)),
}
}
pub
fn pkt2string_exact(pkt: &mut Cursor<&[u8]>, rdlen: u16) -> CDnsResult<String>
{
let mut new_string = vec![0_u8; rdlen as usize];
pkt.read_exact(&mut new_string).map_err(map_read_err)?;
return
String::from_utf8(new_string)
.map_err(|e|
internal_error_map!(CDnsErrorType::InternalError, "UTF-8 decode error, {}", e)
);
}
pub
fn pkt2vec(pkt: &mut Cursor<&[u8]>, rdlen: Option<u16>) -> CDnsResult<Vec<u8>>
{
if let Some(rdl) = rdlen
{
let mut new_buf = vec![0_u8; rdl as usize];
pkt.read_exact(&mut new_buf).map_err(map_read_err)?;
return Ok(new_buf);
}
else
{
let mut new_buf = Vec::new();
pkt.read_to_end(&mut new_buf).map_err(map_read_err)?;
return Ok(new_buf);
}
}
pub
fn pkt2name(pkt: &mut Cursor<&[u8]>) -> CDnsResult<Vec<u8>>
{
let qname_s = pkt.position() as usize;
loop
{
let cnt = pkt.read_u8().map_err(map_read_err)? as usize;
if cnt == 0
{
break;
}
else
{
for _ in 0..cnt
{
let c = pkt.read_u8().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());
}
pub
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 = pkt.read_u8().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 = pkt.read_u8().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)?);
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::with_capacity(comp as usize);
for _ in 0..comp
{
let Some(c) = char::from_u32( pkt.read_u8().map_err(map_read_err)? as u32 )
else
{
internal_error!(CDnsErrorType::DnsResponse, "protocol violation, invalid character received");
};
if c.is_ascii_alphanumeric() == false && c != '-'
{
internal_error!(CDnsErrorType::DnsResponse, "protocol violation, forbidden characters in QNAME response");
}
tmp.push(c);
}
output.push(tmp);
}
}
else
{
internal_error!(CDnsErrorType::DnsResponse, "incorrect compression: {:x}", msb);
}
} }
pub
fn byte2hexchar(b: u8) -> u8
{
match b
{
0..=9 => return '0' as u8 + b,
10..=15 => return 'a' as u8 + (b - 10),
_ => panic!("out of hex range!")
}
}
pub
fn ip2pkt(ip: &IpAddr) -> Vec<u8>
{
match *ip
{
IpAddr::V4(ref ipv4) =>
return ipv4_pkt(ipv4),
IpAddr::V6(ref ipv6) =>
return ipv6_pkt(ipv6)
};
}
const MAX_NAME_LEN: usize = 63;
pub
fn ipv4_pkt(ip: &Ipv4Addr) -> Vec<u8>
{
let mut out: Vec<u8> = Vec::with_capacity(16 + IN_ADDR_ARPA.len());
ip
.octets()
.into_iter()
.rev()
.for_each(|v|
{
let st = v.to_string();
out.push(st.len() as u8);
out.extend(st.as_bytes());
}
);
out.extend_from_slice(IN_ADDR_ARPA);
return out;
}
pub
fn ipv6_pkt(ip: &Ipv6Addr) -> Vec<u8>
{
let mut out: Vec<u8> = Vec::with_capacity(32 + IN_ADDR6_ARPA.len());
let mut octets = ip.octets();
octets.reverse();
for oct in octets
{
let h_oct = byte2hexchar((oct & 0xF0) >> 4);
let l_oct = byte2hexchar(oct & 0x0F);
out.push(1);
out.push(l_oct);
out.push(1);
out.push(h_oct);
}
out.extend(IN_ADDR6_ARPA);
return out;
}
#[cfg(feature = "enable_IDN_support")]
pub mod with_punycode
{
use crate::external::rust_punycode;
use super::*;
pub
fn name2pkt(name: &str) -> CDnsResult<Vec<u8>>
{
let mut out: Vec<u8> = Vec::with_capacity(MAX_NAME_LEN);
for n in name.split(".")
{
let dom =
if n.chars().any(|c| c.is_ascii() == false) == true
{
rust_punycode::encode(n, true)?
}
else
{
n.to_string()
};
common::check_domain_ascii(&dom)?;
out.push((dom.len() & 0xFF) as u8);
out.extend(dom.as_bytes());
}
out.push(0);
if out.len() > 255
{
internal_error!(CDnsErrorType::InternalError,
"DNS name '{}' too long: {}", sanitize_str_unicode(name), out.len());
}
return Ok(out);
}
}
#[cfg(feature = "enable_IDN_support")]
pub use self::with_punycode::*;
#[cfg(not(feature = "enable_IDN_support"))]
pub mod with_punycode
{
use super::*;
pub
fn name2pkt(name: &str) -> CDnsResult<Vec<u8>>
{
let mut out: Vec<u8> = Vec::with_capacity(MAX_NAME_LEN);
for n in name.split(".")
{
let dom =
if n.chars().any(|c| c.is_ascii() == false) == true
{
internal_error!(CDnsErrorType::InternalError,
"DNS name '{}' contains non-ascii charactes, the IDN is disbaled",
sanitize_str_unicode(name));
}
else
{
n.to_string()
};
common::check_domain_ascii(&dom)?;
out.push((dom.len() & 0xFF) as u8);
out.extend(dom.as_bytes());
}
out.push(0);
if out.len() > 255
{
internal_error!(CDnsErrorType::InternalError,
"DNS name '{}' too long: {}", sanitize_str_unicode(name), out.len());
}
return Ok(out);
}
}
#[cfg(not(feature = "enable_IDN_support"))]
pub use self::with_punycode::*;
#[repr(u16)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum QType
{
A = 1,
NS = 2,
MD = 3,
MF = 4,
CNAME = 5,
SOA = 6,
MB = 7,
MG = 8,
MR = 9,
NULL = 10,
WKS = 11,
PTR = 12,
HINFO = 13,
MINFO = 14,
MX = 15,
TXT = 16,
AFSDB = 18,
KEY = 25,
AAAA = 28,
SRV = 33,
CERT = 37,
DS = 43,
RRSIG = 46,
NSEC = 47,
DNSKEY = 48,
NSEC3 = 50,
NSEC3PARAM = 51,
CDS = 59,
CDNSKEY = 60,
OPENPGPKEY = 61,
AXFR = 252,
MAILB = 253,
MAILA = 254,
URI = 256,
CAA = 257,
ALL = 255,
DLV = 32769,
}
impl Default for QType
{
fn default() -> Self
{
return Self::A;
}
}
impl Into<u16> for QType
{
fn into(self) -> u16
{
return self as u16;
}
}
impl fmt::Display for QType
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
match *self
{
Self::A => write!(f, "A"),
Self::NS => write!(f, "NS"),
Self::MD => write!(f, "MD"),
Self::MF => write!(f, "MF"),
Self::CNAME => write!(f, "CNAME"),
Self::SOA => write!(f, "SOA"),
Self::MB => write!(f, "MB"),
Self::MG => write!(f, "MG"),
Self::MR => write!(f, "MR"),
Self::NULL => write!(f, "NULL"),
Self::WKS => write!(f, "WKS"),
Self::PTR => write!(f, "PTR"),
Self::HINFO => write!(f, "HINFO"),
Self::MINFO => write!(f, "MINFO"),
Self::MX => write!(f, "MX"),
Self::TXT => write!(f, "TXT"),
Self::AFSDB => write!(f, "AFSDB"),
Self::KEY => write!(f, "KEY"),
Self::AAAA => write!(f, "AAAA"),
Self::SRV => write!(f, "SRV"),
Self::CERT => write!(f, "CERT"),
Self::DS => write!(f, "DS"),
Self::RRSIG => write!(f, "RRSIG"),
Self::NSEC => write!(f, "NSEC"),
Self::DNSKEY => write!(f, "DNSKEY"),
Self::NSEC3 => write!(f, "NSEC"),
Self::NSEC3PARAM => write!(f, "NSEC3PARAM"),
Self::CDS => write!(f, "CDS"),
Self::CDNSKEY => write!(f, "CDNSKEY"),
Self::OPENPGPKEY => write!(f, "OPENPGPKEY"),
Self::AXFR => write!(f, "AXFR"),
Self::MAILB => write!(f, "MAILB"),
Self::MAILA => write!(f, "MAILA"),
Self::CAA => write!(f, "CAA"),
Self::ALL => write!(f, "ALL"),
Self::DLV => write!(f, "DLV"),
Self::URI => write!(f, "URI"),
}
}
}
impl QType
{
pub
fn ipaddr_match(&self, ip: &IpAddr) -> bool
{
match *self
{
Self::A => return ip.is_ipv4(),
Self::AAAA => return ip.is_ipv6(),
_ => false,
}
}
pub
fn u16_to_qtype(value: u16) -> CDnsResult<QType>
{
match value
{
x if x == Self::AXFR as u16 => return Ok(Self::AXFR),
x if x == Self::MAILB as u16 => return Ok(Self::MAILB),
x if x == Self::MAILA as u16 => return Ok(Self::MAILA),
x if x == Self::ALL as u16 => return Ok(Self::ALL),
x if x == Self::DLV as u16 => return Ok(Self::DLV),
_ => return Self::u16_to_type(value),
}
}
pub
fn u16_to_type(value: u16) -> CDnsResult<QType>
{
match value
{
x if x == Self::A as u16 => return Ok(Self::A),
x if x == Self::NS as u16 => return Ok(Self::NS),
x if x == Self::MD as u16 => return Ok(Self::MD),
x if x == Self::MF as u16 => return Ok(Self::MF),
x if x == Self::CNAME as u16 => return Ok(Self::CNAME),
x if x == Self::SOA as u16 => return Ok(Self::SOA),
x if x == Self::MB as u16 => return Ok(Self::MB),
x if x == Self::MG as u16 => return Ok(Self::MG),
x if x == Self::MR as u16 => return Ok(Self::MR),
x if x == Self::NULL as u16 => return Ok(Self::NULL),
x if x == Self::WKS as u16 => return Ok(Self::WKS),
x if x == Self::PTR as u16 => return Ok(Self::PTR),
x if x == Self::HINFO as u16 => return Ok(Self::HINFO),
x if x == Self::MINFO as u16 => return Ok(Self::MINFO),
x if x == Self::MX as u16 => return Ok(Self::MX),
x if x == Self::TXT as u16 => return Ok(Self::TXT),
x if x == Self::AFSDB as u16 => return Ok(Self::AFSDB),
x if x == Self::KEY as u16 => return Ok(Self::KEY),
x if x == Self::AAAA as u16 => return Ok(Self::AAAA),
x if x == Self::CERT as u16 => return Ok(Self::CERT),
x if x == Self::DS as u16 => return Ok(Self::DS),
x if x == Self::RRSIG as u16 => return Ok(Self::RRSIG),
x if x == Self::NSEC as u16 => return Ok(Self::NSEC),
x if x == Self::DNSKEY as u16 => return Ok(Self::DNSKEY),
x if x == Self::NSEC3 as u16 => return Ok(Self::NSEC3),
x if x == Self::NSEC3PARAM as u16 => return Ok(Self::NSEC3PARAM),
x if x == Self::CDS as u16 => return Ok(Self::CDS),
x if x == Self::CDNSKEY as u16 => return Ok(Self::CDNSKEY),
x if x == Self::OPENPGPKEY as u16 => return Ok(Self::OPENPGPKEY),
_ => internal_error!(CDnsErrorType::DnsResponse, "unknown request record type: '{}'", value),
}
}
}
#[repr(u16)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum QClass
{
IN = 1,
CS = 2,
CH = 3,
HS = 4,
ALL = 255,
}
impl fmt::Display for QClass
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
match *self
{
Self::IN => write!(f, "IN"),
Self::CS => write!(f, "CS"),
Self::CH => write!(f, "CH"),
Self::HS => write!(f, "HS"),
Self::ALL => write!(f, "ALL"),
}
}
}
impl Default for QClass
{
fn default() -> Self
{
return Self::IN;
}
}
impl Into<u16> for QClass
{
fn into(self) -> u16
{
return self as u16;
}
}
impl QClass
{
pub
fn u16_to_qclass(value: u16) -> CDnsResult<QClass>
{
match value
{
x if x == QClass::ALL as u16 => return Ok(QClass::ALL),
_ => Self::u16_to_class(value),
}
}
pub
fn u16_to_class(value: u16) -> CDnsResult<QClass>
{
match value
{
x if x == QClass::IN as u16 => return Ok(QClass::IN),
x if x == QClass::CS as u16 => return Ok(QClass::CS),
x if x == QClass::CH as u16 => return Ok(QClass::CH),
x if x == QClass::HS as u16 => return Ok(QClass::HS),
_ => internal_error!(CDnsErrorType::DnsResponse, "unknown QCLASS type: '{}'", value),
}
}
}
bitflags! {
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
pub struct StatusBits: u16
{
const QR_RESP = 0x8000;
const OPCODE_STANDARD = 0x87FF;
const OPCODE_IQUERY = 0x0040;
const OPCODE_STATUS = 0x0020;
const AUTH_ANSWER = 0x0400;
const TRUN_CATION = 0x0200;
const RECURSION_DESIRED = 0x0100;
const RECURSION_AVAIL = 0x0080;
const RSERVER0 = 0x0040;
const ANSWER_AUTHN = 0x0020;
const NON_AUTH_DATA = 0x0010;
const RESP_NOERROR = 0x0000;
const RESP_FORMERR = 0x0001;
const RESP_SERVFAIL = 0x0002;
const RESP_NXDOMAIN = 0x0003;
const RESP_NOT_IMPL = 0x0004;
const RESP_REFUSED = 0x0005;
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum QDnsName
{
IpV4
{
query: Ipv4Addr,
encoded: Vec<u8>
},
IpV6
{
query: Ipv6Addr,
encoded: Vec<u8>
},
Name
{
query: String,
encoded: Vec<u8>
},
}
impl PartialEq<&[u8]> for QDnsName
{
fn eq(&self, other: &&[u8]) -> bool
{
match self
{
Self::IpV4{ encoded, .. } |
Self::IpV6{ encoded, .. } |
Self::Name{ encoded, .. } =>
return encoded.as_slice() == *other
}
}
}
impl Default for QDnsName
{
fn default() -> Self
{
let def = Ipv4Addr::new(0, 0, 0, 0);
return
Self::IpV4
{
query: def,
encoded: ipv4_pkt(&def)
};
}
}
impl fmt::Display for QDnsName
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
match self
{
Self::IpV4 { query, .. } =>
write!(f, "{}", query),
Self::IpV6 { query, .. } =>
write!(f, "{}", query),
Self::Name { query, .. } =>
write!(f, "{}", query)
}
}
}
impl QDnsName
{
pub
fn is_ipv4(&self) -> bool
{
let Self::IpV4 { .. } = self
else { return false };
return true;
}
pub
fn is_ipv6(&self) -> bool
{
let Self::IpV6 { .. } = self
else { return false };
return true;
}
pub
fn is_ip(&self) -> bool
{
match *self
{
Self::IpV4{ .. } => return true,
Self::IpV6{ .. } => return true,
_ => return false,
}
}
pub
fn get_ip_qtype(&self) -> Option<QType>
{
match *self
{
Self::IpV4{ .. } => return Some(QType::A),
Self::IpV6{ .. } => return Some(QType::AAAA),
Self::Name{ .. } => return None,
}
}
pub
fn get_encoded_len(&self) -> usize
{
match self
{
Self::IpV4{ encoded, .. } |
Self::IpV6{ encoded, .. } |
Self::Name{ encoded, .. } =>
return encoded.len()
}
}
}
impl Hash for QDnsName
{
fn hash<H: Hasher>(&self, state: &mut H)
{
core::mem::discriminant(self).hash(state);
match self
{
Self::IpV4{ query, .. } =>
query.hash(state),
Self::IpV6{ query, .. } =>
query.hash(state),
Self::Name{ query, .. } =>
query.hash(state),
}
}
}
impl PartialEq<str> for QDnsName
{
fn eq(&self, other: &str) -> bool
{
match *self
{
Self::Name{ ref query, .. } =>
return query.as_str() == other,
Self::IpV4{ ref query, .. } =>
{
if let Ok(other_ip) = other.parse::<Ipv4Addr>()
{
return &other_ip == query;
}
return false;
},
Self::IpV6{ ref query, .. } =>
{
if let Ok(other_ip) = other.parse::<Ipv6Addr>()
{
return &other_ip == query;
}
return false;
}
}
}
}
impl TryFrom<&IpAddr> for QDnsName
{
type Error = CDnsError;
fn try_from(ip: &IpAddr) -> Result<Self, Self::Error>
{
let encoded = ip2pkt(ip);
match *ip
{
IpAddr::V4(ref ip) =>
return Ok( Self::IpV4{ query: ip.clone(), encoded: encoded } ),
IpAddr::V6(ref ip) =>
return Ok( Self::IpV6{ query: ip.clone(), encoded: encoded } ),
}
}
}
impl TryFrom<IpAddr> for QDnsName
{
type Error = CDnsError;
fn try_from(ip: IpAddr) -> Result<Self, Self::Error>
{
return Self::try_from(&ip);
}
}
impl TryFrom<&Ipv4Addr> for QDnsName
{
type Error = CDnsError;
fn try_from(ip: &Ipv4Addr) -> Result<Self, Self::Error>
{
return Ok(Self::IpV4{ query: ip.clone(), encoded: ipv4_pkt(ip) });
}
}
impl TryFrom<Ipv4Addr> for QDnsName
{
type Error = CDnsError;
fn try_from(ip: Ipv4Addr) -> Result<Self, Self::Error>
{
return Self::try_from(&ip);
}
}
impl TryFrom<&Ipv6Addr> for QDnsName
{
type Error = CDnsError;
fn try_from(ip: &Ipv6Addr) -> Result<Self, Self::Error>
{
return Ok( Self::IpV6{ query: ip.clone(), encoded: ipv6_pkt(ip) } );
}
}
impl TryFrom<Ipv6Addr> for QDnsName
{
type Error = CDnsError;
fn try_from(ip: Ipv6Addr) -> Result<Self, Self::Error>
{
return Self::try_from(&ip);
}
}
impl TryFrom<&str> for QDnsName
{
type Error = CDnsError;
fn try_from(name: &str) -> Result<Self, Self::Error>
{
return Self::try_from(name.to_string());
}
}
impl TryFrom<String> for QDnsName
{
type Error = CDnsError;
fn try_from(name: String) -> Result<Self, Self::Error>
{
let Ok(ipp) = name.parse::<IpAddr>()
else
{
let enc_name = name2pkt(&name)?;
return Ok( Self::Name{ query: name, encoded: enc_name} );
};
return QDnsName::try_from(ipp);
}
}
impl From<QDnsName> for Vec<u8>
{
fn from(qns: QDnsName) -> Self
{
match qns
{
QDnsName::IpV4{ encoded, .. } |
QDnsName::IpV6{ encoded, .. } |
QDnsName::Name{ encoded, .. } =>
return encoded
}
}
}
impl<'t> From<&'t QDnsName> for &'t [u8]
{
fn from(qns: &'t QDnsName) -> Self
{
match qns
{
QDnsName::IpV4{ encoded, .. } |
QDnsName::IpV6{ encoded, .. } |
QDnsName::Name{ encoded, .. } =>
return encoded.as_slice()
}
}
}
impl<'t> From<&'t QDnsName> for Cow<'t, [u8]>
{
fn from(qns: &'t QDnsName) -> Self
{
match qns
{
QDnsName::IpV4{ encoded, .. } |
QDnsName::IpV6{ encoded, .. } |
QDnsName::Name{ encoded, .. } =>
return Cow::Borrowed(encoded.as_slice())
}
}
}
impl From<&QDnsName> for String
{
fn from(dnsname: &QDnsName) -> Self
{
match *dnsname
{
QDnsName::IpV4{ ref query, .. } =>
{
return query.to_string();
},
QDnsName::IpV6{ ref query, .. } =>
{
return query.to_string();
},
QDnsName::Name{ ref query, .. } =>
{
return query.to_string();
}
}
}
}
impl TryFrom<&QDnsName> for IpAddr
{
type Error = CDnsError;
fn try_from(value: &QDnsName) -> Result<Self, Self::Error>
{
match value
{
QDnsName::IpV4{ query, .. } =>
{
return Ok(IpAddr::V4(query.clone()));
},
QDnsName::IpV6{ query, .. } =>
{
return Ok(IpAddr::V6(query.clone()));
},
QDnsName::Name{ query, .. } =>
{
if let Ok(ip) = query.parse::<Ipv4Addr>()
{
return Ok(IpAddr::V4(ip.clone()));
}
else if let Ok(ip) = query.parse::<Ipv6Addr>()
{
return Ok(IpAddr::V6(ip.clone()));
}
else
{
internal_error!(CDnsErrorType::InternalError, "not ip address!")
}
}
}
}
}
impl TryFrom<QDnsName> for IpAddr
{
type Error = CDnsError;
fn try_from(value: QDnsName) -> Result<Self, Self::Error>
{
match value
{
QDnsName::IpV4{ query, .. } =>
{
return Ok(IpAddr::V4(query));
},
QDnsName::IpV6{ query, .. } =>
{
return Ok(IpAddr::V6(query));
},
QDnsName::Name{ query, .. } =>
{
if let Ok(ip) = query.parse::<Ipv4Addr>()
{
return Ok(IpAddr::V4(ip));
}
else if let Ok(ip) = query.parse::<Ipv6Addr>()
{
return Ok(IpAddr::V6(ip));
}
else
{
internal_error!(CDnsErrorType::InternalError, "not ip address!")
}
}
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct DnsHeader
{
pub id: u16,
pub status: StatusBits,
pub qdcount: u16,
pub ancount: u16,
pub nscount: u16,
pub arcount: u16,
}
impl fmt::Display for DnsHeader
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "ID:{} Status:{:?}, qdcnt:{}, ancnt:{}, nscnt:{}, arcnt:{}",
self.id, self.status, self.qdcount, self.ancount, self.nscount, self.arcount)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DnsRequestAnswer<'temp>
{
pub req_header: DnsRequestHeader<'temp>,
pub response: Vec<DnsResponsePayload>,
pub additional: Vec<DnsResponsePayload>,
pub authoratives: Vec<DnsResponsePayload>,
}
impl<'temp> DnsRequestAnswer<'temp>
{
#[inline]
pub
fn get_req_id(&self) -> u16
{
return self.req_header.get_id();
}
pub
fn verify(&self, req: &DnsRequestHeader<'_>) -> CDnsResult<()>
{
if self.get_req_id() != req.get_id()
{
internal_error!(
CDnsErrorType::DnsResponse,
"request and response ID did not match: '{}' != '{}'",
req.header.id, self.get_req_id()
);
}
if self.req_header.payload != req.payload
{
internal_error!(CDnsErrorType::DnsResponse,
"received request section is different from sent");
}
else if req.payload.qtype != QType::ALL
{
if req.payload.qtype != self.req_header.payload.qtype
{
internal_error!(
CDnsErrorType::DnsResponse,
"requested QTYPE differ received TYPE: '{}' != '{}'",
req.payload.qtype,
self.req_header.payload.qtype
);
}
}
else if self.req_header.header.status.contains(StatusBits::TRUN_CATION) == true
{
internal_error!(CDnsErrorType::MessageTruncated,
"DNS response was truncated, aborting processing");
}
return Ok(());
}
pub(crate)
fn parse(ans: &[u8]) -> CDnsResult<Self>
{
let mut pkt = Cursor::new(ans);
let id = pkt.read_u16::<BigEndian>().map_err(map_read_err)?;
let status = pkt.read_u16::<BigEndian>().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: pkt.read_u16::<BigEndian>().map_err(map_read_err)?,
ancount: pkt.read_u16::<BigEndian>().map_err(map_read_err)?,
nscount: pkt.read_u16::<BigEndian>().map_err(map_read_err)?,
arcount: pkt.read_u16::<BigEndian>().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: Cow::Owned(pkt2name(&mut pkt)?),
qtype: QType::u16_to_qtype(pkt.read_u16::<BigEndian>().map_err(map_read_err)?)?,
qclass: QClass::u16_to_qclass(pkt.read_u16::<BigEndian>().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::new(&mut pkt)?);
}
for _ in 0..header.nscount
{
ns_list.push(DnsResponsePayload::new(&mut pkt)?);
}
for _ in 0..header.arcount
{
rr_list.push(DnsResponsePayload::new(&mut pkt)?);
}
return Ok(
DnsRequestAnswer
{
req_header:
DnsRequestHeader
{
header,
payload: request
},
response: an_list,
additional: rr_list,
authoratives: ns_list,
}
);
}
}
#[derive(Clone, Debug)]
pub struct DnsRequestHeader<'temp>
{
pub header: DnsHeader,
pub payload: DnsRequestPayload<'temp>,
}
impl<'temp> Eq for DnsRequestHeader<'temp> {}
impl<'temp> PartialEq for DnsRequestHeader<'temp>
{
fn eq(&self, other: &DnsRequestHeader<'temp>) -> bool
{
return self.header.id == other.header.id && self.payload == other.payload;
}
}
impl<'temp> Ord for DnsRequestHeader<'temp>
{
fn cmp(&self, other: &Self) -> Ordering
{
return self.header.id.cmp(&other.header.id);
}
}
impl<'temp> PartialOrd for DnsRequestHeader<'temp>
{
fn partial_cmp(&self, other: &Self) -> Option<Ordering>
{
return Some(self.cmp(other));
}
}
impl<'temp> Hash for DnsRequestHeader<'temp>
{
fn hash<H: Hasher>(&self, state: &mut H)
{
self.header.id.hash(state);
self.payload.hash(state);
}
}
impl<'temp> Borrow<u16> for DnsRequestHeader<'temp>
{
fn borrow(&self) -> &u16
{
return &self.header.id;
}
}
impl<'temp> TryFrom<&'temp QDnsReq> for DnsRequestHeader<'temp>
{
type Error = CDnsError;
fn try_from(value: &'temp QDnsReq) -> Result<Self, Self::Error>
{
return DnsRequestHeader::construct_lookup(value); }
}
impl<'temp> fmt::Display for DnsRequestHeader<'temp>
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "Header: {}, Status: {}", self.header, self.payload)
}
}
impl<'temp> DnsRequestHeader<'temp>
{
pub
fn get_id(&self) -> u16
{
return self.header.id;
}
}
impl<'temp> DnsRequestHeader<'temp>
{
pub
fn regenerate_id(&mut self)
{
self.header.id = rand::random();
}
pub
fn derive(&self) -> Self
{
let header =
DnsHeader
{
id: rand::random(),
status: self.header.status,
qdcount: self.header.qdcount,
ancount: self.header.ancount,
nscount: self.header.nscount,
arcount: self.header.arcount,
};
return DnsRequestHeader{ header: header, payload: self.payload.clone() };
}
pub
fn construct_lookup(qreq: &'temp QDnsReq) -> CDnsResult<DnsRequestHeader<'_>>
{
let mut status: StatusBits = StatusBits::empty();
status = (status & !StatusBits::OPCODE_STANDARD) | StatusBits::RECURSION_DESIRED;
let req =
DnsRequestHeader
{
header:
DnsHeader
{
id: rand::random(),
status: status,
qdcount: 1,
ancount: 0,
nscount: 0,
arcount: 0,
},
payload:
DnsRequestPayload
::new(qreq.get_req_name().into(), *qreq.get_type(), QClass::IN)
};
return Ok(req);
}
pub
fn to_bytes(&self, append_len: bool) -> CDnsResult<Vec<u8>>
{
let mut pkt_size: usize = size_of::<DnsHeader>() + self.payload.qname.len() + 4;
if append_len == true
{
pkt_size += 2;
}
let mut pkt = Cursor::new(vec![0_u8; pkt_size]);
if append_len == true
{
pkt.write_u16::<BigEndian>((pkt_size-2) as u16).map_err(map_read_err)?;
}
pkt.write_u16::<BigEndian>(self.header.id).map_err(map_read_err)?;
pkt.write_u16::<BigEndian>(self.header.status.bits()).map_err(map_read_err)?;
pkt.write_u16::<BigEndian>(self.header.qdcount).map_err(map_read_err)?;
pkt.write_u16::<BigEndian>(self.header.ancount).map_err(map_read_err)?;
pkt.write_u16::<BigEndian>(self.header.nscount).map_err(map_read_err)?;
pkt.write_u16::<BigEndian>(self.header.arcount).map_err(map_read_err)?;
pkt.write(&self.payload.qname).map_err(map_read_err)?;
pkt.write_u16::<BigEndian>(self.payload.qtype.into()).map_err(map_read_err)?;
pkt.write_u16::<BigEndian>(self.payload.qclass.into()).map_err(map_read_err)?;
return Ok(pkt.into_inner());
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct DnsRequestPayload<'temp>
{
pub qname: Cow<'temp, [u8]>,
pub qtype: QType,
pub qclass: QClass,
}
impl<'temp> fmt::Display for DnsRequestPayload<'temp>
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "qname:{:?} qtype:{}, qclass:{}", self.qname, self.qtype, self.qclass)
}
}
impl<'temp> DnsRequestPayload<'temp>
{
pub
fn new(qname: Cow<'temp, [u8]>, qtype: QType, qclass: QClass) -> Self
{
return DnsRequestPayload{ qname: qname, qtype: qtype.into(), qclass: qclass.into() };
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct DnsResponsePayload
{
pub name: String,
pub dtype: QType,
pub class: QClass,
pub ttl: i32,
pub rdlength: u16,
pub rdata: DnsRdata,
}
impl DnsResponsePayload
{
pub(crate)
fn new_local(dtype: QType, data: &HostnameEntry) -> Option<Vec<Self>>
{
match dtype
{
QType::A =>
{
let mut out: Vec<Self> = Vec::with_capacity(1);
let IpAddr::V4(ipv4) = data.get_ip()
else { return None; };
out.push(
DnsResponsePayload
{
name: data.get_hostnames()[0].to_string(),
dtype: dtype,
class: QClass::IN,
ttl: i32::MAX,
rdlength: 0,
rdata: RecordA::wrap(RecordA{ ip: ipv4.clone()}),
}
);
return Some(out);
},
QType::AAAA =>
{
let mut out: Vec<Self> = Vec::with_capacity(1);
let IpAddr::V6(ipv6) = data.get_ip()
else { return None };
out.push(
DnsResponsePayload
{
name: data.get_hostnames()[0].to_string(),
dtype: dtype,
class: QClass::IN,
ttl: i32::MAX,
rdlength: 0,
rdata: RecordAAAA::wrap(RecordAAAA{ ip: ipv6.clone() }),
}
);
return Some(out);
},
QType::PTR =>
{
let mut out: Vec<Self> = Vec::with_capacity(data.get_hostnames().len());
for h in data.get_hostnames_iter()
{
out.push(
DnsResponsePayload
{
name: [data.get_ip().to_string().as_str(), ".in-addr.arpa"].concat(),
dtype: dtype,
class: QClass::IN,
ttl: i32::MAX,
rdlength: 0,
rdata: RecordPTR::wrap( RecordPTR { fqdn: h.clone() }),
}
);
}
return Some(out);
},
_ =>
return None
}
}
fn new(pkt: &mut Cursor<&[u8]>) -> CDnsResult<Self>
{
let name = name2str(pkt, None)?;
let qtype = QType::u16_to_type(pkt.read_u16::<BigEndian>().map_err(map_read_err)?)?;
let qclass = QClass::u16_to_class(pkt.read_u16::<BigEndian>().map_err(map_read_err)?)?;
let ttl = pkt.read_i32::<BigEndian>().map_err(map_read_err)?;
let rdlength = pkt.read_u16::<BigEndian>().map_err(map_read_err)?;
let rdata = DnsRdata::convert(pkt, rdlength, qtype)?;
return Ok(
DnsResponsePayload
{
name: name,
dtype: qtype,
class: qclass,
ttl: ttl,
rdlength: rdlength,
rdata: rdata,
}
);
}
pub
fn get_rdata(&self) -> &DnsRdata
{
return &self.rdata;
}
pub
fn borrow_rdata(self) -> DnsRdata
{
return self.rdata;
}
#[cfg(feature = "enable_IDN_support")]
pub
fn get_full_domain_name_with_idn_decode(&self) -> CDnsResult<String>
{
use crate::external::rust_punycode;
return Ok(
self
.name
.split(".")
.map(|v|
rust_punycode::decode(&v)
)
.collect::<CDnsResult<Vec<String>>>()?
.join(".")
);
}
pub
fn get_full_domain_name(&self) -> &str
{
return self.name.as_str();
}
pub
fn get_domain_name_iter(&self) -> std::str::Split<'_, &str>
{
return self.name.split(".");
}
#[cfg(feature = "enable_IDN_support")]
pub
fn get_domain_name_iter_with_idn_decode(&self) -> CDnsResult<std::vec::IntoIter<String>>
{
use crate::external::rust_punycode;
return
self
.name
.split(".")
.map(|v| rust_punycode::decode(&v))
.collect::<CDnsResult<Vec<String>>>()
.map(|v| v.into_iter());
}
}
#[cfg(feature = "enable_IDN_support")]
impl fmt::Display for DnsResponsePayload
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
if self.name.starts_with("xn-") == true
{
let idn =
self.get_full_domain_name_with_idn_decode().unwrap_or_else(|_| "decode_error".into());
write!(f, "{}({}) ", self.name, idn)?;
}
else
{
write!(f, "{} ", self.name)?;
}
write!(f, "{} {} {} {}",
self.dtype, self.class, self.ttl, self.rdata)
}
}
#[cfg(not(feature = "enable_IDN_support"))]
impl fmt::Display for DnsResponsePayload
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
if self.name.starts_with("xn-") == true
{
let idn = self.get_full_domain_name();
write!(f, "{}({}) ", self.name, idn)?;
}
else
{
write!(f, "{} ", self.name)?;
}
write!(f, "{} {} {} {}",
self.dtype, self.class, self.ttl, self.rdata)
}
}
pub trait RecordReader: Sized
{
const REC_TYPE: QType;
const RLEN: u16;
fn read(pkt: &mut Cursor<&[u8]>, rlen: u16, qtype: QType) -> CDnsResult<Self>;
fn wrap(self) -> DnsRdata;
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RecordA
{
pub ip: Ipv4Addr
}
impl RecordReader for RecordA
{
const REC_TYPE: QType = QType::A;
const RLEN: u16 = 4;
fn read(pkt: &mut Cursor<&[u8]>, rlen: u16, qtype: QType) -> CDnsResult<Self>
{
if rlen != Self::RLEN
{
internal_error!(CDnsErrorType::DnsResponse, "RecordA len expected: '4', got: '{}'", rlen);
}
else if qtype != Self::REC_TYPE
{
internal_error!(CDnsErrorType::DnsResponse, "expexted '{}', got: '{}'", Self::REC_TYPE, qtype);
}
let ip = pkt.read_u32::<BigEndian>().map_err(map_read_err)?;
return Ok(Self{ip: Ipv4Addr::from(ip) });
}
fn wrap(self) -> DnsRdata
{
return DnsRdata::A(self);
}
}
impl fmt::Display for RecordA
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "{} {}", Self::REC_TYPE, self.ip)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RecordAAAA
{
pub ip: Ipv6Addr
}
impl RecordReader for RecordAAAA
{
const REC_TYPE: QType = QType::AAAA;
const RLEN: u16 = 16;
fn read(pkt: &mut Cursor<&[u8]>, rlen: u16, qtype: QType) -> CDnsResult<Self>
{
if rlen != Self::RLEN
{
internal_error!(CDnsErrorType::DnsResponse, "RecordAAAA len expected: '{}', got: '{}'", Self::RLEN, rlen);
}
else if qtype != Self::REC_TYPE
{
internal_error!(CDnsErrorType::DnsResponse, "expexted '{}', got: '{}'", Self::REC_TYPE, qtype);
}
let ip = pkt.read_u128::<BigEndian>().map_err(map_read_err)?;
return Ok(Self{ ip: Ipv6Addr::from(ip) });
}
fn wrap(self) -> DnsRdata
{
return DnsRdata::AAAA(self);
}
}
impl fmt::Display for RecordAAAA
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "{} {}", Self::REC_TYPE, self.ip)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RecordPTR
{
pub fqdn: String
}
impl RecordReader for RecordPTR
{
const REC_TYPE: QType = QType::PTR;
const RLEN: u16 = 0;
fn read(pkt: &mut Cursor<&[u8]>, rlen: u16, qtype: QType) -> CDnsResult<Self>
{
if qtype != Self::REC_TYPE
{
internal_error!(CDnsErrorType::DnsResponse, "expexted '{}', got: '{}'", Self::REC_TYPE, qtype);
}
return Ok(
Self{ fqdn: name2str(pkt, Some(rlen))? }
);
}
fn wrap(self) -> DnsRdata
{
return DnsRdata::PTR(self);
}
}
impl fmt::Display for RecordPTR
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "{} {}", Self::REC_TYPE, self.fqdn)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RecordMX
{
pub preference: u16,
pub exchange: String
}
impl RecordReader for RecordMX
{
const REC_TYPE: QType = QType::MX;
const RLEN: u16 = 2;
fn read(pkt: &mut Cursor<&[u8]>, rlen: u16, qtype: QType) -> CDnsResult<Self>
{
if rlen <= Self::RLEN
{
internal_error!(CDnsErrorType::DnsResponse, "RecordMX len expected: '{}', got: '{}'", Self::RLEN, rlen);
}
else if qtype != Self::REC_TYPE
{
internal_error!(CDnsErrorType::DnsResponse, "expexted '{}', got: '{}'", Self::REC_TYPE, qtype);
}
return Ok(
Self
{
preference: pkt.read_u16::<BigEndian>().map_err(map_read_err)?,
exchange: name2str(pkt, Some(rlen - 2))?,
}
);
}
fn wrap(self) -> DnsRdata
{
return DnsRdata::MX(self);
}
}
impl fmt::Display for RecordMX
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "{} {} {}", Self::REC_TYPE, self.preference, self.exchange)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RecordCNAME
{
pub fqdn: String
}
impl RecordReader for RecordCNAME
{
const REC_TYPE: QType = QType::CNAME;
const RLEN: u16 = 0;
fn read(pkt: &mut Cursor<&[u8]>, rlen: u16, qtype: QType) -> CDnsResult<Self>
{
if qtype != Self::REC_TYPE
{
internal_error!(CDnsErrorType::DnsResponse, "expexted '{}', got: '{}'", Self::REC_TYPE, qtype);
}
return Ok(
Self{ fqdn: name2str(pkt, Some(rlen))? }
);
}
fn wrap(self) -> DnsRdata
{
return DnsRdata::CNAME(self);
}
}
impl fmt::Display for RecordCNAME
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "{} {}", Self::REC_TYPE, self.fqdn)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RecordNS
{
pub fqdn: String
}
impl RecordReader for RecordNS
{
const REC_TYPE: QType = QType::NS;
const RLEN: u16 = 0;
fn read(pkt: &mut Cursor<&[u8]>, rlen: u16, qtype: QType) -> CDnsResult<Self>
{
if qtype != Self::REC_TYPE
{
internal_error!(CDnsErrorType::DnsResponse, "expexted '{}', got: '{}'", Self::REC_TYPE, qtype);
}
return Ok(
Self{ fqdn: name2str(pkt, Some(rlen))? }
);
}
fn wrap(self) -> DnsRdata
{
return DnsRdata::NS(self);
}
}
impl fmt::Display for RecordNS
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "{} {}", Self::REC_TYPE, self.fqdn)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RecordSOA
{
pub pnm: String,
pub ram: String,
pub serial: u32,
pub interv_refr: u32,
pub interv_retry: u32,
pub expire_limit: u32,
pub min_ttl: u32,
}
impl RecordReader for RecordSOA
{
const REC_TYPE: QType = QType::SOA;
const RLEN: u16 = 0;
fn read(pkt: &mut Cursor<&[u8]>, _rlen: u16, qtype: QType) -> CDnsResult<Self>
{
if qtype != Self::REC_TYPE
{
internal_error!(CDnsErrorType::DnsResponse, "expexted '{}', got: '{}'", Self::REC_TYPE, qtype);
}
return Ok(
Self
{
pnm: name2str(pkt, None)?,
ram: name2str(pkt, None)?,
serial: pkt.read_u32::<BigEndian>().map_err(map_read_err)?,
interv_refr: pkt.read_u32::<BigEndian>().map_err(map_read_err)?,
interv_retry: pkt.read_u32::<BigEndian>().map_err(map_read_err)?,
expire_limit: pkt.read_u32::<BigEndian>().map_err(map_read_err)?,
min_ttl: pkt.read_u32::<BigEndian>().map_err(map_read_err)?,
}
);
}
fn wrap(self) -> DnsRdata
{
return DnsRdata::SOA(self);
}
}
impl fmt::Display for RecordSOA
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "{} {} {} {} {} {} {} {}", Self::REC_TYPE, self.pnm, self.ram, self.serial,
self.interv_refr, self.interv_retry, self.expire_limit, self.min_ttl)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RecordSRV
{
pub priority: u16,
pub weight: u16,
pub port: u16,
pub target: String,
}
impl RecordReader for RecordSRV
{
const REC_TYPE: QType = QType::SRV;
const RLEN: u16 = 0;
fn read(pkt: &mut Cursor<&[u8]>, _rlen: u16, qtype: QType) -> CDnsResult<Self>
{
if qtype != Self::REC_TYPE
{
internal_error!(CDnsErrorType::DnsResponse, "expexted '{}', got: '{}'", Self::REC_TYPE, qtype);
}
return Ok(
Self
{
priority: pkt.read_u16::<BigEndian>().map_err(map_read_err)?,
weight: pkt.read_u16::<BigEndian>().map_err(map_read_err)?,
port: pkt.read_u16::<BigEndian>().map_err(map_read_err)?,
target: name2str(pkt, None)?,
}
);
}
fn wrap(self) -> DnsRdata
{
return DnsRdata::SRV(self);
}
}
impl fmt::Display for RecordSRV
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "{} {} {} {} \"{}\"", Self::REC_TYPE, self.priority, self.weight, self.port, self.target)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RecordTXT
{
pub txt_strings: String,
}
impl RecordReader for RecordTXT
{
const REC_TYPE: QType = QType::TXT;
const RLEN: u16 = 2;
fn read(pkt: &mut Cursor<&[u8]>, rlen: u16, qtype: QType) -> CDnsResult<Self>
{
if rlen <= Self::RLEN
{
internal_error!(CDnsErrorType::DnsResponse, "RecordTXT len expected: '{}' >, got: '{}'", Self::RLEN, rlen);
}
else if qtype != Self::REC_TYPE
{
internal_error!(CDnsErrorType::DnsResponse, "expexted '{}', got: '{}'", Self::REC_TYPE, qtype);
}
let mut read_length = 0;
let dmod = ((rlen % 255) > 0) as u16;
let cap = rlen / 255 + dmod;
let mut txt_datas = Vec::<String>::with_capacity(cap as usize);
while read_length < rlen
{
let mut txt_data = Vec::new();
loop
{
let blk_size = pkt.read_u8().map_err(map_read_err)?;
read_length += blk_size as u16 + 1;
let mut new_txt = Vec::with_capacity(blk_size as usize); unsafe { new_txt.set_len(blk_size as usize); }
pkt.read_exact(&mut new_txt).map_err(map_read_err)?;
txt_data.push(new_txt);
if blk_size < 255
{
break;
}
}
if txt_data.is_empty() == true
{
internal_error!(
CDnsErrorType::DnsResponse,
"unexpected EOF in TXT rec cur position: '{}', data_length: '{}', read: '{}'",
pkt.position(), rlen, read_length
);
}
let data = txt_data.concat();
txt_datas.push(
String::from_utf8(data)
.map_err(|e|
internal_error_map!(CDnsErrorType::InternalError, "UTF-8 decode error, {}", e)
)?
);
}
return Ok(
Self
{
txt_strings: txt_datas.concat()
}
);
}
fn wrap(self) -> DnsRdata
{
return DnsRdata::TXT(self);
}
}
impl fmt::Display for RecordTXT
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "{} {}", Self::REC_TYPE, self.txt_strings)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RecordURI
{
pub priority: u16,
pub weight: u16,
pub target: String,
}
impl RecordReader for RecordURI
{
const REC_TYPE: QType = QType::URI;
const RLEN: u16 = 4;
fn read(pkt: &mut Cursor<&[u8]>, rlen: u16, qtype: QType) -> CDnsResult<Self>
{
if rlen <= Self::RLEN
{
internal_error!(CDnsErrorType::DnsResponse, "RecordURI len expected: '{}' >, got: '{}'", Self::RLEN, rlen);
}
else if qtype != Self::REC_TYPE
{
internal_error!(CDnsErrorType::DnsResponse, "expexted '{}', got: '{}'", Self::REC_TYPE, qtype);
}
return Ok(
Self
{
priority: pkt.read_u16::<BigEndian>().map_err(map_read_err)?,
weight: pkt.read_u16::<BigEndian>().map_err(map_read_err)?,
target: pkt2string_exact(pkt, rlen - 4)?,
}
);
}
fn wrap(self) -> DnsRdata
{
return DnsRdata::URI(self);
}
}
impl fmt::Display for RecordURI
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "{} {} {} \"{}\"", Self::REC_TYPE, self.priority, self.weight, self.target)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RecordRRSIG
{
type_cov: u16,
alg: u8,
labels: u8,
orig_ttl: u32,
sig_exp: u32,
sig_inc: u32,
keytag: u16,
sig_name: String,
key: Vec<u8>,
}
impl RecordReader for RecordRRSIG
{
const REC_TYPE: QType = QType::RRSIG;
const RLEN: u16 = 19;
fn read(pkt: &mut Cursor<&[u8]>, rlen: u16, _qtype: QType) -> CDnsResult<Self>
{
let data = vec![0_u8; rlen as usize];
let pos = pkt.position();
let mut res =
Self
{
type_cov: pkt.read_u16::<BigEndian>().map_err(map_read_err)?,
alg: pkt.read_u8().map_err(map_read_err)?,
labels: pkt.read_u8().map_err(map_read_err)?,
orig_ttl: pkt.read_u32::<BigEndian>().map_err(map_read_err)?,
sig_exp: pkt.read_u32::<BigEndian>().map_err(map_read_err)?,
sig_inc: pkt.read_u32::<BigEndian>().map_err(map_read_err)?,
keytag: pkt.read_u16::<BigEndian>().map_err(map_read_err)?,
sig_name: name2str(pkt,None)?,
key: Vec::new(),
};
let left_rdlen = rlen - (pkt.position() - pos) as u16;
res.key = pkt2vec(pkt, Some(left_rdlen))?;
return Ok(res);
}
fn wrap(self) -> DnsRdata
{
return DnsRdata::RRSIG(self);
}
}
impl fmt::Display for RecordRRSIG
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "{} {} {} {} {} {} {} {} {} {}",
Self::REC_TYPE, self.type_cov, self.alg, self.labels, self.orig_ttl,
self.sig_exp, self.sig_inc, self.keytag, self.sig_name,
self.key.iter().map(|k| format!("{:02X}", k)).collect::<Vec<String>>().concat()
)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RecordOther
{
pub qtype: QType,
pub data: Vec<u8>
}
impl RecordReader for RecordOther
{
const REC_TYPE: QType = QType::ALL;
const RLEN: u16 = 0;
fn read(pkt: &mut Cursor<&[u8]>, rlen: u16, qtype: QType) -> CDnsResult<Self>
{
let mut data = vec![0_u8; rlen as usize];
pkt.read_exact(&mut data).map_err(map_read_err)?;
return Ok(
Self
{
qtype: qtype,
data: data,
}
);
}
fn wrap(self) -> DnsRdata
{
return DnsRdata::OTHER(self);
}
}
impl fmt::Display for RecordOther
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "{} RAW:", self.qtype)?;
for d in self.data.iter()
{
write!(f, "{:X}", d)?;
}
return Ok(());
}
}
pub(crate)
fn sanitize_str_unicode(st: &str) -> String
{
let mut out = String::with_capacity(st.len());
for c in st.chars()
{
if c.is_alphanumeric() == true ||
c.is_ascii_punctuation() == true ||
c == ' '
{
out.push(c);
}
else
{
let mut buf = [0_u8; 4];
c.encode_utf8(&mut buf);
let formatted: String =
buf[0..c.len_utf8()].into_iter()
.map(|c| format!("\\x{:02x}", c))
.collect();
out.push_str(&formatted);
}
}
return out;
}
pub(crate)
fn check_domain_ascii(st: &str) -> CDnsResult<()>
{
if st.len() == 0
{
internal_error!(CDnsErrorType::InternalError,
"name empty: '{}' in: '{}'", st.len(), sanitize_str_unicode(st));
}
else if st.starts_with("-") == true
{
internal_error!(CDnsErrorType::InternalError,
"cannot start from '-' in: '{}'", sanitize_str_unicode(st));
}
else if st.ends_with("-") == true
{
internal_error!(CDnsErrorType::InternalError,
"cannot start from '-' in: '{}'", sanitize_str_unicode(st));
}
else if st.len() >= MAX_NAME_LEN
{
internal_error!(CDnsErrorType::InternalError,
"name too long: '{}' in: '{}'", st.len(), sanitize_str_unicode(st));
}
else if st.chars().any(|v| v.is_alphanumeric() == false && v != '-') == true
{
internal_error!(CDnsErrorType::InternalError,
"contains non-valid ASCII chars in: '{}'", sanitize_str_unicode(st));
}
return Ok(());
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum DnsRdata
{
None,
A(RecordA),
NS(RecordNS),
CNAME(RecordCNAME),
SOA(RecordSOA),
PTR(RecordPTR),
MX(RecordMX),
TXT(RecordTXT),
AAAA(RecordAAAA),
SRV(RecordSRV),
URI(RecordURI),
RRSIG(RecordRRSIG),
OTHER(RecordOther),
}
impl fmt::Display for DnsRdata
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
match self
{
Self::None =>
write!(f, "No record"),
Self::A(ip) =>
write!(f, "{}", ip),
Self::NS(ns) =>
write!(f, "{}", ns),
Self::AAAA(aaaa) =>
write!(f, "{}", aaaa),
Self::MX(mx) =>
write!(f, "{}", mx),
Self::CNAME(cname) =>
write!(f, "{}", cname),
Self::PTR(ptr) =>
write!(f, "{}", ptr),
Self::SOA(soa) =>
write!(f, "{}", soa),
Self::TXT(txt) =>
write!(f, "{}", txt),
Self::SRV(srv) =>
write!(f, "{}", srv),
Self::URI(uri) =>
write!(f, "{}", uri),
Self::RRSIG(rrsig) =>
write!(f, "{}", rrsig),
Self::OTHER(other) =>
write!(f, "{}", other),
}
}
}
impl Default for DnsRdata
{
fn default() -> Self
{
return Self::None;
}
}
impl DnsRdata
{
#[inline]
pub
fn is_some(&self) -> bool
{
return *self != Self::None;
}
}
impl DnsRdata
{
pub
fn convert(pkt: &mut Cursor<&[u8]>, rlen: u16, qtype: QType) -> CDnsResult<Self>
{
match qtype
{
RecordA::REC_TYPE =>
return Ok(RecordA::wrap(RecordA::read(pkt, rlen, qtype)?)),
RecordAAAA::REC_TYPE =>
return Ok(RecordAAAA::wrap(RecordAAAA::read(pkt, rlen, qtype)?)),
RecordPTR::REC_TYPE =>
return Ok(RecordPTR::wrap(RecordPTR::read(pkt, rlen, qtype)?)),
RecordMX::REC_TYPE =>
return Ok(RecordMX::wrap(RecordMX::read(pkt, rlen, qtype)?)),
RecordCNAME::REC_TYPE =>
return Ok(RecordCNAME::wrap(RecordCNAME::read(pkt, rlen, qtype)?)),
RecordSOA::REC_TYPE =>
return Ok(RecordSOA::wrap(RecordSOA::read(pkt, rlen, qtype)?)),
RecordSRV::REC_TYPE =>
return Ok(RecordSRV::wrap(RecordSRV::read(pkt, rlen, qtype)?)),
RecordTXT::REC_TYPE =>
return Ok(RecordTXT::wrap(RecordTXT::read(pkt, rlen, qtype)?)),
RecordURI::REC_TYPE =>
return Ok(RecordURI::wrap(RecordURI::read(pkt, rlen, qtype)?)),
RecordNS::REC_TYPE =>
return Ok(RecordNS::wrap(RecordNS::read(pkt, rlen, qtype)?)),
RecordRRSIG::REC_TYPE =>
return Ok(RecordRRSIG::wrap(RecordRRSIG::read(pkt, rlen, qtype)?)),
_ =>
return Ok(RecordOther::wrap(RecordOther::read(pkt, rlen, qtype)?)),
}
}
pub
fn get_ip(&self) -> Option<IpAddr>
{
match self
{
Self::A(a) => return Some(IpAddr::V4(a.ip)),
Self::AAAA(a) => return Some(IpAddr::V6(a.ip)),
_ => return None
}
}
}
#[cfg(test)]
mod tests
{
use std::io::Cursor;
use crate::common::*;
#[test]
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);
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);
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));
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));
assert_eq!(r4.is_ok(), false);
}
#[test]
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);
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);
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);
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);
assert_eq!(r4.is_ok(), false);
}
#[test]
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: Cow::Borrowed(b"\x01\x38\x01\x38\x01\x38\x01\x38\x07\x69\x6e\x2d\x61\x64\x64\x72\x04\x61\x72\x70\x61\x00"),
qtype: QType::PTR,
qclass: QClass::IN,
}
};
let now = Instant::now();
let ans = DnsRequestAnswer::parse(pkt.as_slice());
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.req_header.header.id, 0x15c8);
assert_eq!(ans.req_header.header.ancount, 1);
assert_eq!(ans.req_header.header.arcount, 2);
assert_eq!(ans.req_header.header.nscount, 0);
assert_eq!(ans.req_header.header.qdcount, 1);
assert_eq!(ans.response[0].rdata, RecordPTR::wrap(RecordPTR { 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, RecordA::wrap(RecordA { 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, RecordA::wrap(RecordA { 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);
}
#[test]
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: Cow::Borrowed(b"\x05\x67\x6d\x61\x69\x6c\x03\x63\x6f\x6d\x00"),
qtype: QType::MX,
qclass: QClass::IN,
}
};
let now = Instant::now();
let ans = DnsRequestAnswer::parse(pkt.as_slice());
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.req_header.header.id, 0x4b38);
assert_eq!(ans.req_header.header.ancount, 5);
assert_eq!(ans.req_header.header.arcount, 0);
assert_eq!(ans.req_header.header.nscount, 0);
assert_eq!(ans.req_header.header.qdcount, 1);
assert_eq!(ans.response[0].rdata, RecordMX::wrap(RecordMX { 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, RecordMX::wrap(RecordMX { 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, RecordMX::wrap(RecordMX { 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, RecordMX::wrap(RecordMX { 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, RecordMX::wrap(RecordMX { 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);
}
#[test]
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: Cow::Borrowed(b"\x06\x72\x65\x6c\x6b\x6f\x6d\x02\x73\x6b\x00"),
qtype: QType::ALL,
qclass: QClass::IN,
}
};
let now = Instant::now();
let ans = DnsRequestAnswer::parse(pkt.as_slice());
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.req_header.header.id, 0xd079);
assert_eq!(ans.req_header.header.ancount, 23);
assert_eq!(ans.req_header.header.arcount, 0);
assert_eq!(ans.req_header.header.nscount, 0);
assert_eq!(ans.req_header.header.qdcount, 1);
let ansord = &ans.response[0];
assert_eq!(
ansord.rdata,
RecordSOA::wrap(
RecordSOA
{
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 mut a1curs = Cursor::new(a1.as_slice());
let a1len = a1.len();
let ansord = &ans.response[1];
assert_eq!(ansord.rdata, RecordRRSIG::wrap( RecordRRSIG::read(&mut a1curs, a1len as u16, QType::RRSIG).unwrap() ));
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, a1len as u16);
let ansord = &ans.response[2];
assert_eq!(ansord.rdata, RecordNS::wrap( RecordNS{ 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);
}
#[test]
fn test_request()
{
use std::time::Instant;
use std::net::IpAddr;
let ipp = IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8));
let qdnsreq =
QDnsReq::new_into(&ipp, QType::PTR)
.unwrap();
let now = Instant::now();
let req = DnsRequestHeader::construct_lookup(&qdnsreq);
let elapsed = now.elapsed();
println!("Elapsed: {:.2?}", elapsed);
assert_eq!(req.is_ok(), true, "{}", req.err().unwrap());
let pkt = req.unwrap().to_bytes(false);
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..]);
}
#[test]
fn test_request_1()
{
use std::time::Instant;
use std::net::IpAddr;
let ipp = IpAddr::V4("100.150.111.80".parse().unwrap());
let qdnsreq =
QDnsReq::new_into(&ipp, QType::PTR)
.unwrap();
let now = Instant::now();
let req = DnsRequestHeader::construct_lookup(&qdnsreq);
let elapsed = now.elapsed();
println!("Elapsed: {:.2?}", elapsed);
assert_eq!(req.is_ok(), true, "{}", req.err().unwrap());
let pkt = req.unwrap().to_bytes(false);
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..]);
}
#[test]
fn test_request6()
{
use std::time::Instant;
use std::net::IpAddr;
let ipp = IpAddr::V6("2a00:1450:4003:802::200e".parse().unwrap());
let qdnsreq =
QDnsReq::new_into(&ipp, QType::PTR)
.unwrap();
let now = Instant::now();
let req = DnsRequestHeader::construct_lookup(&qdnsreq);
let elapsed = now.elapsed();
println!("Elapsed: {:.2?}", elapsed);
assert_eq!(req.is_ok(), true, "{}", req.err().unwrap());
let pkt = req.unwrap().to_bytes(false);
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..]);
}
}