use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt};
use chrono::{Utc, TimeZone};
use data_encoding::{BASE32HEX, BASE64};
use rand::Rng;
use std::{collections::HashMap, cmp::max};
use std::io::{Cursor, Read, Seek, SeekFrom};
use std::fmt::{self, Display};
use strum_macros::EnumString;
pub mod error;
use error::{ParseError, EncodeError};
#[derive(PartialEq, Copy, Clone, Debug)]
pub enum DnsOpcode {
QUERY,
IQUERY,
STATUS,
NOTIFY,
UPDATE,
DSO
}
#[derive(PartialEq, Copy, Clone, Debug)]
pub enum DnsRcode {
NOERROR,
FORMERR,
SERVFAIL,
NXDOMAIN,
NOTIMP,
REFUSED,
YXDOMAIN,
YXRRSET,
NXRRSET,
NOTAUTH,
NOTZONE,
DSOTYPENI,
BADVERSBADSIG,
BADKEY,
BADTIME,
BADMODE,
BADNAME,
BADALG,
BADTRUNC,
BADCOOKIE
}
#[derive(PartialEq, Copy, Clone, EnumString, Debug)]
pub enum DnsType {
A,
NS,
CNAME,
SOA,
PTR,
HINFO,
MX,
TXT,
RP,
KEY,
AAAA,
LOC,
SRV,
NAPTR,
CERT,
DNAME,
OPT,
DS,
SSHFP,
RRSIG,
NSEC,
DNSKEY,
NSEC3,
NSEC3PARAM,
TLSA,
OPENPGPKEY,
CAA
}
#[derive(PartialEq, Copy, Clone, Debug)]
pub enum DnsClass {
IN,
CH,
HS,
NONE,
ANY
}
pub struct DnsFlags {
flags: u16
}
pub struct DnsHeader {
msg_id: u16,
qr: bool,
opcode: DnsOpcode,
flags: DnsFlags,
rcode: Option<DnsRcode>,
qdcount: u16,
ancount: u16,
nscount: u16,
arcount: u16
}
pub struct DnsQuestion {
qname: String,
qtype: DnsType,
qclass: DnsClass
}
#[derive(Clone)]
pub enum DnsRecord {
OPT {
name: String,
atype: DnsType,
payload_size: u16,
rcode: DnsRcode,
edns_version: u8,
flags: HashMap<&'static str, bool>,
rdata: Vec<u8>,
parsed_rdata: Vec<String>
},
NONOPT {
name: String,
atype: DnsType,
class: DnsClass,
ttl: u32,
rdata: Vec<u8>,
parsed_rdata: Vec<String>
}
}
pub struct DnsMessage {
pub header: DnsHeader,
questions: Vec<DnsQuestion>,
answers: Vec<DnsRecord>,
authoritative_answers: Vec<DnsRecord>,
additional_answers: Vec<DnsRecord>
}
impl DnsOpcode {
pub fn encode(&self) -> u8 {
match self {
DnsOpcode::QUERY => 0,
DnsOpcode::IQUERY => 1,
DnsOpcode::STATUS => 2,
DnsOpcode::NOTIFY => 4,
DnsOpcode::UPDATE => 5,
DnsOpcode::DSO => 6
}
}
pub fn parse(val: u8) -> Result<DnsOpcode, ParseError> {
Ok(match val {
0 => DnsOpcode::QUERY,
1 => DnsOpcode::IQUERY,
2 => DnsOpcode::STATUS,
4 => DnsOpcode::NOTIFY,
5 => DnsOpcode::UPDATE,
6 => DnsOpcode::DSO,
x => return Err(ParseError::InvalidOpcode(x))
})
}
}
impl Display for DnsOpcode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self)
}
}
impl DnsRcode {
pub fn encode(&self) -> u8 {
match self {
DnsRcode::NOERROR => 0,
DnsRcode::FORMERR => 1,
DnsRcode::SERVFAIL => 2,
DnsRcode::NXDOMAIN => 3,
DnsRcode::NOTIMP => 4,
DnsRcode::REFUSED => 5,
DnsRcode::YXDOMAIN => 6,
DnsRcode::YXRRSET => 7,
DnsRcode::NXRRSET => 8,
DnsRcode::NOTAUTH => 9,
DnsRcode::NOTZONE => 10,
DnsRcode::DSOTYPENI => 11,
DnsRcode::BADVERSBADSIG => 16 & 0b1111,
DnsRcode::BADKEY => 17 & 0b1111,
DnsRcode::BADTIME => 18 & 0b1111,
DnsRcode::BADMODE => 19 & 0b1111,
DnsRcode::BADNAME => 20 & 0b1111,
DnsRcode::BADALG => 21 & 0b1111,
DnsRcode::BADTRUNC => 22 & 0b1111,
DnsRcode::BADCOOKIE => 23 & 0b1111
}
}
pub fn parse(val: u16) -> Result<DnsRcode, ParseError> {
Ok(match val {
0 => DnsRcode::NOERROR,
1 => DnsRcode::FORMERR,
2 => DnsRcode::SERVFAIL,
3 => DnsRcode::NXDOMAIN,
4 => DnsRcode::NOTIMP,
5 => DnsRcode::REFUSED,
6 => DnsRcode::YXDOMAIN,
7 => DnsRcode::YXRRSET,
8 => DnsRcode::NXRRSET,
9 => DnsRcode::NOTAUTH,
10 => DnsRcode::NOTZONE,
11 => DnsRcode::DSOTYPENI,
16 => DnsRcode::BADVERSBADSIG,
17 => DnsRcode::BADKEY,
18 => DnsRcode::BADTIME,
19 => DnsRcode::BADMODE,
20 => DnsRcode::BADNAME,
21 => DnsRcode::BADALG,
22 => DnsRcode::BADTRUNC,
23 => DnsRcode::BADCOOKIE,
x => return Err(ParseError::InvalidRcode(x))
})
}
}
impl Display for DnsRcode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self)
}
}
impl DnsType {
pub fn encode(&self) -> u16 {
match self {
DnsType::A => 1,
DnsType::NS => 2,
DnsType::CNAME => 5,
DnsType::SOA => 6,
DnsType::PTR => 12,
DnsType::HINFO => 13,
DnsType::MX => 15,
DnsType::TXT => 16,
DnsType::RP => 17,
DnsType::KEY => 25,
DnsType::AAAA => 28,
DnsType::LOC => 29,
DnsType::SRV => 33,
DnsType::NAPTR => 35,
DnsType::CERT => 37,
DnsType::DNAME => 39,
DnsType::OPT => 41,
DnsType::DS => 43,
DnsType::SSHFP => 44,
DnsType::RRSIG => 46,
DnsType::NSEC => 47,
DnsType::DNSKEY => 48,
DnsType::NSEC3 => 50,
DnsType::NSEC3PARAM => 51,
DnsType::TLSA => 52,
DnsType::OPENPGPKEY => 61,
DnsType::CAA => 257
}
}
pub fn parse(val: u16) -> Result<DnsType, ParseError> {
Ok(match val {
1 => DnsType::A,
2 => DnsType::NS,
5 => DnsType::CNAME,
6 => DnsType::SOA,
12 => DnsType::PTR,
13 => DnsType::HINFO,
15 => DnsType::MX,
16 => DnsType::TXT,
17 => DnsType::RP,
25 => DnsType::KEY,
28 => DnsType::AAAA,
29 => DnsType::LOC,
33 => DnsType::SRV,
35 => DnsType::NAPTR,
37 => DnsType::CERT,
39 => DnsType::DNAME,
41 => DnsType::OPT,
43 => DnsType::DS,
44 => DnsType::SSHFP,
46 => DnsType::RRSIG,
47 => DnsType::NSEC,
48 => DnsType::DNSKEY,
50 => DnsType::NSEC3,
51 => DnsType::NSEC3PARAM,
52 => DnsType::TLSA,
61 => DnsType::OPENPGPKEY,
257 => DnsType::CAA,
x => return Err(ParseError::InvalidType(x))
})
}
pub fn rdata_schema(&self) -> &str {
match self {
DnsType::A => "ip4",
DnsType::NS
| DnsType::CNAME
| DnsType::DNAME
| DnsType::PTR => "qname",
DnsType::SOA => "qname qname u32 u32 u32 u32 u32",
DnsType::HINFO => "string string",
DnsType::MX => "u16 qname",
DnsType::TXT => "text",
DnsType::RP => "qname qname",
DnsType::KEY
| DnsType::DNSKEY => "u16 u8 u8 base64",
DnsType::AAAA => "ip6",
DnsType::LOC => "u8 u8 u8 u8 u32 u32 u32",
DnsType::SRV => "u16 u16 u16 qname",
DnsType::NAPTR => "u16 u16 string string string qname",
DnsType::CERT => "u16 u16 u8 base64",
DnsType::OPT => "options",
DnsType::DS => "u16 u8 u8 hex",
DnsType::SSHFP => "u8 u8 hex",
DnsType::RRSIG => "qtype u8 u8 u32 time time u16 qname base64",
DnsType::NSEC => "qname types",
DnsType::NSEC3 => "u8 u8 u16 salt hash types",
DnsType::NSEC3PARAM => "u8 u8 u16 salt",
DnsType::TLSA => "u8 u8 u8 hex",
DnsType::OPENPGPKEY => "base64",
DnsType::CAA => "u8 property"
}
}
}
impl Display for DnsType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self)
}
}
impl DnsClass {
pub fn encode(&self) -> u16 {
match self {
DnsClass::IN => 1,
DnsClass::CH => 3,
DnsClass::HS => 4,
DnsClass::NONE => 254,
DnsClass::ANY => 255
}
}
pub fn parse(val: u16) -> Result<DnsClass, ParseError> {
Ok(match val {
1 => DnsClass::IN,
3 => DnsClass::CH,
4 => DnsClass::HS,
254 => DnsClass::NONE,
255 => DnsClass::ANY,
x => return Err(ParseError::InvalidClass(x))
})
}
}
impl Display for DnsClass {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self)
}
}
impl DnsFlags {
pub fn new(aa: bool, tc: bool, rd: bool, ra: bool, ad: bool, cd: bool) -> Self {
let aa = if aa {1} else {0};
let tc = if tc {1} else {0};
let rd = if rd {1} else {0};
let ra = if ra {1} else {0};
let ad = if ad {1} else {0};
let cd = if cd {1} else {0};
DnsFlags {
flags: (aa << 10) + (tc << 9) + (rd << 8) + (ra << 7) + (ad << 5) + (cd << 4)
}
}
pub fn from_flags(flags: u16) -> Self {
DnsFlags { flags }
}
pub fn as_flags(&self) -> u16 {
self.flags
}
pub fn aa(&self) -> bool { (self.flags & (1 << 10)) != 0 }
pub fn tc(&self) -> bool { (self.flags & (1 << 9)) != 0 }
pub fn rd(&self) -> bool { (self.flags & (1 << 8)) != 0 }
pub fn ra(&self) -> bool { (self.flags & (1 << 7)) != 0 }
pub fn ad(&self) -> bool { (self.flags & (1 << 5)) != 0 }
pub fn cd(&self) -> bool { (self.flags & (1 << 4)) != 0 }
}
impl DnsHeader {
pub fn new_response_header(msg_id: u16, opcode: DnsOpcode, flags: DnsFlags, rcode: DnsRcode, counts: [u16; 4]) -> Self {
DnsHeader {
msg_id,
qr: true,
opcode,
flags,
rcode: Some(rcode),
qdcount: counts[0],
ancount: counts[1],
nscount: counts[2],
arcount: counts[3]
}
}
pub fn new_query_header(msg_id: u16, opcode: DnsOpcode, flags: DnsFlags,
edns: bool, qdcount: u16) -> Result<Self, EncodeError> {
if flags.aa() || flags.ra() {
Err(EncodeError::AaOrRaInQuery)
} else {
Ok(DnsHeader {
msg_id,
qr: false,
opcode,
flags,
rcode: None,
qdcount,
ancount: 0,
nscount: 0,
arcount: if edns {1} else {0}
})
}
}
pub fn encode(&self) -> Result<Vec<u8>, EncodeError> {
let mut res = Vec::new();
let qr = if self.qr {1u16} else {0u16};
let opcode = self.opcode.encode() as u16;
let rcode = match &self.rcode {
Some(val) => val.encode() as u16,
None => 0u16
};
let line_two = (qr << 15) + (opcode << 11) + self.flags.as_flags() + rcode;
res.write_u16::<NetworkEndian>(self.msg_id)?;
res.write_u16::<NetworkEndian>(line_two)?;
res.write_u16::<NetworkEndian>(self.qdcount)?;
res.write_u16::<NetworkEndian>(self.ancount)?;
res.write_u16::<NetworkEndian>(self.nscount)?;
res.write_u16::<NetworkEndian>(self.arcount)?;
Ok(res)
}
pub fn parse(header: &mut Cursor<&[u8]>) -> Result<Self, ParseError> {
let msg_id = header.read_u16::<NetworkEndian>()?;
let line_two = header.read_u16::<NetworkEndian>()?;
let qr = (line_two & (1 << 15)) >> 15;
let opcode = DnsOpcode::parse(((line_two & (0b1111 << 11)) >> 11) as u8)?;
let flags = DnsFlags::from_flags(line_two & 0b0000011110110000);
let rcode = DnsRcode::parse(line_two & 0b1111)?;
Ok(DnsHeader {
msg_id,
qr: qr != 0,
opcode,
flags,
rcode: if qr != 0 {Some(rcode)} else {None},
qdcount: header.read_u16::<NetworkEndian>()?,
ancount: header.read_u16::<NetworkEndian>()?,
nscount: header.read_u16::<NetworkEndian>()?,
arcount: header.read_u16::<NetworkEndian>()?
})
}
pub fn info_str(&self) -> String {
let mut s = String::new();
if let Some(rcode) = self.rcode {
s.push_str(format!("id: {}, opcode: {}, rcode: {}, flags: ", self.msg_id, self.opcode,
rcode).as_str());
} else {
s.push_str(format!("id: {}, opcode: {}, flags: ", self.msg_id, self.opcode).as_str());
}
if self.flags.aa() { s.push_str("aa ") }
if self.flags.tc() { s.push_str("tc ") }
if self.flags.rd() { s.push_str("rd ") }
if self.flags.ra() { s.push_str("ra ") }
if self.flags.ad() { s.push_str("ad ") }
if self.flags.cd() { s.push_str("cd ") }
s.remove(s.len() - 1);
s
}
}
impl Display for DnsHeader {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut s = String::new();
if self.qr {
s.push_str("DNS Response (");
} else {
s.push_str("DNS Query (");
}
s.push_str(&self.info_str());
s.push(')');
write!(f, "{}", s)
}
}
impl DnsQuestion {
pub fn new(domain: &str, qtype: DnsType, qclass: DnsClass) -> Self {
DnsQuestion {
qname: domain.to_string(),
qtype,
qclass
}
}
pub fn encode(&self) -> Result<Vec<u8>, EncodeError> {
let mut question = DnsMessage::encode_qname(self.qname.as_str())?;
question.write_u16::<NetworkEndian>(self.qtype.encode())?;
question.write_u16::<NetworkEndian>(self.qclass.encode())?;
Ok(question)
}
pub fn parse(msg: &mut Cursor<&[u8]>) -> Result<Self, ParseError> {
let qname = DnsMessage::parse_qname(msg)?;
let qtype = DnsType::parse(msg.read_u16::<NetworkEndian>()?)?;
let qclass = DnsClass::parse(msg.read_u16::<NetworkEndian>()?)?;
Ok(DnsQuestion {
qname,
qtype,
qclass
})
}
pub fn as_padded_string(&self, owner_len: usize) -> String {
let mut res = String::new();
let mut owner = self.qname.clone();
while owner.len() < owner_len {
owner.push(' ');
}
res.push_str(format!(
"{} {} {}", owner, self.qclass, self.qtype
).as_str());
res
}
}
impl Display for DnsQuestion {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "DNS Question for '{}' (type: {}, class: {})", self.qname, self.qtype, self.qclass)
}
}
impl DnsRecord {
pub fn new_opt_record(payload_size: u16, do_flag: bool) -> Self {
let mut flags = HashMap::new();
flags.insert("do", do_flag);
DnsRecord::OPT {
name: "".to_string(),
atype: DnsType::OPT,
payload_size,
rcode: DnsRcode::NOERROR,
edns_version: 0,
flags,
rdata: vec![],
parsed_rdata: Default::default()
}
}
pub fn encode(&self) -> Result<Vec<u8>, EncodeError> {
match self {
DnsRecord::NONOPT { name, atype, class, ttl, rdata, .. } => {
let mut record = DnsMessage::encode_qname(name.as_str())?;
record.write_u16::<NetworkEndian>(atype.encode())?;
record.write_u16::<NetworkEndian>(class.encode())?;
record.write_u32::<NetworkEndian>(*ttl)?;
record.write_u16::<NetworkEndian>(rdata.len() as u16)?;
record.append(&mut rdata.clone());
Ok(record)
},
DnsRecord::OPT { name, atype, payload_size, rcode, edns_version, flags,
rdata, .. } => {
let mut record = DnsMessage::encode_qname(name.as_str())?;
record.write_u16::<NetworkEndian>(atype.encode())?;
record.write_u16::<NetworkEndian>(*payload_size)?;
let rcode = (((rcode.encode() as u16) & 0b111111110000) >> 4) as u8;
record.write_u8(rcode)?;
record.write_u8(*edns_version)?;
if flags.contains_key("do") && flags["do"] {
record.write_u16::<NetworkEndian>(1 << 15)?;
} else {
record.write_u16::<NetworkEndian>(0)?;
}
record.write_u16::<NetworkEndian>(rdata.len() as u16)?;
record.append(&mut rdata.clone());
Ok(record)
}
}
}
pub fn parse(msg: &mut Cursor<&[u8]>, rcode: DnsRcode) -> Result<Self, ParseError> {
let name = DnsMessage::parse_qname(msg)?;
let t = msg.read_u16::<NetworkEndian>()?;
let atype = DnsType::parse(t)?;
if atype == DnsType::OPT {
return DnsRecord::parse_opt_record(msg, name, rcode);
}
let class = DnsClass::parse(msg.read_u16::<NetworkEndian>()?)?;
let ttl = msg.read_u32::<NetworkEndian>()?;
let rdlength = msg.read_u16::<NetworkEndian>()?;
let mut rdata = vec![0; rdlength as usize];
let pos_rdata_start = msg.position();
msg.read_exact(&mut rdata)?;
msg.set_position(pos_rdata_start);
let parsed_rdata = DnsRecord::parse_rdata(&atype, msg, rdlength)?;
Ok(DnsRecord::NONOPT {
name,
atype,
class,
ttl,
rdata,
parsed_rdata
})
}
pub fn parse_rdata(atype: &DnsType, msg: &mut Cursor<&[u8]>, rdlength: u16) -> Result<Vec<String>, ParseError> {
let mut res = Vec::new();
let schema = atype.rdata_schema();
let mut len_read = 0u16;
for token in schema.split(' ') {
let pos_before = msg.position();
res.push(match token {
"u8" => { msg.read_u8()?.to_string() },
"u16" => { msg.read_u16::<NetworkEndian>()?.to_string() },
"u32" => { msg.read_u32::<NetworkEndian>()?.to_string() },
"qname" => { DnsMessage::parse_qname(msg)? },
"string" => { format!("\"{}\"", DnsMessage::parse_string(msg)?) },
"ip4" => { format!("{}.{}.{}.{}", msg.read_u8()?, msg.read_u8()?, msg.read_u8()?, msg.read_u8()?) },
"ip6" => {
let mut addr = String::new();
for _ in 0..8 {
addr.push_str(format!("{:x}:", msg.read_u16::<NetworkEndian>()?).as_str());
}
addr.remove(addr.len() - 1);
addr
},
"text" => {
let mut s = String::new();
let mut len = 0;
while len < rdlength - len_read {
let t = DnsMessage::parse_string(msg)?;
s.push_str(t.as_str());
len += (t.len() as u16) + 1;
}
s
},
"hex" => {
let mut hex = String::new();
for _i in 0..(rdlength - len_read) {
hex.push_str(format!("{:02x}", msg.read_u8()?).as_str());
}
hex
},
"qtype" => {
DnsType::parse(msg.read_u16::<NetworkEndian>()?)?.to_string()
},
"base64" => {
let mut data = vec![0; (rdlength - len_read) as usize];
msg.read_exact(&mut data)?;
BASE64.encode(&data)
},
"types" => {
let bitmap = DnsRecord::parse_nsec_type_bitmap(msg, len_read, rdlength)?;
DnsRecord::interpret_nsec_type_bitmap(bitmap)?
},
"salt" => {
let salt_len = msg.read_u8()?;
if salt_len == 0 {
"-".to_string()
} else {
let mut salt = String::new();
for _ in 0..salt_len {
salt.push_str(format!("{:02x}", msg.read_u8()?).as_str());
}
salt
}
},
"hash" => {
let hash_len = msg.read_u8()? as usize;
let mut hash = vec![0; hash_len];
msg.read_exact(&mut hash)?;
BASE32HEX.encode(&hash)
},
"property" => {
let tag_len = msg.read_u8()?;
let mut property = String::new();
for _i in 0..tag_len {
property.push(msg.read_u8()? as char);
}
property.push(' ');
for _i in 0..(rdlength - (tag_len as u16) - 1 - len_read) {
property.push(msg.read_u8()? as char);
}
property
},
"options" => {
let mut len = 0;
let mut s = String::new();
while len < rdlength - len_read {
let option_code = msg.read_u16::<NetworkEndian>()?;
s.push_str(option_code.to_string().as_str());
s.push_str(": ");
let option_len = msg.read_u16::<NetworkEndian>()?;
for _ in 0..option_len {
s.push_str(format!("{:02x}", msg.read_u8()?).as_str());
}
s.push_str(", ");
len += option_len + 4;
}
if !s.is_empty() {
s.remove(s.len() - 1);
s.remove(s.len() - 1);
}
s
},
"time" => {
let ts = msg.read_u32::<NetworkEndian>()?;
Utc.timestamp(ts as i64, 0).format("%Y%m%d%H%M%S").to_string()
},
x => {
panic!(format!("Invalid rdata schema key: {}", x))
}
});
len_read += (msg.position() - pos_before) as u16;
}
Ok(res)
}
pub fn interpret_nsec_type_bitmap(types: Vec<u16>) -> Result<String, ParseError> {
let mut res = String::new();
for t in types {
let t = match DnsType::parse(t) {
Ok(r) => r.to_string(),
Err(_) => format!("TYPE{}", t)
};
res.push_str(t.as_str());
res.push(' ');
}
if !res.is_empty() {
res.remove(res.len() - 1);
}
Ok(res)
}
pub fn parse_nsec_type_bitmap(msg: &mut Cursor<&[u8]>, len_read: u16, rdlength: u16)
-> Result<Vec<u16>, ParseError> {
let mut len_read = len_read;
let mut available_types = Vec::new();
while len_read < rdlength {
let window_number = msg.read_u8()?;
let bitmap_len = msg.read_u8()?;
for i in 0..bitmap_len {
let byte = msg.read_u8()?;
for j in 0..8 {
if (byte & (0b10000000 >> j)) != 0 {
let type_num = ((window_number as u16) << 8) + (i * 8 + j) as u16;
available_types.push(type_num);
}
}
}
len_read += (2 + bitmap_len) as u16;
}
Ok(available_types)
}
pub fn as_padded_string(&self, owner_len: usize, atype_len: usize) -> String {
match self {
DnsRecord::NONOPT { name, atype, class, ttl, parsed_rdata, .. } => {
let mut res = String::new();
let mut owner = name.clone();
while owner.len() < owner_len {
owner.push(' ');
}
let mut atype = atype.to_string();
while atype.len() < atype_len {
atype.push(' ');
}
res.push_str(format!(
"{} {:>5} {} {} {}", owner, ttl, class, atype,
DnsRecord::format_parsed_rdata(parsed_rdata)
).as_str());
res
},
DnsRecord::OPT { .. } => {
panic!()
}
}
}
fn parse_opt_record(msg: &mut Cursor<&[u8]>, name: String, rcode: DnsRcode)
-> Result<DnsRecord, ParseError> {
if !name.eq("") {
return Err(ParseError::InvalidOptName(name))
}
let payload_size = msg.read_u16::<NetworkEndian>()?;
let ext_rcode = msg.read_u8()?;
let rcode = match ext_rcode {
0 => rcode,
x => DnsRcode::parse(((x as u16) << 4) + (rcode.encode() as u16))?
};
let edns_version = msg.read_u8()?;
let mut flags = HashMap::new();
flags.insert(
"do",
msg.read_u16::<NetworkEndian>()? & (1 << 15) != 0
);
let rdlength = msg.read_u16::<NetworkEndian>()?;
let mut rdata = vec![0; rdlength as usize];
let pos_rdata_start = msg.position();
msg.read_exact(&mut rdata)?;
msg.set_position(pos_rdata_start);
let parsed_rdata = DnsRecord::parse_rdata(&DnsType::OPT, msg, rdlength)?;
Ok(DnsRecord::OPT {
name,
atype: DnsType::OPT,
payload_size,
rcode,
edns_version,
flags,
rdata,
parsed_rdata
})
}
fn format_parsed_rdata(parsed_rdata: &[String]) -> String {
let mut res = String::new();
for val in parsed_rdata {
res.push_str(format!("{} ", val).as_str());
}
if !parsed_rdata.is_empty() {
res.remove(res.len() - 1);
}
res
}
}
impl Display for DnsRecord {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DnsRecord::NONOPT { name, atype, class, ttl, parsed_rdata, ..} => {
write!(
f, "DNS Record for '{}' (type: {}, class: {}, ttl: {}, rdata: {})",
name, atype, class, ttl, DnsRecord::format_parsed_rdata(parsed_rdata)
)
},
DnsRecord::OPT { payload_size, edns_version, flags,
parsed_rdata, .. } => {
let mut s = format!(
"DNS OPT Record (EDNS version: {}, payload size: {}, flags: ",
edns_version, payload_size
);
for (key, value) in flags {
if *value { s.push_str(key); s.push(' '); }
}
s.remove(s.len() - 1);
s.push_str(format!(
", rdata: {})", DnsRecord::format_parsed_rdata(parsed_rdata)
).as_str());
write!(f, "{}", s)
}
}
}
}
impl DnsMessage {
pub fn new_query(domain: &str, qtype: DnsType, opcode: DnsOpcode, flags: DnsFlags,
edns: bool, do_flag: bool, bufsize: u16) -> Result<Self, EncodeError> {
if do_flag && !edns {
return Err(EncodeError::DoSetButNoEdns);
}
if flags.aa() || flags.ra() {
return Err(EncodeError::AaOrRaInQuery);
}
let msg_id = rand::thread_rng().gen_range(0..(1u32 << 16)) as u16;
let mut additional_answers = Vec::new();
if edns {
additional_answers.push(DnsRecord::new_opt_record(bufsize, do_flag));
}
Ok(DnsMessage {
header: DnsHeader::new_query_header(msg_id, opcode, flags, edns, 1)?,
questions: vec![DnsQuestion::new(domain, qtype, DnsClass::IN)],
answers: Vec::new(),
authoritative_answers: Vec::new(),
additional_answers
})
}
pub fn new_response(msg_id: u16, opcode: DnsOpcode, flags: DnsFlags, rcode: DnsRcode, questions: Vec<DnsQuestion>,
records: [Vec<DnsRecord>; 3]) -> Self {
DnsMessage {
header: DnsHeader::new_response_header(
msg_id, opcode, flags, rcode,
[questions.len() as u16, records[0].len() as u16, records[1].len() as u16, records[2].len() as u16]
),
questions,
answers: records[0].clone(),
authoritative_answers: records[1].clone(),
additional_answers: records[2].clone()
}
}
pub fn encode(&self) -> Result<Vec<u8>, EncodeError> {
let mut res = self.header.encode()?;
for question in &self.questions {
res.append(&mut question.encode()?);
}
for record in &self.answers {
res.append(&mut record.encode()?);
}
for record in &self.authoritative_answers {
res.append(&mut record.encode()?);
}
for record in &self.additional_answers {
res.append(&mut record.encode()?);
}
Ok(res)
}
pub fn parse(msg: &mut Cursor<&[u8]>) -> Result<Self, ParseError> {
let mut header = DnsHeader::parse(msg)?;
if header.flags.tc() {
return Err(ParseError::TruncatedMessage);
}
let qdcount = header.qdcount;
let ancount = header.ancount;
let nscount = header.nscount;
let arcount = header.arcount;
let questions = DnsMessage::parse_questions(msg, qdcount)?;
let mut answers = Vec::new();
let mut authoritative_answers = Vec::new();
let mut additional_answers = Vec::new();
if ancount > 0 {
answers = DnsMessage::parse_records(
msg, ancount, header.rcode.unwrap()
)?;
}
if nscount > 0 {
authoritative_answers = DnsMessage::parse_records(
msg, nscount, header.rcode.unwrap()
)?;
}
if arcount > 0 {
additional_answers = DnsMessage::parse_records(
msg, arcount, header.rcode.unwrap()
)?;
}
for answer in &additional_answers {
if let DnsRecord::OPT { rcode, .. } = answer {
header.rcode = Some(*rcode);
}
}
Ok(DnsMessage {
header,
questions,
answers,
authoritative_answers,
additional_answers
})
}
pub fn encode_qname(domain: &str) -> Result<Vec<u8>, EncodeError> {
if domain.bytes().len() > 255 {
return Err(EncodeError::DomainTooLong(domain.bytes().len())
);
}
if domain.eq("") {
return Ok(vec![0]);
}
let mut res = Vec::new();
for label in domain.split('.') {
if label.bytes().len() > 63 {
return Err(EncodeError::LabelTooLong(label.bytes().len())
);
}
res.write_u8(label.len() as u8)?;
label.bytes().for_each(|b| res.push(b));
}
res.write_u8(0)?;
Ok(res)
}
pub fn parse_string(msg: &mut Cursor<&[u8]>) -> Result<String, ParseError> {
let length = msg.read_u8()?;
let mut res = String::new();
for _i in 0..length {
res.push(msg.read_u8()? as char);
}
Ok(res)
}
pub fn parse_qname(msg: &mut Cursor<&[u8]>) -> Result<String, ParseError> {
let mut domain = String::new();
let mut c = msg.read_u8()?;
while c != 0 {
if (c & 0b11000000) != 0 {
c &= 0b00111111;
let offset = ((c as u16) << 8) + (msg.read_u8()? as u16);
let pos_after_pointer = msg.position() as i64;
msg.seek(SeekFrom::Start(offset as u64))?;
domain.push_str(DnsMessage::parse_qname(msg)?.as_str());
msg.seek(SeekFrom::Start(pos_after_pointer as u64))?;
return Ok(domain);
} else if (c & 0b01000000) != 0 || (c & 0b10000000) != 0 {
return Err(ParseError::InvalidLabelType(c));
}
for _i in 0..c {
domain.push(msg.read_u8()? as char);
}
domain.push('.');
c = msg.read_u8()?;
}
Ok(domain)
}
fn parse_questions(msg: &mut Cursor<&[u8]>, qdcount: u16) -> Result<Vec<DnsQuestion>, ParseError> {
let mut questions = Vec::with_capacity(qdcount as usize);
for _i in 0..qdcount {
questions.push(DnsQuestion::parse(msg)?);
}
Ok(questions)
}
fn parse_records(msg: &mut Cursor<&[u8]>, ancount: u16, rcode: DnsRcode) -> Result<Vec<DnsRecord>, ParseError> {
let mut answers = Vec::with_capacity(ancount as usize);
for _i in 0..ancount {
answers.push(DnsRecord::parse(msg, rcode)?);
}
Ok(answers)
}
}
impl Display for DnsMessage {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut res = String::new();
let mut additional_answers = self.additional_answers.clone();
let mut opt_index = -1;
let mut max_owner_len = 0;
let mut max_type_len = 0;
for q in &self.questions {
max_owner_len = max(max_owner_len, q.qname.len());
max_type_len = max(max_type_len, q.qtype.to_string().len());
}
let answers = [&self.answers, &self.authoritative_answers, &self.additional_answers];
let answers_iter = answers
.iter()
.flat_map(|a| a.iter());
for (i, answer) in answers_iter.enumerate() {
match answer {
DnsRecord::OPT { .. } => {
opt_index = (i - self.answers.len() - self.authoritative_answers.len()) as isize;
},
DnsRecord::NONOPT { name, atype, .. } => {
max_owner_len = max(max_owner_len, name.len());
max_type_len = max(max_type_len, atype.to_string().len());
}
}
}
if opt_index != -1 {
additional_answers.remove(opt_index as usize);
}
res.push_str(format!("Header:\n\t{}\n\n", self.header.info_str()).as_str());
res.push_str("Question Section:\n");
for question in &self.questions {
res.push('\t');
res.push_str(question.as_padded_string(max_owner_len).as_str());
res.push('\n');
}
res.push('\n');
if !self.answers.is_empty() {
res.push_str("Answer Section:\n");
for answer in &self.answers {
res.push('\t');
res.push_str(answer.as_padded_string(max_owner_len, max_type_len).as_str());
res.push('\n');
}
res.push('\n');
}
if !self.authoritative_answers.is_empty() {
res.push_str("Authoritative Section:\n");
for answer in &self.authoritative_answers {
res.push('\t');
res.push_str(answer.as_padded_string(max_owner_len, max_type_len).as_str());
res.push('\n');
}
res.push('\n');
}
if !additional_answers.is_empty() {
res.push_str("Additional Section:\n");
for answer in &additional_answers {
res.push('\t');
res.push_str(answer.as_padded_string(max_owner_len, max_type_len).as_str());
res.push('\n');
}
}
while res.chars().nth(res.len() - 1).unwrap() == '\n' {
res.remove(res.len() - 1);
}
write!(f, "{}", res)
}
}