use std::convert::TryInto; /// 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 #[derive(Debug)] pub enum DNSOpCode { Query = 0, IQuery = 1, // obsolete Status = 2, Notify = 4, Update = 5, } impl From for DNSOpCode { fn from(val: u16) -> Self { match val { 0 => DNSOpCode::Query, 1 => DNSOpCode::IQuery, 2 => DNSOpCode::Status, 4 => DNSOpCode::Notify, 5 => DNSOpCode::Update, _ => panic!("KACKE"), } } } /// 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. #[derive(Debug)] pub enum DNSRCode { NoError = 0, FormErr = 1, ServFail = 2, NXDomain = 3, NotImp = 4, Refused = 5, YXDomain = 6, YXRRSet = 7, NXRRSet = 8, NotAuth = 9, NotZone = 10, } impl From for DNSRCode { fn from(val: u16) -> Self { 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, _ => 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, /// 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 pub qd_zo_count: u16, /// TODO: add documuentation about this count pub an_pr_count: u16, /// TODO: add documuentation about this count pub ns_up_count: u16, /// TODO: add documuentation about this count pub ar_count: u16, } #[derive(Debug)] pub struct DNSQuery { pub hdr: DNSHeader, pub name: String, pub qclass: u16, pub qtype: u16, } 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; pub fn from_udp_datagram(datagram: &[u8]) -> Result { if datagram.len() < 11 { return Err(DNSParseError::DatagramLengthError); } let id = u16::from_be_bytes((&datagram[..2]).try_into()?); let flags = u16::from_be_bytes((&datagram[2..4]).try_into()?); 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); 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()?); Ok(DNSHeader { id, qr, opcode, aa, tc, rd, ra, ad, cd, rcode, qd_zo_count, an_pr_count, ns_up_count, ar_count, }) } } #[derive(Debug)] pub enum DNSParseError { DatagramLengthError, SliceError(std::array::TryFromSliceError) } impl From for DNSParseError { fn from(err: std::array::TryFromSliceError) -> Self { DNSParseError::SliceError(err) } }