diff options
| author | luwenpeng <[email protected]> | 2023-08-01 11:39:21 +0800 |
|---|---|---|
| committer | luwenpeng <[email protected]> | 2023-08-01 11:44:26 +0800 |
| commit | dcef6523773d81e225aadf2f782df2994ed67866 (patch) | |
| tree | 3a7d48a49f5260b233561c17375bf1db2c341eb2 /src | |
| parent | aee47ce205a0803e3359f31124fea04f9aaf7b11 (diff) | |
[feature] Support DNS Decode
Diffstat (limited to 'src')
| -rw-r--r-- | src/protocol/dns.rs | 3882 | ||||
| -rw-r--r-- | src/protocol/mod.rs | 3 |
2 files changed, 3884 insertions, 1 deletions
diff --git a/src/protocol/dns.rs b/src/protocol/dns.rs new file mode 100644 index 0000000..27dde8a --- /dev/null +++ b/src/protocol/dns.rs @@ -0,0 +1,3882 @@ +use nom::bits; +use nom::character; +use nom::combinator; +use nom::error::Error; +use nom::error::ErrorKind; +use nom::multi; +use nom::number; +use nom::sequence; +use nom::Err; +use nom::IResult; +use nom::Needed; +use std::fmt; +use std::fmt::Display; +use std::str; + +/****************************************************************************** + * DNS Header Definitions + ******************************************************************************/ + +/* + * DNS Header Definitions + * 1 1 1 1 1 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | ID | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * |QR| Opcode |AA|TC|RD|RA| Z | RCODE | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | QDCOUNT | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | ANCOUNT | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | NSCOUNT | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | ARCOUNT | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + */ + +/****************************** DNS_HDR_QR ******************************/ + +#[allow(non_camel_case_types)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum DNS_HDR_QR { + Query = 0, + Response = 1, + Other = 2, +} + +impl DNS_HDR_QR { + fn from_u8(n: u8) -> DNS_HDR_QR { + match n { + 0 => DNS_HDR_QR::Query, + 1 => DNS_HDR_QR::Response, + _ => DNS_HDR_QR::Other, + } + } +} + +impl Display for DNS_HDR_QR { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + DNS_HDR_QR::Query => write!(f, "QUERY"), + DNS_HDR_QR::Response => write!(f, "RESPONSE"), + DNS_HDR_QR::Other => write!(f, "OTHER"), + } + } +} + +/****************************** DNS_HDR_OPCODE ******************************/ + +// https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-5 +#[allow(non_camel_case_types)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum DNS_HDR_OPCODE { + Query, + IQuery, + Status, + Notify, + Update, + DSO, // DNS Stateful Operations + Other(u8), +} + +impl DNS_HDR_OPCODE { + fn from_u8(n: u8) -> DNS_HDR_OPCODE { + match n { + 0 => DNS_HDR_OPCODE::Query, + 1 => DNS_HDR_OPCODE::IQuery, + 2 => DNS_HDR_OPCODE::Status, + // 3 => Unassigned + 4 => DNS_HDR_OPCODE::Notify, + 5 => DNS_HDR_OPCODE::Update, + 6 => DNS_HDR_OPCODE::DSO, + n => DNS_HDR_OPCODE::Other(n), + } + } +} + +impl Display for DNS_HDR_OPCODE { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + DNS_HDR_OPCODE::Query => write!(f, "QUERY"), + DNS_HDR_OPCODE::IQuery => write!(f, "IQUERY"), + DNS_HDR_OPCODE::Status => write!(f, "STATUS"), + DNS_HDR_OPCODE::Notify => write!(f, "NOTIFY"), + DNS_HDR_OPCODE::Update => write!(f, "UPDATE"), + DNS_HDR_OPCODE::DSO => write!(f, "DSO"), + DNS_HDR_OPCODE::Other(n) => write!(f, "OTHER {}", n), + } + } +} + +/****************************** DNS_HDR_RCODE ******************************/ + +// https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-6 +#[allow(non_camel_case_types)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum DNS_HDR_RCODE { + NoError, // No Error + FormErr, // Format Error + ServFail, // Server Failure + NXDomain, // Non-Existent Domain + NotImp, // Not Implemented + Refused, // Query Refused + YXDomain, // Name Exists when it should not + YXRRSet, // RR Set Exists when it should not + NXRRSet, // RR Set that should exist does not + NotAuth, // Server Not Authoritative for zone / Not Authorized + NotZone, // Name not contained in zone + DSOTYPENI, // DSO-TYPE Not Implemented + BADSIG, // TSIG Signature Failure + BADKEY, // Key not recognized + BADTIME, // Signature out of time window + BADMODE, // Bad TKEY Mode + BADNAME, // Duplicate key name + BADALG, // Algorithm not supported + BADTRUNC, // Bad Truncation + BADCOOKIE, // Bad/missing Server Cookie + Other(u8), +} + +impl DNS_HDR_RCODE { + fn from_u8(n: u8) -> DNS_HDR_RCODE { + match n { + 0 => DNS_HDR_RCODE::NoError, + 1 => DNS_HDR_RCODE::FormErr, + 2 => DNS_HDR_RCODE::ServFail, + 3 => DNS_HDR_RCODE::NXDomain, + 4 => DNS_HDR_RCODE::NotImp, + 5 => DNS_HDR_RCODE::Refused, + 6 => DNS_HDR_RCODE::YXDomain, + 7 => DNS_HDR_RCODE::YXRRSet, + 8 => DNS_HDR_RCODE::NXRRSet, + 9 => DNS_HDR_RCODE::NotAuth, + 10 => DNS_HDR_RCODE::NotZone, + 11 => DNS_HDR_RCODE::DSOTYPENI, + // hole + 16 => DNS_HDR_RCODE::BADSIG, + 17 => DNS_HDR_RCODE::BADKEY, + 18 => DNS_HDR_RCODE::BADTIME, + 19 => DNS_HDR_RCODE::BADMODE, + 20 => DNS_HDR_RCODE::BADNAME, + 21 => DNS_HDR_RCODE::BADALG, + 22 => DNS_HDR_RCODE::BADTRUNC, + 23 => DNS_HDR_RCODE::BADCOOKIE, + n => DNS_HDR_RCODE::Other(n), + } + } +} + +impl Display for DNS_HDR_RCODE { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + DNS_HDR_RCODE::NoError => write!(f, "NOERROR"), + DNS_HDR_RCODE::FormErr => write!(f, "FORMERR"), + DNS_HDR_RCODE::ServFail => write!(f, "SERVFAIL"), + DNS_HDR_RCODE::NXDomain => write!(f, "NXDOMAIN"), + DNS_HDR_RCODE::NotImp => write!(f, "NOTIMP"), + DNS_HDR_RCODE::Refused => write!(f, "REFUSED"), + DNS_HDR_RCODE::YXDomain => write!(f, "YXDOMAIN"), + DNS_HDR_RCODE::YXRRSet => write!(f, "YXRRSET"), + DNS_HDR_RCODE::NXRRSet => write!(f, "NXRRSET"), + DNS_HDR_RCODE::NotAuth => write!(f, "NOTAUTH"), + DNS_HDR_RCODE::NotZone => write!(f, "NOTZONE"), + DNS_HDR_RCODE::DSOTYPENI => write!(f, "DSOTYPENI"), + DNS_HDR_RCODE::BADSIG => write!(f, "BADSIG"), + DNS_HDR_RCODE::BADKEY => write!(f, "BADKEY"), + DNS_HDR_RCODE::BADTIME => write!(f, "BADTIME"), + DNS_HDR_RCODE::BADMODE => write!(f, "BADMODE"), + DNS_HDR_RCODE::BADNAME => write!(f, "BADNAME"), + DNS_HDR_RCODE::BADALG => write!(f, "BADALG"), + DNS_HDR_RCODE::BADTRUNC => write!(f, "BADTRUNC"), + DNS_HDR_RCODE::BADCOOKIE => write!(f, "BADCOOKIE"), + DNS_HDR_RCODE::Other(n) => write!(f, "OTHER {}", n), + } + } +} + +/****************************** DNS_HEADER ******************************/ + +#[allow(non_camel_case_types)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct DNS_HEADER { + pub id: u16, + pub qr: DNS_HDR_QR, + pub op_code: DNS_HDR_OPCODE, + pub aa: bool, // Authoritative Answer + pub tc: bool, // TrunCation + pub rd: bool, // Recursion Desired + pub ra: bool, // Recursion Available + pub ad: bool, // TODO ??? + pub cd: bool, // TODO ??? + pub r_code: DNS_HDR_RCODE, // Response Code + pub qd_count: u16, + pub an_count: u16, + pub ns_count: u16, + pub ar_count: u16, +} + +fn hdr_bits_decode( + input: &[u8], +) -> IResult< + &[u8], + ( + DNS_HDR_QR, + DNS_HDR_OPCODE, + bool, + bool, + bool, + bool, + bool, + bool, + DNS_HDR_RCODE, + ), +> { + bits::<_, _, Error<(&[u8], usize)>, _, _>(sequence::tuple(( + bits::streaming::take(1usize), + bits::streaming::take(4usize), + bits::streaming::take(1usize), + bits::streaming::take(1usize), + bits::streaming::take(1usize), + bits::streaming::take(1usize), + combinator::verify(bits::streaming::take(1usize), |res: &u8| *res == 0), // Reserved 1 bit + bits::streaming::take(1usize), + bits::streaming::take(1usize), + bits::streaming::take(4usize), + )))(input) + .map( + |(rest, res): (&[u8], (u8, u8, u8, u8, u8, u8, u8, u8, u8, u8))| { + ( + rest, + ( + DNS_HDR_QR::from_u8(res.0), + DNS_HDR_OPCODE::from_u8(res.1), + res.2 == 1, + res.3 == 1, + res.4 == 1, + res.5 == 1, + res.7 == 1, + res.8 == 1, + DNS_HDR_RCODE::from_u8(res.9), + ), + ) + }, + ) +} + +impl DNS_HEADER { + fn decode(input: &[u8]) -> IResult<&[u8], DNS_HEADER> { + sequence::tuple(( + number::streaming::be_u16, + hdr_bits_decode, + number::streaming::be_u16, + number::streaming::be_u16, + number::streaming::be_u16, + number::streaming::be_u16, + ))(input) + .map(|(rest, res)| { + ( + rest, + DNS_HEADER { + id: res.0, + qr: res.1 .0, + op_code: res.1 .1, + aa: res.1 .2, + tc: res.1 .3, + rd: res.1 .4, + ra: res.1 .5, + ad: res.1 .6, + cd: res.1 .7, + r_code: res.1 .8, + qd_count: res.2, + an_count: res.3, + ns_count: res.4, + ar_count: res.5, + }, + ) + }) + } +} + +impl Display for DNS_HEADER { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + ";; ->>HEADER<<- opcode: {}, status: {}, id: {}\n;; flags:", + self.op_code, self.r_code, self.id, + )?; + if self.qr == DNS_HDR_QR::Response { + write!(f, " qr")?; + } + if self.aa { + write!(f, " aa")?; + } + if self.tc { + write!(f, " tc")?; + } + if self.rd { + write!(f, " rd")?; + } + if self.ra { + write!(f, " ra")?; + } + if self.ad { + write!(f, " ad")?; + } + if self.cd { + write!(f, " cd")?; + } + write!( + f, + "; QUERY: {}, ANSWER: {}, AUTHORITY: {}, ADDITIONAL: {}\n", + self.qd_count, self.an_count, self.ns_count, self.ar_count + ) + } +} + +/****************************************************************************** + * DNS Question Section Definitions + ******************************************************************************/ + +/* + * DNS Question Section Definitions + * 1 1 1 1 1 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | | + * / QNAME / + * / / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | QTYPE | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | QCLASS | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + */ + +/****************************** DNS_QTYPE ******************************/ + +// https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-4 +#[allow(non_camel_case_types)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum DNS_QTYPE { + A, // a host address + NS, // an authoritative name server + MD, // a mail destination (OBSOLETE - use MX) + MF, // a mail forwarder (OBSOLETE - use MX) + CNAME, // the canonical name for an alias + SOA, // marks the start of a zone of authority + MB, // a mailbox domain name (EXPERIMENTAL) + MG, // a mail group member (EXPERIMENTAL) + MR, // a mail rename domain name (EXPERIMENTAL) + NULL, // a null RR (EXPERIMENTAL) + WKS, // a well known service description + PTR, // a domain name pointer + HINFO, // host information + MINFO, // mailbox or mail list information + MX, // mail exchange + TXT, // text strings + RP, // for Responsible Person + AFSDB, // for AFS Data Base location + X25, // for X.25 PSDN address + ISDN, // for ISDN address + RT, // for Route Through + NSAP, // for NSAP address, NSAP style A record + NSAPPTR, // for domain name pointer, NSAP style + SIG, // for security signature + KEY, // for security signature + PX, // X.400 mail mapping information + GPOS, // Geographical Position + AAAA, // IP6 Address + LOC, // Location Information + NXT, // Next Domain (OBSOLETE) + EID, // Endpoint Identifier + NIMLOC, // Nimrod Locator + SRV, // Server Selection + ATMA, // ATM Address + NAPTR, // Naming Authority Pointer + KX, // Key Exchanger + CERT, // CERT + A6, // A6 (OBSOLETE - use AAAA) + DNAME, // DNAME + SINK, // SINK + OPT, // OPT + APL, // APL + DS, // Delegation Signer + SSHFP, // SSH Key Fingerprint + IPSECKEY, // IPSECKEY + RRSIG, // RRSIG + NSEC, // NSEC + DNSKEY, // DNSKEY + DHCID, // DHCID + NSEC3, // NSEC3 + NSEC3PARAM, // NSEC3PARAM + TLSA, // TLSA + SMIMEA, // S/MIME cert association + HIP, // Host Identity Protocol + NINFO, // NINFO + RKEY, // RKEY + TALINK, // Trust Anchor LINK + CDS, // Child DS + CDNSKEY, // DNSKEY(s) the Child wants reflected in DS + OPENPGPKEY, // OpenPGP Key + CSYNC, // Child-To-Parent Synchronization + ZONEMD, // Message Digest Over Zone Data + SVCB, // General Purpose Service Binding + HTTPS, // Service Binding type for use with HTTP + SPF, + UINFO, + UID, + GID, + UNSPEC, + NID, + L32, + L64, + LP, + EUI48, // an EUI-48 address + EUI64, // an EUI-64 address + TKEY, // Transaction Key + TSIG, // Transaction Signature + IXFR, // incremental transfer + AXFR, // transfer of an entire zone + MAILB, // mailbox-related RRs (MB, MG or MR) + MAILA, // mail agent RRs (OBSOLETE - see MX) + ANY, // A request for some or all records the server has available + URI, // URI + CAA, // Certification Authority Restriction + AVC, // Application Visibility and Control + DOA, // Digital Object Architecture + AMTRELAY, // Automatic Multicast Tunneling Relay + TA, // DNSSEC Trust Authorities + DLV, // DNSSEC Lookaside Validation (OBSOLETE) + Other(u16), +} + +impl DNS_QTYPE { + fn from_u16(n: u16) -> DNS_QTYPE { + match n { + 1 => DNS_QTYPE::A, + 2 => DNS_QTYPE::NS, + 3 => DNS_QTYPE::MD, + 4 => DNS_QTYPE::MF, + 5 => DNS_QTYPE::CNAME, + 6 => DNS_QTYPE::SOA, + 7 => DNS_QTYPE::MB, + 8 => DNS_QTYPE::MG, + 9 => DNS_QTYPE::MR, + 10 => DNS_QTYPE::NULL, + 11 => DNS_QTYPE::WKS, + 12 => DNS_QTYPE::PTR, + 13 => DNS_QTYPE::HINFO, + 14 => DNS_QTYPE::MINFO, + 15 => DNS_QTYPE::MX, + 16 => DNS_QTYPE::TXT, + 17 => DNS_QTYPE::RP, + 18 => DNS_QTYPE::AFSDB, + 19 => DNS_QTYPE::X25, + 20 => DNS_QTYPE::ISDN, + 21 => DNS_QTYPE::RT, + 22 => DNS_QTYPE::NSAP, + 23 => DNS_QTYPE::NSAPPTR, + 24 => DNS_QTYPE::SIG, + 25 => DNS_QTYPE::KEY, + 26 => DNS_QTYPE::PX, + 27 => DNS_QTYPE::GPOS, + 28 => DNS_QTYPE::AAAA, + 29 => DNS_QTYPE::LOC, + 30 => DNS_QTYPE::NXT, + 31 => DNS_QTYPE::EID, + 32 => DNS_QTYPE::NIMLOC, + 33 => DNS_QTYPE::SRV, + 34 => DNS_QTYPE::ATMA, + 35 => DNS_QTYPE::NAPTR, + 36 => DNS_QTYPE::KX, + 37 => DNS_QTYPE::CERT, + 38 => DNS_QTYPE::A6, + 39 => DNS_QTYPE::DNAME, + 40 => DNS_QTYPE::SINK, + 41 => DNS_QTYPE::OPT, + 42 => DNS_QTYPE::APL, + 43 => DNS_QTYPE::DS, + 44 => DNS_QTYPE::SSHFP, + 45 => DNS_QTYPE::IPSECKEY, + 46 => DNS_QTYPE::RRSIG, + 47 => DNS_QTYPE::NSEC, + 48 => DNS_QTYPE::DNSKEY, + 49 => DNS_QTYPE::DHCID, + 50 => DNS_QTYPE::NSEC3, + 51 => DNS_QTYPE::NSEC3PARAM, + 52 => DNS_QTYPE::TLSA, + 53 => DNS_QTYPE::SMIMEA, + // hole + 55 => DNS_QTYPE::HIP, + 56 => DNS_QTYPE::NINFO, + 57 => DNS_QTYPE::RKEY, + 58 => DNS_QTYPE::TALINK, + 59 => DNS_QTYPE::CDS, + 60 => DNS_QTYPE::CDNSKEY, + 61 => DNS_QTYPE::OPENPGPKEY, + 62 => DNS_QTYPE::CSYNC, + 63 => DNS_QTYPE::ZONEMD, + 64 => DNS_QTYPE::SVCB, + 65 => DNS_QTYPE::HTTPS, + // hole + 99 => DNS_QTYPE::SPF, + 100 => DNS_QTYPE::UINFO, + 101 => DNS_QTYPE::UID, + 102 => DNS_QTYPE::GID, + 103 => DNS_QTYPE::UNSPEC, + 104 => DNS_QTYPE::NID, + 105 => DNS_QTYPE::L32, + 106 => DNS_QTYPE::L64, + 107 => DNS_QTYPE::LP, + 108 => DNS_QTYPE::EUI48, + 109 => DNS_QTYPE::EUI64, + // hole + 249 => DNS_QTYPE::TKEY, + 250 => DNS_QTYPE::TSIG, + 251 => DNS_QTYPE::IXFR, + 252 => DNS_QTYPE::AXFR, + 253 => DNS_QTYPE::MAILB, + 254 => DNS_QTYPE::MAILA, + 255 => DNS_QTYPE::ANY, + 256 => DNS_QTYPE::URI, + 257 => DNS_QTYPE::CAA, + 258 => DNS_QTYPE::AVC, + 259 => DNS_QTYPE::DOA, + 260 => DNS_QTYPE::AMTRELAY, + // hole + 32768 => DNS_QTYPE::TA, + 32769 => DNS_QTYPE::DLV, + n => DNS_QTYPE::Other(n), + } + } +} + +impl Display for DNS_QTYPE { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + DNS_QTYPE::A => write!(f, "A"), + DNS_QTYPE::NS => write!(f, "NS"), + DNS_QTYPE::MD => write!(f, "MD"), + DNS_QTYPE::MF => write!(f, "MF"), + DNS_QTYPE::CNAME => write!(f, "CNAME"), + DNS_QTYPE::SOA => write!(f, "SOA"), + DNS_QTYPE::MB => write!(f, "MB"), + DNS_QTYPE::MG => write!(f, "MG"), + DNS_QTYPE::MR => write!(f, "MR"), + DNS_QTYPE::NULL => write!(f, "NULL"), + DNS_QTYPE::WKS => write!(f, "WKS"), + DNS_QTYPE::PTR => write!(f, "PTR"), + DNS_QTYPE::HINFO => write!(f, "HINFO"), + DNS_QTYPE::MINFO => write!(f, "MINFO"), + DNS_QTYPE::MX => write!(f, "MX"), + DNS_QTYPE::TXT => write!(f, "TXT"), + DNS_QTYPE::RP => write!(f, "RP"), + DNS_QTYPE::AFSDB => write!(f, "AFSDB"), + DNS_QTYPE::X25 => write!(f, "X25"), + DNS_QTYPE::ISDN => write!(f, "ISDN"), + DNS_QTYPE::RT => write!(f, "RT"), + DNS_QTYPE::NSAP => write!(f, "NSAP"), + DNS_QTYPE::NSAPPTR => write!(f, "NSAPPTR"), + DNS_QTYPE::SIG => write!(f, "SIG"), + DNS_QTYPE::KEY => write!(f, "KEY"), + DNS_QTYPE::PX => write!(f, "PX"), + DNS_QTYPE::GPOS => write!(f, "GPOS"), + DNS_QTYPE::AAAA => write!(f, "AAAA"), + DNS_QTYPE::LOC => write!(f, "LOC"), + DNS_QTYPE::NXT => write!(f, "NXT"), + DNS_QTYPE::EID => write!(f, "EID"), + DNS_QTYPE::NIMLOC => write!(f, "NIMLOC"), + DNS_QTYPE::SRV => write!(f, "SRV"), + DNS_QTYPE::ATMA => write!(f, "ATMA"), + DNS_QTYPE::NAPTR => write!(f, "NAPTR"), + DNS_QTYPE::KX => write!(f, "KX"), + DNS_QTYPE::CERT => write!(f, "CERT"), + DNS_QTYPE::A6 => write!(f, "A6"), + DNS_QTYPE::DNAME => write!(f, "DNAME"), + DNS_QTYPE::SINK => write!(f, "SINK"), + DNS_QTYPE::OPT => write!(f, "OPT"), + DNS_QTYPE::APL => write!(f, "APL"), + DNS_QTYPE::DS => write!(f, "DS"), + DNS_QTYPE::SSHFP => write!(f, "SSHFP"), + DNS_QTYPE::IPSECKEY => write!(f, "IPSECKEY"), + DNS_QTYPE::RRSIG => write!(f, "RRSIG"), + DNS_QTYPE::NSEC => write!(f, "NSEC"), + DNS_QTYPE::DNSKEY => write!(f, "DNSKEY"), + DNS_QTYPE::DHCID => write!(f, "DHCID"), + DNS_QTYPE::NSEC3 => write!(f, "NSEC3"), + DNS_QTYPE::NSEC3PARAM => write!(f, "NSEC3PARAM"), + DNS_QTYPE::TLSA => write!(f, "TLSA"), + DNS_QTYPE::SMIMEA => write!(f, "SMIMEA"), + DNS_QTYPE::HIP => write!(f, "HIP"), + DNS_QTYPE::NINFO => write!(f, "NINFO"), + DNS_QTYPE::RKEY => write!(f, "RKEY"), + DNS_QTYPE::TALINK => write!(f, "TALINK"), + DNS_QTYPE::CDS => write!(f, "CDS"), + DNS_QTYPE::CDNSKEY => write!(f, "CDNSKEY"), + DNS_QTYPE::OPENPGPKEY => write!(f, "OPENPGPKEY"), + DNS_QTYPE::CSYNC => write!(f, "CSYNC"), + DNS_QTYPE::ZONEMD => write!(f, "ZONEMD"), + DNS_QTYPE::SVCB => write!(f, "SVCB"), + DNS_QTYPE::HTTPS => write!(f, "HTTPS"), + DNS_QTYPE::SPF => write!(f, "SPF"), + DNS_QTYPE::UINFO => write!(f, "UINFO"), + DNS_QTYPE::UID => write!(f, "UID"), + DNS_QTYPE::GID => write!(f, "GID"), + DNS_QTYPE::UNSPEC => write!(f, "UNSPEC"), + DNS_QTYPE::NID => write!(f, "NID"), + DNS_QTYPE::L32 => write!(f, "L32"), + DNS_QTYPE::L64 => write!(f, "L64"), + DNS_QTYPE::LP => write!(f, "LP"), + DNS_QTYPE::EUI48 => write!(f, "EUI48"), + DNS_QTYPE::EUI64 => write!(f, "EUI64"), + DNS_QTYPE::TKEY => write!(f, "TKEY"), + DNS_QTYPE::TSIG => write!(f, "TSIG"), + DNS_QTYPE::IXFR => write!(f, "IXFR"), + DNS_QTYPE::AXFR => write!(f, "AXFR"), + DNS_QTYPE::MAILB => write!(f, "MAILB"), + DNS_QTYPE::MAILA => write!(f, "MAILA"), + DNS_QTYPE::ANY => write!(f, "ANY"), + DNS_QTYPE::URI => write!(f, "URI"), + DNS_QTYPE::CAA => write!(f, "CAA"), + DNS_QTYPE::AVC => write!(f, "AVC"), + DNS_QTYPE::DOA => write!(f, "DOA"), + DNS_QTYPE::AMTRELAY => write!(f, "AMTRELAY"), + DNS_QTYPE::TA => write!(f, "TA"), + DNS_QTYPE::DLV => write!(f, "DLV"), + DNS_QTYPE::Other(n) => write!(f, "TYPE{}", n), + } + } +} + +/****************************** DNS_QCLASS ******************************/ + +// https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-2 +#[allow(non_camel_case_types)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum DNS_QCLASS { + Internet, + Unassigned, + Chaos, + Hesiod, + QCLASS_NONE, + QCLASS_ANY, + Other(u16), +} + +impl DNS_QCLASS { + fn from_u16(n: u16) -> DNS_QCLASS { + match n { + 1 => DNS_QCLASS::Internet, + 2 => DNS_QCLASS::Unassigned, + 3 => DNS_QCLASS::Chaos, + 4 => DNS_QCLASS::Hesiod, + 254 => DNS_QCLASS::QCLASS_NONE, + 255 => DNS_QCLASS::QCLASS_ANY, + n => DNS_QCLASS::Other(n), + } + } +} + +impl Display for DNS_QCLASS { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + DNS_QCLASS::Internet => write!(f, "Internet"), + DNS_QCLASS::Unassigned => write!(f, "Unassigned"), + DNS_QCLASS::Chaos => write!(f, "Chaos"), + DNS_QCLASS::Hesiod => write!(f, "Hesiod"), + DNS_QCLASS::QCLASS_NONE => write!(f, "QCLASS_NONE"), + DNS_QCLASS::QCLASS_ANY => write!(f, "QCLASS_ANY"), + DNS_QCLASS::Other(n) => write!(f, "CLASS{}", n), + } + } +} + +/****************************** DNS_QUESTION_SECTION ******************************/ + +#[allow(non_camel_case_types)] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct DNS_QUESTION_SECTION { + pub qname: String, + pub qtype: DNS_QTYPE, + pub qclass: DNS_QCLASS, + size: usize, // DNS 原始数据中 DNS_QUESTION_SECTION 占多少字节 +} + +impl DNS_QUESTION_SECTION { + fn decode<'a, 'b>( + input: &'a [u8], + labels: &'b mut DNS_LABEL_TABLE, + position: usize, + ) -> IResult<&'a [u8], DNS_QUESTION_SECTION> { + let (input, (name, size)) = dname_decode(input, labels, position)?; + let (input, qtype) = number::streaming::be_u16(input)?; + let (input, qclass) = number::streaming::be_u16(input)?; + + Ok(( + input, + DNS_QUESTION_SECTION { + qname: name.to_string(), + qtype: DNS_QTYPE::from_u16(qtype), + qclass: DNS_QCLASS::from_u16(qclass), + size: 4 + size, + }, + )) + } +} + +impl Display for DNS_QUESTION_SECTION { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, ";{}\t{}\t{}\n", self.qname, self.qclass, self.qtype) + } +} + +/****************************************************************************** + * DNS Resource Record Definitions + ******************************************************************************/ + +/* + * DNS Resource Record Definitions + * 1 1 1 1 1 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | | + * / / + * / NAME / + * | | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | TYPE | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | CLASS | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | TTL | + * | | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | RDLENGTH | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--| + * / RDATA / + * / / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + */ + +#[allow(non_camel_case_types)] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct DNS_RR_HDR { + pub qname: String, + pub rr_type: DNS_QTYPE, + pub rr_class: DNS_QCLASS, + pub ttl: i32, + pub rd_length: u16, + size: usize, // DNS 原始数据中 DNS_RR_HDR 占多少字节 +} + +impl DNS_RR_HDR { + fn decode<'a>( + input: &'a [u8], + labels: &mut DNS_LABEL_TABLE, + position: usize, + ) -> IResult<&'a [u8], DNS_RR_HDR> { + let (input, (name, size)) = dname_decode(input, labels, position)?; + let (input, rr_type) = number::streaming::be_u16(input)?; + let (input, rr_class) = number::streaming::be_u16(input)?; + let (input, rr_ttl) = number::streaming::be_i32(input)?; + let (input, rr_rdlength) = number::streaming::be_u16(input)?; + + Ok(( + input, + DNS_RR_HDR { + qname: name.to_string(), + rr_type: DNS_QTYPE::from_u16(rr_type), + rr_class: DNS_QCLASS::from_u16(rr_class), + ttl: rr_ttl, + rd_length: rr_rdlength, + size: 10 + size, + }, + )) + } +} + +/* + * A RDATA format + * 1 1 1 1 1 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | ADDRESS | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * ADDRESS : A 32 bit Internet address + */ + +/* + * AAAA RDATA format + * 1 1 1 1 1 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | ADDRESS | + * | | + * | | + * | | + * | | + * | | + * | | + * | | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * ADDRESS : A 128 bit IPv6 address is encoded in the data portion of an AAAA + * resource record in network byte order (high-order byte first). + */ + +/* + * CNAME RDATA format + * 1 1 1 1 1 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * / CNAME / + * / / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * CNAME : A <domain-name> which specifies the canonical or primary + * name for the owner. The owner name is an alias. + */ + +/* + * HINFO RDATA format + * 1 1 1 1 1 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * / CPU / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * / OS / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * CPU : A <character-string> which specifies the CPU type. + * OS : A <character-string> which specifies the operating system type. + */ + +/* + * MX RDATA format + * 1 1 1 1 1 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | PREFERENCE | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * / EXCHANGE / + * / / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * PREFERENCE : A 16 bit integer which specifies the preference given to + * this RR among others at the same owner. Lower values are preferred. + * EXCHANGE : A <domain-name> which specifies a host willing to act as a mail exchange for the owner name. + */ + +/* + * NS RDATA format + * 1 1 1 1 1 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * / NSDNAME / + * / / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * NSDNAME : A <domain-name> which specifies a host which should be + * authoritative for the specified class and domain. + */ + +/* + * PTR RDATA format + * 1 1 1 1 1 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * / PTRDNAME / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * PTRDNAME : A <domain-name> which points to some location in the domain name space. + */ + +/* + * SOA RDATA format + * 1 1 1 1 1 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * / MNAME / + * / / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * / RNAME / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | SERIAL | + * | | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | REFRESH | + * | | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | RETRY | + * | | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | EXPIRE | + * | | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | MINIMUM | + * | | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * MNAME : The <domain-name> of the name server that was the original or primary source of data for this zone. + * RNAME : A <domain-name> which specifies the mailbox of the person responsible for this zone. + * SERIAL : The unsigned 32 bit version number of the original copy of the zone. + * Zone transfers preserve this value. This value wraps and should be + * compared using sequence space arithmetic. + * REFRESH : A 32 bit time interval before the zone should be refreshed. + * RETRY : A 32 bit time interval that should elapse before a failed refresh should be retried. + * EXPIRE : A 32 bit time value that specifies the upper limit on + * the time interval that can elapse before the zone is no longer authoritative. + * MINIMUM : The unsigned 32 bit minimum TTL field that should beexported with any RR from this zone. + */ + +/* + * TXT RDATA format + * 1 1 1 1 1 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * / TXT-DATA / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * TXT-DATA : One or more <character-string>s. + */ + +#[allow(non_camel_case_types)] +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum DNS_RR_DATA { + A { + address: String, + }, + AAAA { + address: String, + }, + CNAME { + cname: String, + }, + HINFO { + cpu: Vec<u8>, + os: Vec<u8>, + }, + MX { + preference: u16, + exchange: String, + }, + NS { + nsdname: String, + }, + PTR { + ptrdnmae: String, + }, + SOA { + mname: String, + rname: String, + serial: u32, + refresh: i32, + retry: i32, + expire: i32, + minimum: i32, + }, + TXT { + txtdata: Vec<String>, + }, + Other { + qtype: DNS_QTYPE, + data: Vec<u8>, + }, +} + +impl DNS_RR_DATA { + fn decode<'a>( + input: &'a [u8], + labels: &mut DNS_LABEL_TABLE, + position: usize, + rr_hdr: &DNS_RR_HDR, + ) -> IResult<&'a [u8], DNS_RR_DATA> { + match (rr_hdr.rr_class, rr_hdr.rr_type) { + (DNS_QCLASS::Internet, DNS_QTYPE::A) => { + let (input, num) = number::streaming::be_u32(input)?; + Ok(( + input, + DNS_RR_DATA::A { + address: address_v4_decode(num), + }, + )) + } + (DNS_QCLASS::Internet, DNS_QTYPE::AAAA) => { + let (input, num) = number::streaming::be_u128(input)?; + Ok(( + input, + DNS_RR_DATA::AAAA { + address: address_v6_decode(num), + }, + )) + } + (_, DNS_QTYPE::CNAME) => { + let (input, (name, _size)) = dname_decode(input, labels, position)?; + Ok(( + input, + DNS_RR_DATA::CNAME { + cname: name.to_string(), + }, + )) + } + (_, DNS_QTYPE::HINFO) => { + let (input, _cpu) = multi::length_data(number::streaming::be_u8)(input)?; + let (input, _os) = multi::length_data(number::streaming::be_u8)(input)?; + Ok(( + input, + DNS_RR_DATA::HINFO { + cpu: _cpu.to_vec(), + os: _os.to_vec(), + }, + )) + } + (_, DNS_QTYPE::MX) => { + let (input, _preference) = number::streaming::be_u16(input)?; + let (input, (name, _size)) = dname_decode(input, labels, position + 2)?; + Ok(( + input, + DNS_RR_DATA::MX { + preference: _preference, + exchange: name.to_string(), + }, + )) + } + (_, DNS_QTYPE::NS) => { + let (input, (name, _size)) = dname_decode(input, labels, position)?; + Ok(( + input, + DNS_RR_DATA::NS { + nsdname: name.to_string(), + }, + )) + } + (_, DNS_QTYPE::PTR) => { + let (input, (name, _size)) = dname_decode(input, labels, position)?; + Ok(( + input, + DNS_RR_DATA::PTR { + ptrdnmae: name.to_string(), + }, + )) + } + (_, DNS_QTYPE::SOA) => { + let (input, (_mname, mname_size)) = dname_decode(input, labels, position)?; + let mname_string = _mname.to_string(); + let (input, (_rname, _rname_size)) = + dname_decode(input, labels, position + mname_size)?; + let (input, _serial) = number::streaming::be_u32(input)?; + let (input, _refresh) = number::streaming::be_i32(input)?; + let (input, _retry) = number::streaming::be_i32(input)?; + let (input, _expire) = number::streaming::be_i32(input)?; + let (input, _minimum) = number::streaming::be_i32(input)?; + Ok(( + input, + DNS_RR_DATA::SOA { + mname: mname_string, + rname: _rname.to_string(), + serial: _serial, + refresh: _refresh, + retry: _retry, + expire: _expire, + minimum: _minimum, + }, + )) + } + (_, DNS_QTYPE::TXT) => { + let mut input = input; + let mut nleft = rr_hdr.rd_length; + let mut ret = Vec::new(); + loop { + let (__input, data) = character_string_decode(input)?; + ret.push(str::from_utf8(data).unwrap().to_string()); + + input = __input; + nleft = nleft - 1 - (data.len() as u16); + + if nleft < 2 { + break; + } + } + Ok((input, DNS_RR_DATA::TXT { txtdata: ret })) + } + (_, _) => { + let slen: usize = rr_hdr.rd_length.into(); + let rdata = &input[0..slen]; + Ok(( + &input[slen..], + DNS_RR_DATA::Other { + qtype: rr_hdr.rr_type, + data: rdata.to_vec(), + }, + )) + } + } + } +} + +/****************************** DNS_RR_SECTION ******************************/ + +#[allow(non_camel_case_types)] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct DNS_RR_SECTION { + pub hdr: DNS_RR_HDR, + pub data: DNS_RR_DATA, +} + +impl DNS_RR_SECTION { + fn decode<'a, 'b>( + input: &'a [u8], + labels: &'b mut DNS_LABEL_TABLE, + position: usize, + ) -> IResult<&'a [u8], DNS_RR_SECTION> { + let (left, rr_hdr) = DNS_RR_HDR::decode(input, labels, position)?; + let (left, rr_data) = DNS_RR_DATA::decode(left, labels, position + rr_hdr.size, &rr_hdr)?; + + Ok(( + left, + DNS_RR_SECTION { + hdr: rr_hdr, + data: rr_data, + }, + )) + } +} + +impl Display for DNS_RR_SECTION { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}\t\t{}\t{}\t{}\t", + self.hdr.qname, self.hdr.ttl, self.hdr.rr_class, self.hdr.rr_type + )?; + match &self.data { + DNS_RR_DATA::A { address: _address } => { + write!(f, "{}\n", _address) + } + DNS_RR_DATA::AAAA { address: _address } => { + write!(f, "{}\n", _address) + } + DNS_RR_DATA::CNAME { cname: _cname } => { + write!(f, "{}\n", _cname) + } + DNS_RR_DATA::HINFO { cpu: _cpu, os: _os } => { + write!(f, "\\# ")?; + for i in &*_cpu { + write!(f, "{:02X}", i)?; + } + write!(f, "\n\\# ")?; + for i in &*_os { + write!(f, "{:02X}", i)?; + } + write!(f, "\n") + } + DNS_RR_DATA::MX { + preference: _preference, + exchange: _exchange, + } => { + write!(f, "{} {}\n", _preference, _exchange) + } + DNS_RR_DATA::NS { nsdname: _nsdname } => { + write!(f, "{}\n", _nsdname) + } + DNS_RR_DATA::PTR { + ptrdnmae: _ptrdnmae, + } => { + write!(f, "{}\n", _ptrdnmae) + } + DNS_RR_DATA::SOA { + mname: _mname, + rname: _rname, + serial: _serial, + refresh: _refresh, + retry: _retry, + expire: _expire, + minimum: _minimum, + } => { + write!( + f, + "{} {} {} {} {} {} {}\n", + _mname, _rname, _serial, _refresh, _retry, _expire, _minimum, + ) + } + DNS_RR_DATA::TXT { txtdata: _txtdata } => { + for i in _txtdata { + write!(f, "{}", i)?; + } + Ok(()) + } + DNS_RR_DATA::Other { + qtype: _qtype, + data: _data, + } => { + write!(f, "\\# ")?; + for i in &*_data { + write!(f, "{:02X}", i)?; + } + write!(f, "\n") + } + } + } +} + +/****************************************************************************** + * DNS Message + ******************************************************************************/ + +/****************************** DNS_LABEL_TYPE ******************************/ + +// https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-10 +#[allow(non_camel_case_types)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum DNS_LABEL_TYPE { + Normal(usize), // Normal label lower 6 bits is the length of the label + Pointer(usize), // Compressed label the lower 6 bits and the 8 bits from next octet form a pointer to the compression target + End, + Other, +} + +impl DNS_LABEL_TYPE { + fn peek(input: &[u8]) -> IResult<&[u8], (u8, u8)> { + combinator::peek(bits::<_, _, Error<(&[u8], usize)>, _, _>(sequence::tuple( + (bits::streaming::take(1usize), bits::streaming::take(1usize)), + )))(input) + } + + fn decode(input: &[u8]) -> IResult<&[u8], DNS_LABEL_TYPE> { + match Self::peek(input) { + // 域名压缩 + Ok((_, (1u8, 1u8))) => bits::<_, _, Error<(&[u8], usize)>, _, _>(sequence::tuple(( + bits::streaming::take(1usize), + bits::streaming::take(1usize), + bits::streaming::take(6usize), + bits::streaming::take(8usize), + )))(input) + .map(|(input, (_, _, left, right)): (&[u8], (u8, u8, u8, u8))| { + ( + input, + DNS_LABEL_TYPE::Pointer((((left as u16) << 8) | right as u16) as usize), + ) + }), + // 域名未压缩 + Ok((_, (0u8, 0u8))) => bits::<_, _, Error<(&[u8], usize)>, _, _>(sequence::tuple(( + bits::streaming::take(1usize), + bits::streaming::take(1usize), + bits::streaming::take(6usize), + )))(input) + .map(|(input, (_, _, size)): (&[u8], (u8, u8, u8))| { + if size == 0 { + // 域名未压缩时,需要在域名最后加 0x00 表示名域名结束 + // 域名压缩时,就不用在最后加 0x00 了 + return (input, DNS_LABEL_TYPE::End); + } else { + return (input, DNS_LABEL_TYPE::Normal(size as usize)); + } + }), + _ => Ok((input, DNS_LABEL_TYPE::Other)), + } + } +} + +/****************************** DNS_LABEL_TABLE ******************************/ + +#[allow(non_camel_case_types)] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct DNS_LABEL_TABLE { + pub labels: Vec<((usize, usize), String)>, +} + +impl DNS_LABEL_TABLE { + fn new() -> DNS_LABEL_TABLE { + DNS_LABEL_TABLE { labels: vec![] } + } + + /* + * position : byte position in message + * size : byte size in message (can be smaller than domain's size) + * domain : the full domain + */ + fn set(&mut self, position: usize, size: usize, domain: &str) -> &str { + self.labels.push(((position, size), domain.to_string())); + &self.labels.last().unwrap().1 + } + + fn get(&self, position: usize) -> Option<&str> { + // labels 为空 + if self.labels.len() == 0 { + return None; + } + + // position 太小 + let mut cursor = self.labels.first().unwrap(); + if position < cursor.0 .0 { + return None; + } + + let mut index: usize = 0; + while position >= (cursor.0 .0 + cursor.0 .1) { + cursor = match self.labels.get(index + 1) { + Some(c) => c, + _ => { + return None; + } + }; + index += 1; + } + + if position < cursor.0 .0 { + // hole + return None; + } + + if position == cursor.0 .0 { + return Some(&cursor.1); + } + + if cursor.1.as_bytes()[position - cursor.0 .0 - 1] == b'.' { + return Some(cursor.1.split_at(position - cursor.0 .0).1); + } + None + } +} + +/****************************** DNS_MESSAGE ******************************/ + +#[allow(non_camel_case_types)] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct DNS_MESSAGE { + pub header: DNS_HEADER, + labels: DNS_LABEL_TABLE, + pub qd: Vec<DNS_QUESTION_SECTION>, + pub an: Vec<DNS_RR_SECTION>, + pub ns: Vec<DNS_RR_SECTION>, + pub ar: Vec<DNS_RR_SECTION>, +} + +impl DNS_MESSAGE { + fn new(header: DNS_HEADER) -> DNS_MESSAGE { + DNS_MESSAGE { + header: header, + labels: DNS_LABEL_TABLE::new(), + qd: vec![], + an: vec![], + ns: vec![], + ar: vec![], + } + } + + pub fn decode(input: &[u8]) -> IResult<&[u8], DNS_MESSAGE> { + let (mut input, header) = DNS_HEADER::decode(input)?; + let mut message = DNS_MESSAGE::new(header); + let mut position = 12; + + // QDCOUNT + for _ in 0..header.qd_count { + let (left, qd) = DNS_QUESTION_SECTION::decode(input, &mut message.labels, position)?; + input = left; + position += qd.size; + message.qd.push(qd); + } + + // ANCOUNT + for _ in 0..header.an_count { + let (left, rr) = DNS_RR_SECTION::decode(input, &mut message.labels, position)?; + position += rr.hdr.size + rr.hdr.rd_length as usize; + + message.an.push(rr); + input = left; + } + + // NSCOUNT + for _ in 0..header.ns_count { + let (left, rr) = DNS_RR_SECTION::decode(input, &mut message.labels, position)?; + position += rr.hdr.size + rr.hdr.rd_length as usize; + + message.ns.push(rr); + input = left; + } + + // ARCOUNT + for _ in 0..header.ar_count { + let (left, rr) = DNS_RR_SECTION::decode(input, &mut message.labels, position)?; + position += rr.hdr.size + rr.hdr.rd_length as usize; + + message.ar.push(rr); + input = left; + } + + Ok((input, message)) + } +} + +impl Display for DNS_MESSAGE { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}\n", self.header)?; + if self.qd.len() > 0 { + write!(f, ";; QUESTION SECTION:\n")?; + for qde in &self.qd { + write!(f, "{}\n", qde)?; + } + } + if self.an.len() > 0 { + write!(f, ";; ANSWER SECTION:\n")?; + for ane in &self.an { + ane.fmt(f)?; + } + } + if self.ns.len() > 0 { + write!(f, ";; AUTHORITY SECTION:\n")?; + for nse in &self.ns { + nse.fmt(f)?; + } + } + if self.ar.len() > 0 { + write!(f, ";; ADDITIONAL SECTION:\n")?; + for are in &self.ar { + are.fmt(f)?; + } + } + Ok(()) + } +} + +/****************************************************************************** + * Utils + ******************************************************************************/ + +fn address_v4_decode(n: u32) -> String { + let ipb = n.to_be_bytes(); + format!("{}.{}.{}.{}", ipb[0], ipb[1], ipb[2], ipb[3]) +} + +fn address_v6_decode(n: u128) -> String { + let ipb = n.to_be_bytes(); + format!("{:02X}{:02X}:{:02X}{:02X}:{:02X}{:02X}:{:02X}{:02X}:{:02X}{:02X}:{:02X}{:02X}:{:02X}{:02X}:{:02X}{:02X}", ipb[0], ipb[1], ipb[2], ipb[3], ipb[4], ipb[5], ipb[6], ipb[7], ipb[8], ipb[9], ipb[10], ipb[11], ipb[12], ipb[13], ipb[14], ipb[15]) +} + +/* https://www.ietf.org/rfc/rfc1035.txt + * + * 3.3. Standard RRs + * + * <character-string> is a single length octet followed by that number of characters. + * <character-string> is treated as binary information, and can be up to 256 characters in length (including the length octet). + */ +fn character_string_decode(input: &[u8]) -> IResult<&[u8], &[u8]> { + multi::length_data(number::streaming::be_u8)(input) +} + +/* + * https://www.ietf.org/rfc/rfc1035.txt + * + * 2.3.1. Preferred name syntax + * + * The labels must follow the rules for ARPANET host names. They must + * start with a letter, end with a letter or digit, and have as interior + * characters only letters, digits, and hyphen. There are also some + * restrictions on the length. Labels must be 63 characters or less. + */ +// 为了解析出更多的域名,此处未严格遵守 RFC 中规定的 “字母” “数字” 和 “-” 的顺序,并增加了 ‘_’ 字符 +fn label_character_verify(c: u8) -> bool { + character::is_alphabetic(c) || character::is_digit(c) || (c == b'-') || (c == b'_') +} + +fn label_decode<'a>( + input: &'a [u8], + size: usize, + name: &mut String, +) -> IResult<&'a [u8], &'static str> { + if input.len() < size { + return Err(Err::Incomplete(Needed::new(size - input.len()))); + } + + for i in 0..size { + if !label_character_verify(input[i]) { + return Err(Err::Failure(Error::new(input, ErrorKind::Fail))); + } + } + + name.push_str(str::from_utf8(&input[0..size]).unwrap()); + return Ok((input.get(size..).unwrap(), "OK")); +} + +/* + * https://www.ietf.org/rfc/rfc1035.txt + * + * 4.1.4. Message compression + * + * The compression scheme allows a domain name in a message to be represented as either: + * + * - a sequence of labels ending in a zero octet + * - a pointer + * - a sequence of labels ending with a pointer + */ +fn dname_decode<'a, 'b>( + data: &'a [u8], + labels: &'b mut DNS_LABEL_TABLE, + position: usize, +) -> IResult<&'a [u8], (&'b str, usize)> { + let mut input = data; + let mut total_size = 0usize; + let mut name = String::new(); + loop { + match DNS_LABEL_TYPE::decode(input) { + Ok((inner_input, label_type)) => match label_type { + // 域名未压缩 + DNS_LABEL_TYPE::Normal(size) => { + total_size += 1 + size; + if name.len() > 0 { + name.push('.'); + } + + let (left, _) = label_decode(inner_input, size, &mut name)?; + input = left; + } + // 域名压缩 + DNS_LABEL_TYPE::Pointer(_position) => { + total_size += 2; + if name.len() > 0 { + name.push('.'); + } + + let label = match labels.get(_position) { + Some(label) => label, + _ => { + return Err(Err::Failure(Error::new(inner_input, ErrorKind::Fail))); + } + }; + name.push_str(label); + let n = labels.set(position, total_size, &name); + return Ok((inner_input, (n, total_size))); + } + // 域名未压缩 + DNS_LABEL_TYPE::End => { + total_size += 1; + name.push('.'); + + let n = labels.set(position, total_size, &name); + return Ok((inner_input, (n, total_size))); + } + DNS_LABEL_TYPE::Other => { + return Err(Err::Failure(Error::new(inner_input, ErrorKind::Fail))); + } + }, + _ => { + return Err(Err::Failure(Error::new(input, ErrorKind::Fail))); + } + } + } +} + +/****************************************************************************** + * TEST + ******************************************************************************/ + +/* +fn main() -> io::Result<()> { + // dig @1.1.1.1 A www.kpn.com + let id = 52749u16; + let query_flags = 0b0_0000_0010_000_0000u16; + let counts = [0u8, 1u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8]; + // let qname = [0b00000011u8, b'w', b'w', b'w', 0b00000011u8, b'k', b'p', b'n', 0b00000011u8, b'c', b'o', b'm', 0b00000000u8]; + let qname = [0b00000011u8, b'k', b'p', b'n', 0b00000011u8, b'c', b'o', b'm', 0b00000000u8]; + // let qname = [0b00001010u8, b'q', b'u', b'a', b't', b'e', b'r', b'n', b'i', b'o', b'n', 0b00000101u8, b's', b'p', b'a', b'c', b'e', 0b00000000u8]; + let qtype = 48u16; + let qclass = 1u16; + let mut query_msg = vec![]; + query_msg.extend_from_slice(&id.to_be_bytes()); + query_msg.extend_from_slice(&query_flags.to_be_bytes()); + query_msg.extend_from_slice(&counts); + query_msg.extend_from_slice(&qname); + query_msg.extend_from_slice(&qtype.to_be_bytes()); + query_msg.extend_from_slice(&qclass.to_be_bytes()); + let socket = UdpSocket::bind("0.0.0.0:34254").expect("Failed to open UDP connection."); + socket.connect(("1.1.1.1", 53)).expect("Failed to connect to server."); + socket.send(&query_msg).expect("Failed to send query to server."); + let mut buf = [0; 512]; + let input = match socket.recv(&mut buf) { + Ok(received) => { + // println!("received {} bytes {:?}\n", received, &buf[..received]); + &buf[..received] + }, + Err(_e) => { + // println!("recv function failed: {:?}\n", e); + return Ok(()); + }, + }; + match DNS_MESSAGE::decode(input) { + Ok((_rest, message)) => { + println!("{}", message); + Ok(()) + }, + Err(err) => { + println!("err:{:?}\n", err); + Ok(()) + }, + + } +} +*/ + +#[cfg(test)] +mod tests { + use super::character_string_decode; + use super::dname_decode; + use super::label_decode; + use super::DNS_HDR_OPCODE; + use super::DNS_HDR_QR; + use super::DNS_HDR_RCODE; + use super::DNS_HEADER; + use super::DNS_LABEL_TABLE; + use super::DNS_MESSAGE; + use super::DNS_QCLASS; + use super::DNS_QTYPE; + use super::DNS_QUESTION_SECTION; + use super::DNS_RR_DATA; + use super::DNS_RR_HDR; + use super::DNS_RR_SECTION; + + const LAST_SLICE: &'static [u8] = &[]; + + /************************************************************************** + * test util + **************************************************************************/ + + #[test] + fn test_character_string_decode() { + let input = [0b00000101u8, b'h', b'i', b'!', b'!', 0b11000000u8]; + let result = character_string_decode(&input); + assert_eq!( + result, + Ok((&[][..], &[b'h', b'i', b'!', b'!', 0b11000000u8][..])) + ); + + let input = [0b00000101u8, 0b11000000u8]; + let result = character_string_decode(&input); + assert_eq!(result, Err(nom::Err::Incomplete(nom::Needed::new(4)))); + } + + #[test] + fn test_label_table() { + let mut table = DNS_LABEL_TABLE::new(); + table.set(30, 16, "www.example.com."); + + assert_eq!(table.get(29), None); + assert_eq!(table.get(30), Some("www.example.com.")); + assert_eq!(table.get(31), None); + assert_eq!(table.get(32), None); + assert_eq!(table.get(33), None); + assert_eq!(table.get(34), Some("example.com.")); + assert_eq!(table.get(35), None); + assert_eq!(table.get(36), None); + assert_eq!(table.get(37), None); + assert_eq!(table.get(38), None); + assert_eq!(table.get(39), None); + assert_eq!(table.get(40), None); + assert_eq!(table.get(41), None); + assert_eq!(table.get(42), Some("com.")); + assert_eq!(table.get(43), None); + } + + #[test] + fn test_label_decode() { + /* + * [letter] -> OK + * [digit] -> OK + * [-] -> OK + * [other] -> Err + */ + + let buf1 = &[b'a']; // OK + let buf2 = &[b'1']; // OK + let buf3 = &[b'-']; // OK + let buf4 = &[b'_']; // OK + let buf5 = &[b'+']; // Err + + let mut str = String::new(); + let ret = label_decode(buf1, 1, &mut str); + assert_eq!(str, "a"); + assert_eq!(ret, Ok((LAST_SLICE, "OK"))); + + let mut str = String::new(); + let ret = label_decode(buf2, 1, &mut str); + assert_eq!(str, "1"); + assert_eq!(ret, Ok((LAST_SLICE, "OK"))); + + let mut str = String::new(); + let ret = label_decode(buf3, 1, &mut str); + assert_eq!(str, "-"); + assert_eq!(ret, Ok((LAST_SLICE, "OK"))); + + let mut str = String::new(); + let ret = label_decode(buf4, 1, &mut str); + assert_eq!(str, "_"); + assert_eq!(ret, Ok((LAST_SLICE, "OK"))); + + let mut str = String::new(); + let ret = label_decode(buf5, 1, &mut str); + assert_eq!(str, ""); + assert_ne!(ret, Ok((LAST_SLICE, "OK"))); + } + + #[test] + fn test_dname_decode() { + let mut table = DNS_LABEL_TABLE::new(); + + // uncompress name: www.example.com + let input1 = [ + 0x03, // uncompress and lable length 3 + 0x77, 0x77, 0x77, // www + 0x07, // lable length 7 + 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, // example + 0x03, // lable length 3 + 0x63, 0x6f, 0x6d, // com + 0x00, // label length 0 + ]; + + let result = dname_decode(&input1, &mut table, 10); + assert_eq!(result, Ok((LAST_SLICE, ("www.example.com.", 17)))); + assert_eq!(table.get(10), Some("www.example.com.")); + + // NOTE: second_msg_position >= first_msg_position + data_length + + // compress name: www.example.com + let input2 = [0b11000000u8, 0b00001010u8]; // Pointer to 10 + let result = dname_decode(&input2, &mut table, 50); + assert_eq!(result, Ok((LAST_SLICE, ("www.example.com.", 2)))); + assert_eq!(table.get(50), Some("www.example.com.")); + } + + /************************************************************************** + * test decode query + **************************************************************************/ + + #[test] + fn test_query_a_decode() { + /* + * dig www.baidu.com + * + * Domain Name System (query) + * Transaction ID: 0xa59a + * Flags: 0x0120 Standard query + * 0... .... .... .... = Response: Message is a query + * .000 0... .... .... = Opcode: Standard query (0) + * .... ..0. .... .... = Truncated: Message is not truncated + * .... ...1 .... .... = Recursion desired: Do query recursively + * .... .... .0.. .... = Z: reserved (0) + * .... .... ..1. .... = AD bit: Set + * .... .... ...0 .... = Non-authenticated data: Unacceptable + * Questions: 1 + * Answer RRs: 0 + * Authority RRs: 0 + * Additional RRs: 1 + * Queries + * www.baidu.com: type A, class IN + * Name: www.baidu.com + * [Name Length: 13] + * [Label Count: 3] + * Type: A (Host Address) (1) + * Class: IN (0x0001) + * Additional records + * <Root>: type OPT + * Name: <Root> + * Type: OPT (41) + * UDP payload size: 4096 + * Higher bits in extended RCODE: 0x00 + * EDNS0 version: 0 + * Z: 0x0000 + * 0... .... .... .... = DO bit: Cannot handle DNSSEC security RRs + * .000 0000 0000 0000 = Reserved: 0x0000 + * Data length: 0 + */ + let bytes = [ + 0xa5, 0x9a, /* Transaction ID: 0xa59a */ + 0x01, 0x20, /* Flags: 0x0120 Standard query */ + 0x00, 0x01, /* Questions: 1 */ + 0x00, 0x00, /* Answer RRs: 0 */ + 0x00, 0x00, /* Authority RRs: 0 */ + 0x00, 0x01, /* Additional RRs: 1 */ + 0x03, 0x77, 0x77, 0x77, 0x05, 0x62, 0x61, 0x69, 0x64, 0x75, 0x03, 0x63, 0x6f, 0x6d, + 0x00, /* Name: www.baidu.com */ + 0x00, 0x01, /* Type: A (Host Address) (1) */ + 0x00, 0x01, /* Type: A (Host Address) (1) */ + 0x00, /* Name: <Root> */ + 0x00, 0x29, /* Type: OPT (41) */ + 0x10, 0x00, /* UDP payload size: 4096 */ + 0x00, /* Higher bits in extended RCODE: 0x00 */ + 0x00, /* EDNS0 version: 0 */ + 0x00, 0x00, /* Z: 0x0000 */ + 0x00, 0x00, /* Data length: 0 */ + ]; + + let expectation = DNS_MESSAGE { + header: DNS_HEADER { + id: 0xa59a, + qr: DNS_HDR_QR::Query, + op_code: DNS_HDR_OPCODE::Query, + aa: false, + tc: false, + rd: true, + ra: false, + ad: true, + cd: false, + r_code: DNS_HDR_RCODE::NoError, + qd_count: 1, + an_count: 0, + ns_count: 0, + ar_count: 1, + }, + labels: DNS_LABEL_TABLE { + labels: vec![ + ((12, 15), "www.baidu.com.".to_string()), + ((31, 1), ".".to_string()), + ], + }, + qd: vec![DNS_QUESTION_SECTION { + qname: "www.baidu.com.".to_string(), + qtype: DNS_QTYPE::A, + qclass: DNS_QCLASS::Internet, + size: 19, + }], + an: vec![], + ns: vec![], + ar: vec![ + (DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: ".".to_string(), + rr_type: DNS_QTYPE::OPT, + rr_class: DNS_QCLASS::Other(4096), + ttl: 0, + rd_length: 0, + size: 11, + }, + data: DNS_RR_DATA::Other { + qtype: DNS_QTYPE::OPT, + data: vec![], + }, + }), + ], + }; + + assert_eq!(DNS_MESSAGE::decode(&bytes), Ok((LAST_SLICE, expectation))); + + match DNS_MESSAGE::decode(&bytes) { + Ok((left, message)) => { + println!("{:?}; left {:?}", message, left); + } + Err(err) => { + println!("{:?}\n", err); + } + } + // assert_eq!(0, 1); + } + + #[test] + fn test_query_aaaa_decode() { + /* + * dig www.baidu.com aaaa + * + * Domain Name System (query) + * Transaction ID: 0xf086 + * Flags: 0x0120 Standard query + * 0... .... .... .... = Response: Message is a query + * .000 0... .... .... = Opcode: Standard query (0) + * .... ..0. .... .... = Truncated: Message is not truncated + * .... ...1 .... .... = Recursion desired: Do query recursively + * .... .... .0.. .... = Z: reserved (0) + * .... .... ..1. .... = AD bit: Set + * .... .... ...0 .... = Non-authenticated data: Unacceptable + * Questions: 1 + * Answer RRs: 0 + * Authority RRs: 0 + * Additional RRs: 1 + * Queries + * www.baidu.com: type AAAA, class IN + * Name: www.baidu.com + * [Name Length: 13] + * [Label Count: 3] + * Type: AAAA (IPv6 Address) (28) + * Class: IN (0x0001) + * Additional records + * <Root>: type OPT + * Name: <Root> + * Type: OPT (41) + * UDP payload size: 4096 + * Higher bits in extended RCODE: 0x00 + * EDNS0 version: 0 + * Z: 0x0000 + * 0... .... .... .... = DO bit: Cannot handle DNSSEC security RRs + * .000 0000 0000 0000 = Reserved: 0x0000 + * Data length: 0 + */ + + let bytes = [ + 0xf0, 0x86, /* Transaction ID: 0xf086 */ + 0x01, 0x20, /* Flags: 0x0120 Standard query */ + 0x00, 0x01, /* Questions: 1 */ + 0x00, 0x00, /* Answer RRs: 0 */ + 0x00, 0x00, /* Authority RRs: 0 */ + 0x00, 0x01, /* Additional RRs: 1 */ + 0x03, 0x77, 0x77, 0x77, 0x05, 0x62, 0x61, 0x69, 0x64, 0x75, 0x03, 0x63, 0x6f, 0x6d, + 0x00, /* Name: www.baidu.com */ + 0x00, 0x1c, /* Type: AAAA (IPv6 Address) (28) */ + 0x00, 0x01, /* Class: IN (0x0001) */ + 0x00, /* Name: <Root> */ + 0x00, 0x29, /* Type: OPT (41) */ + 0x10, 0x00, /* UDP payload size: 4096 */ + 0x00, /* Higher bits in extended RCODE: 0x00 */ + 0x00, /* EDNS0 version: 0 */ + 0x00, 0x00, /* Z: 0x0000 */ + 0x00, 0x00, /* Data length: 0 */ + ]; + + let expectation = DNS_MESSAGE { + header: DNS_HEADER { + id: 0xf086, + qr: DNS_HDR_QR::Query, + op_code: DNS_HDR_OPCODE::Query, + aa: false, + tc: false, + rd: true, + ra: false, + ad: true, + cd: false, + r_code: DNS_HDR_RCODE::NoError, + qd_count: 1, + an_count: 0, + ns_count: 0, + ar_count: 1, + }, + labels: DNS_LABEL_TABLE { + labels: vec![ + ((12, 15), "www.baidu.com.".to_string()), + ((31, 1), ".".to_string()), + ], + }, + qd: vec![DNS_QUESTION_SECTION { + qname: "www.baidu.com.".to_string(), + qtype: DNS_QTYPE::AAAA, + qclass: DNS_QCLASS::Internet, + size: 19, + }], + an: vec![], + ns: vec![], + ar: vec![ + (DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: ".".to_string(), + rr_type: DNS_QTYPE::OPT, + rr_class: DNS_QCLASS::Other(4096), + ttl: 0, + rd_length: 0, + size: 11, + }, + data: DNS_RR_DATA::Other { + qtype: DNS_QTYPE::OPT, + data: vec![], + }, + }), + ], + }; + + assert_eq!(DNS_MESSAGE::decode(&bytes), Ok((LAST_SLICE, expectation))); + + match DNS_MESSAGE::decode(&bytes) { + Ok((left, message)) => { + println!("{:?}; left {:?}", message, left); + } + Err(err) => { + println!("{:?}\n", err); + } + } + // assert_eq!(0, 1); + } + + #[test] + fn test_query_cname_decode() { + /* + * dig www.baidu.com cname + * + * Domain Name System (query) + * Transaction ID: 0x61a5 + * Flags: 0x0120 Standard query + * 0... .... .... .... = Response: Message is a query + * .000 0... .... .... = Opcode: Standard query (0) + * .... ..0. .... .... = Truncated: Message is not truncated + * .... ...1 .... .... = Recursion desired: Do query recursively + * .... .... .0.. .... = Z: reserved (0) + * .... .... ..1. .... = AD bit: Set + * .... .... ...0 .... = Non-authenticated data: Unacceptable + * Questions: 1 + * Answer RRs: 0 + * Authority RRs: 0 + * Additional RRs: 1 + * Queries + * www.baidu.com: type CNAME, class IN + * Name: www.baidu.com + * [Name Length: 13] + * [Label Count: 3] + * Type: CNAME (Canonical NAME for an alias) (5) + * Class: IN (0x0001) + * Additional records + * <Root>: type OPT + * Name: <Root> + * Type: OPT (41) + * UDP payload size: 4096 + * Higher bits in extended RCODE: 0x00 + * EDNS0 version: 0 + * Z: 0x0000 + * 0... .... .... .... = DO bit: Cannot handle DNSSEC security RRs + * .000 0000 0000 0000 = Reserved: 0x0000 + * Data length: 0 + */ + + let bytes = [ + 0x61, 0xa5, /* Transaction ID: 0x61a5 */ + 0x01, 0x20, /* Flags: 0x0120 Standard query */ + 0x00, 0x01, /* Questions: 1 */ + 0x00, 0x00, /* Answer RRs: 0 */ + 0x00, 0x00, /* Authority RRs: 0 */ + 0x00, 0x01, /* Additional RRs: 1 */ + 0x03, 0x77, 0x77, 0x77, 0x05, 0x62, 0x61, 0x69, 0x64, 0x75, 0x03, 0x63, 0x6f, 0x6d, + 0x00, /* Name: www.baidu.com */ + 0x00, 0x05, /* Type: CNAME (Canonical NAME for an alias) (5) */ + 0x00, 0x01, /* Class: IN (0x0001) */ + 0x00, /* Name: <Root> */ + 0x00, 0x29, /* Type: OPT (41) */ + 0x10, 0x00, /* UDP payload size: 4096 */ + 0x00, /* Higher bits in extended RCODE: 0x00 */ + 0x00, /* EDNS0 version: 0 */ + 0x00, 0x00, /* Z: 0x0000 */ + 0x00, 0x00, /* Data length: 0 */ + ]; + + let expectation = DNS_MESSAGE { + header: DNS_HEADER { + id: 0x61a5, + qr: DNS_HDR_QR::Query, + op_code: DNS_HDR_OPCODE::Query, + aa: false, + tc: false, + rd: true, + ra: false, + ad: true, + cd: false, + r_code: DNS_HDR_RCODE::NoError, + qd_count: 1, + an_count: 0, + ns_count: 0, + ar_count: 1, + }, + labels: DNS_LABEL_TABLE { + labels: vec![ + ((12, 15), "www.baidu.com.".to_string()), + ((31, 1), ".".to_string()), + ], + }, + qd: vec![DNS_QUESTION_SECTION { + qname: "www.baidu.com.".to_string(), + qtype: DNS_QTYPE::CNAME, + qclass: DNS_QCLASS::Internet, + size: 19, + }], + an: vec![], + ns: vec![], + ar: vec![ + (DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: ".".to_string(), + rr_type: DNS_QTYPE::OPT, + rr_class: DNS_QCLASS::Other(4096), + ttl: 0, + rd_length: 0, + size: 11, + }, + data: DNS_RR_DATA::Other { + qtype: DNS_QTYPE::OPT, + data: vec![], + }, + }), + ], + }; + + assert_eq!(DNS_MESSAGE::decode(&bytes), Ok((LAST_SLICE, expectation))); + + match DNS_MESSAGE::decode(&bytes) { + Ok((left, message)) => { + println!("{:?}; left {:?}", message, left); + } + Err(err) => { + println!("{:?}\n", err); + } + } + // assert_eq!(0, 1); + } + + // TODO + #[test] + fn test_query_hinfo_decode() {} + + #[test] + fn test_query_mx_decode() { + /* + * dig 163.com mx + * + * Domain Name System (query) + * Transaction ID: 0xd35c + * Flags: 0x0120 Standard query + * 0... .... .... .... = Response: Message is a query + * .000 0... .... .... = Opcode: Standard query (0) + * .... ..0. .... .... = Truncated: Message is not truncated + * .... ...1 .... .... = Recursion desired: Do query recursively + * .... .... .0.. .... = Z: reserved (0) + * .... .... ..1. .... = AD bit: Set + * .... .... ...0 .... = Non-authenticated data: Unacceptable + * Questions: 1 + * Answer RRs: 0 + * Authority RRs: 0 + * Additional RRs: 1 + * Queries + * 163.com: type MX, class IN + * Name: 163.com + * [Name Length: 7] + * [Label Count: 2] + * Type: MX (Mail eXchange) (15) + * Class: IN (0x0001) + * Additional records + * <Root>: type OPT + * Name: <Root> + * Type: OPT (41) + * UDP payload size: 4096 + * Higher bits in extended RCODE: 0x00 + * EDNS0 version: 0 + * Z: 0x0000 + * 0... .... .... .... = DO bit: Cannot handle DNSSEC security RRs + * .000 0000 0000 0000 = Reserved: 0x0000 + * Data length: 0 + */ + + let bytes = [ + 0xd3, 0x5c, /* Transaction ID: 0xd35c */ + 0x01, 0x20, /* Flags: 0x0120 Standard query */ + 0x00, 0x01, /* Questions: 1 */ + 0x00, 0x00, /* Answer RRs: 0 */ + 0x00, 0x00, /* Authority RRs: 0 */ + 0x00, 0x01, /* Additional RRs: 1 */ + 0x03, 0x31, 0x36, 0x33, 0x03, 0x63, 0x6f, 0x6d, 0x00, /* Name: 163.com */ + 0x00, 0x0f, /* Type: MX (Mail eXchange) (15) */ + 0x00, 0x01, /* Class: IN (0x0001) */ + 0x00, /* Name: <Root> */ + 0x00, 0x29, /* Type: OPT (41) */ + 0x10, 0x00, /* UDP payload size: 4096 */ + 0x00, /* Higher bits in extended RCODE: 0x00 */ + 0x00, /* EDNS0 version: 0 */ + 0x00, 0x00, /* Z: 0x0000 */ + 0x00, 0x00, /* Data length: 0 */ + ]; + + let expectation = DNS_MESSAGE { + header: DNS_HEADER { + id: 0xd35c, + qr: DNS_HDR_QR::Query, + op_code: DNS_HDR_OPCODE::Query, + aa: false, + tc: false, + rd: true, + ra: false, + ad: true, + cd: false, + r_code: DNS_HDR_RCODE::NoError, + qd_count: 1, + an_count: 0, + ns_count: 0, + ar_count: 1, + }, + labels: DNS_LABEL_TABLE { + labels: vec![ + ((12, 9), "163.com.".to_string()), + ((25, 1), ".".to_string()), + ], + }, + qd: vec![DNS_QUESTION_SECTION { + qname: "163.com.".to_string(), + qtype: DNS_QTYPE::MX, + qclass: DNS_QCLASS::Internet, + size: 13, + }], + an: vec![], + ns: vec![], + ar: vec![ + (DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: ".".to_string(), + rr_type: DNS_QTYPE::OPT, + rr_class: DNS_QCLASS::Other(4096), + ttl: 0, + rd_length: 0, + size: 11, + }, + data: DNS_RR_DATA::Other { + qtype: DNS_QTYPE::OPT, + data: vec![], + }, + }), + ], + }; + + assert_eq!(DNS_MESSAGE::decode(&bytes), Ok((LAST_SLICE, expectation))); + + match DNS_MESSAGE::decode(&bytes) { + Ok((left, message)) => { + println!("{:?}; left {:?}", message, left); + } + Err(err) => { + println!("{:?}\n", err); + } + } + // assert_eq!(0, 1); + } + + // TODO + #[test] + fn test_query_nx_decode() {} + + // TODO + #[test] + fn test_query_ptr_decode() {} + + #[test] + fn test_query_soa_decode() { + /* + * dig www.baidu.com soa + * + * Domain Name System (query) + * Transaction ID: 0xb613 + * Flags: 0x0120 Standard query + * 0... .... .... .... = Response: Message is a query + * .000 0... .... .... = Opcode: Standard query (0) + * .... ..0. .... .... = Truncated: Message is not truncated + * .... ...1 .... .... = Recursion desired: Do query recursively + * .... .... .0.. .... = Z: reserved (0) + * .... .... ..1. .... = AD bit: Set + * .... .... ...0 .... = Non-authenticated data: Unacceptable + * Questions: 1 + * Answer RRs: 0 + * Authority RRs: 0 + * Additional RRs: 1 + * Queries + * www.baidu.com: type SOA, class IN + * Name: www.baidu.com + * [Name Length: 13] + * [Label Count: 3] + * Type: SOA (Start Of a zone of Authority) (6) + * Class: IN (0x0001) + * Additional records + * <Root>: type OPT + * Name: <Root> + * Type: OPT (41) + * UDP payload size: 4096 + * Higher bits in extended RCODE: 0x00 + * EDNS0 version: 0 + * Z: 0x0000 + * 0... .... .... .... = DO bit: Cannot handle DNSSEC security RRs + * .000 0000 0000 0000 = Reserved: 0x0000 + * Data length: 0 + */ + + let bytes = [ + 0xb6, 0x13, /* Transaction ID: 0xb613 */ + 0x01, 0x20, /* Flags: 0x0120 Standard query */ + 0x00, 0x01, /* Questions: 1 */ + 0x00, 0x00, /* Answer RRs: 0 */ + 0x00, 0x00, /* Authority RRs: 0 */ + 0x00, 0x01, /* Additional RRs: 1 */ + 0x03, 0x77, 0x77, 0x77, 0x05, 0x62, 0x61, 0x69, 0x64, 0x75, 0x03, 0x63, 0x6f, 0x6d, + 0x00, /* Name: www.baidu.com */ + 0x00, 0x06, /* Type: SOA (Start Of a zone of Authority) (6) */ + 0x00, 0x01, /* Class: IN (0x0001) */ + 0x00, /* Name: <Root> */ + 0x00, 0x29, /* Type: OPT (41) */ + 0x10, 0x00, /* UDP payload size: 4096 */ + 0x00, /* Higher bits in extended RCODE: 0x00 */ + 0x00, /* EDNS0 version: 0 */ + 0x00, 0x00, /* Z: 0x0000 */ + 0x00, 0x00, /* Data length: 0 */ + ]; + + let expectation = DNS_MESSAGE { + header: DNS_HEADER { + id: 0xb613, + qr: DNS_HDR_QR::Query, + op_code: DNS_HDR_OPCODE::Query, + aa: false, + tc: false, + rd: true, + ra: false, + ad: true, + cd: false, + r_code: DNS_HDR_RCODE::NoError, + qd_count: 1, + an_count: 0, + ns_count: 0, + ar_count: 1, + }, + labels: DNS_LABEL_TABLE { + labels: vec![ + ((12, 15), "www.baidu.com.".to_string()), + ((31, 1), ".".to_string()), + ], + }, + qd: vec![DNS_QUESTION_SECTION { + qname: "www.baidu.com.".to_string(), + qtype: DNS_QTYPE::SOA, + qclass: DNS_QCLASS::Internet, + size: 19, + }], + an: vec![], + ns: vec![], + ar: vec![ + (DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: ".".to_string(), + rr_type: DNS_QTYPE::OPT, + rr_class: DNS_QCLASS::Other(4096), + ttl: 0, + rd_length: 0, + size: 11, + }, + data: DNS_RR_DATA::Other { + qtype: DNS_QTYPE::OPT, + data: vec![], + }, + }), + ], + }; + + assert_eq!(DNS_MESSAGE::decode(&bytes), Ok((LAST_SLICE, expectation))); + + match DNS_MESSAGE::decode(&bytes) { + Ok((left, message)) => { + println!("{:?}; left {:?}", message, left); + } + Err(err) => { + println!("{:?}\n", err); + } + } + // assert_eq!(0, 1); + } + + #[test] + fn test_query_txt_decode() { + /* + * dig baidu.com txt + * + * Domain Name System (query) + * Transaction ID: 0x6721 + * Flags: 0x0120 Standard query + * 0... .... .... .... = Response: Message is a query + * .000 0... .... .... = Opcode: Standard query (0) + * .... ..0. .... .... = Truncated: Message is not truncated + * .... ...1 .... .... = Recursion desired: Do query recursively + * .... .... .0.. .... = Z: reserved (0) + * .... .... ..1. .... = AD bit: Set + * .... .... ...0 .... = Non-authenticated data: Unacceptable + * Questions: 1 + * Answer RRs: 0 + * Authority RRs: 0 + * Additional RRs: 1 + * Queries + * baidu.com: type TXT, class IN + * Name: baidu.com + * [Name Length: 9] + * [Label Count: 2] + * Type: TXT (Text strings) (16) + * Class: IN (0x0001) + * Additional records + * <Root>: type OPT + * Name: <Root> + * Type: OPT (41) + * UDP payload size: 4096 + * Higher bits in extended RCODE: 0x00 + * EDNS0 version: 0 + * Z: 0x0000 + * 0... .... .... .... = DO bit: Cannot handle DNSSEC security RRs + * .000 0000 0000 0000 = Reserved: 0x0000 + * Data length: 0 + */ + let bytes = [ + 0x67, 0x21, /* Transaction ID: 0x6721 */ + 0x01, 0x20, /* Flags: 0x0120 Standard query */ + 0x00, 0x01, /* Questions: 1 */ + 0x00, 0x00, /* Answer RRs: 0 */ + 0x00, 0x00, /* Authority RRs: 0 */ + 0x00, 0x01, /* Additional RRs: 1 */ + /* Queries */ + 0x05, 0x62, 0x61, 0x69, 0x64, 0x75, 0x03, 0x63, 0x6f, 0x6d, + 0x00, /* Name: baidu.com */ + 0x00, 0x10, /* Type: TXT (Text strings) (16) */ + 0x00, 0x01, /* Class: IN (0x0001) */ + /* Additional records */ + 0x00, /* Name: <Root> */ + 0x00, 0x29, /* Type: OPT (41) */ + 0x10, 0x00, /* UDP payload size: 4096 */ + 0x00, /* Higher bits in extended RCODE: 0x00 */ + 0x00, /* EDNS0 version: 0 */ + 0x00, 0x00, /* Z: 0x0000 */ + 0x00, 0x00, /* Data length: 0 */ + ]; + + let expectation = DNS_MESSAGE { + header: DNS_HEADER { + id: 0x6721, + qr: DNS_HDR_QR::Query, + op_code: DNS_HDR_OPCODE::Query, + aa: false, + tc: false, + rd: true, + ra: false, + ad: true, + cd: false, + r_code: DNS_HDR_RCODE::NoError, + qd_count: 1, + an_count: 0, + ns_count: 0, + ar_count: 1, + }, + labels: DNS_LABEL_TABLE { + labels: vec![ + ((12, 11), "baidu.com.".to_string()), + ((27, 1), ".".to_string()), + ], + }, + qd: vec![DNS_QUESTION_SECTION { + qname: "baidu.com.".to_string(), + qtype: DNS_QTYPE::TXT, + qclass: DNS_QCLASS::Internet, + size: 15, + }], + an: vec![], + ns: vec![], + ar: vec![ + (DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: ".".to_string(), + rr_type: DNS_QTYPE::OPT, + rr_class: DNS_QCLASS::Other(4096), + ttl: 0, + rd_length: 0, + size: 11, + }, + data: DNS_RR_DATA::Other { + qtype: DNS_QTYPE::OPT, + data: vec![], + }, + }), + ], + }; + + assert_eq!(DNS_MESSAGE::decode(&bytes), Ok((LAST_SLICE, expectation))); + + match DNS_MESSAGE::decode(&bytes) { + Ok((left, message)) => { + println!("{:?}; left {:?}", message, left); + } + Err(err) => { + println!("{:?}\n", err); + } + } + // assert_eq!(0, 1); + } + + // TODO + #[test] + fn test_query_other_decode() {} + + /************************************************************************** + * test decode answer + **************************************************************************/ + + #[test] + fn test_answer_a_decode() { + // test in test_answer_txt_decode + } + + #[test] + fn test_answer_aaaa_decode() { + // test in test_answer_txt_decode + } + + #[test] + fn test_answer_ns_decode() { + // test in test_answer_txt_decode + } + + // TODO + #[test] + fn test_answer_hinfo_decode() {} + + #[test] + fn test_answer_mx_decode() { + /* + * Domain Name System (response) + * Transaction ID: 0xd35c + * Flags: 0x8180 Standard query response, No error + * 1... .... .... .... = Response: Message is a response + * .000 0... .... .... = Opcode: Standard query (0) + * .... .0.. .... .... = Authoritative: Server is not an authority for domain + * .... ..0. .... .... = Truncated: Message is not truncated + * .... ...1 .... .... = Recursion desired: Do query recursively + * .... .... 1... .... = Recursion available: Server can do recursive queries + * .... .... .0.. .... = Z: reserved (0) + * .... .... ..0. .... = Answer authenticated: Answer/authority portion was not authenticated by the server + * .... .... ...0 .... = Non-authenticated data: Unacceptable + * .... .... .... 0000 = Reply code: No error (0) + * Questions: 1 + * Answer RRs: 4 + * Authority RRs: 7 + * Additional RRs: 13 + * Queries + * 163.com: type MX, class IN + * Name: 163.com + * [Name Length: 7] + * [Label Count: 2] + * Type: MX (Mail eXchange) (15) + * Class: IN (0x0001) + * Answers + * 163.com: type MX, class IN, preference 10, mx 163mx02.mxmail.netease.com + * Name: 163.com + * Type: MX (Mail eXchange) (15) + * Class: IN (0x0001) + * Time to live: 12492 (3 hours, 28 minutes, 12 seconds) + * Data length: 27 + * Preference: 10 + * Mail Exchange: 163mx02.mxmail.netease.com + * 163.com: type MX, class IN, preference 50, mx 163mx00.mxmail.netease.com + * Name: 163.com + * Type: MX (Mail eXchange) (15) + * Class: IN (0x0001) + * Time to live: 12492 (3 hours, 28 minutes, 12 seconds) + * Data length: 12 + * Preference: 50 + * Mail Exchange: 163mx00.mxmail.netease.com + * 163.com: type MX, class IN, preference 10, mx 163mx03.mxmail.netease.com + * Name: 163.com + * Type: MX (Mail eXchange) (15) + * Class: IN (0x0001) + * Time to live: 12492 (3 hours, 28 minutes, 12 seconds) + * Data length: 12 + * Preference: 10 + * Mail Exchange: 163mx03.mxmail.netease.com + * 163.com: type MX, class IN, preference 10, mx 163mx01.mxmail.netease.com + * Name: 163.com + * Type: MX (Mail eXchange) (15) + * Class: IN (0x0001) + * Time to live: 12492 (3 hours, 28 minutes, 12 seconds) + * Data length: 12 + * Preference: 10 + * Mail Exchange: 163mx01.mxmail.netease.com + * Authoritative nameservers + * 163.com: type NS, class IN, ns ns5.nease.net + * Name: 163.com + * Type: NS (authoritative Name Server) (2) + * Class: IN (0x0001) + * Time to live: 34967 (9 hours, 42 minutes, 47 seconds) + * Data length: 15 + * Name Server: ns5.nease.net + * 163.com: type NS, class IN, ns ns3.nease.net + * Name: 163.com + * Type: NS (authoritative Name Server) (2) + * Class: IN (0x0001) + * Time to live: 34967 (9 hours, 42 minutes, 47 seconds) + * Data length: 6 + * Name Server: ns3.nease.net + * 163.com: type NS, class IN, ns ns1.nease.net + * Name: 163.com + * Type: NS (authoritative Name Server) (2) + * Class: IN (0x0001) + * Time to live: 34967 (9 hours, 42 minutes, 47 seconds) + * Data length: 6 + * Name Server: ns1.nease.net + * 163.com: type NS, class IN, ns ns8.166.com + * Name: 163.com + * Type: NS (authoritative Name Server) (2) + * Class: IN (0x0001) + * Time to live: 34967 (9 hours, 42 minutes, 47 seconds) + * Data length: 10 + * Name Server: ns8.166.com + * 163.com: type NS, class IN, ns ns4.nease.net + * Name: 163.com + * Type: NS (authoritative Name Server) (2) + * Class: IN (0x0001) + * Time to live: 34967 (9 hours, 42 minutes, 47 seconds) + * Data length: 6 + * Name Server: ns4.nease.net + * 163.com: type NS, class IN, ns ns2.166.com + * Name: 163.com + * Type: NS (authoritative Name Server) (2) + * Class: IN (0x0001) + * Time to live: 34967 (9 hours, 42 minutes, 47 seconds) + * Data length: 6 + * Name Server: ns2.166.com + * 163.com: type NS, class IN, ns ns6.nease.net + * Name: 163.com + * Type: NS (authoritative Name Server) (2) + * Class: IN (0x0001) + * Time to live: 34967 (9 hours, 42 minutes, 47 seconds) + * Data length: 6 + * Name Server: ns6.nease.net + * Additional records + * 163mx03.mxmail.netease.com: type A, class IN, addr 220.181.12.119 + * Name: 163mx03.mxmail.netease.com + * Type: A (Host Address) (1) + * Class: IN (0x0001) + * Time to live: 442 (7 minutes, 22 seconds) + * Data length: 4 + * Address: 220.181.12.119 + * 163mx01.mxmail.netease.com: type A, class IN, addr 220.181.12.117 + * Name: 163mx01.mxmail.netease.com + * Type: A (Host Address) (1) + * Class: IN (0x0001) + * Time to live: 442 (7 minutes, 22 seconds) + * Data length: 4 + * Address: 220.181.12.117 + * 163mx02.mxmail.netease.com: type A, class IN, addr 220.181.12.118 + * Name: 163mx02.mxmail.netease.com + * Type: A (Host Address) (1) + * Class: IN (0x0001) + * Time to live: 442 (7 minutes, 22 seconds) + * Data length: 4 + * Address: 220.181.12.118 + * 163mx00.mxmail.netease.com: type A, class IN, addr 220.181.12.180 + * Name: 163mx00.mxmail.netease.com + * Type: A (Host Address) (1) + * Class: IN (0x0001) + * Time to live: 442 (7 minutes, 22 seconds) + * Data length: 4 + * Address: 220.181.12.180 + * ns6.nease.net: type A, class IN, addr 54.228.156.72 + * Name: ns6.nease.net + * Type: A (Host Address) (1) + * Class: IN (0x0001) + * Time to live: 5583 (1 hour, 33 minutes, 3 seconds) + * Data length: 4 + * Address: 54.228.156.72 + * ns1.nease.net: type A, class IN, addr 42.186.35.222 + * Name: ns1.nease.net + * Type: A (Host Address) (1) + * Class: IN (0x0001) + * Time to live: 32644 (9 hours, 4 minutes, 4 seconds) + * Data length: 4 + * Address: 42.186.35.222 + * ns4.nease.net: type A, class IN, addr 103.72.16.81 + * Name: ns4.nease.net + * Type: A (Host Address) (1) + * Class: IN (0x0001) + * Time to live: 51974 (14 hours, 26 minutes, 14 seconds) + * Data length: 4 + * Address: 103.72.16.81 + * ns3.nease.net: type A, class IN, addr 220.181.36.234 + * Name: ns3.nease.net + * Type: A (Host Address) (1) + * Class: IN (0x0001) + * Time to live: 41374 (11 hours, 29 minutes, 34 seconds) + * Data length: 4 + * Address: 220.181.36.234 + * ns2.166.com: type A, class IN, addr 103.71.201.3 + * Name: ns2.166.com + * Type: A (Host Address) (1) + * Class: IN (0x0001) + * Time to live: 73500 (20 hours, 25 minutes) + * Data length: 4 + * Address: 103.71.201.3 + * ns5.nease.net: type A, class IN, addr 121.195.179.18 + * Name: ns5.nease.net + * Type: A (Host Address) (1) + * Class: IN (0x0001) + * Time to live: 34614 (9 hours, 36 minutes, 54 seconds) + * Data length: 4 + * Address: 121.195.179.18 + * ns8.166.com: type A, class IN, addr 18.182.82.158 + * Name: ns8.166.com + * Type: A (Host Address) (1) + * Class: IN (0x0001) + * Time to live: 64949 (18 hours, 2 minutes, 29 seconds) + * Data length: 4 + * Address: 18.182.82.158 + * ns8.166.com: type A, class IN, addr 44.228.163.69 + * Name: ns8.166.com + * Type: A (Host Address) (1) + * Class: IN (0x0001) + * Time to live: 64949 (18 hours, 2 minutes, 29 seconds) + * Data length: 4 + * Address: 44.228.163.69 + * <Root>: type OPT + * Name: <Root> + * Type: OPT (41) + * UDP payload size: 4096 + * Higher bits in extended RCODE: 0x00 + * EDNS0 version: 0 + * Z: 0x0000 + * 0... .... .... .... = DO bit: Cannot handle DNSSEC security RRs + * .000 0000 0000 0000 = Reserved: 0x0000 + * Data length: 0 + */ + + let bytes = [ + 0xd3, 0x5c, 0x81, 0x80, 0x00, 0x01, 0x00, 0x04, 0x00, 0x07, 0x00, 0x0d, 0x03, 0x31, + 0x36, 0x33, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x0f, 0x00, 0x01, 0xc0, 0x0c, 0x00, + 0x0f, 0x00, 0x01, 0x00, 0x00, 0x30, 0xcc, 0x00, 0x1b, 0x00, 0x0a, 0x07, 0x31, 0x36, + 0x33, 0x6d, 0x78, 0x30, 0x32, 0x06, 0x6d, 0x78, 0x6d, 0x61, 0x69, 0x6c, 0x07, 0x6e, + 0x65, 0x74, 0x65, 0x61, 0x73, 0x65, 0xc0, 0x10, 0xc0, 0x0c, 0x00, 0x0f, 0x00, 0x01, + 0x00, 0x00, 0x30, 0xcc, 0x00, 0x0c, 0x00, 0x32, 0x07, 0x31, 0x36, 0x33, 0x6d, 0x78, + 0x30, 0x30, 0xc0, 0x2f, 0xc0, 0x0c, 0x00, 0x0f, 0x00, 0x01, 0x00, 0x00, 0x30, 0xcc, + 0x00, 0x0c, 0x00, 0x0a, 0x07, 0x31, 0x36, 0x33, 0x6d, 0x78, 0x30, 0x33, 0xc0, 0x2f, + 0xc0, 0x0c, 0x00, 0x0f, 0x00, 0x01, 0x00, 0x00, 0x30, 0xcc, 0x00, 0x0c, 0x00, 0x0a, + 0x07, 0x31, 0x36, 0x33, 0x6d, 0x78, 0x30, 0x31, 0xc0, 0x2f, 0xc0, 0x0c, 0x00, 0x02, + 0x00, 0x01, 0x00, 0x00, 0x88, 0x97, 0x00, 0x0f, 0x03, 0x6e, 0x73, 0x35, 0x05, 0x6e, + 0x65, 0x61, 0x73, 0x65, 0x03, 0x6e, 0x65, 0x74, 0x00, 0xc0, 0x0c, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x88, 0x97, 0x00, 0x06, 0x03, 0x6e, 0x73, 0x33, 0xc0, 0x98, 0xc0, + 0x0c, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x88, 0x97, 0x00, 0x06, 0x03, 0x6e, 0x73, + 0x31, 0xc0, 0x98, 0xc0, 0x0c, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x88, 0x97, 0x00, + 0x0a, 0x03, 0x6e, 0x73, 0x38, 0x03, 0x31, 0x36, 0x36, 0xc0, 0x10, 0xc0, 0x0c, 0x00, + 0x02, 0x00, 0x01, 0x00, 0x00, 0x88, 0x97, 0x00, 0x06, 0x03, 0x6e, 0x73, 0x34, 0xc0, + 0x98, 0xc0, 0x0c, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x88, 0x97, 0x00, 0x06, 0x03, + 0x6e, 0x73, 0x32, 0xc0, 0xd7, 0xc0, 0x0c, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x88, + 0x97, 0x00, 0x06, 0x03, 0x6e, 0x73, 0x36, 0xc0, 0x98, 0xc0, 0x66, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x01, 0xba, 0x00, 0x04, 0xdc, 0xb5, 0x0c, 0x77, 0xc0, 0x7e, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0xba, 0x00, 0x04, 0xdc, 0xb5, 0x0c, 0x75, 0xc0, + 0x27, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0xba, 0x00, 0x04, 0xdc, 0xb5, 0x0c, + 0x76, 0xc0, 0x4e, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0xba, 0x00, 0x04, 0xdc, + 0xb5, 0x0c, 0xb4, 0xc1, 0x0d, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x15, 0xcf, 0x00, + 0x04, 0x36, 0xe4, 0x9c, 0x48, 0xc0, 0xc1, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x7f, + 0x84, 0x00, 0x04, 0x2a, 0xba, 0x23, 0xde, 0xc0, 0xe9, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0xcb, 0x06, 0x00, 0x04, 0x67, 0x48, 0x10, 0x51, 0xc0, 0xaf, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0xa1, 0x9e, 0x00, 0x04, 0xdc, 0xb5, 0x24, 0xea, 0xc0, 0xfb, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x1f, 0x1c, 0x00, 0x04, 0x67, 0x47, 0xc9, 0x03, 0xc0, + 0x94, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x87, 0x36, 0x00, 0x04, 0x79, 0xc3, 0xb3, + 0x12, 0xc0, 0xd3, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xfd, 0xb5, 0x00, 0x04, 0x12, + 0xb6, 0x52, 0x9e, 0xc0, 0xd3, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xfd, 0xb5, 0x00, + 0x04, 0x2c, 0xe4, 0xa3, 0x45, 0x00, 0x00, 0x29, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + ]; + + let expectation = DNS_MESSAGE { + header: DNS_HEADER { + id: 0xd35c, + qr: DNS_HDR_QR::Response, + op_code: DNS_HDR_OPCODE::Query, + aa: false, + tc: false, + rd: true, + ra: true, + ad: false, + cd: false, + r_code: DNS_HDR_RCODE::NoError, + qd_count: 1, + an_count: 4, + ns_count: 7, + ar_count: 13, + }, + labels: DNS_LABEL_TABLE { + labels: vec![ + ((12, 9), "163.com.".to_string()), + ((25, 2), "163.com.".to_string()), + ((39, 25), "163mx02.mxmail.netease.com.".to_string()), + ((64, 2), "163.com.".to_string()), + ((78, 10), "163mx00.mxmail.netease.com.".to_string()), + ((88, 2), "163.com.".to_string()), + ((102, 10), "163mx03.mxmail.netease.com.".to_string()), + ((112, 2), "163.com.".to_string()), + ((126, 10), "163mx01.mxmail.netease.com.".to_string()), + ((136, 2), "163.com.".to_string()), + ((148, 15), "ns5.nease.net.".to_string()), + ((163, 2), "163.com.".to_string()), + ((175, 6), "ns3.nease.net.".to_string()), + ((181, 2), "163.com.".to_string()), + ((193, 6), "ns1.nease.net.".to_string()), + ((199, 2), "163.com.".to_string()), + ((211, 10), "ns8.166.com.".to_string()), + ((221, 2), "163.com.".to_string()), + ((233, 6), "ns4.nease.net.".to_string()), + ((239, 2), "163.com.".to_string()), + ((251, 6), "ns2.166.com.".to_string()), + ((257, 2), "163.com.".to_string()), + ((269, 6), "ns6.nease.net.".to_string()), + ((275, 2), "163mx03.mxmail.netease.com.".to_string()), + ((291, 2), "163mx01.mxmail.netease.com.".to_string()), + ((307, 2), "163mx02.mxmail.netease.com.".to_string()), + ((323, 2), "163mx00.mxmail.netease.com.".to_string()), + ((339, 2), "ns6.nease.net.".to_string()), + ((355, 2), "ns1.nease.net.".to_string()), + ((371, 2), "ns4.nease.net.".to_string()), + ((387, 2), "ns3.nease.net.".to_string()), + ((403, 2), "ns2.166.com.".to_string()), + ((419, 2), "ns5.nease.net.".to_string()), + ((435, 2), "ns8.166.com.".to_string()), + ((451, 2), "ns8.166.com.".to_string()), + ((467, 1), ".".to_string()), + ], + }, + qd: vec![DNS_QUESTION_SECTION { + qname: "163.com.".to_string(), + qtype: DNS_QTYPE::MX, + qclass: DNS_QCLASS::Internet, + size: 13, + }], + an: vec![ + (DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: "163.com.".to_string(), + rr_type: DNS_QTYPE::MX, + rr_class: DNS_QCLASS::Internet, + ttl: 12492, + rd_length: 27, + size: 12, + }, + data: DNS_RR_DATA::MX { + preference: 10, + exchange: "163mx02.mxmail.netease.com.".to_string(), + }, + }), + (DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: "163.com.".to_string(), + rr_type: DNS_QTYPE::MX, + rr_class: DNS_QCLASS::Internet, + ttl: 12492, + rd_length: 12, + size: 12, + }, + data: DNS_RR_DATA::MX { + preference: 50, + exchange: "163mx00.mxmail.netease.com.".to_string(), + }, + }), + (DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: "163.com.".to_string(), + rr_type: DNS_QTYPE::MX, + rr_class: DNS_QCLASS::Internet, + ttl: 12492, + rd_length: 12, + size: 12, + }, + data: DNS_RR_DATA::MX { + preference: 10, + exchange: "163mx03.mxmail.netease.com.".to_string(), + }, + }), + (DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: "163.com.".to_string(), + rr_type: DNS_QTYPE::MX, + rr_class: DNS_QCLASS::Internet, + ttl: 12492, + rd_length: 12, + size: 12, + }, + data: DNS_RR_DATA::MX { + preference: 10, + exchange: "163mx01.mxmail.netease.com.".to_string(), + }, + }), + ], + ns: vec![ + (DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: "163.com.".to_string(), + rr_type: DNS_QTYPE::NS, + rr_class: DNS_QCLASS::Internet, + ttl: 34967, + rd_length: 15, + size: 12, + }, + data: DNS_RR_DATA::NS { + nsdname: "ns5.nease.net.".to_string(), + }, + }), + (DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: "163.com.".to_string(), + rr_type: DNS_QTYPE::NS, + rr_class: DNS_QCLASS::Internet, + ttl: 34967, + rd_length: 6, + size: 12, + }, + data: DNS_RR_DATA::NS { + nsdname: "ns3.nease.net.".to_string(), + }, + }), + (DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: "163.com.".to_string(), + rr_type: DNS_QTYPE::NS, + rr_class: DNS_QCLASS::Internet, + ttl: 34967, + rd_length: 6, + size: 12, + }, + data: DNS_RR_DATA::NS { + nsdname: "ns1.nease.net.".to_string(), + }, + }), + (DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: "163.com.".to_string(), + rr_type: DNS_QTYPE::NS, + rr_class: DNS_QCLASS::Internet, + ttl: 34967, + rd_length: 10, + size: 12, + }, + data: DNS_RR_DATA::NS { + nsdname: "ns8.166.com.".to_string(), + }, + }), + (DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: "163.com.".to_string(), + rr_type: DNS_QTYPE::NS, + rr_class: DNS_QCLASS::Internet, + ttl: 34967, + rd_length: 6, + size: 12, + }, + data: DNS_RR_DATA::NS { + nsdname: "ns4.nease.net.".to_string(), + }, + }), + (DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: "163.com.".to_string(), + rr_type: DNS_QTYPE::NS, + rr_class: DNS_QCLASS::Internet, + ttl: 34967, + rd_length: 6, + size: 12, + }, + data: DNS_RR_DATA::NS { + nsdname: "ns2.166.com.".to_string(), + }, + }), + (DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: "163.com.".to_string(), + rr_type: DNS_QTYPE::NS, + rr_class: DNS_QCLASS::Internet, + ttl: 34967, + rd_length: 6, + size: 12, + }, + data: DNS_RR_DATA::NS { + nsdname: "ns6.nease.net.".to_string(), + }, + }), + ], + ar: vec![ + (DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: "163mx03.mxmail.netease.com.".to_string(), + rr_type: DNS_QTYPE::A, + rr_class: DNS_QCLASS::Internet, + ttl: 442, + rd_length: 4, + size: 12, + }, + data: DNS_RR_DATA::A { + address: "220.181.12.119".to_string(), + }, + }), + (DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: "163mx01.mxmail.netease.com.".to_string(), + rr_type: DNS_QTYPE::A, + rr_class: DNS_QCLASS::Internet, + ttl: 442, + rd_length: 4, + size: 12, + }, + data: DNS_RR_DATA::A { + address: "220.181.12.117".to_string(), + }, + }), + (DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: "163mx02.mxmail.netease.com.".to_string(), + rr_type: DNS_QTYPE::A, + rr_class: DNS_QCLASS::Internet, + ttl: 442, + rd_length: 4, + size: 12, + }, + data: DNS_RR_DATA::A { + address: "220.181.12.118".to_string(), + }, + }), + (DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: "163mx00.mxmail.netease.com.".to_string(), + rr_type: DNS_QTYPE::A, + rr_class: DNS_QCLASS::Internet, + ttl: 442, + rd_length: 4, + size: 12, + }, + data: DNS_RR_DATA::A { + address: "220.181.12.180".to_string(), + }, + }), + (DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: "ns6.nease.net.".to_string(), + rr_type: DNS_QTYPE::A, + rr_class: DNS_QCLASS::Internet, + ttl: 5583, + rd_length: 4, + size: 12, + }, + data: DNS_RR_DATA::A { + address: "54.228.156.72".to_string(), + }, + }), + (DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: "ns1.nease.net.".to_string(), + rr_type: DNS_QTYPE::A, + rr_class: DNS_QCLASS::Internet, + ttl: 32644, + rd_length: 4, + size: 12, + }, + data: DNS_RR_DATA::A { + address: "42.186.35.222".to_string(), + }, + }), + (DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: "ns4.nease.net.".to_string(), + rr_type: DNS_QTYPE::A, + rr_class: DNS_QCLASS::Internet, + ttl: 51974, + rd_length: 4, + size: 12, + }, + data: DNS_RR_DATA::A { + address: "103.72.16.81".to_string(), + }, + }), + (DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: "ns3.nease.net.".to_string(), + rr_type: DNS_QTYPE::A, + rr_class: DNS_QCLASS::Internet, + ttl: 41374, + rd_length: 4, + size: 12, + }, + data: DNS_RR_DATA::A { + address: "220.181.36.234".to_string(), + }, + }), + (DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: "ns2.166.com.".to_string(), + rr_type: DNS_QTYPE::A, + rr_class: DNS_QCLASS::Internet, + ttl: 73500, + rd_length: 4, + size: 12, + }, + data: DNS_RR_DATA::A { + address: "103.71.201.3".to_string(), + }, + }), + (DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: "ns5.nease.net.".to_string(), + rr_type: DNS_QTYPE::A, + rr_class: DNS_QCLASS::Internet, + ttl: 34614, + rd_length: 4, + size: 12, + }, + data: DNS_RR_DATA::A { + address: "121.195.179.18".to_string(), + }, + }), + (DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: "ns8.166.com.".to_string(), + rr_type: DNS_QTYPE::A, + rr_class: DNS_QCLASS::Internet, + ttl: 64949, + rd_length: 4, + size: 12, + }, + data: DNS_RR_DATA::A { + address: "18.182.82.158".to_string(), + }, + }), + (DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: "ns8.166.com.".to_string(), + rr_type: DNS_QTYPE::A, + rr_class: DNS_QCLASS::Internet, + ttl: 64949, + rd_length: 4, + size: 12, + }, + data: DNS_RR_DATA::A { + address: "44.228.163.69".to_string(), + }, + }), + (DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: ".".to_string(), + rr_type: DNS_QTYPE::OPT, + rr_class: DNS_QCLASS::Other(4096), + ttl: 0, + rd_length: 0, + size: 11, + }, + data: DNS_RR_DATA::Other { + qtype: DNS_QTYPE::OPT, + data: vec![], + }, + }), + ], + }; + + assert_eq!(DNS_MESSAGE::decode(&bytes), Ok((LAST_SLICE, expectation))); + + match DNS_MESSAGE::decode(&bytes) { + Ok((left, message)) => { + println!("{:?}; left {:?}", message, left); + } + Err(err) => { + println!("{:?}\n", err); + } + } + // assert_eq!(0, 1); + } + + // TODO + #[test] + fn dns_answer_nx() {} + + // TODO + #[test] + fn test_answer_ptr_decode() {} + + #[test] + fn test_answer_cname_decode() { + // test in test_answer_soa_decode + } + + #[test] + fn test_answer_soa_decode() { + /* + * Domain Name System (response) + * Transaction ID: 0xb613 + * Flags: 0x8180 Standard query response, No error + * 1... .... .... .... = Response: Message is a response + * .000 0... .... .... = Opcode: Standard query (0) + * .... .0.. .... .... = Authoritative: Server is not an authority for domain + * .... ..0. .... .... = Truncated: Message is not truncated + * .... ...1 .... .... = Recursion desired: Do query recursively + * .... .... 1... .... = Recursion available: Server can do recursive queries + * .... .... .0.. .... = Z: reserved (0) + * .... .... ..0. .... = Answer authenticated: Answer/authority portion was not authenticated by the server + * .... .... ...0 .... = Non-authenticated data: Unacceptable + * .... .... .... 0000 = Reply code: No error (0) + * Questions: 1 + * Answer RRs: 1 + * Authority RRs: 1 + * Additional RRs: 1 + * Queries + * www.baidu.com: type SOA, class IN + * Name: www.baidu.com + * [Name Length: 13] + * [Label Count: 3] + * Type: SOA (Start Of a zone of Authority) (6) + * Class: IN (0x0001) + * Answers + * www.baidu.com: type CNAME, class IN, cname www.a.shifen.com + * Name: www.baidu.com + * Type: CNAME (Canonical NAME for an alias) (5) + * Class: IN (0x0001) + * Time to live: 221 (3 minutes, 41 seconds) + * Data length: 15 + * CNAME: www.a.shifen.com + * Authoritative nameservers + * a.shifen.com: type SOA, class IN, mname ns1.a.shifen.com + * Name: a.shifen.com + * Type: SOA (Start Of a zone of Authority) (6) + * Class: IN (0x0001) + * Time to live: 296 (4 minutes, 56 seconds) + * Data length: 45 + * Primary name server: ns1.a.shifen.com + * Responsible authority's mailbox: baidu_dns_master.baidu.com + * Serial Number: 2211240019 + * Refresh Interval: 5 (5 seconds) + * Retry Interval: 5 (5 seconds) + * Expire limit: 2592000 (30 days) + * Minimum TTL: 3600 (1 hour) + * Additional records + * <Root>: type OPT + * Name: <Root> + * Type: OPT (41) + * UDP payload size: 4096 + * Higher bits in extended RCODE: 0x00 + * EDNS0 version: 0 + * Z: 0x0000 + * 0... .... .... .... = DO bit: Cannot handle DNSSEC security RRs + * .000 0000 0000 0000 = Reserved: 0x0000 + * Data length: 0 + * [Request In: 14] + * [Time: 0.075849000 seconds] + */ + + let bytes = [ + 0xb6, 0x13, /* Transaction ID: 0xb613 */ + 0x81, 0x80, /* Flags: 0x8180 Standard query response, No error */ + 0x00, 0x01, /* Questions: 1 */ + 0x00, 0x01, /* Answer RRs: 1 */ + 0x00, 0x01, /* Authority RRs: 1 */ + 0x00, 0x01, /* Additional RRs: 1 */ + /* Queries */ + 0x03, 0x77, 0x77, 0x77, 0x05, 0x62, 0x61, 0x69, 0x64, 0x75, 0x03, 0x63, 0x6f, 0x6d, + 0x00, /* Name: www.baidu.com */ + 0x00, 0x06, /* Type: SOA (Start Of a zone of Authority) (6) */ + 0x00, 0x01, /* Class: IN (0x0001) */ + /* Answers */ + 0xc0, 0x0c, /* Name: www.baidu.com */ + 0x00, 0x05, /* Type: CNAME (Canonical NAME for an alias) (5) */ + 0x00, 0x01, /* Class: IN (0x0001) */ + 0x00, 0x00, 0x00, 0xdd, /* Time to live: 221 (3 minutes, 41 seconds) */ + 0x00, 0x0f, /* Data length: 15 */ + 0x03, 0x77, 0x77, 0x77, 0x01, 0x61, 0x06, 0x73, 0x68, 0x69, 0x66, 0x65, 0x6e, 0xc0, + 0x16, /* CNAME: www.a.shifen.com */ + /* Authoritative nameservers */ + 0xc0, 0x2f, /* Name: a.shifen.com */ + 0x00, 0x06, /* Type: SOA (Start Of a zone of Authority) (6) */ + 0x00, 0x01, /* Class: IN (0x0001) */ + 0x00, 0x00, 0x01, 0x28, /* Time to live: 296 (4 minutes, 56 seconds) */ + 0x00, 0x2d, /* Data length: 45 */ + 0x03, 0x6e, 0x73, 0x31, 0xc0, 0x2f, /* Primary name server: ns1.a.shifen.com */ + 0x10, 0x62, 0x61, 0x69, 0x64, 0x75, 0x5f, 0x64, 0x6e, 0x73, 0x5f, 0x6d, 0x61, 0x73, + 0x74, 0x65, 0x72, 0xc0, + 0x10, /* Responsible authority's mailbox: baidu_dns_master.baidu.com */ + 0x83, 0xcc, 0xd8, 0x53, /* Serial Number: 2211240019 */ + 0x00, 0x00, 0x00, 0x05, /* Refresh Interval: 5 (5 seconds) */ + 0x00, 0x00, 0x00, 0x05, /* Retry Interval: 5 (5 seconds) */ + 0x00, 0x27, 0x8d, 0x00, /* Expire limit: 2592000 (30 days) */ + 0x00, 0x00, 0x0e, 0x10, /* Minimum TTL: 3600 (1 hour) */ + /* Additional records */ + 0x00, /* Name: <Root> */ + 0x00, 0x29, /* Type: OPT (41) */ + 0x10, 0x00, /* UDP payload size: 4096 */ + 0x00, /* Higher bits in extended RCODE: 0x00 */ + 0x00, /* EDNS0 version: 0 */ + 0x00, 0x00, /* Z: 0x0000 */ + 0x00, 0x00, /* Data length: 0 */ + ]; + + let expectation = DNS_MESSAGE { + header: DNS_HEADER { + id: 0xb613, + qr: DNS_HDR_QR::Response, + op_code: DNS_HDR_OPCODE::Query, + aa: false, + tc: false, + rd: true, + ra: true, + ad: false, + cd: false, + r_code: DNS_HDR_RCODE::NoError, + qd_count: 1, + an_count: 1, + ns_count: 1, + ar_count: 1, + }, + labels: DNS_LABEL_TABLE { + labels: vec![ + ((12, 15), "www.baidu.com.".to_string()), + ((31, 2), "www.baidu.com.".to_string()), + ((43, 15), "www.a.shifen.com.".to_string()), + ((58, 2), "a.shifen.com.".to_string()), + ((70, 6), "ns1.a.shifen.com.".to_string()), + ((76, 19), "baidu_dns_master.baidu.com.".to_string()), + ((115, 1), ".".to_string()), + ], + }, + qd: vec![DNS_QUESTION_SECTION { + qname: "www.baidu.com.".to_string(), + qtype: DNS_QTYPE::SOA, + qclass: DNS_QCLASS::Internet, + size: 19, + }], + an: vec![ + (DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: "www.baidu.com.".to_string(), + rr_type: DNS_QTYPE::CNAME, + rr_class: DNS_QCLASS::Internet, + ttl: 221, + rd_length: 15, + size: 12, + }, + data: DNS_RR_DATA::CNAME { + cname: "www.a.shifen.com.".to_string(), + }, + }), + ], + ns: vec![ + (DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: "a.shifen.com.".to_string(), + rr_type: DNS_QTYPE::SOA, + rr_class: DNS_QCLASS::Internet, + ttl: 296, + rd_length: 45, + size: 12, + }, + data: DNS_RR_DATA::SOA { + mname: "ns1.a.shifen.com.".to_string(), + rname: "baidu_dns_master.baidu.com.".to_string(), + serial: 2211240019, + refresh: 5, + retry: 5, + expire: 2592000, + minimum: 3600, + }, + }), + ], + ar: vec![ + (DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: ".".to_string(), + rr_type: DNS_QTYPE::OPT, + rr_class: DNS_QCLASS::Other(4096), + ttl: 0, + rd_length: 0, + size: 11, + }, + data: DNS_RR_DATA::Other { + qtype: DNS_QTYPE::OPT, + data: vec![], + }, + }), + ], + }; + + assert_eq!(DNS_MESSAGE::decode(&bytes), Ok((LAST_SLICE, expectation))); + + match DNS_MESSAGE::decode(&bytes) { + Ok((left, message)) => { + println!("{:?}; left {:?}", message, left); + } + Err(err) => { + println!("{:?}\n", err); + } + } + // assert_eq!(0, 1); + } + + #[test] + fn test_answer_txt_decode() { + /* + * Domain Name System (response) + * Transaction ID: 0x6721 + * Flags: 0x8180 Standard query response, No error + * 1... .... .... .... = Response: Message is a response + * .000 0... .... .... = Opcode: Standard query (0) + * .... .0.. .... .... = Authoritative: Server is not an authority for domain + * .... ..0. .... .... = Truncated: Message is not truncated + * .... ...1 .... .... = Recursion desired: Do query recursively + * .... .... 1... .... = Recursion available: Server can do recursive queries + * .... .... .0.. .... = Z: reserved (0) + * .... .... ..0. .... = Answer authenticated: Answer/authority portion was not authenticated by the server + * .... .... ...0 .... = Non-authenticated data: Unacceptable + * .... .... .... 0000 = Reply code: No error (0) + * Questions: 1 + * Answer RRs: 3 + * Authority RRs: 5 + * Additional RRs: 10 + * Queries + * baidu.com: type TXT, class IN + * Name: baidu.com + * [Name Length: 9] + * [Label Count: 2] + * Type: TXT (Text strings) (16) + * Class: IN (0x0001) + * Answers + * baidu.com: type TXT, class IN + * Name: baidu.com + * Type: TXT (Text strings) (16) + * Class: IN (0x0001) + * Time to live: 7200 (2 hours) + * Data length: 75 + * TXT Length: 74 + * TXT: _globalsign-domain-verification=qjb28W2jJSrWj04NHpB0CvgK9tle5JkOq-EcyWBgnE + * baidu.com: type TXT, class IN + * Name: baidu.com + * Type: TXT (Text strings) (16) + * Class: IN (0x0001) + * Time to live: 7200 (2 hours) + * Data length: 69 + * TXT Length: 68 + * TXT: google-site-verification=GHb98-6msqyx_qqjGl5eRatD3QTHyVB6-xQ3gJB5UwM + * baidu.com: type TXT, class IN + * Name: baidu.com + * Type: TXT (Text strings) (16) + * Class: IN (0x0001) + * Time to live: 7200 (2 hours) + * Data length: 113 + * TXT Length: 112 + * TXT: v=spf1 include:spf1.baidu.com include:spf2.baidu.com include:spf3.baidu.com include:spf4.baidu.com a mx ptr -all + * Authoritative nameservers + * baidu.com: type NS, class IN, ns ns7.baidu.com + * Name: baidu.com + * Type: NS (authoritative Name Server) (2) + * Class: IN (0x0001) + * Time to live: 53898 (14 hours, 58 minutes, 18 seconds) + * Data length: 6 + * Name Server: ns7.baidu.com + * baidu.com: type NS, class IN, ns dns.baidu.com + * Name: baidu.com + * Type: NS (authoritative Name Server) (2) + * Class: IN (0x0001) + * Time to live: 53898 (14 hours, 58 minutes, 18 seconds) + * Data length: 6 + * Name Server: dns.baidu.com + * baidu.com: type NS, class IN, ns ns3.baidu.com + * Name: baidu.com + * Type: NS (authoritative Name Server) (2) + * Class: IN (0x0001) + * Time to live: 53898 (14 hours, 58 minutes, 18 seconds) + * Data length: 6 + * Name Server: ns3.baidu.com + * baidu.com: type NS, class IN, ns ns2.baidu.com + * Name: baidu.com + * Type: NS (authoritative Name Server) (2) + * Class: IN (0x0001) + * Time to live: 53898 (14 hours, 58 minutes, 18 seconds) + * Data length: 6 + * Name Server: ns2.baidu.com + * baidu.com: type NS, class IN, ns ns4.baidu.com + * Name: baidu.com + * Type: NS (authoritative Name Server) (2) + * Class: IN (0x0001) + * Time to live: 53898 (14 hours, 58 minutes, 18 seconds) + * Data length: 6 + * Name Server: ns4.baidu.com + * Additional records + * ns3.baidu.com: type A, class IN, addr 36.152.45.193 + * Name: ns3.baidu.com + * Type: A (Host Address) (1) + * Class: IN (0x0001) + * Time to live: 25922 (7 hours, 12 minutes, 2 seconds) + * Data length: 4 + * Address: 36.152.45.193 + * ns3.baidu.com: type A, class IN, addr 112.80.248.64 + * Name: ns3.baidu.com + * Type: A (Host Address) (1) + * Class: IN (0x0001) + * Time to live: 25922 (7 hours, 12 minutes, 2 seconds) + * Data length: 4 + * Address: 112.80.248.64 + * ns2.baidu.com: type A, class IN, addr 220.181.33.31 + * Name: ns2.baidu.com + * Type: A (Host Address) (1) + * Class: IN (0x0001) + * Time to live: 23967 (6 hours, 39 minutes, 27 seconds) + * Data length: 4 + * Address: 220.181.33.31 + * ns7.baidu.com: type A, class IN, addr 180.76.76.92 + * Name: ns7.baidu.com + * Type: A (Host Address) (1) + * Class: IN (0x0001) + * Time to live: 50794 (14 hours, 6 minutes, 34 seconds) + * Data length: 4 + * Address: 180.76.76.92 + * ns4.baidu.com: type A, class IN, addr 14.215.178.80 + * Name: ns4.baidu.com + * Type: A (Host Address) (1) + * Class: IN (0x0001) + * Time to live: 58399 (16 hours, 13 minutes, 19 seconds) + * Data length: 4 + * Address: 14.215.178.80 + * ns4.baidu.com: type A, class IN, addr 111.45.3.226 + * Name: ns4.baidu.com + * Type: A (Host Address) (1) + * Class: IN (0x0001) + * Time to live: 58399 (16 hours, 13 minutes, 19 seconds) + * Data length: 4 + * Address: 111.45.3.226 + * dns.baidu.com: type A, class IN, addr 110.242.68.134 + * Name: dns.baidu.com + * Type: A (Host Address) (1) + * Class: IN (0x0001) + * Time to live: 114889 (1 day, 7 hours, 54 minutes, 49 seconds) + * Data length: 4 + * Address: 110.242.68.134 + * ns7.baidu.com: type AAAA, class IN, addr 240e:940:603:4:0:ff:b01b:589a + * Name: ns7.baidu.com + * Type: AAAA (IPv6 Address) (28) + * Class: IN (0x0001) + * Time to live: 80536 (22 hours, 22 minutes, 16 seconds) + * Data length: 16 + * AAAA Address: 240e:940:603:4:0:ff:b01b:589a + * ns7.baidu.com: type AAAA, class IN, addr 240e:bf:b801:1002:0:ff:b024:26de + * Name: ns7.baidu.com + * Type: AAAA (IPv6 Address) (28) + * Class: IN (0x0001) + * Time to live: 80536 (22 hours, 22 minutes, 16 seconds) + * Data length: 16 + * AAAA Address: 240e:bf:b801:1002:0:ff:b024:26de + * <Root>: type OPT + * Name: <Root> + * Type: OPT (41) + * UDP payload size: 4096 + * Higher bits in extended RCODE: 0x00 + * EDNS0 version: 0 + * Z: 0x0000 + * 0... .... .... .... = DO bit: Cannot handle DNSSEC security RRs + * .000 0000 0000 0000 = Reserved: 0x0000 + * Data length: 0 + */ + + let bytes = [ + 0x67, 0x21, 0x81, 0x80, 0x00, 0x01, 0x00, 0x03, 0x00, 0x05, 0x00, 0x0a, 0x05, 0x62, + 0x61, 0x69, 0x64, 0x75, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x10, 0x00, 0x01, 0xc0, + 0x0c, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x1c, 0x20, 0x00, 0x4b, 0x4a, 0x5f, 0x67, + 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x69, 0x67, 0x6e, 0x2d, 0x64, 0x6f, 0x6d, 0x61, + 0x69, 0x6e, 0x2d, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x3d, 0x71, 0x6a, 0x62, 0x32, 0x38, 0x57, 0x32, 0x6a, 0x4a, 0x53, 0x72, 0x57, + 0x6a, 0x30, 0x34, 0x4e, 0x48, 0x70, 0x42, 0x30, 0x43, 0x76, 0x67, 0x4b, 0x39, 0x74, + 0x6c, 0x65, 0x35, 0x4a, 0x6b, 0x4f, 0x71, 0x2d, 0x45, 0x63, 0x79, 0x57, 0x42, 0x67, + 0x6e, 0x45, 0xc0, 0x0c, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x1c, 0x20, 0x00, 0x45, + 0x44, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2d, 0x73, 0x69, 0x74, 0x65, 0x2d, 0x76, + 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3d, 0x47, 0x48, + 0x62, 0x39, 0x38, 0x2d, 0x36, 0x6d, 0x73, 0x71, 0x79, 0x78, 0x5f, 0x71, 0x71, 0x6a, + 0x47, 0x6c, 0x35, 0x65, 0x52, 0x61, 0x74, 0x44, 0x33, 0x51, 0x54, 0x48, 0x79, 0x56, + 0x42, 0x36, 0x2d, 0x78, 0x51, 0x33, 0x67, 0x4a, 0x42, 0x35, 0x55, 0x77, 0x4d, 0xc0, + 0x0c, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x1c, 0x20, 0x00, 0x71, 0x70, 0x76, 0x3d, + 0x73, 0x70, 0x66, 0x31, 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x3a, 0x73, + 0x70, 0x66, 0x31, 0x2e, 0x62, 0x61, 0x69, 0x64, 0x75, 0x2e, 0x63, 0x6f, 0x6d, 0x20, + 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x3a, 0x73, 0x70, 0x66, 0x32, 0x2e, 0x62, + 0x61, 0x69, 0x64, 0x75, 0x2e, 0x63, 0x6f, 0x6d, 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75, + 0x64, 0x65, 0x3a, 0x73, 0x70, 0x66, 0x33, 0x2e, 0x62, 0x61, 0x69, 0x64, 0x75, 0x2e, + 0x63, 0x6f, 0x6d, 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x3a, 0x73, 0x70, + 0x66, 0x34, 0x2e, 0x62, 0x61, 0x69, 0x64, 0x75, 0x2e, 0x63, 0x6f, 0x6d, 0x20, 0x61, + 0x20, 0x6d, 0x78, 0x20, 0x70, 0x74, 0x72, 0x20, 0x2d, 0x61, 0x6c, 0x6c, 0xc0, 0x0c, + 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0xd2, 0x8a, 0x00, 0x06, 0x03, 0x6e, 0x73, 0x37, + 0xc0, 0x0c, 0xc0, 0x0c, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0xd2, 0x8a, 0x00, 0x06, + 0x03, 0x64, 0x6e, 0x73, 0xc0, 0x0c, 0xc0, 0x0c, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, + 0xd2, 0x8a, 0x00, 0x06, 0x03, 0x6e, 0x73, 0x33, 0xc0, 0x0c, 0xc0, 0x0c, 0x00, 0x02, + 0x00, 0x01, 0x00, 0x00, 0xd2, 0x8a, 0x00, 0x06, 0x03, 0x6e, 0x73, 0x32, 0xc0, 0x0c, + 0xc0, 0x0c, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0xd2, 0x8a, 0x00, 0x06, 0x03, 0x6e, + 0x73, 0x34, 0xc0, 0x0c, 0xc1, 0x70, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x65, 0x42, + 0x00, 0x04, 0x24, 0x98, 0x2d, 0xc1, 0xc1, 0x70, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x65, 0x42, 0x00, 0x04, 0x70, 0x50, 0xf8, 0x40, 0xc1, 0x82, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x5d, 0x9f, 0x00, 0x04, 0xdc, 0xb5, 0x21, 0x1f, 0xc1, 0x4c, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0xc6, 0x6a, 0x00, 0x04, 0xb4, 0x4c, 0x4c, 0x5c, 0xc1, 0x94, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xe4, 0x1f, 0x00, 0x04, 0x0e, 0xd7, 0xb2, 0x50, + 0xc1, 0x94, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xe4, 0x1f, 0x00, 0x04, 0x6f, 0x2d, + 0x03, 0xe2, 0xc1, 0x5e, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0xc0, 0xc9, 0x00, 0x04, + 0x6e, 0xf2, 0x44, 0x86, 0xc1, 0x4c, 0x00, 0x1c, 0x00, 0x01, 0x00, 0x01, 0x3a, 0x98, + 0x00, 0x10, 0x24, 0x0e, 0x09, 0x40, 0x06, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0xff, + 0xb0, 0x1b, 0x58, 0x9a, 0xc1, 0x4c, 0x00, 0x1c, 0x00, 0x01, 0x00, 0x01, 0x3a, 0x98, + 0x00, 0x10, 0x24, 0x0e, 0x00, 0xbf, 0xb8, 0x01, 0x10, 0x02, 0x00, 0x00, 0x00, 0xff, + 0xb0, 0x24, 0x26, 0xde, 0x00, 0x00, 0x29, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, + ]; + + let expectation = DNS_MESSAGE { + header: DNS_HEADER { + id: 0x6721, + qr: DNS_HDR_QR::Response, + op_code: DNS_HDR_OPCODE::Query, + aa: false, + tc: false, + rd: true, + ra: true, + ad: false, + cd: false, + r_code: DNS_HDR_RCODE::NoError, + qd_count: 1, + an_count: 3, + ns_count: 5, + ar_count: 10, + }, + labels: DNS_LABEL_TABLE { + labels: vec![ + ((12, 11), "baidu.com.".to_string()), + ((27, 2), "baidu.com.".to_string()), + ((114, 2), "baidu.com.".to_string()), + ((195, 2), "baidu.com.".to_string()), + ((320, 2), "baidu.com.".to_string()), + ((332, 6), "ns7.baidu.com.".to_string()), + ((338, 2), "baidu.com.".to_string()), + ((350, 6), "dns.baidu.com.".to_string()), + ((356, 2), "baidu.com.".to_string()), + ((368, 6), "ns3.baidu.com.".to_string()), + ((374, 2), "baidu.com.".to_string()), + ((386, 6), "ns2.baidu.com.".to_string()), + ((392, 2), "baidu.com.".to_string()), + ((404, 6), "ns4.baidu.com.".to_string()), + ((410, 2), "ns3.baidu.com.".to_string()), + ((426, 2), "ns3.baidu.com.".to_string()), + ((442, 2), "ns2.baidu.com.".to_string()), + ((458, 2), "ns7.baidu.com.".to_string()), + ((474, 2), "ns4.baidu.com.".to_string()), + ((490, 2), "ns4.baidu.com.".to_string()), + ((506, 2), "dns.baidu.com.".to_string()), + ((522, 2), "ns7.baidu.com.".to_string()), + ((550, 2), "ns7.baidu.com.".to_string()), + ((578, 1), ".".to_string()), + ] + }, + qd: vec![ + DNS_QUESTION_SECTION { + qname: "baidu.com.".to_string(), + qtype: DNS_QTYPE::TXT, + qclass: DNS_QCLASS::Internet, + size: 15 + } + ], + an: vec![ + (DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: "baidu.com.".to_string(), + rr_type: DNS_QTYPE::TXT, + rr_class: DNS_QCLASS::Internet, + ttl: 7200, + rd_length: 75, + size: 12 + }, + data:DNS_RR_DATA::TXT{ txtdata:vec!["_globalsign-domain-verification=qjb28W2jJSrWj04NHpB0CvgK9tle5JkOq-EcyWBgnE".to_string()]},} + ), + (DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: "baidu.com.".to_string(), + rr_type: DNS_QTYPE::TXT, + rr_class: DNS_QCLASS::Internet, + ttl: 7200, + rd_length: 69, + size: 12 + }, + data:DNS_RR_DATA::TXT{ txtdata:vec!["google-site-verification=GHb98-6msqyx_qqjGl5eRatD3QTHyVB6-xQ3gJB5UwM".to_string()]},} + ), + (DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: "baidu.com.".to_string(), + rr_type: DNS_QTYPE::TXT, + rr_class: DNS_QCLASS::Internet, + ttl: 7200, + rd_length: 113, + size: 12 + }, + data: DNS_RR_DATA::TXT{ txtdata:vec!["v=spf1 include:spf1.baidu.com include:spf2.baidu.com include:spf3.baidu.com include:spf4.baidu.com a mx ptr -all".to_string()]}, } + )], + ns: vec![ + (DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: "baidu.com.".to_string(), + rr_type: DNS_QTYPE::NS, + rr_class: DNS_QCLASS::Internet, + ttl: 53898, + rd_length: 6, + size: 12 + }, + data: DNS_RR_DATA::NS{nsdname: "ns7.baidu.com.".to_string()},} + ), + (DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: "baidu.com.".to_string(), + rr_type: DNS_QTYPE::NS, + rr_class: DNS_QCLASS::Internet, + ttl: 53898, + rd_length: 6, + size: 12 + }, + data: DNS_RR_DATA::NS{nsdname: "dns.baidu.com.".to_string()},} + ), + (DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: "baidu.com.".to_string(), + rr_type: DNS_QTYPE::NS, + rr_class: DNS_QCLASS::Internet, + ttl: 53898, + rd_length: 6, + size: 12 + }, + data: DNS_RR_DATA::NS{nsdname: "ns3.baidu.com.".to_string()},} + ), + (DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: "baidu.com.".to_string(), + rr_type: DNS_QTYPE::NS, + rr_class: DNS_QCLASS::Internet, + ttl: 53898, + rd_length: 6, + size: 12 + }, + data: DNS_RR_DATA:: NS{ nsdname: "ns2.baidu.com.".to_string()},} + ), + (DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: "baidu.com.".to_string(), + rr_type: DNS_QTYPE::NS, + rr_class: DNS_QCLASS::Internet, + ttl: 53898, + rd_length: 6, + size: 12 + }, + data: DNS_RR_DATA::NS{nsdname: "ns4.baidu.com.".to_string()},} + ) + ], + ar: vec![ + (DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: "ns3.baidu.com.".to_string(), + rr_type: DNS_QTYPE::A, + rr_class: DNS_QCLASS::Internet, + ttl: 25922, + rd_length: 4, + size: 12 + }, + data: DNS_RR_DATA::A{address: "36.152.45.193".to_string()}, + } + ), + (DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: "ns3.baidu.com.".to_string(), + rr_type: DNS_QTYPE::A, + rr_class: DNS_QCLASS::Internet, + ttl: 25922, + rd_length: 4, + size: 12 + }, + data: DNS_RR_DATA::A{address: "112.80.248.64".to_string()}, + } + ), + ( + DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: "ns2.baidu.com.".to_string(), + rr_type: DNS_QTYPE::A, + rr_class: DNS_QCLASS::Internet, + ttl: 23967, + rd_length: 4, + size: 12 + }, + data: DNS_RR_DATA::A{address: "220.181.33.31".to_string()}, + } + ), + ( + DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: "ns7.baidu.com.".to_string(), + rr_type: DNS_QTYPE::A, + rr_class: DNS_QCLASS::Internet, + ttl: 50794, + rd_length: 4, + size: 12 + }, + data: DNS_RR_DATA::A{address: "180.76.76.92".to_string()}, + } + ), + ( + DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: "ns4.baidu.com.".to_string(), + rr_type: DNS_QTYPE::A, + rr_class: DNS_QCLASS::Internet, + ttl: 58399, + rd_length: 4, + size: 12 + }, + data:DNS_RR_DATA::A{address: "14.215.178.80".to_string()}, + } + ), + ( + DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: "ns4.baidu.com.".to_string(), + rr_type: DNS_QTYPE::A, + rr_class: DNS_QCLASS::Internet, + ttl: 58399, + rd_length: 4, + size: 12 + }, + data:DNS_RR_DATA::A{address: "111.45.3.226".to_string()},} + ), + ( + DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: "dns.baidu.com.".to_string(), + rr_type: DNS_QTYPE::A, + rr_class: DNS_QCLASS::Internet, + ttl: 114889, + rd_length: 4, + size: 12 + }, + data: DNS_RR_DATA::A{address: "110.242.68.134".to_string()},} + ), + (DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: "ns7.baidu.com.".to_string(), + rr_type: DNS_QTYPE::AAAA, + rr_class: DNS_QCLASS::Internet, + ttl: 80536, + rd_length: 16, + size: 12 + }, + data: DNS_RR_DATA::AAAA{ address : "240E:0940:0603:0004:0000:00FF:B01B:589A".to_string()},} + ), + (DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: "ns7.baidu.com.".to_string(), + rr_type: DNS_QTYPE::AAAA, + rr_class: DNS_QCLASS::Internet, + ttl: 80536, + rd_length: 16, + size: 12 + }, + data: DNS_RR_DATA::AAAA{ address: "240E:00BF:B801:1002:0000:00FF:B024:26DE".to_string()},} + ), + (DNS_RR_SECTION { + hdr: DNS_RR_HDR { + qname: ".".to_string(), + rr_type: DNS_QTYPE::OPT, + rr_class: DNS_QCLASS::Other(4096), + ttl: 0, + rd_length: 0, + size: 11 + }, + data:DNS_RR_DATA::Other{ qtype: DNS_QTYPE::OPT,data:vec![]},} + ) + ] + }; + + assert_eq!(DNS_MESSAGE::decode(&bytes), Ok((LAST_SLICE, expectation))); + + match DNS_MESSAGE::decode(&bytes) { + Ok((left, message)) => { + println!("{:?}; left {:?}", message, left); + } + Err(err) => { + println!("{:?}\n", err); + } + } + // assert_eq!(0, 1); + } + + // TODO + #[test] + fn test_answer_other_decode() {} +} diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index cff0932..3427b8b 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -3,4 +3,5 @@ pub mod ip; pub mod ipv4; pub mod ipv6; pub mod udp; -pub mod tcp;
\ No newline at end of file +pub mod tcp; +pub mod dns;
\ No newline at end of file |
