Add DNS implementation - Query and RR for IN/A

This commit is contained in:
_Frky 2022-08-04 15:13:09 +02:00
parent 379f48ed80
commit 0dd0e1d645
5 changed files with 1749 additions and 0 deletions

93
src/proto/dns/cst.rs Normal file
View file

@ -0,0 +1,93 @@
// This file is part of masscanned.
// Copyright 2022 - The IVRE project
//
// Masscanned is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Masscanned is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Masscanned. If not, see <http://www.gnu.org/licenses/>.
use strum_macros::EnumIter;
#[derive(PartialEq, Debug, Clone, Copy, EnumIter)]
pub enum DNSType {
NONE,
A,
TXT, // value: 16 - text strings
}
impl From<u16> for DNSType {
fn from(item: u16) -> Self {
match item {
1 => DNSType::A,
16 => DNSType::TXT,
_ => DNSType::NONE,
}
}
}
impl From<DNSType> for u16 {
fn from(item: DNSType) -> Self {
match item {
DNSType::A => 1,
DNSType::TXT => 16,
_ => 0,
}
}
}
#[derive(PartialEq, Debug, Clone, Copy, EnumIter)]
pub enum DNSClass {
NONE,
IN, // value: 1 - the Internet
CH, // value: 3 - the CHAOS class
}
impl From<u16> for DNSClass {
fn from(item: u16) -> Self {
match item {
1 => DNSClass::IN,
3 => DNSClass::CH,
_ => DNSClass::NONE,
}
}
}
impl From<DNSClass> for u16 {
fn from(item: DNSClass) -> Self {
match item {
DNSClass::IN => 1,
DNSClass::CH => 3,
_ => 0,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn type_parse() {
/* type TXT */
assert!(DNSType::from(1) == DNSType::A);
assert!(1 as u16 == DNSType::A.into());
assert!(DNSType::from(16) == DNSType::TXT);
assert!(16 as u16 == DNSType::TXT.into());
}
#[test]
fn class_parse() {
assert!(DNSClass::from(1) == DNSClass::IN);
assert!(1 as u16 == DNSClass::IN.into());
assert!(DNSClass::from(3) == DNSClass::CH);
assert!(3 as u16 == DNSClass::CH.into());
}
}

383
src/proto/dns/header.rs Normal file
View file

@ -0,0 +1,383 @@
// This file is part of masscanned.
// Copyright 2022 - The IVRE project
//
// Masscanned is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Masscanned is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Masscanned. If not, see <http://www.gnu.org/licenses/>.
use std::convert::TryFrom;
use crate::proto::dissector::{MPacket, PacketDissector};
use crate::proto::ClientInfo;
use crate::proto::TCPControlBlock;
use crate::Masscanned;
#[derive(PartialEq)]
pub enum DNSHeaderState {
Id,
Flags,
QDCount,
ANCount,
NSCount,
ARCount,
End,
}
pub struct DNSHeader {
pub d: PacketDissector<DNSHeaderState>,
pub id: u16,
pub flags: u16,
pub _qr: bool,
pub _opcode: u8,
pub _aa: bool,
pub _tc: bool,
pub _rd: bool,
pub _ra: bool,
pub _z: u8,
pub _rcode: u8,
pub qdcount: u16,
pub ancount: u16,
pub nscount: u16,
pub arcount: u16,
}
impl TryFrom<Vec<u8>> for DNSHeader {
type Error = &'static str;
fn try_from(item: Vec<u8>) -> Result<Self, Self::Error> {
let mut hdr = DNSHeader::new();
for b in item {
hdr.parse(&b);
}
if hdr.d.state == DNSHeaderState::End {
Ok(hdr)
} else {
Err("packet is incomplete")
}
}
}
impl From<&DNSHeader> for Vec<u8> {
fn from(item: &DNSHeader) -> Self {
let mut v = Vec::new();
/* id */
v.push((item.id >> 8) as u8);
v.push((item.id & 0xFF) as u8);
/* flags */
/* QR | OPCODE | AA | TC | RD */
v.push(
((item._qr as u8) << 7)
| (item._opcode << 3)
| ((item._aa as u8) << 2)
| ((item._tc as u8) << 1)
| (item._rd as u8),
);
/* AA | ZZZ | RCODE */
v.push(0);
/* qdcount */
v.push((item.qdcount >> 8) as u8);
v.push((item.qdcount & 0xFF) as u8);
/* ancount */
v.push((item.ancount >> 8) as u8);
v.push((item.ancount & 0xFF) as u8);
/* nscount */
v.push((item.nscount >> 8) as u8);
v.push((item.nscount & 0xFF) as u8);
/* arcount */
v.push((item.arcount >> 8) as u8);
v.push((item.arcount & 0xFF) as u8);
v
}
}
impl MPacket for DNSHeader {
fn new() -> Self {
DNSHeader {
d: PacketDissector::new(DNSHeaderState::Id),
id: 0,
flags: 0,
_qr: false,
_opcode: 0,
_aa: false,
_tc: false,
_rd: false,
_ra: false,
_z: 0,
_rcode: 0,
qdcount: 0,
ancount: 0,
nscount: 0,
arcount: 0,
}
}
fn parse(&mut self, byte: &u8) {
match self.d.state {
DNSHeaderState::Id => {
self.id = self.d.read_u16(byte, self.id, DNSHeaderState::Flags);
}
DNSHeaderState::Flags => {
self.flags = self.d.read_u16(byte, self.flags, DNSHeaderState::QDCount);
}
DNSHeaderState::QDCount => {
self.qdcount = self.d.read_u16(byte, self.qdcount, DNSHeaderState::ANCount);
}
DNSHeaderState::ANCount => {
self.ancount = self.d.read_u16(byte, self.ancount, DNSHeaderState::NSCount);
}
DNSHeaderState::NSCount => {
self.nscount = self.d.read_u16(byte, self.nscount, DNSHeaderState::ARCount);
}
DNSHeaderState::ARCount => {
self.arcount = self.d.read_u16(byte, self.arcount, DNSHeaderState::End);
}
DNSHeaderState::End => {}
}
/* we need this to be executed at the same call
* the state changes to End, hence it is not in the
* match structure
**/
if self.d.state == DNSHeaderState::End {
self._qr = (self.flags >> 15) == 1;
self._opcode = ((self.flags >> 11) & 0x0F) as u8;
self._aa = (self.flags >> 10) & 0x01 == 1;
self._tc = (self.flags >> 9) & 0x01 == 1;
self._rd = (self.flags >> 8) & 0x01 == 1;
self._ra = (self.flags >> 7) & 0x01 == 1;
self._z = ((self.flags >> 4) & 0x07) as u8;
self._rcode = (self.flags & 0x0F) as u8;
}
}
fn repl(
&self,
_masscanned: &Masscanned,
_client_info: &ClientInfo,
_tcb: Option<&mut TCPControlBlock>,
) -> Option<Vec<u8>> {
let mut r = DNSHeader::new();
r.id = self.id;
r._qr = true;
r._opcode = self._opcode;
r._aa = true;
r._tc = false;
/* RFC1035
* Recursion Desired - this bit may be set in a query and
* is copied into the response. */
r._rd = self._rd;
r._ra = false;
r.qdcount = self.qdcount;
r.ancount = self.qdcount;
Some(Vec::<u8>::from(&r))
}
}
#[cfg(test)]
mod tests {
use super::*;
use pnet::util::MacAddr;
use std::str::FromStr;
use crate::logger::MetaLogger;
#[test]
fn parse_all() {
let payload = b"\xb3\x07\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00";
let hdr = match DNSHeader::try_from(payload.to_vec()) {
Ok(_hdr) => _hdr,
Err(e) => panic!("error while parsing DNS header: {}", e),
};
assert!(hdr.d.state == DNSHeaderState::End);
assert!(hdr.id == 0xb307);
assert!(hdr.flags == 0x0100);
assert!(hdr._qr == false);
assert!(hdr._opcode == 0);
assert!(hdr._aa == false);
assert!(hdr._tc == false);
assert!(hdr._rd == true);
assert!(hdr._ra == false);
assert!(hdr._z == 0);
assert!(hdr._rcode == 0);
assert!(hdr.qdcount == 1);
assert!(hdr.ancount == 0);
assert!(hdr.nscount == 0);
assert!(hdr.arcount == 0);
assert!(Vec::<u8>::from(&hdr) == payload.to_vec());
/* KO */
let payload = b"\xb3\x07\x01\x00\x00\x01\x00\x00\x00\x00\x00";
match DNSHeader::try_from(payload.to_vec()) {
Ok(_) => panic!("parsing should have failed"),
Err(_) => {}
};
}
#[test]
fn parse_byte_by_byte() {
/* OK */
let payload = b"\xb3\x07\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00";
let mut hdr = DNSHeader::new();
for b in payload {
assert!(hdr.d.state != DNSHeaderState::End);
hdr.parse(b);
}
assert!(hdr.d.state == DNSHeaderState::End);
assert!(hdr.id == 0xb307);
assert!(hdr.flags == 0x0100);
assert!(hdr._qr == false);
assert!(hdr._opcode == 0);
assert!(hdr._aa == false);
assert!(hdr._tc == false);
assert!(hdr._rd == true);
assert!(hdr._ra == false);
assert!(hdr._z == 0);
assert!(hdr._rcode == 0);
assert!(hdr.qdcount == 1);
assert!(hdr.ancount == 0);
assert!(hdr.nscount == 0);
assert!(hdr.arcount == 0);
assert!(Vec::<u8>::from(&hdr) == payload.to_vec());
/* KO */
let payload = b"\xb3\x07\x01\x00\x00\x01\x00\x00\x00\x00\x00";
let mut hdr = DNSHeader::new();
for b in payload {
hdr.parse(b);
}
assert!(hdr.d.state != DNSHeaderState::End);
}
fn consistency_qd_rr(qd: &DNSHeader, rr: &DNSHeader) {
assert!(rr.id == qd.id);
assert!(rr._qr == true);
assert!(rr._opcode == qd._opcode);
assert!(rr._aa == true);
assert!(rr._tc == false);
assert!(rr._rd == qd._rd);
assert!(rr._ra == false);
assert!(rr._z == 0);
assert!(rr._rcode == 0);
/* check flags */
assert!(rr.flags >> 15 == rr._qr as u16);
assert!((rr.flags >> 11) & 0xF == rr._opcode as u16);
assert!((rr.flags >> 10) & 0x1 == rr._aa as u16);
assert!((rr.flags >> 9) & 0x1 == rr._tc as u16);
assert!((rr.flags >> 8) & 0x1 == rr._rd as u16);
assert!((rr.flags >> 7) & 0x1 == rr._ra as u16);
assert!((rr.flags >> 4) & 0x7 == rr._z as u16);
assert!(rr.flags & 0xF == rr._rcode as u16);
assert!(rr.qdcount == qd.qdcount);
assert!(rr.ancount == qd.qdcount);
assert!(rr.nscount == 0);
assert!(rr.arcount == 0);
}
#[test]
fn repl_id() {
let masscanned = Masscanned {
synack_key: [0, 0],
mac: MacAddr::from_str("00:00:00:00:00:00").expect("error parsing default MAC address"),
iface: None,
ip_addresses: None,
log: MetaLogger::new(),
};
let client_info = ClientInfo::new();
let mut hdr = DNSHeader::new();
hdr._qr = false;
for id in [0x1234, 0x4321, 0xffff, 0x0, 0x1337] {
hdr.id = id;
let hdr_repl = if let Some(r) = hdr.repl(&masscanned, &client_info, None) {
DNSHeader::try_from(r).unwrap()
} else {
panic!("expected DNS header answer, got None");
};
consistency_qd_rr(&hdr, &hdr_repl);
}
}
#[test]
fn repl_opcode() {
let masscanned = Masscanned {
synack_key: [0, 0],
mac: MacAddr::from_str("00:00:00:00:00:00").expect("error parsing default MAC address"),
iface: None,
ip_addresses: None,
log: MetaLogger::new(),
};
let client_info = ClientInfo::new();
let mut hdr = DNSHeader::new();
hdr._qr = false;
/* opcode */
for opcode in 0..3 {
hdr._opcode = opcode;
let hdr_repl = if let Some(r) = hdr.repl(&masscanned, &client_info, None) {
DNSHeader::try_from(r).unwrap()
} else {
panic!("expected DNS header answer, got None");
};
consistency_qd_rr(&hdr, &hdr_repl);
}
}
#[test]
fn repl_rd() {
let masscanned = Masscanned {
synack_key: [0, 0],
mac: MacAddr::from_str("00:00:00:00:00:00").expect("error parsing default MAC address"),
iface: None,
ip_addresses: None,
log: MetaLogger::new(),
};
let client_info = ClientInfo::new();
let mut hdr = DNSHeader::new();
hdr._qr = false;
/* rd */
for rd in [false, true] {
hdr._rd = rd;
let hdr_repl = if let Some(r) = hdr.repl(&masscanned, &client_info, None) {
DNSHeader::try_from(r).unwrap()
} else {
panic!("expected DNS header answer, got None");
};
consistency_qd_rr(&hdr, &hdr_repl);
}
}
#[test]
fn repl_ancount() {
let masscanned = Masscanned {
synack_key: [0, 0],
mac: MacAddr::from_str("00:00:00:00:00:00").expect("error parsing default MAC address"),
iface: None,
ip_addresses: None,
log: MetaLogger::new(),
};
let client_info = ClientInfo::new();
let mut hdr = DNSHeader::new();
hdr._qr = false;
/* rd */
for qdcount in 0..16 {
hdr.qdcount = qdcount;
let hdr_repl = if let Some(r) = hdr.repl(&masscanned, &client_info, None) {
DNSHeader::try_from(r).unwrap()
} else {
panic!("expected DNS header answer, got None");
};
consistency_qd_rr(&hdr, &hdr_repl);
}
}
}

687
src/proto/dns/mod.rs Normal file
View file

@ -0,0 +1,687 @@
// This file is part of masscanned.
// Copyright 2022 - The IVRE project
//
// Masscanned is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Masscanned is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Masscanned. If not, see <http://www.gnu.org/licenses/>.
use std::convert::TryFrom;
mod cst;
mod header;
use header::{DNSHeader, DNSHeaderState};
mod query;
use query::{DNSQuery, DNSQueryState};
mod rr;
use rr::{DNSRRState, DNSRR};
use crate::proto::dissector::{MPacket, PacketDissector};
use crate::proto::ClientInfo;
use crate::proto::TCPControlBlock;
use crate::Masscanned;
#[derive(PartialEq, Debug)]
enum DNSState {
Header,
Query,
Answer,
Authority,
Additional,
End,
}
pub struct DNSPacket {
d: PacketDissector<DNSState>,
header: DNSHeader,
qd: Vec<DNSQuery>,
rr: Vec<DNSRR>,
ns: Vec<DNSRR>,
ar: Vec<DNSRR>,
}
impl TryFrom<Vec<u8>> for DNSPacket {
type Error = &'static str;
fn try_from(item: Vec<u8>) -> Result<Self, Self::Error> {
let mut dns = DNSPacket::new();
for b in item {
dns.parse(&b);
}
if dns.d.state == DNSState::End {
Ok(dns)
} else {
Err("packet is incomplete")
}
}
}
impl From<&DNSPacket> for Vec<u8> {
fn from(item: &DNSPacket) -> Self {
let mut v = Vec::new();
v.extend(Vec::<u8>::from(&item.header));
for qd in &item.qd {
v.extend(Vec::<u8>::from(qd));
}
for rr in &item.rr {
v.extend(Vec::<u8>::from(rr));
}
for ns in &item.ns {
v.extend(Vec::<u8>::from(ns));
}
for ar in &item.ar {
v.extend(Vec::<u8>::from(ar));
}
v
}
}
impl MPacket for DNSPacket {
fn new() -> Self {
DNSPacket {
d: PacketDissector::new(DNSState::Header),
header: DNSHeader::new(),
qd: Vec::new(),
rr: Vec::new(),
ns: Vec::new(),
ar: Vec::new(),
}
}
fn parse(&mut self, byte: &u8) {
match self.d.state {
DNSState::Header => {
self.header.parse(byte);
if self.header.d.state == DNSHeaderState::End {
if self.header.qdcount > 0 {
self.qd.push(DNSQuery::new());
self.d.next_state(DNSState::Query);
} else if self.header.ancount > 0 {
self.rr.push(DNSRR::new());
self.d.next_state(DNSState::Answer);
} else if self.header.nscount > 0 {
self.d.next_state(DNSState::Authority);
} else if self.header.arcount > 0 {
self.d.next_state(DNSState::Additional);
} else {
self.d.next_state(DNSState::End);
}
}
}
DNSState::Query => {
let qdcount = self.qd.len();
self.qd[qdcount - 1].parse(byte);
if self.qd[qdcount - 1].d.state == DNSQueryState::End {
if self.header.qdcount as usize > self.qd.len() {
self.qd.push(DNSQuery::new());
} else if self.header.ancount > 0 {
self.rr.push(DNSRR::new());
self.d.next_state(DNSState::Answer);
} else if self.header.nscount > 0 {
self.d.next_state(DNSState::Authority);
} else if self.header.arcount > 0 {
self.d.next_state(DNSState::Additional);
} else {
self.d.next_state(DNSState::End);
}
}
}
DNSState::Answer => {
let ancount = self.rr.len();
self.rr[ancount - 1].parse(byte);
if self.rr[ancount - 1].d.state == DNSRRState::End {
if self.header.ancount as usize > self.rr.len() {
self.rr.push(DNSRR::new());
} else if self.header.nscount > 0 {
self.d.next_state(DNSState::Authority);
} else if self.header.arcount > 0 {
self.d.next_state(DNSState::Additional);
} else {
self.d.next_state(DNSState::End);
}
}
}
_ => {}
}
}
fn repl(
&self,
masscanned: &Masscanned,
client_info: &ClientInfo,
_tcb: Option<&mut TCPControlBlock>,
) -> Option<Vec<u8>> {
let mut ans = DNSPacket::new();
ans.header = if let Some(hdr) = self.header.repl(&masscanned, &client_info, None) {
if let Ok(h) = DNSHeader::try_from(hdr) {
h
} else {
return None;
}
} else {
return None;
};
/* reply to qd */
for qd in &self.qd {
if let Ok(q) = DNSQuery::try_from(Vec::<u8>::from(qd)) {
ans.qd.push(q);
} else {
return None;
}
if let Some(raw_rr) = qd.repl(&masscanned, &client_info, None) {
if let Ok(rr) = DNSRR::try_from(raw_rr) {
ans.rr.push(rr);
} else {
return None;
}
} else {
return None;
}
}
Some(Vec::<u8>::from(&ans))
}
}
#[cfg(test)]
mod tests {
use super::cst::{DNSClass, DNSType};
use super::*;
use pnet::util::MacAddr;
use std::net::{IpAddr, Ipv4Addr};
use std::str::FromStr;
use crate::logger::MetaLogger;
#[test]
fn parse_qd_all() {
/* OK */
/* scapy: DNS(id=0x1337,
* qd=DNSQR(qname="www.example1.com")/DNSQR(qname="www.example2.com")/DNSQR(qname="www.example3.com"))
**/
let payload = b"\x137\x01\x00\x00\x03\x00\x00\x00\x00\x00\x00\x03www\x08example1\x03com\x00\x00\x01\x00\x01\x03www\x08example2\x03com\x00\x00\x01\x00\x01\x03www\x08example3\x03com\x00\x00\x01\x00\x01";
let dns = match DNSPacket::try_from(payload.to_vec()) {
Ok(_dns) => _dns,
Err(e) => panic!("error while parsing DNS packet: {}", e),
};
assert!(dns.header.id == 0x1337);
assert!(dns.header._qr == false);
assert!(dns.header._opcode == 0);
assert!(dns.header._aa == false);
assert!(dns.header._tc == false);
assert!(dns.header._rd == true);
assert!(dns.header._ra == false);
assert!(dns.header._z == 0);
assert!(dns.header._rcode == 0);
assert!(dns.header.qdcount == 3);
assert!(dns.header.ancount == 0);
assert!(dns.header.nscount == 0);
assert!(dns.header.arcount == 0);
assert!(dns.qd.len() == 3);
assert!(
dns.qd[0].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x31,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.qd[0].type_ == DNSType::A);
assert!(dns.qd[0].class == DNSClass::IN);
assert!(
dns.qd[1].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x32,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.qd[1].type_ == DNSType::A);
assert!(dns.qd[1].class == DNSClass::IN);
assert!(
dns.qd[2].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x33,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.qd[2].type_ == DNSType::A);
assert!(dns.qd[2].class == DNSClass::IN);
/* KO */
let payload = b"\x137\x01\x00\x00\x03\x00\x00\x00\x00\x00\x00\x03www\x08example1\x03com\x00\x00\x01\x00\x01\x03www\x08example2\x03com\x00\x00\x01\x00\x01\x03www\x08example3\x03com\x00\x00\x01\x00";
match DNSPacket::try_from(payload.to_vec()) {
Ok(_) => panic!("parsing should have failed"),
Err(_) => {}
}
let payload = b"xxx";
match DNSPacket::try_from(payload.to_vec()) {
Ok(_) => panic!("parsing should have failed"),
Err(_) => {}
}
}
#[test]
fn parse_qd_byte_by_byte() {
/* scapy: DNS(id=0x1337,
* qd=DNSQR(qname="www.example1.com")/DNSQR(qname="www.example2.com")/DNSQR(qname="www.example3.com"))
**/
let payload = b"\x137\x01\x00\x00\x03\x00\x00\x00\x00\x00\x00\x03www\x08example1\x03com\x00\x00\x01\x00\x01\x03www\x08example2\x03com\x00\x00\x01\x00\x01\x03www\x08example3\x03com\x00\x00\x01\x00\x01";
let mut dns = DNSPacket::new();
for b in payload {
assert!(dns.d.state != DNSState::End);
dns.parse(&b);
}
assert!(dns.d.state == DNSState::End);
assert!(dns.header.id == 0x1337);
assert!(dns.header._qr == false);
assert!(dns.header._opcode == 0);
assert!(dns.header._aa == false);
assert!(dns.header._tc == false);
assert!(dns.header._rd == true);
assert!(dns.header._ra == false);
assert!(dns.header._z == 0);
assert!(dns.header._rcode == 0);
assert!(dns.header.qdcount == 3);
assert!(dns.header.ancount == 0);
assert!(dns.header.nscount == 0);
assert!(dns.header.arcount == 0);
assert!(dns.qd.len() == 3);
assert!(
dns.qd[0].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x31,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.qd[0].type_ == DNSType::A);
assert!(dns.qd[0].class == DNSClass::IN);
assert!(
dns.qd[1].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x32,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.qd[1].type_ == DNSType::A);
assert!(dns.qd[1].class == DNSClass::IN);
assert!(
dns.qd[2].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x33,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.qd[2].type_ == DNSType::A);
assert!(dns.qd[2].class == DNSClass::IN);
}
#[test]
fn parse_rr_all() {
/* OK */
/* scapy: DNS(id=1234, qr=True, aa=True, qd=None,
* an=DNSRR(rrname="www.example1.com", rdata="127.0.0.1")/DNSRR(rrname="www.example2.com", rdata="127.0.0.2")/DNSRR(rrname="www.example3.com", rdata="127.0.0.3"))
**/
let payload = b"\x04\xd2\x85\x00\x00\x00\x00\x03\x00\x00\x00\x00\x03www\x08example1\x03com\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x7f\x00\x00\x01\x03www\x08example2\x03com\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x7f\x00\x00\x02\x03www\x08example3\x03com\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x7f\x00\x00\x03";
let dns = match DNSPacket::try_from(payload.to_vec()) {
Ok(_dns) => _dns,
Err(e) => panic!("error while parsing DNS packet: {}", e),
};
assert!(dns.header.id == 1234);
assert!(dns.header._qr == true);
assert!(dns.header._opcode == 0);
assert!(dns.header._aa == true);
assert!(dns.header._tc == false);
assert!(dns.header._rd == true);
assert!(dns.header._ra == false);
assert!(dns.header._z == 0);
assert!(dns.header._rcode == 0);
assert!(dns.header.qdcount == 0);
assert!(dns.header.ancount == 3);
assert!(dns.header.nscount == 0);
assert!(dns.header.arcount == 0);
assert!(dns.rr.len() == 3);
assert!(
dns.rr[0].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x31,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.rr[0].type_ == DNSType::A);
assert!(dns.rr[0].class == DNSClass::IN);
assert!(dns.rr[0].rdata == [0x7f, 0x00, 0x00, 0x01]);
assert!(
dns.rr[1].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x32,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.rr[1].type_ == DNSType::A);
assert!(dns.rr[1].class == DNSClass::IN);
assert!(dns.rr[1].rdata == [0x7f, 0x00, 0x00, 0x02]);
assert!(
dns.rr[2].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x33,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.rr[2].type_ == DNSType::A);
assert!(dns.rr[2].class == DNSClass::IN);
assert!(dns.rr[2].rdata == [0x7f, 0x00, 0x00, 0x03]);
/* KO */
let payload = b"\x04\xd2\x85\x00\x00\x00\x00\x04\x00\x00\x00\x00\x03www\x08example1\x03com\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x7f\x00\x00\x01\x03www\x08example2\x03com\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x7f\x00\x00\x02\x03www\x08example3\x03com\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x7f\x00\x00\x03";
match DNSPacket::try_from(payload.to_vec()) {
Ok(_) => panic!("parsing should have failed"),
Err(_) => {}
}
let payload = b"xxx";
match DNSPacket::try_from(payload.to_vec()) {
Ok(_) => panic!("parsing should have failed"),
Err(_) => {}
}
}
#[test]
fn parse_rr_byte_by_byte() {
/* scapy: DNS(id=1234, qr=True, aa=True, qd=None,
* an=DNSRR(rrname="www.example1.com", rdata="127.0.0.1")/DNSRR(rrname="www.example2.com", rdata="127.0.0.2")/DNSRR(rrname="www.example3.com", rdata="127.0.0.3"))
**/
let payload = b"\x04\xd2\x85\x00\x00\x00\x00\x03\x00\x00\x00\x00\x03www\x08example1\x03com\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x7f\x00\x00\x01\x03www\x08example2\x03com\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x7f\x00\x00\x02\x03www\x08example3\x03com\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x7f\x00\x00\x03";
let mut dns = DNSPacket::new();
for b in payload {
assert!(dns.d.state != DNSState::End);
dns.parse(&b);
}
assert!(dns.d.state == DNSState::End);
assert!(dns.header.id == 1234);
assert!(dns.header._qr == true);
assert!(dns.header._opcode == 0);
assert!(dns.header._aa == true);
assert!(dns.header._tc == false);
assert!(dns.header._rd == true);
assert!(dns.header._ra == false);
assert!(dns.header._z == 0);
assert!(dns.header._rcode == 0);
assert!(dns.header.qdcount == 0);
assert!(dns.header.ancount == 3);
assert!(dns.header.nscount == 0);
assert!(dns.header.arcount == 0);
assert!(dns.rr.len() == 3);
assert!(
dns.rr[0].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x31,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.rr[0].type_ == DNSType::A);
assert!(dns.rr[0].class == DNSClass::IN);
assert!(dns.rr[0].rdata == [0x7f, 0x00, 0x00, 0x01]);
assert!(
dns.rr[1].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x32,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.rr[1].type_ == DNSType::A);
assert!(dns.rr[1].class == DNSClass::IN);
assert!(dns.rr[1].rdata == [0x7f, 0x00, 0x00, 0x02]);
assert!(
dns.rr[2].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x33,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.rr[2].type_ == DNSType::A);
assert!(dns.rr[2].class == DNSClass::IN);
assert!(dns.rr[2].rdata == [0x7f, 0x00, 0x00, 0x03]);
}
#[test]
fn parse_qd_rr_all() {
/* scapy: DNS(id=1234, qr=True, aa=True,
* qd=DNSQR(qname="www.example1.com")/DNSQR(qname="www.example2.com")/DNSQR(qname="www.example3.com"),
* an=DNSRR(rrname="www.example1.com", rdata="127.0.0.1")/DNSRR(rrname="www.example2.com", rdata="127.0.0.2")/DNSRR(rrname="www.example3.com", rdata="127.0.0.3"))
*/
let payload = b"\x04\xd2\x85\x00\x00\x03\x00\x03\x00\x00\x00\x00\x03www\x08example1\x03com\x00\x00\x01\x00\x01\x03www\x08example2\x03com\x00\x00\x01\x00\x01\x03www\x08example3\x03com\x00\x00\x01\x00\x01\x03www\x08example1\x03com\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x7f\x00\x00\x01\x03www\x08example2\x03com\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x7f\x00\x00\x02\x03www\x08example3\x03com\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x7f\x00\x00\x03";
let dns = match DNSPacket::try_from(payload.to_vec()) {
Ok(_dns) => _dns,
Err(e) => panic!("error while parsing DNS packet: {}", e),
};
assert!(dns.header.id == 1234);
assert!(dns.header._qr == true);
assert!(dns.header._opcode == 0);
assert!(dns.header._aa == true);
assert!(dns.header._tc == false);
assert!(dns.header._rd == true);
assert!(dns.header._ra == false);
assert!(dns.header._z == 0);
assert!(dns.header._rcode == 0);
assert!(dns.header.qdcount == 3);
assert!(dns.header.ancount == 3);
assert!(dns.header.nscount == 0);
assert!(dns.header.arcount == 0);
assert!(dns.qd.len() == 3);
assert!(
dns.qd[0].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x31,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.qd[0].type_ == DNSType::A);
assert!(dns.qd[0].class == DNSClass::IN);
assert!(
dns.qd[1].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x32,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.qd[1].type_ == DNSType::A);
assert!(dns.qd[1].class == DNSClass::IN);
assert!(
dns.qd[2].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x33,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.qd[2].type_ == DNSType::A);
assert!(dns.qd[2].class == DNSClass::IN);
assert!(dns.rr.len() == 3);
assert!(
dns.rr[0].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x31,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.rr[0].type_ == DNSType::A);
assert!(dns.rr[0].class == DNSClass::IN);
assert!(dns.rr[0].rdata == [0x7f, 0x00, 0x00, 0x01]);
assert!(
dns.rr[1].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x32,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.rr[1].type_ == DNSType::A);
assert!(dns.rr[1].class == DNSClass::IN);
assert!(dns.rr[1].rdata == [0x7f, 0x00, 0x00, 0x02]);
assert!(
dns.rr[2].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x33,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.rr[2].type_ == DNSType::A);
assert!(dns.rr[2].class == DNSClass::IN);
assert!(dns.rr[2].rdata == [0x7f, 0x00, 0x00, 0x03]);
}
#[test]
fn parse_qr_rr_byte_by_byte() {
/* scapy: DNS(id=1234, qr=True, aa=True,
* qd=DNSQR(qname="www.example1.com")/DNSQR(qname="www.example2.com")/DNSQR(qname="www.example3.com"),
* an=DNSRR(rrname="www.example1.com", rdata="127.0.0.1")/DNSRR(rrname="www.example2.com", rdata="127.0.0.2")/DNSRR(rrname="www.example3.com", rdata="127.0.0.3"))
*/
let payload = b"\x04\xd2\x85\x00\x00\x03\x00\x03\x00\x00\x00\x00\x03www\x08example1\x03com\x00\x00\x01\x00\x01\x03www\x08example2\x03com\x00\x00\x01\x00\x01\x03www\x08example3\x03com\x00\x00\x01\x00\x01\x03www\x08example1\x03com\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x7f\x00\x00\x01\x03www\x08example2\x03com\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x7f\x00\x00\x02\x03www\x08example3\x03com\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x7f\x00\x00\x03";
let mut dns = DNSPacket::new();
for b in payload {
assert!(dns.d.state != DNSState::End);
dns.parse(&b);
}
assert!(dns.d.state == DNSState::End);
assert!(dns.header.id == 1234);
assert!(dns.header._qr == true);
assert!(dns.header._opcode == 0);
assert!(dns.header._aa == true);
assert!(dns.header._tc == false);
assert!(dns.header._rd == true);
assert!(dns.header._ra == false);
assert!(dns.header._z == 0);
assert!(dns.header._rcode == 0);
assert!(dns.header.qdcount == 3);
assert!(dns.header.ancount == 3);
assert!(dns.header.nscount == 0);
assert!(dns.header.arcount == 0);
assert!(dns.qd.len() == 3);
assert!(
dns.qd[0].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x31,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.qd[0].type_ == DNSType::A);
assert!(dns.qd[0].class == DNSClass::IN);
assert!(
dns.qd[1].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x32,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.qd[1].type_ == DNSType::A);
assert!(dns.qd[1].class == DNSClass::IN);
assert!(
dns.qd[2].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x33,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.qd[2].type_ == DNSType::A);
assert!(dns.qd[2].class == DNSClass::IN);
assert!(dns.rr.len() == 3);
assert!(
dns.rr[0].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x31,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.rr[0].type_ == DNSType::A);
assert!(dns.rr[0].class == DNSClass::IN);
assert!(dns.rr[0].rdata == [0x7f, 0x00, 0x00, 0x01]);
assert!(
dns.rr[1].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x32,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.rr[1].type_ == DNSType::A);
assert!(dns.rr[1].class == DNSClass::IN);
assert!(dns.rr[1].rdata == [0x7f, 0x00, 0x00, 0x02]);
assert!(
dns.rr[2].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x33,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.rr[2].type_ == DNSType::A);
assert!(dns.rr[2].class == DNSClass::IN);
assert!(dns.rr[2].rdata == [0x7f, 0x00, 0x00, 0x03]);
}
#[test]
fn reply_in_a() {
let masscanned = Masscanned {
synack_key: [0, 0],
mac: MacAddr::from_str("00:00:00:00:00:00").expect("error parsing default MAC address"),
iface: None,
ip_addresses: None,
log: MetaLogger::new(),
};
let mut client_info = ClientInfo::new();
/* scapy: DNS(id=0x1337,
* qd=DNSQR(qname="www.example.com"))
**/
let payload = b"\x137\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03www\x07example\x03com\x00\x00\x01\x00\x01";
let dns = DNSPacket::try_from(payload.to_vec()).unwrap();
for ip in [
Ipv4Addr::new(127, 0, 0, 1),
Ipv4Addr::new(0, 0, 0, 0),
Ipv4Addr::new(4, 3, 2, 1),
] {
client_info.ip.dst = Some(IpAddr::V4(ip));
let ans = if let Some(a) = dns.repl(&masscanned, &client_info, None) {
DNSPacket::try_from(a).unwrap()
} else {
panic!("expected a reply, got None");
};
assert!(ans.header.id == 0x1337);
assert!(ans.header._qr == true);
assert!(ans.header._opcode == 0);
assert!(ans.header._aa == true);
assert!(ans.header._tc == false);
assert!(ans.header._rd == dns.header._rd);
assert!(ans.header._ra == false);
assert!(ans.header._z == 0);
assert!(ans.header._rcode == 0);
assert!(ans.header.qdcount == 1);
assert!(ans.header.ancount == 1);
assert!(ans.header.nscount == 0);
assert!(ans.header.arcount == 0);
assert!(ans.qd.len() == 1);
assert!(
ans.qd[0].name
== [
0x03, 0x77, 0x77, 0x77, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(ans.qd[0].type_ == DNSType::A);
assert!(ans.qd[0].class == DNSClass::IN);
assert!(ans.rr.len() == 1);
assert!(
ans.rr[0].name
== [
0x03, 0x77, 0x77, 0x77, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(ans.rr[0].type_ == DNSType::A);
assert!(ans.rr[0].class == DNSClass::IN);
assert!(ans.rr[0].rdata == ip.octets());
}
}
}

335
src/proto/dns/query.rs Normal file
View file

@ -0,0 +1,335 @@
// This file is part of masscanned.
// Copyright 2022 - The IVRE project
//
// Masscanned is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Masscanned is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Masscanned. If not, see <http://www.gnu.org/licenses/>.
use super::cst::{DNSClass, DNSType};
use super::rr::DNSRR;
use std::convert::TryFrom;
use std::net::IpAddr;
use crate::proto::dissector::{MPacket, PacketDissector};
use crate::proto::ClientInfo;
use crate::proto::TCPControlBlock;
use crate::Masscanned;
#[derive(PartialEq)]
pub enum DNSQueryState {
Name,
Type,
Class,
End,
}
pub struct DNSQuery {
pub d: PacketDissector<DNSQueryState>,
/* RFC 1035 - Section 4.1.2 */
pub name: Vec<u8>,
_u_type: u16,
pub type_: DNSType,
_u_class: u16,
pub class: DNSClass,
}
impl TryFrom<Vec<u8>> for DNSQuery {
type Error = &'static str;
fn try_from(item: Vec<u8>) -> Result<Self, Self::Error> {
let mut query = DNSQuery::new();
for b in item {
query.parse(&b);
}
if query.d.state == DNSQueryState::End {
Ok(query)
} else {
Err("packet is incomplete")
}
}
}
impl From<&DNSQuery> for Vec<u8> {
fn from(item: &DNSQuery) -> Self {
let mut v = Vec::new();
/* name */
v.extend(&item.name);
/* type */
v.push(((u16::from(item.type_)) >> 8) as u8);
v.push(((u16::from(item.type_)) & 0xFF) as u8);
/* class */
v.push(((u16::from(item.class)) >> 8) as u8);
v.push(((u16::from(item.class)) & 0xFF) as u8);
/* return */
v
}
}
impl MPacket for DNSQuery {
fn new() -> Self {
DNSQuery {
d: PacketDissector::new(DNSQueryState::Name),
name: Vec::new(),
_u_type: 0,
type_: DNSType::NONE,
_u_class: 0,
class: DNSClass::NONE,
}
}
fn parse(&mut self, byte: &u8) {
match self.d.state {
DNSQueryState::Name => {
self.name.push(*byte);
if *byte == 0 {
self.d.next_state(DNSQueryState::Type);
}
}
DNSQueryState::Type => {
self._u_type = self.d.read_u16(byte, self._u_type, DNSQueryState::Class);
}
DNSQueryState::Class => {
self._u_class = self.d.read_u16(byte, self._u_class, DNSQueryState::End);
}
DNSQueryState::End => {}
}
/* we need this to be executed at the same call
* the state changes to End, hence it is not in the
* match structure
**/
if self.d.state == DNSQueryState::End {
self.type_ = DNSType::from(self._u_type);
self.class = DNSClass::from(self._u_class);
}
}
fn repl(
&self,
_masscanned: &Masscanned,
client_info: &ClientInfo,
_tcb: Option<&mut TCPControlBlock>,
) -> Option<Vec<u8>> {
match self.class {
DNSClass::IN => {
match self.type_ {
DNSType::A => {
let mut rr = DNSRR::new();
/* copy request */
for b in &self.name {
rr.name.push(*b);
}
rr.type_ = DNSType::A;
rr.class = DNSClass::IN;
rr.ttl = 43200;
rr.rdata = match client_info.ip.dst {
Some(IpAddr::V4(ip)) => ip.octets().to_vec(),
Some(IpAddr::V6(_)) => Vec::new(),
None => Vec::new(),
};
rr.rdlen = rr.rdata.len() as u16;
Some(Vec::<u8>::from(&rr))
}
_ => None,
}
}
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use pnet::util::MacAddr;
use std::net::{IpAddr, Ipv4Addr};
use std::str::FromStr;
use strum::IntoEnumIterator;
use crate::client::ClientInfoSrcDst;
use crate::logger::MetaLogger;
#[test]
fn parse_in_a_all() {
/* A */
let payload = b"\x03www\x07example\x03com\x00\x00\x01\x00\x01";
let qr = match DNSQuery::try_from(payload.to_vec()) {
Ok(_qr) => _qr,
Err(e) => panic!("error while parsing DNS query: {}", e),
};
assert!(
qr.name
== [
0x03, 0x77, 0x77, 0x77, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x03,
0x63, 0x6f, 0x6d, 0x00
]
);
assert!(qr.type_ == DNSType::A);
assert!(qr.class == DNSClass::IN);
assert!(Vec::<u8>::from(&qr) == payload.to_vec());
/* TXT */
let payload = b"\x07version\x04bind\x00\x00\x10\x00\x03";
let qr = match DNSQuery::try_from(payload.to_vec()) {
Ok(_qr) => _qr,
Err(e) => panic!("error while parsing DNS query: {}", e),
};
assert!(qr.type_ == DNSType::TXT);
assert!(qr.class == DNSClass::CH);
assert!(Vec::<u8>::from(&qr) == payload.to_vec());
/* KO */
let payload = b"xxx";
match DNSQuery::try_from(payload.to_vec()) {
Ok(_) => panic!("parsing should have failed"),
Err(_) => {}
}
}
#[test]
fn parse_in_a_byte_by_byte() {
/* A */
let payload = b"\x03www\x07example\x03com\x00\x00\x01\x00\x01";
let mut qr = DNSQuery::new();
for b in payload {
qr.parse(b);
}
assert!(qr.d.state == DNSQueryState::End);
assert!(
qr.name
== [
0x03, 0x77, 0x77, 0x77, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x03,
0x63, 0x6f, 0x6d, 0x00
]
);
assert!(qr.type_ == DNSType::A);
assert!(qr.class == DNSClass::IN);
assert!(Vec::<u8>::from(&qr) == payload.to_vec());
/* TXT */
let payload = b"\x07version\x04bind\x00\x00\x10\x00\x03";
let mut qr = DNSQuery::new();
for b in payload {
qr.parse(b);
}
assert!(qr.d.state == DNSQueryState::End);
assert!(qr.type_ == DNSType::TXT);
assert!(qr.class == DNSClass::CH);
assert!(Vec::<u8>::from(&qr) == payload.to_vec());
/* KO */
let payload = b"xxx";
let mut qr = DNSQuery::new();
for b in payload {
qr.parse(b);
}
assert!(qr.d.state != DNSQueryState::End);
}
#[test]
fn reply_in_a() {
let masscanned = Masscanned {
synack_key: [0, 0],
mac: MacAddr::from_str("00:00:00:00:00:00").expect("error parsing default MAC address"),
iface: None,
ip_addresses: None,
log: MetaLogger::new(),
};
let ip_src = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
let ip_dst = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2));
let client_info = ClientInfo {
mac: ClientInfoSrcDst {
src: None,
dst: None,
},
ip: ClientInfoSrcDst {
src: Some(ip_src),
dst: Some(ip_dst),
},
transport: None,
port: ClientInfoSrcDst {
src: None,
dst: None,
},
cookie: None,
};
/* TXT */
let payload = b"\x07version\x04bind\x00\x00\x10\x00\x03";
let mut qr = DNSQuery::new();
for b in payload {
qr.parse(b);
}
assert!(qr.type_ == DNSType::TXT);
assert!(qr.class == DNSClass::CH);
/* A */
let payload = b"\x03www\x07example\x03com\x00\x00\x01\x00\x01";
let mut qr = DNSQuery::new();
for b in payload {
qr.parse(b);
}
assert!(
qr.name
== [
0x03, 0x77, 0x77, 0x77, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x03,
0x63, 0x6f, 0x6d, 0x00
]
);
assert!(qr.type_ == DNSType::A);
assert!(qr.class == DNSClass::IN);
let rr_raw = match qr.repl(&masscanned, &client_info, None) {
None => {
panic!()
}
Some(r) => r,
};
let mut rr = DNSRR::new();
for b in rr_raw {
rr.parse(&b);
}
assert!(rr.name == qr.name);
assert!(rr.type_ == DNSType::A);
assert!(rr.class == DNSClass::IN);
assert!(rr.ttl == 43200);
assert!(rr.rdata == [127, 0, 0, 2]);
}
#[test]
fn repl() {
let masscanned = Masscanned {
synack_key: [0, 0],
mac: MacAddr::from_str("00:00:00:00:00:00").expect("error parsing default MAC address"),
iface: None,
ip_addresses: None,
log: MetaLogger::new(),
};
let client_info = ClientInfo::new();
/* exhaustive tests */
let supported: Vec<(DNSClass, DNSType)> = vec![(DNSClass::IN, DNSType::A)];
let mut qd = DNSQuery::new();
qd.name = vec![
0x03, 0x77, 0x77, 0x77, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x03, 0x63,
0x6f, 0x6d, 0x00,
];
for c in DNSClass::iter() {
qd.class = c;
for t in DNSType::iter() {
qd.type_ = t;
if supported.contains(&(c, t)) {
if qd.repl(&masscanned, &client_info, None) == None {
panic!("expected reply, got None");
}
} else {
if qd.repl(&masscanned, &client_info, None) != None {
panic!("expected no reply, got one for {:?}, {:?}", c, t);
}
}
}
}
}
}

251
src/proto/dns/rr.rs Normal file
View file

@ -0,0 +1,251 @@
// This file is part of masscanned.
// Copyright 2022 - The IVRE project
//
// Masscanned is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Masscanned is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Masscanned. If not, see <http://www.gnu.org/licenses/>.
use super::cst::{DNSClass, DNSType};
use std::convert::TryFrom;
use crate::proto::dissector::{MPacket, PacketDissector};
use crate::proto::ClientInfo;
use crate::proto::TCPControlBlock;
use crate::Masscanned;
#[derive(PartialEq, Debug)]
pub enum DNSRRState {
Name,
Type,
Class,
TTL,
RDLength,
RData,
End,
}
pub struct DNSRR {
pub d: PacketDissector<DNSRRState>,
/* RFC 1035 - Section 3.2.1 */
pub name: Vec<u8>,
_u_type: u16,
pub type_: DNSType,
_u_class: u16,
pub class: DNSClass,
pub ttl: u32,
pub rdlen: u16,
pub rdata: Vec<u8>,
}
impl From<&DNSRR> for Vec<u8> {
fn from(item: &DNSRR) -> Self {
/* CAUTION: for the rdlen field:
* - if item.rdlen is not 0, its value is packed
* - if item.rdlen = 0, then the length of item.rdata is used instead
*/
let mut v = Vec::new();
/* name */
for b in &item.name {
v.push(b.clone());
}
/* type */
let type_: u16 = item.type_.into();
v.push((type_ >> 8) as u8);
v.push((type_ & 0xFF) as u8);
/* class */
let class: u16 = item.class.into();
v.push((class >> 8) as u8);
v.push((class & 0xFF) as u8);
/* ttl */
v.push((item.ttl >> 24) as u8);
v.push((item.ttl >> 16) as u8);
v.push((item.ttl >> 8) as u8);
v.push((item.ttl & 0xFF) as u8);
/* rdlen */
let rdlen = if item.rdlen == 0 {
item.rdata.len() as u16
} else {
item.rdlen
};
v.push((rdlen >> 8) as u8);
v.push((rdlen & 0xFF) as u8);
/* rdata */
for b in &item.rdata {
v.push(b.clone());
}
v
}
}
impl TryFrom<Vec<u8>> for DNSRR {
type Error = &'static str;
fn try_from(item: Vec<u8>) -> Result<Self, Self::Error> {
let mut rr = DNSRR::new();
for b in item {
rr.parse(&b);
}
if rr.d.state == DNSRRState::End {
Ok(rr)
} else {
Err("packet is incomplete")
}
}
}
impl MPacket for DNSRR {
fn new() -> Self {
DNSRR {
d: PacketDissector::new(DNSRRState::Name),
name: Vec::new(),
_u_type: 0,
type_: DNSType::NONE,
_u_class: 0,
class: DNSClass::NONE,
rdlen: 0,
ttl: 0,
rdata: Vec::new(),
}
}
fn parse(&mut self, byte: &u8) {
match self.d.state {
DNSRRState::Name => {
self.name.push(*byte);
if *byte == 0 {
self.d.next_state(DNSRRState::Type);
}
}
DNSRRState::Type => {
self._u_type = self.d.read_u16(byte, self._u_type, DNSRRState::Class);
}
DNSRRState::Class => {
self._u_class = self.d.read_u16(byte, self._u_class, DNSRRState::TTL);
}
DNSRRState::TTL => {
self.ttl = self.d.read_u32(byte, self.ttl, DNSRRState::RDLength);
}
DNSRRState::RDLength => {
self.rdlen = self.d.read_u16(byte, self.rdlen, DNSRRState::RData);
/* when read the rdlen, check if len is 0 */
if self.d.state == DNSRRState::RData && self.rdlen == 0 {
self.d.state = DNSRRState::End;
}
}
DNSRRState::RData => {
self.rdata.push(*byte);
if self.rdata.len() == self.rdlen as usize {
self.d.next_state(DNSRRState::End);
}
}
DNSRRState::End => {}
}
/* we need this to be executed at the same call
* the state changes to End, hence it is not in the
* match structure
**/
if self.d.state == DNSRRState::End {
self.type_ = DNSType::from(self._u_type);
self.class = DNSClass::from(self._u_class);
}
}
fn repl(
&self,
_masscanned: &Masscanned,
_client_info: &ClientInfo,
_tcb: Option<&mut TCPControlBlock>,
) -> Option<Vec<u8>> {
None
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn build() {
let mut rr = DNSRR::new();
rr.name = b"\x03www\x07example\x03com\x00".to_vec();
rr.class = DNSClass::IN;
rr.type_ = DNSType::A;
rr.ttl = 1234;
rr.rdlen = 4;
rr.rdata = b"\x7f\x00\x00\x01".to_vec();
assert!(Vec::<u8>::from(&rr) == b"\x03www\x07example\x03com\x00\x00\x01\x00\x01\x00\x00\x04\xd2\x00\x04\x7f\x00\x00\x01");
}
#[test]
fn parse_all() {
/*
* raw(DNSRR(rrname="www.example.com", rdata="127.0.0.1"))
*/
let payload = b"\x03www\x07example\x03com\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x7f\x00\x00\x01";
let rr = match DNSRR::try_from(payload.to_vec()) {
Ok(r) => r,
Err(e) => panic!("error while parsing DNS RR: {}", e),
};
assert!(rr.name == b"\x03www\x07example\x03com\x00");
assert!(rr.class == DNSClass::IN);
assert!(rr.type_ == DNSType::A);
assert!(rr.rdata == b"\x7f\x00\x00\x01");
assert!(Vec::<u8>::from(&rr) == payload.to_vec());
/*
* empty data
*/
let payload = b"\x03www\x07example\x03com\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00";
let rr = match DNSRR::try_from(payload.to_vec()) {
Ok(r) => r,
Err(e) => panic!("error while parsing DNS RR: {}", e),
};
assert!(rr.name == b"\x03www\x07example\x03com\x00");
assert!(rr.class == DNSClass::IN);
assert!(rr.type_ == DNSType::A);
assert!(rr.rdata == b"");
assert!(Vec::<u8>::from(&rr) == payload.to_vec());
}
#[test]
fn parse_byte_by_byte() {
/*
* raw(DNSRR(rrname="www.example.com", rdata="127.0.0.1"))
*/
let payload = b"\x03www\x07example\x03com\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x7f\x00\x00\x01";
let mut rr = DNSRR::new();
for b in payload {
assert!(rr.d.state != DNSRRState::End);
rr.parse(b);
}
assert!(rr.d.state == DNSRRState::End);
assert!(rr.name == b"\x03www\x07example\x03com\x00");
assert!(rr.class == DNSClass::IN);
assert!(rr.type_ == DNSType::A);
assert!(rr.rdata == b"\x7f\x00\x00\x01");
assert!(Vec::<u8>::from(&rr) == payload.to_vec());
/*
* empty data
*/
let payload = b"\x03www\x07example\x03com\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00";
let mut rr = DNSRR::new();
for b in payload {
assert!(rr.d.state != DNSRRState::End);
rr.parse(b);
}
assert!(rr.name == b"\x03www\x07example\x03com\x00");
assert!(rr.class == DNSClass::IN);
assert!(rr.type_ == DNSType::A);
assert!(rr.rdata == b"");
assert!(Vec::<u8>::from(&rr) == payload.to_vec());
}
}