#![allow(dead_code)]
use wsdf::tap::{Field, Offset, Packet};
use wsdf::{version, Dispatch, Protocol, ProtocolField};
version!("0.0.1", 4, 0);
#[derive(Protocol)]
#[wsdf(
proto_desc = "Baby DNS by wsdf",
proto_name = "Baby DNS",
proto_filter = "baby_dns",
decode_from = [("udp.port", 53)],
)]
struct BabyDNS {
identification: u16,
flags: u16,
#[wsdf(rename = "Number of Questions")]
number_of_questions: u16,
#[wsdf(rename = "Number of Answers")]
number_of_answers: u16,
#[wsdf(rename = "Number of Authority RRs")]
number_of_authority_rrs: u16,
#[wsdf(rename = "Number of Additional RRs")]
number_of_additional_rrs: u16,
#[wsdf(len_field = "number_of_questions")]
questions: Vec<Question>,
#[wsdf(len_field = "number_of_answers")]
answers: Vec<ResourceRecord>,
#[wsdf(len_field = "number_of_authority_rrs")]
domain_authority: Vec<ResourceRecord>,
#[wsdf(len_field = "number_of_additional_rrs")]
additional_information: Vec<ResourceRecord>,
}
#[derive(ProtocolField)]
struct Question {
name: CharStr,
#[wsdf(decode_with = "decode_qtype", rename = "Type")]
type_: u16,
#[wsdf(decode_with = "decode_class")]
class: u16,
}
#[derive(ProtocolField)]
struct ResourceRecord {
name: CharStr,
#[wsdf(decode_with = "decode_qtype", rename = "Type")]
type_: u16,
#[wsdf(decode_with = "decode_class")]
class: u16,
#[wsdf(rename = "TTL")]
ttl: Seconds,
#[wsdf(rename = "RR Data Length")]
rdlength: u16,
#[wsdf(dispatch_field = "type_", rename = "RR Data")]
rdata: Rdata,
}
#[derive(ProtocolField, Dispatch)]
enum Rdata {
#[wsdf(rename = "A (Host address)")]
A(#[wsdf(typ = "FT_IPv4", display = "BASE_NETMASK")] u32),
#[wsdf(rename = "NS (Authoritative name server)")]
NS(
CharStr,
),
#[wsdf(rename = "CNAME (Canonical name)")]
Cname(
CharStr,
),
#[wsdf(rename = "SOA (Start of authority zone)")]
Soa {
mname: CharStr,
rname: CharStr,
serial: u32,
refresh: Seconds,
retry: Seconds,
expire: Seconds,
minimum: Seconds,
},
#[wsdf(rename = "PTR (Domain name pointer)")]
Ptr(
CharStr,
),
#[wsdf(rename = "MX (Mail exchange)")]
MX {
preference: u16,
mail_exchanger: CharStr,
},
#[wsdf(rename = "AAAA (IPv6 address)")]
Aaaa(#[wsdf(typ = "FT_IPv6", display = "BASE_NONE")] [u8; 16]),
Unknown(#[wsdf(consume_with = "drain_rdata")] Vec<u8>),
}
impl Rdata {
fn dispatch_type_(type_: &u16) -> RdataDispatch {
use RdataDispatch::*;
match *type_ {
1 => A,
2 => NS,
5 => Cname,
6 => Soa,
12 => Ptr,
15 => MX,
28 => Aaaa,
_ => Unknown, }
}
}
#[derive(ProtocolField)]
struct CharStr(#[wsdf(consume_with = "CharStr::consume")] Vec<u8>);
impl CharStr {
fn consume(Packet(packet): Packet, Offset(offset): Offset) -> (usize, String) {
if offset >= packet.len() {
return (
0,
format!("Unexpected EOF (expected more bytes at offset {})", offset),
);
}
if packet[offset] & 0b11000000 != 0 {
let mut p: u16 = 0;
p += (0b00111111 & packet[offset]) as u16;
p <<= 8;
p |= packet[offset + 1] as u16;
let (_, ret) = Self::consume(Packet(packet), Offset(p as usize));
return (2, ret);
}
let mut i = offset;
let mut ret = String::new();
loop {
if packet[i] & 0b11000000 != 0 {
i += 2;
break;
}
let n = packet[i] as usize;
if n == 0 {
i += 1;
break;
}
let s = std::str::from_utf8(&packet[i + 1..i + 1 + n]).unwrap_or("<invalid utf8>");
ret.push_str(s);
ret.push('.');
i += 1 + n;
}
ret.pop();
(i - offset, ret)
}
}
fn decode_class(Field(x): Field<u16>) -> String {
let s = match x {
1 => "Internet (1)",
3 => "Chaos (3)",
4 => "Hesoid (4)",
_ => return format!("Unknown ({})", x),
};
s.to_string()
}
fn decode_qtype(Field(x): Field<u16>) -> String {
let s = match x {
1 => "A [Host address] (1)",
2 => "NS [Authoritative name server] (2)",
5 => "CNAME [Canonical name for an alias] (5)",
6 => "SOA [Start of a zone of authority] (6)",
12 => "PTR [Domain name pointer] (12)",
15 => "MX [Mail exchange] (15)",
28 => "AAAA [IP6 address] (28)",
_ => return format!("Unknown ({})", x),
};
s.to_string()
}
#[derive(ProtocolField)]
struct Seconds(#[wsdf(decode_with = "Seconds::decode")] u32);
impl Seconds {
fn decode(Field(s): Field<u32>) -> String {
let seconds = s % 60;
let minutes = (s / 60) % 60;
let hours = (s / 60) / 60;
if hours > 0 {
format!("{} hr, {} min, {} sec", hours, minutes, seconds)
} else if minutes > 0 {
format!("{} min, {} sec", minutes, seconds)
} else {
format!("{} sec", seconds)
}
}
}
fn drain_rdata(Packet(packet): Packet, Offset(offset): Offset) -> (usize, &'static str) {
let mut n: u16 = 0;
n += packet[offset - 2] as u16;
n <<= 8;
n += packet[offset - 1] as u16;
(n as usize, "Gave up on decoding (╯°□°)╯︵ ┻━┻")
}