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 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 which specifies the CPU type. * OS : A 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 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 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 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 of the name server that was the original or primary source of data for this zone. * RNAME : A 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 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, Vec), MX(u16, String), NS(String), PTR(String), SOA(String, String, u32, i32, i32, i32, i32), TXT(Vec), Other(DNS_QTYPE, Vec), } /****************************************************************************** * 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, 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, 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 * * is a single length octet followed by that number of characters. * 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 * : type OPT * Name: * 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: */ 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 * : type OPT * Name: * 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: */ 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 * : type OPT * Name: * 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: */ 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 * : type OPT * Name: * 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: */ 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 * : type OPT * Name: * 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: */ 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 * : type OPT * Name: * 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: */ 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 * : type OPT * Name: * 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 * : type OPT * Name: * 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: */ 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 * : type OPT * Name: * 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() {} }