2022-03-15 17:01:17 +01:00
|
|
|
use std::convert::TryInto;
|
|
|
|
|
2022-04-03 18:22:47 +02:00
|
|
|
/// Taken from RFC 6895, 2.2. OpCode Assignment
|
|
|
|
/// Currently, DNS OpCodes are assigned as follows:
|
|
|
|
/// OpCode Name Reference
|
|
|
|
///
|
|
|
|
/// 0 Query [RFC1035]
|
|
|
|
/// 1 IQuery (Inverse Query, OBSOLETE) [RFC3425]
|
|
|
|
/// 2 Status [RFC1035]
|
|
|
|
/// 3 Unassigned
|
|
|
|
/// 4 Notify [RFC1996]
|
|
|
|
/// 5 Update [RFC2136]
|
|
|
|
/// 6-15 Unassigned
|
2022-03-13 21:56:17 +01:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum DNSOpCode {
|
2022-04-03 18:22:47 +02:00
|
|
|
Query = 0,
|
|
|
|
IQuery = 1, // obsolete
|
|
|
|
Status = 2,
|
|
|
|
Notify = 4,
|
|
|
|
Update = 5,
|
2022-03-13 21:56:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
impl From<u16> for DNSOpCode {
|
|
|
|
fn from(val: u16) -> Self {
|
|
|
|
match val {
|
2022-04-03 18:22:47 +02:00
|
|
|
0 => DNSOpCode::Query,
|
|
|
|
1 => DNSOpCode::IQuery,
|
|
|
|
2 => DNSOpCode::Status,
|
|
|
|
4 => DNSOpCode::Notify,
|
|
|
|
5 => DNSOpCode::Update,
|
2022-03-13 21:56:17 +01:00
|
|
|
_ => panic!("KACKE"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-03 18:22:47 +02:00
|
|
|
/// Taken from RFC 6895, 2.3. RCODE Assignment
|
|
|
|
///
|
|
|
|
/// RCODE Name Description Reference
|
|
|
|
///
|
|
|
|
/// 0 NoError No Error [RFC1035]
|
|
|
|
/// 1 FormErr Format Error [RFC1035]
|
|
|
|
/// 2 ServFail Server Failure [RFC1035]
|
|
|
|
/// 3 NXDomain Non-Existent Domain [RFC1035]
|
|
|
|
/// 4 NotImp Not Implemented [RFC1035]
|
|
|
|
/// 5 Refused Query Refused [RFC1035]
|
|
|
|
/// 6 YXDomain Name Exists when it should not [RFC2136]
|
|
|
|
/// 7 YXRRSet RR Set Exists when it should not [RFC2136]
|
|
|
|
/// 8 NXRRSet RR Set that should exist does not [RFC2136]
|
|
|
|
/// 9 NotAuth Server Not Authoritative for zone [RFC2136]
|
|
|
|
/// 9 NotAuth Not Authorized [RFC2845]
|
|
|
|
/// 10 NotZone Name not contained in zone [RFC2136]
|
|
|
|
///
|
|
|
|
/// 11 - 15 Unassigned
|
|
|
|
///
|
|
|
|
/// 16 BADVERS Bad OPT Version [RFC6891]
|
|
|
|
/// 16 BADSIG TSIG Signature Failure [RFC2845]
|
|
|
|
/// 17 BADKEY Key not recognized [RFC2845]
|
|
|
|
/// 18 BADTIME Signature out of time window [RFC2845]
|
|
|
|
/// 19 BADMODE Bad TKEY Mode [RFC2930]
|
|
|
|
/// 20 BADNAME Duplicate key name [RFC2930]
|
|
|
|
/// 21 BADALG Algorithm not supported [RFC2930]
|
|
|
|
/// 22 BADTRUNC Bad Truncation [RFC4635]
|
|
|
|
///
|
|
|
|
/// 23 - 3,840
|
|
|
|
/// 0x0017 - 0x0F00 Unassigned
|
|
|
|
///
|
|
|
|
/// 3,841 - 4,095
|
|
|
|
/// 0x0F01 - 0x0FFF Reserved for Private Use
|
|
|
|
///
|
|
|
|
/// 4,096 - 65,534
|
|
|
|
/// 0x1000 - 0xFFFE Unassigned
|
|
|
|
///
|
|
|
|
/// 65,535
|
|
|
|
/// 0xFFFF Reserved; can only be allocated by Standards Action.
|
2022-03-13 21:56:17 +01:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum DNSRCode {
|
2022-04-03 18:22:47 +02:00
|
|
|
NoError = 0,
|
|
|
|
FormErr = 1,
|
|
|
|
ServFail = 2,
|
|
|
|
NXDomain = 3,
|
|
|
|
NotImp = 4,
|
|
|
|
Refused = 5,
|
|
|
|
YXDomain = 6,
|
|
|
|
YXRRSet = 7,
|
|
|
|
NXRRSet = 8,
|
|
|
|
NotAuth = 9,
|
|
|
|
NotZone = 10,
|
2022-03-13 21:56:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
impl From<u16> for DNSRCode {
|
|
|
|
fn from(val: u16) -> Self {
|
|
|
|
match val {
|
2022-04-03 18:22:47 +02:00
|
|
|
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,
|
2022-03-13 21:56:17 +01:00
|
|
|
_ => panic!("KACKE"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct DNSHeader {
|
|
|
|
/// used by the requester to match up replies to outstanding queries
|
|
|
|
pub id: u16,
|
|
|
|
/// specifies whether this message is a query (false), or a response (true)
|
|
|
|
pub qr: bool,
|
|
|
|
pub opcode: DNSOpCode,
|
2022-03-15 17:01:17 +01:00
|
|
|
/// specifies that the responding name server is an authority for the domain name in question section
|
|
|
|
pub aa: bool,
|
|
|
|
/// specifies that this message was truncated due to length greater than that permitted on the transmission channel
|
|
|
|
pub tc: bool,
|
|
|
|
/// it directs the name server to pursue the query recursively
|
|
|
|
pub rd: bool,
|
|
|
|
/// denotes whether recursive query support is available in the name server
|
|
|
|
pub ra: bool,
|
|
|
|
/// TODO: add documuentation about this flag
|
|
|
|
pub ad: bool,
|
|
|
|
/// TODO: add documuentation about this flag
|
|
|
|
pub cd: bool,
|
|
|
|
pub rcode: DNSRCode,
|
|
|
|
/// TODO: add documuentation about this count
|
2022-03-13 21:56:17 +01:00
|
|
|
pub qd_zo_count: u16,
|
2022-03-15 17:01:17 +01:00
|
|
|
/// TODO: add documuentation about this count
|
2022-03-13 21:56:17 +01:00
|
|
|
pub an_pr_count: u16,
|
2022-03-15 17:01:17 +01:00
|
|
|
/// TODO: add documuentation about this count
|
2022-03-13 21:56:17 +01:00
|
|
|
pub ns_up_count: u16,
|
2022-03-15 17:01:17 +01:00
|
|
|
/// TODO: add documuentation about this count
|
2022-03-13 21:56:17 +01:00
|
|
|
pub ar_count: u16,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct DNSQuery {
|
|
|
|
pub hdr: DNSHeader,
|
|
|
|
pub name: String,
|
|
|
|
pub qclass: u16,
|
|
|
|
pub qtype: u16,
|
|
|
|
}
|
2022-03-15 17:01:17 +01:00
|
|
|
|
|
|
|
impl DNSHeader {
|
|
|
|
const QR_MASK: u16 = 0x0001;
|
|
|
|
const OPCODE_MASK: u16 = 0x000e;
|
|
|
|
const AA_MASK: u16 = 0x0020;
|
|
|
|
const TC_MASK: u16 = 0x0040;
|
|
|
|
const RD_MASK: u16 = 0x0080;
|
|
|
|
const RA_MASK: u16 = 0x0100;
|
|
|
|
const AD_MASK: u16 = 0x0400;
|
|
|
|
const CD_MASK: u16 = 0x0800;
|
|
|
|
const RCODE_MASK: u16 = 0xf000;
|
|
|
|
|
|
|
|
const OPCODE_OFFSET: u16 = 1;
|
|
|
|
const RCODE_OFFSET: u16 = 11;
|
|
|
|
|
2022-04-03 17:10:59 +02:00
|
|
|
pub fn from_udp_datagram(datagram: &[u8]) -> Result<Self, DNSParseError> {
|
2022-03-15 17:01:17 +01:00
|
|
|
if datagram.len() < 11 {
|
2022-04-03 17:10:59 +02:00
|
|
|
return Err(DNSParseError::DatagramLengthError);
|
2022-03-15 17:01:17 +01:00
|
|
|
}
|
|
|
|
|
2022-04-03 17:01:07 +02:00
|
|
|
let id = u16::from_be_bytes((&datagram[..2]).try_into()?);
|
2022-03-15 17:01:17 +01:00
|
|
|
|
2022-04-03 17:01:07 +02:00
|
|
|
let flags = u16::from_be_bytes((&datagram[2..4]).try_into()?);
|
2022-03-15 17:01:17 +01:00
|
|
|
let qr = (flags & DNSHeader::QR_MASK) != 0;
|
|
|
|
let opcode = DNSOpCode::from((flags & DNSHeader::OPCODE_MASK) >> DNSHeader::OPCODE_OFFSET);
|
|
|
|
let aa = (flags & DNSHeader::AA_MASK) != 0;
|
|
|
|
let tc = (flags & DNSHeader::TC_MASK) != 0;
|
|
|
|
let rd = (flags & DNSHeader::RD_MASK) != 0;
|
|
|
|
let ra = (flags & DNSHeader::RA_MASK) != 0;
|
|
|
|
let ad = (flags & DNSHeader::AD_MASK) != 0;
|
|
|
|
let cd = (flags & DNSHeader::CD_MASK) != 0;
|
|
|
|
let rcode = DNSRCode::from((flags & DNSHeader::RCODE_MASK) >> DNSHeader::RCODE_OFFSET);
|
|
|
|
|
2022-04-03 17:01:07 +02:00
|
|
|
let qd_zo_count = u16::from_be_bytes((&datagram[4..6]).try_into()?);
|
|
|
|
let an_pr_count = u16::from_be_bytes((&datagram[6..8]).try_into()?);
|
|
|
|
let ns_up_count = u16::from_be_bytes((&datagram[8..10]).try_into()?);
|
|
|
|
let ar_count = u16::from_be_bytes((&datagram[10..12]).try_into()?);
|
2022-03-15 17:01:17 +01:00
|
|
|
|
|
|
|
Ok(DNSHeader {
|
|
|
|
id,
|
|
|
|
qr,
|
|
|
|
opcode,
|
|
|
|
aa,
|
|
|
|
tc,
|
|
|
|
rd,
|
|
|
|
ra,
|
|
|
|
ad,
|
|
|
|
cd,
|
|
|
|
rcode,
|
|
|
|
qd_zo_count,
|
|
|
|
an_pr_count,
|
|
|
|
ns_up_count,
|
|
|
|
ar_count,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2022-04-03 17:01:07 +02:00
|
|
|
|
2022-04-03 17:10:59 +02:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum DNSParseError {
|
|
|
|
DatagramLengthError,
|
|
|
|
SliceError(std::array::TryFromSliceError)
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<std::array::TryFromSliceError> for DNSParseError {
|
2022-04-03 17:01:07 +02:00
|
|
|
fn from(err: std::array::TryFromSliceError) -> Self {
|
2022-04-03 17:10:59 +02:00
|
|
|
DNSParseError::SliceError(err)
|
2022-04-03 17:01:07 +02:00
|
|
|
}
|
|
|
|
}
|