summaryrefslogtreecommitdiff
path: root/src/protocol/dns.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/protocol/dns.rs')
-rw-r--r--src/protocol/dns.rs3728
1 files changed, 3728 insertions, 0 deletions
diff --git a/src/protocol/dns.rs b/src/protocol/dns.rs
new file mode 100644
index 0000000..6e82a51
--- /dev/null
+++ b/src/protocol/dns.rs
@@ -0,0 +1,3728 @@
+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;
+
+/******************************************************************************
+ * Format
+ ******************************************************************************/
+
+/*
+ * 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 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 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 /
+ * / /
+ * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ */
+
+/*
+ * 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.
+ */
+
+/******************************************************************************
+ * DNS Header Struct
+ ******************************************************************************/
+
+#[allow(non_camel_case_types)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum DNS_HDR_QR {
+ Query = 0,
+ Response = 1,
+ Other = 2,
+}
+
+// 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),
+}
+
+// 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),
+}
+
+#[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,
+}
+
+/******************************************************************************
+ * DNS Question Section Struct
+ ******************************************************************************/
+
+// 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),
+}
+
+// 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),
+}
+
+#[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,
+ pub size: usize, // DNS 原始数据中 DNS_QUESTION_SECTION 占多少字节
+}
+
+/******************************************************************************
+ * DNS RR Struct
+ ******************************************************************************/
+
+#[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,
+ pub size: usize, // DNS 原始数据中 DNS_RR_HDR 占多少字节
+}
+
+#[allow(non_camel_case_types)]
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum DNS_RR_DATA {
+ A(String),
+ AAAA(String),
+ CNAME(String),
+ HINFO(Vec<u8>, Vec<u8>),
+ MX(u16, String),
+ NS(String),
+ PTR(String),
+ SOA(String, String, u32, i32, i32, i32, i32),
+ TXT(Vec<String>),
+ Other(DNS_QTYPE, Vec<u8>),
+}
+
+/******************************************************************************
+ * DNS Message Struct
+ ******************************************************************************/
+
+// 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,
+}
+
+#[allow(non_camel_case_types)]
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct DNS_LABEL_TABLE {
+ pub labels: Vec<((usize, usize), String)>,
+}
+
+#[allow(non_camel_case_types)]
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct DNS_MESSAGE {
+ pub header: DNS_HEADER,
+ pub label_table: DNS_LABEL_TABLE,
+ pub qd: Vec<DNS_QUESTION_SECTION>,
+ pub an: Vec<(DNS_RR_HDR, DNS_RR_DATA)>,
+ pub ns: Vec<(DNS_RR_HDR, DNS_RR_DATA)>,
+ pub ar: Vec<(DNS_RR_HDR, DNS_RR_DATA)>,
+}
+
+/******************************************************************************
+ * impl from_u8 trait
+ ******************************************************************************/
+
+fn u32_to_ipv4(n: u32) -> String {
+ let ipb = n.to_be_bytes();
+ format!("{}.{}.{}.{}", ipb[0], ipb[1], ipb[2], ipb[3])
+}
+
+fn u128_to_ipv6(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])
+}
+
+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 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 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 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 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 fmt trait
+ ******************************************************************************/
+
+fn format_rr(f: &mut fmt::Formatter<'_>, head: &DNS_RR_HDR, rdata: &DNS_RR_DATA) -> fmt::Result {
+ write!(
+ f,
+ "{}\t\t{}\t{}\t{}\t",
+ head.qname, head.ttl, head.rr_class, head.rr_type
+ )?;
+ match rdata {
+ DNS_RR_DATA::A(ip) => {
+ write!(f, "{}\n", ip)
+ }
+ DNS_RR_DATA::AAAA(ip) => {
+ write!(f, "{}\n", ip)
+ }
+ DNS_RR_DATA::CNAME(name) => {
+ write!(f, "{}\n", name)
+ }
+ DNS_RR_DATA::HINFO(cpu, 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(n, name) => {
+ write!(f, "{} {}\n", n, name)
+ }
+ DNS_RR_DATA::NS(name) => {
+ write!(f, "{}\n", name)
+ }
+ DNS_RR_DATA::PTR(name) => {
+ write!(f, "{}\n", name)
+ }
+ DNS_RR_DATA::SOA(mname, rname, serial, refresh, retry, expire, minimum) => {
+ write!(
+ f,
+ "{} {} {} {} {} {} {}\n",
+ mname, rname, serial, refresh, retry, expire, minimum,
+ )
+ }
+ DNS_RR_DATA::TXT(txt) => {
+ for i in txt {
+ write!(f, "{}", i)?;
+ }
+ Ok(())
+ }
+ DNS_RR_DATA::Other(_rr_type, data) => {
+ write!(f, "\\# ")?;
+ for i in &*data {
+ write!(f, "{:02X}", i)?;
+ }
+ write!(f, "\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, "OPCODE{}", 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, "RCODE{}", n),
+ }
+ }
+}
+
+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
+ )
+ }
+}
+
+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),
+ }
+ }
+}
+
+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),
+ }
+ }
+}
+
+impl Display for DNS_QUESTION_SECTION {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, ";{}\t\t{}\t{}\n", self.qname, self.qclass, self.qtype)
+ }
+}
+
+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 {
+ format_rr(f, &ane.0, &ane.1)?;
+ }
+ }
+ if self.ns.len() > 0 {
+ write!(f, ";; AUTHORITY SECTION:\n")?;
+ for nse in &self.ns {
+ format_rr(f, &nse.0, &nse.1)?;
+ }
+ }
+ if self.ar.len() > 0 {
+ write!(f, ";; ADDITIONAL SECTION:\n")?;
+ for are in &self.ar {
+ format_rr(f, &are.0, &are.1)?;
+ }
+ }
+ Ok(())
+ }
+}
+
+/******************************************************************************
+ * Parse DNS Header
+ ******************************************************************************/
+
+fn parse_header_bits(
+ 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),
+ ),
+ )
+ },
+ )
+}
+
+fn parse_dns_header(input: &[u8]) -> IResult<&[u8], DNS_HEADER> {
+ sequence::tuple((
+ number::streaming::be_u16,
+ parse_header_bits,
+ 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,
+ },
+ )
+ })
+}
+
+/******************************************************************************
+ * Parse Name
+ ******************************************************************************/
+
+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 add_label(&mut self, position: usize, size: usize, domain: &str) -> &str {
+ self.labels.push(((position, size), domain.to_string()));
+ &self.labels.last().unwrap().1
+ }
+
+ fn get_label(&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
+ }
+}
+
+/*
+ * 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 is_label_character(c: u8) -> bool {
+ character::is_alphabetic(c) || character::is_digit(c) || (c == b'-') || (c == b'_')
+}
+
+fn parse_label<'a>(
+ input: &'a [u8],
+ size: usize,
+ name: &mut String,
+) -> IResult<&'a [u8], &'static str> {
+ if input.len() < size {
+ println!("invalid label size");
+ return Err(Err::Incomplete(Needed::new(size - input.len())));
+ }
+
+ for i in 0..size {
+ if !is_label_character(input[i]) {
+ println!("invalid label character");
+ 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"));
+}
+
+fn peek_label_type(input: &[u8]) -> IResult<&[u8], (u8, u8)> {
+ combinator::peek(bits::<_, _, Error<(&[u8], usize)>, _, _>(sequence::tuple(
+ (bits::streaming::take(1usize), bits::streaming::take(1usize)),
+ )))(input)
+}
+
+fn parse_label_type(input: &[u8]) -> IResult<&[u8], DNS_LABEL_TYPE> {
+ match peek_label_type(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)),
+ }
+}
+
+/*
+ * 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 parse_domain_name<'a, 'b>(
+ data: &'a [u8],
+ label_table: &'b mut DNS_LABEL_TABLE,
+ msg_position: usize,
+) -> IResult<&'a [u8], (&'b str, usize)> {
+ let mut input = data;
+ let mut total_size = 0usize;
+ let mut name = String::new();
+ loop {
+ match parse_label_type(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, _) = parse_label(inner_input, size, &mut name)?;
+ input = left;
+ }
+ // 域名压缩
+ DNS_LABEL_TYPE::Pointer(position) => {
+ total_size += 2;
+ if name.len() > 0 {
+ name.push('.');
+ }
+
+ let label = match label_table.get_label(position) {
+ Some(label) => label,
+ _ => {
+ println!("invalid label type");
+ return Err(Err::Failure(Error::new(inner_input, ErrorKind::Fail)));
+ }
+ };
+ name.push_str(label);
+ let n = label_table.add_label(msg_position, total_size, &name);
+ return Ok((inner_input, (n, total_size)));
+ }
+ // 域名未压缩
+ DNS_LABEL_TYPE::End => {
+ total_size += 1;
+ name.push('.');
+
+ let n = label_table.add_label(msg_position, total_size, &name);
+ return Ok((inner_input, (n, total_size)));
+ }
+ DNS_LABEL_TYPE::Other => {
+ println!("invalid label type");
+ return Err(Err::Failure(Error::new(inner_input, ErrorKind::Fail)));
+ }
+ },
+ _ => {
+ println!("invalid label type");
+ return Err(Err::Failure(Error::new(input, ErrorKind::Fail)));
+ }
+ }
+ }
+}
+
+/******************************************************************************
+ * Parse DNS Question Section
+ ******************************************************************************/
+
+fn parse_question_entry<'a, 'b>(
+ input: &'a [u8],
+ label_table: &'b mut DNS_LABEL_TABLE,
+ msg_position: usize,
+) -> IResult<&'a [u8], DNS_QUESTION_SECTION> {
+ let (input, (name, size)) = parse_domain_name(input, label_table, msg_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,
+ },
+ ))
+}
+
+fn parse_question_section<'a>(
+ data: &'a [u8],
+ label_table: &mut DNS_LABEL_TABLE,
+ qd_position: usize,
+ count: u16,
+) -> IResult<&'a [u8], (Vec<DNS_QUESTION_SECTION>, usize)> {
+ let mut input = data;
+ let mut qd_vec = vec![];
+ let mut pos = qd_position;
+
+ // there are count DNS Question Sections in total
+ for _ in 0..count {
+ let (left, qd) = parse_question_entry(input, label_table, pos)?;
+ input = left;
+ pos += qd.size;
+ qd_vec.push(qd);
+ }
+
+ Ok((input, (qd_vec, pos)))
+}
+
+/******************************************************************************
+ * Parse DNS Resource Record
+ ******************************************************************************/
+
+/* 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 parse_character_string(input: &[u8]) -> IResult<&[u8], &[u8]> {
+ multi::length_data(number::streaming::be_u8)(input)
+}
+
+fn parse_rr_header<'a>(
+ input: &'a [u8],
+ label_table: &mut DNS_LABEL_TABLE,
+ position: usize,
+) -> IResult<&'a [u8], DNS_RR_HDR> {
+ let (input, (name, size)) = parse_domain_name(input, label_table, 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,
+ },
+ ))
+}
+
+fn parse_rr_rdata<'a>(
+ input: &'a [u8],
+ label_table: &mut DNS_LABEL_TABLE,
+ position: usize,
+ rr_header: &DNS_RR_HDR,
+) -> IResult<&'a [u8], DNS_RR_DATA> {
+ match (rr_header.rr_class, rr_header.rr_type) {
+ (DNS_QCLASS::Internet, DNS_QTYPE::A) => {
+ let (input, num) = number::streaming::be_u32(input)?;
+ Ok((input, DNS_RR_DATA::A(u32_to_ipv4(num))))
+ }
+ (DNS_QCLASS::Internet, DNS_QTYPE::AAAA) => {
+ let (input, num) = number::streaming::be_u128(input)?;
+ Ok((input, DNS_RR_DATA::AAAA(u128_to_ipv6(num))))
+ }
+ (_, DNS_QTYPE::CNAME) => {
+ let (input, (name, _size)) = parse_domain_name(input, label_table, position)?;
+ Ok((input, DNS_RR_DATA::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.to_vec(), os.to_vec())))
+ }
+ (_, DNS_QTYPE::MX) => {
+ let (input, preference) = number::streaming::be_u16(input)?;
+ let (input, (name, _size)) = parse_domain_name(input, label_table, position + 2)?;
+ Ok((input, DNS_RR_DATA::MX(preference, name.to_string())))
+ }
+ (_, DNS_QTYPE::NS) => {
+ let (input, (name, _size)) = parse_domain_name(input, label_table, position)?;
+ Ok((input, DNS_RR_DATA::NS(name.to_string())))
+ }
+ (_, DNS_QTYPE::PTR) => {
+ let (input, (name, _size)) = parse_domain_name(input, label_table, position)?;
+ Ok((input, DNS_RR_DATA::PTR(name.to_string())))
+ }
+ (_, DNS_QTYPE::SOA) => {
+ let (input, (mname, mname_size)) = parse_domain_name(input, label_table, position)?;
+ let mname_string = mname.to_string();
+ let (input, (rname, _rname_size)) =
+ parse_domain_name(input, label_table, 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_string,
+ rname.to_string(),
+ serial,
+ refresh,
+ retry,
+ expire,
+ minimum,
+ ),
+ ))
+ }
+ (_, DNS_QTYPE::TXT) => {
+ let mut input = input;
+ let mut nleft = rr_header.rd_length;
+ let mut ret = Vec::new();
+ loop {
+ let (__input, data) = parse_character_string(input)?;
+ // TODO str::from_utf8(data).unwrap() 不安全 ???
+ 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(ret)))
+ }
+ (_, _) => {
+ let slen: usize = rr_header.rd_length.into();
+ let rdata = &input[0..slen];
+ Ok((
+ &input[slen..],
+ DNS_RR_DATA::Other(rr_header.rr_type, rdata.to_vec()),
+ ))
+ }
+ }
+}
+
+/******************************************************************************
+ * Parse DNS Message
+ ******************************************************************************/
+
+impl DNS_MESSAGE {
+ fn new(header: DNS_HEADER) -> DNS_MESSAGE {
+ DNS_MESSAGE {
+ header: header,
+ label_table: DNS_LABEL_TABLE::new(),
+ qd: vec![],
+ an: vec![],
+ ns: vec![],
+ ar: vec![],
+ }
+ }
+}
+
+pub fn parse_dns_message(input: &[u8]) -> IResult<&[u8], DNS_MESSAGE> {
+ let (input, dns_header) = parse_dns_header(input)?;
+ println!("{:?}", dns_header);
+
+ let mut message = DNS_MESSAGE::new(dns_header);
+
+ let (mut input, (qd, mut pos)) =
+ parse_question_section(input, &mut message.label_table, 12, dns_header.qd_count)?;
+ println!("{:?}", qd);
+
+ message.qd = qd;
+ for _ in 0..dns_header.an_count {
+ let (left, rr_header) = parse_rr_header(input, &mut message.label_table, pos)?;
+ println!("AN: {:?}", rr_header);
+ pos += rr_header.size;
+
+ let (left, rr_data) = parse_rr_rdata(left, &mut message.label_table, pos, &rr_header)?;
+ println!("AN: {:?}", rr_data);
+ pos += rr_header.rd_length as usize;
+
+ message.an.push((rr_header, rr_data));
+ input = left;
+ }
+
+ for _ in 0..dns_header.ns_count {
+ let (left, rr_header) = parse_rr_header(input, &mut message.label_table, pos)?;
+ println!("NS: {:?}", rr_header);
+ pos += rr_header.size;
+
+ let (left, rr_data) = parse_rr_rdata(left, &mut message.label_table, pos, &rr_header)?;
+ println!("NS: {:?}", rr_data);
+ pos += rr_header.rd_length as usize;
+
+ message.ns.push((rr_header, rr_data));
+ input = left;
+ }
+
+ for _ in 0..dns_header.ar_count {
+ let (left, rr_header) = parse_rr_header(input, &mut message.label_table, pos)?;
+ println!("AR: {:?}", rr_header);
+ pos += rr_header.size;
+
+ let (left, rr_data) = parse_rr_rdata(left, &mut message.label_table, pos, &rr_header)?;
+ println!("AR: {:?}", rr_data);
+ pos += rr_header.rd_length as usize;
+
+ message.ar.push((rr_header, rr_data));
+ input = left;
+ }
+
+ Ok((input, message))
+}
+
+/******************************************************************************
+ * 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 parse_dns_message(input) {
+ Ok((_rest, message)) => {
+ println!("{}", message);
+ Ok(())
+ },
+ Err(err) => {
+ println!("err:{:?}\n", err);
+ Ok(())
+ },
+
+ }
+}
+*/
+
+#[cfg(test)]
+mod tests {
+ use super::parse_character_string;
+ use super::parse_dns_message;
+ use super::parse_domain_name;
+ use super::parse_label;
+ 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;
+
+ const LAST_SLICE: &'static [u8] = &[];
+
+ /**************************************************************************
+ * test util
+ **************************************************************************/
+
+ // OK
+ #[test]
+ fn character_string() {
+ let input = [0b00000101u8, b'h', b'i', b'!', b'!', 0b11000000u8];
+ let result = parse_character_string(&input);
+ assert_eq!(
+ result,
+ Ok((&[][..], &[b'h', b'i', b'!', b'!', 0b11000000u8][..]))
+ );
+
+ let input = [0b00000101u8, 0b11000000u8];
+ let result = parse_character_string(&input);
+ assert_eq!(result, Err(nom::Err::Incomplete(nom::Needed::new(4))));
+ }
+
+ // OK
+ #[test]
+ fn manipulate_label() {
+ let mut table = DNS_LABEL_TABLE::new();
+
+ table.add_label(30, 16, "www.example.com.");
+
+ println!("DNS_LABEL_TABLE: {:?}\n", table);
+ assert_eq!(table.get_label(29), None);
+ assert_eq!(table.get_label(30), Some("www.example.com."));
+ assert_eq!(table.get_label(31), None);
+ assert_eq!(table.get_label(32), None);
+ assert_eq!(table.get_label(33), None);
+ assert_eq!(table.get_label(34), Some("example.com."));
+ assert_eq!(table.get_label(35), None);
+ assert_eq!(table.get_label(36), None);
+ assert_eq!(table.get_label(37), None);
+ assert_eq!(table.get_label(38), None);
+ assert_eq!(table.get_label(39), None);
+ assert_eq!(table.get_label(40), None);
+ assert_eq!(table.get_label(41), None);
+ assert_eq!(table.get_label(42), Some("com."));
+ assert_eq!(table.get_label(43), None);
+ }
+
+ // OK
+ #[test]
+ fn parse_label_works() {
+ /*
+ * [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 = parse_label(buf1, 1, &mut str);
+ assert_eq!(str, "a");
+ assert_eq!(ret, Ok((LAST_SLICE, "OK")));
+
+ let mut str = String::new();
+ let ret = parse_label(buf2, 1, &mut str);
+ assert_eq!(str, "1");
+ assert_eq!(ret, Ok((LAST_SLICE, "OK")));
+
+ let mut str = String::new();
+ let ret = parse_label(buf3, 1, &mut str);
+ assert_eq!(str, "-");
+ assert_eq!(ret, Ok((LAST_SLICE, "OK")));
+
+ let mut str = String::new();
+ let ret = parse_label(buf4, 1, &mut str);
+ assert_eq!(str, "_");
+ assert_eq!(ret, Ok((LAST_SLICE, "OK")));
+
+ let mut str = String::new();
+ let ret = parse_label(buf5, 1, &mut str);
+ assert_eq!(str, "");
+ assert_ne!(ret, Ok((LAST_SLICE, "OK")));
+ }
+
+ // OK
+ #[test]
+ fn parse_domain_name_works() {
+ 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 = parse_domain_name(&input1, &mut table, 10);
+ assert_eq!(result, Ok((LAST_SLICE, ("www.example.com.", 17))));
+ assert_eq!(table.get_label(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 = parse_domain_name(&input2, &mut table, 50);
+ assert_eq!(result, Ok((LAST_SLICE, ("www.example.com.", 2))));
+ assert_eq!(table.get_label(50), Some("www.example.com."));
+ }
+
+ /**************************************************************************
+ * test parse query
+ **************************************************************************/
+
+ // OK
+ #[test]
+ fn dns_query_a() {
+ /*
+ * 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,
+ },
+ label_table: 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_HDR {
+ qname: ".".to_string(),
+ rr_type: DNS_QTYPE::OPT,
+ rr_class: DNS_QCLASS::Other(4096),
+ ttl: 0,
+ rd_length: 0,
+ size: 11,
+ },
+ DNS_RR_DATA::Other(DNS_QTYPE::OPT, vec![]),
+ )],
+ };
+
+ assert_eq!(parse_dns_message(&bytes), Ok((LAST_SLICE, expectation)));
+
+ /*
+ match parse_dns_message(&bytes) {
+ Ok((_rest, message)) => {
+ println!("{:?}", message);
+ }
+ Err(err) => {
+ println!("{:?}\n", err);
+ }
+ }
+ assert_eq!(0, 1);
+ */
+ }
+
+ // OK
+ #[test]
+ fn dns_query_aaaa() {
+ /*
+ * 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,
+ },
+ label_table: 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_HDR {
+ qname: ".".to_string(),
+ rr_type: DNS_QTYPE::OPT,
+ rr_class: DNS_QCLASS::Other(4096),
+ ttl: 0,
+ rd_length: 0,
+ size: 11,
+ },
+ DNS_RR_DATA::Other(DNS_QTYPE::OPT, vec![]),
+ )],
+ };
+
+ assert_eq!(parse_dns_message(&bytes), Ok((LAST_SLICE, expectation)));
+
+ /*
+ match parse_dns_message(&bytes) {
+ Ok((_rest, message)) => {
+ println!("{:?}", message);
+ }
+ Err(err) => {
+ println!("{:?}\n", err);
+ }
+ }
+ assert_eq!(0, 1);
+ */
+ }
+
+ // OK
+ #[test]
+ fn dns_query_cname() {
+ /*
+ * 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,
+ },
+ label_table: 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_HDR {
+ qname: ".".to_string(),
+ rr_type: DNS_QTYPE::OPT,
+ rr_class: DNS_QCLASS::Other(4096),
+ ttl: 0,
+ rd_length: 0,
+ size: 11,
+ },
+ DNS_RR_DATA::Other(DNS_QTYPE::OPT, vec![]),
+ )],
+ };
+
+ assert_eq!(parse_dns_message(&bytes), Ok((LAST_SLICE, expectation)));
+
+ /*
+ match parse_dns_message(&bytes) {
+ Ok((_rest, message)) => {
+ println!("{:?}", message);
+ }
+ Err(err) => {
+ println!("{:?}\n", err);
+ }
+ }
+ assert_eq!(0, 1);
+ */
+ }
+
+ // TODO
+ #[test]
+ fn dns_query_hinfo() {}
+
+ // OK
+ #[test]
+ fn dns_query_mx() {
+ /*
+ * 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,
+ },
+ label_table: 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_HDR {
+ qname: ".".to_string(),
+ rr_type: DNS_QTYPE::OPT,
+ rr_class: DNS_QCLASS::Other(4096),
+ ttl: 0,
+ rd_length: 0,
+ size: 11,
+ },
+ DNS_RR_DATA::Other(DNS_QTYPE::OPT, vec![]),
+ )],
+ };
+
+ assert_eq!(parse_dns_message(&bytes), Ok((LAST_SLICE, expectation)));
+
+ /*
+ match parse_dns_message(&bytes) {
+ Ok((_rest, message)) => {
+ println!("{:?}", message);
+ }
+ Err(err) => {
+ println!("{:?}\n", err);
+ }
+ }
+ assert_eq!(0, 1);
+ */
+ }
+
+ // TODO
+ #[test]
+ fn dns_query_nx() {}
+
+ // TODO
+ #[test]
+ fn dns_query_ptr() {}
+
+ // OK
+ #[test]
+ fn dns_query_soa() {
+ /*
+ * 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,
+ },
+ label_table: 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_HDR {
+ qname: ".".to_string(),
+ rr_type: DNS_QTYPE::OPT,
+ rr_class: DNS_QCLASS::Other(4096),
+ ttl: 0,
+ rd_length: 0,
+ size: 11,
+ },
+ DNS_RR_DATA::Other(DNS_QTYPE::OPT, vec![]),
+ )],
+ };
+
+ assert_eq!(parse_dns_message(&bytes), Ok((LAST_SLICE, expectation)));
+
+ /*
+ match parse_dns_message(&bytes) {
+ Ok((_rest, message)) => {
+ println!("{:?}", message);
+ }
+ Err(err) => {
+ println!("{:?}\n", err);
+ }
+ }
+ assert_eq!(0, 1);
+ */
+ }
+
+ // OK
+ #[test]
+ fn dns_query_txt() {
+ /*
+ * 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,
+ },
+ label_table: 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_HDR {
+ qname: ".".to_string(),
+ rr_type: DNS_QTYPE::OPT,
+ rr_class: DNS_QCLASS::Other(4096),
+ ttl: 0,
+ rd_length: 0,
+ size: 11,
+ },
+ DNS_RR_DATA::Other(DNS_QTYPE::OPT, vec![]),
+ )],
+ };
+
+ assert_eq!(parse_dns_message(&bytes), Ok((LAST_SLICE, expectation)));
+
+ /*
+ match parse_dns_message(&bytes) {
+ Ok((_rest, message)) => {
+ println!("{:?}", message);
+ }
+ Err(err) => {
+ println!("{:?}\n", err);
+ }
+ }
+ assert_eq!(0, 1);
+ */
+ }
+
+ // TODO
+ #[test]
+ fn dns_query_other() {}
+
+ /**************************************************************************
+ * test parse answer
+ **************************************************************************/
+
+ // OK
+ #[test]
+ fn dns_answer_a() {
+ // test in dns_answer_txt
+ }
+
+ // OK
+ #[test]
+ fn dns_answer_aaaa() {
+ // test in dns_answer_txt
+ }
+
+ // OK
+ #[test]
+ fn dns_answer_ns() {
+ // test in dns_answer_txt
+ }
+
+ // TODO
+ #[test]
+ fn dns_answer_hinfo() {}
+
+ // OK
+ #[test]
+ fn dns_answer_mx() {
+ /*
+ * 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,
+ },
+ label_table: 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_HDR {
+ qname: "163.com.".to_string(),
+ rr_type: DNS_QTYPE::MX,
+ rr_class: DNS_QCLASS::Internet,
+ ttl: 12492,
+ rd_length: 27,
+ size: 12,
+ },
+ DNS_RR_DATA::MX(10, "163mx02.mxmail.netease.com.".to_string()),
+ ),
+ (
+ 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,
+ },
+ DNS_RR_DATA::MX(50, "163mx00.mxmail.netease.com.".to_string()),
+ ),
+ (
+ 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,
+ },
+ DNS_RR_DATA::MX(10, "163mx03.mxmail.netease.com.".to_string()),
+ ),
+ (
+ 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,
+ },
+ DNS_RR_DATA::MX(10, "163mx01.mxmail.netease.com.".to_string()),
+ ),
+ ],
+ ns: vec![
+ (
+ 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,
+ },
+ DNS_RR_DATA::NS("ns5.nease.net.".to_string()),
+ ),
+ (
+ 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,
+ },
+ DNS_RR_DATA::NS("ns3.nease.net.".to_string()),
+ ),
+ (
+ 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,
+ },
+ DNS_RR_DATA::NS("ns1.nease.net.".to_string()),
+ ),
+ (
+ 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,
+ },
+ DNS_RR_DATA::NS("ns8.166.com.".to_string()),
+ ),
+ (
+ 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,
+ },
+ DNS_RR_DATA::NS("ns4.nease.net.".to_string()),
+ ),
+ (
+ 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,
+ },
+ DNS_RR_DATA::NS("ns2.166.com.".to_string()),
+ ),
+ (
+ 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,
+ },
+ DNS_RR_DATA::NS("ns6.nease.net.".to_string()),
+ ),
+ ],
+ ar: vec![
+ (
+ 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,
+ },
+ DNS_RR_DATA::A("220.181.12.119".to_string()),
+ ),
+ (
+ 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,
+ },
+ DNS_RR_DATA::A("220.181.12.117".to_string()),
+ ),
+ (
+ 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,
+ },
+ DNS_RR_DATA::A("220.181.12.118".to_string()),
+ ),
+ (
+ 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,
+ },
+ DNS_RR_DATA::A("220.181.12.180".to_string()),
+ ),
+ (
+ 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,
+ },
+ DNS_RR_DATA::A("54.228.156.72".to_string()),
+ ),
+ (
+ 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,
+ },
+ DNS_RR_DATA::A("42.186.35.222".to_string()),
+ ),
+ (
+ 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,
+ },
+ DNS_RR_DATA::A("103.72.16.81".to_string()),
+ ),
+ (
+ 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,
+ },
+ DNS_RR_DATA::A("220.181.36.234".to_string()),
+ ),
+ (
+ 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,
+ },
+ DNS_RR_DATA::A("103.71.201.3".to_string()),
+ ),
+ (
+ 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,
+ },
+ DNS_RR_DATA::A("121.195.179.18".to_string()),
+ ),
+ (
+ 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,
+ },
+ DNS_RR_DATA::A("18.182.82.158".to_string()),
+ ),
+ (
+ 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,
+ },
+ DNS_RR_DATA::A("44.228.163.69".to_string()),
+ ),
+ (
+ DNS_RR_HDR {
+ qname: ".".to_string(),
+ rr_type: DNS_QTYPE::OPT,
+ rr_class: DNS_QCLASS::Other(4096),
+ ttl: 0,
+ rd_length: 0,
+ size: 11,
+ },
+ DNS_RR_DATA::Other(DNS_QTYPE::OPT, vec![]),
+ ),
+ ],
+ };
+
+ assert_eq!(parse_dns_message(&bytes), Ok((LAST_SLICE, expectation)));
+
+ /*
+ match parse_dns_message(&bytes) {
+ Ok((_rest, message)) => {
+ println!("{:?}", message);
+ }
+ Err(err) => {
+ println!("{:?}\n", err);
+ }
+ }
+ assert_eq!(0, 1);
+ */
+ }
+
+ // TODO
+ #[test]
+ fn dns_answer_nx() {}
+
+ // TODO
+ #[test]
+ fn dns_answer_ptr() {}
+
+ // OK
+ #[test]
+ fn dns_answer_cname() {
+ // test in dns_answer_soa
+ }
+
+ // OK
+ #[test]
+ fn dns_answer_soa() {
+ /*
+ * 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,
+ },
+ label_table: 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_HDR {
+ qname: "www.baidu.com.".to_string(),
+ rr_type: DNS_QTYPE::CNAME,
+ rr_class: DNS_QCLASS::Internet,
+ ttl: 221,
+ rd_length: 15,
+ size: 12,
+ },
+ DNS_RR_DATA::CNAME("www.a.shifen.com.".to_string()),
+ )],
+ ns: vec![(
+ 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,
+ },
+ DNS_RR_DATA::SOA(
+ "ns1.a.shifen.com.".to_string(),
+ "baidu_dns_master.baidu.com.".to_string(),
+ 2211240019,
+ 5,
+ 5,
+ 2592000,
+ 3600,
+ ),
+ )],
+ ar: vec![(
+ DNS_RR_HDR {
+ qname: ".".to_string(),
+ rr_type: DNS_QTYPE::OPT,
+ rr_class: DNS_QCLASS::Other(4096),
+ ttl: 0,
+ rd_length: 0,
+ size: 11,
+ },
+ DNS_RR_DATA::Other(DNS_QTYPE::OPT, vec![]),
+ )],
+ };
+
+ assert_eq!(parse_dns_message(&bytes), Ok((LAST_SLICE, expectation)));
+
+ /*
+ match parse_dns_message(&bytes) {
+ Ok((_rest, message)) => {
+ println!("{:?}", message);
+ }
+ Err(err) => {
+ println!("{:?}\n", err);
+ }
+ }
+ assert_eq!(0, 1);
+ */
+ }
+
+ // OK
+ #[test]
+ fn dns_answer_txt() {
+ /*
+ * 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,
+ },
+ label_table: 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_HDR {
+ qname: "baidu.com.".to_string(),
+ rr_type: DNS_QTYPE::TXT,
+ rr_class: DNS_QCLASS::Internet,
+ ttl: 7200,
+ rd_length: 75,
+ size: 12
+ },
+ DNS_RR_DATA::TXT(vec!["_globalsign-domain-verification=qjb28W2jJSrWj04NHpB0CvgK9tle5JkOq-EcyWBgnE".to_string()]),
+ ),
+ (
+ 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
+ },
+ DNS_RR_DATA::TXT(vec!["google-site-verification=GHb98-6msqyx_qqjGl5eRatD3QTHyVB6-xQ3gJB5UwM".to_string()]),
+ ),
+ (
+ 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
+ },
+ DNS_RR_DATA::TXT(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_HDR {
+ qname: "baidu.com.".to_string(),
+ rr_type: DNS_QTYPE::NS,
+ rr_class: DNS_QCLASS::Internet,
+ ttl: 53898,
+ rd_length: 6,
+ size: 12
+ },
+ DNS_RR_DATA::NS("ns7.baidu.com.".to_string()),
+ ),
+ (
+ 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
+ },
+ DNS_RR_DATA::NS("dns.baidu.com.".to_string()),
+ ),
+ (
+ 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
+ },
+ DNS_RR_DATA::NS("ns3.baidu.com.".to_string()),
+ ),
+ (
+ 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
+ },
+ DNS_RR_DATA:: NS("ns2.baidu.com.".to_string()),
+ ),
+ (
+ 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
+ },
+ DNS_RR_DATA::NS("ns4.baidu.com.".to_string()),
+ )
+ ],
+ ar: vec![
+ (
+ 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
+ },
+ DNS_RR_DATA::A("36.152.45.193".to_string()),
+ ),
+ (
+ 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
+ },
+ DNS_RR_DATA::A("112.80.248.64".to_string()),
+ ),
+ (
+ 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
+ },
+ DNS_RR_DATA::A("220.181.33.31".to_string()),
+ ),
+ (
+ 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
+ },
+ DNS_RR_DATA::A("180.76.76.92".to_string()),
+ ),
+ (
+ 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
+ },
+ DNS_RR_DATA::A("14.215.178.80".to_string()),
+ ),
+ (
+ 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
+ },
+ DNS_RR_DATA::A("111.45.3.226".to_string()),
+ ),
+ (
+ 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
+ },
+ DNS_RR_DATA::A("110.242.68.134".to_string()),
+ ),
+ (
+ 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
+ },
+ DNS_RR_DATA::AAAA("240E:0940:0603:0004:0000:00FF:B01B:589A".to_string()),
+ ),
+ (
+ 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
+ },
+ DNS_RR_DATA::AAAA("240E:00BF:B801:1002:0000:00FF:B024:26DE".to_string()),
+ ),
+ (
+ DNS_RR_HDR {
+ qname: ".".to_string(),
+ rr_type: DNS_QTYPE::OPT,
+ rr_class: DNS_QCLASS::Other(4096),
+ ttl: 0,
+ rd_length: 0,
+ size: 11
+ },
+ DNS_RR_DATA::Other(DNS_QTYPE::OPT, vec![]),
+ )
+ ]
+ };
+
+ assert_eq!(parse_dns_message(&bytes), Ok((LAST_SLICE, expectation)));
+
+ /*
+ match parse_dns_message(&bytes) {
+ Ok((_rest, message)) => {
+ println!("{:?}", message);
+ }
+ Err(err) => {
+ println!("{:?}\n", err);
+ }
+ }
+ assert_eq!(0, 1);
+ */
+ }
+
+ // TODO
+ #[test]
+ fn dns_answer_other() {}
+}