use std::convert::TryInto; #[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"), } } } // TODO: FIXME: TECHNISCHE SCHULD (alle pubs weg und gucken wie es richtig geht) #[derive(Debug)] pub enum DNSRCode { NOERROR = 0, FORMERR = 1, SERVFAIL = 2, NXDOMAIN = 3, NOTIMP = 4, REFUSED = 5, XYDOMAIN = 6, XYRRSET = 7, NXRRSET = 8, NOTAUTH = 9, NOTINZONE = 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::XYDOMAIN, 7 => DNSRCode::XYRRSET, 8 => DNSRCode::NXRRSET, 9 => DNSRCode::NOTAUTH, 10 => DNSRCode::NOTINZONE, _ => 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("Not enough data"); } let id = u16::from_be_bytes((&datagram[..2]).try_into().map_err(|_err| "Invalid id")?); let flags = u16::from_be_bytes( (&datagram[2..4]) .try_into() .map_err(|_err| "Invalid flags")?, ); 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() .map_err(|_err| "Invalid qd_zo_count")?, ); let an_pr_count = u16::from_be_bytes( (&datagram[6..8]) .try_into() .map_err(|_err| "Invalid an_pr_count")?, ); let ns_up_count = u16::from_be_bytes( (&datagram[8..10]) .try_into() .map_err(|_err| "Invalid ns_up_count")?, ); let ar_count = u16::from_be_bytes( (&datagram[10..12]) .try_into() .map_err(|_err| "Invalid ar_count")?, ); Ok(DNSHeader { id, qr, opcode, aa, tc, rd, ra, ad, cd, rcode, qd_zo_count, an_pr_count, ns_up_count, ar_count, }) } }