use crate::protocol::codec::Decode; 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::str; /* * 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 ******************************************************************************/ #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum DNSHeaderQR { Query = 0, Response = 1, Other = 2, } impl DNSHeaderQR { fn from_u8(n: u8) -> DNSHeaderQR { match n { 0 => DNSHeaderQR::Query, 1 => DNSHeaderQR::Response, _ => DNSHeaderQR::Other, } } } // https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-5 #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum DNSHeaderOpcode { Query, IQuery, Status, Notify, Update, DSO, // DNS Stateful Operations Other(u8), } impl DNSHeaderOpcode { fn from_u8(n: u8) -> DNSHeaderOpcode { match n { 0 => DNSHeaderOpcode::Query, 1 => DNSHeaderOpcode::IQuery, 2 => DNSHeaderOpcode::Status, // 3 => Unassigned 4 => DNSHeaderOpcode::Notify, 5 => DNSHeaderOpcode::Update, 6 => DNSHeaderOpcode::DSO, n => DNSHeaderOpcode::Other(n), } } } // https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-6 #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum DNSHeaderRcode { NoError, // No Error FormErr, // Format Error ServFail, // Server Failure NXDomain, // Non-Existent Domain NotImp, // Not Implemented Refused, // Query Refused YXDomain, // Name Exists when it should not YXRRSet, // RR Set Exists when it should not NXRRSet, // RR Set that should exist does not NotAuth, // Server Not Authoritative for zone / Not Authorized NotZone, // Name not contained in zone DSOTYPENI, // DSO-TYPE Not Implemented BADSIG, // TSIG Signature Failure BADKEY, // Key not recognized BADTIME, // Signature out of time window BADMODE, // Bad TKEY Mode BADNAME, // Duplicate key name BADALG, // Algorithm not supported BADTRUNC, // Bad Truncation BADCOOKIE, // Bad/missing Server Cookie Other(u8), } impl DNSHeaderRcode { fn from_u8(n: u8) -> DNSHeaderRcode { match n { 0 => DNSHeaderRcode::NoError, 1 => DNSHeaderRcode::FormErr, 2 => DNSHeaderRcode::ServFail, 3 => DNSHeaderRcode::NXDomain, 4 => DNSHeaderRcode::NotImp, 5 => DNSHeaderRcode::Refused, 6 => DNSHeaderRcode::YXDomain, 7 => DNSHeaderRcode::YXRRSet, 8 => DNSHeaderRcode::NXRRSet, 9 => DNSHeaderRcode::NotAuth, 10 => DNSHeaderRcode::NotZone, 11 => DNSHeaderRcode::DSOTYPENI, // hole 16 => DNSHeaderRcode::BADSIG, 17 => DNSHeaderRcode::BADKEY, 18 => DNSHeaderRcode::BADTIME, 19 => DNSHeaderRcode::BADMODE, 20 => DNSHeaderRcode::BADNAME, 21 => DNSHeaderRcode::BADALG, 22 => DNSHeaderRcode::BADTRUNC, 23 => DNSHeaderRcode::BADCOOKIE, n => DNSHeaderRcode::Other(n), } } } #[derive(Clone, Debug, PartialEq, Eq)] pub struct DNSHeader { pub id: u16, pub qr: DNSHeaderQR, pub op_code: DNSHeaderOpcode, 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: DNSHeaderRcode, // Response Code pub qd_count: u16, pub an_count: u16, pub ns_count: u16, pub ar_count: u16, } fn bit_decode( input: &[u8], ) -> IResult< &[u8], ( DNSHeaderQR, DNSHeaderOpcode, bool, bool, bool, bool, bool, bool, DNSHeaderRcode, ), > { 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, ( DNSHeaderQR::from_u8(res.0), DNSHeaderOpcode::from_u8(res.1), res.2 == 1, res.3 == 1, res.4 == 1, res.5 == 1, res.7 == 1, res.8 == 1, DNSHeaderRcode::from_u8(res.9), ), ) }, ) } impl DNSHeader { fn decode(input: &[u8]) -> IResult<&[u8], DNSHeader> { sequence::tuple(( number::streaming::be_u16, bit_decode, number::streaming::be_u16, number::streaming::be_u16, number::streaming::be_u16, number::streaming::be_u16, ))(input) .map(|(rest, res)| { ( rest, DNSHeader { 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, }, ) }) } } /****************************************************************************** * DNS Question Section ******************************************************************************/ // https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-4 #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum DNSQtype { A, // a host address NS, // an authoritative name server MD, // a mail destination (OBSOLETE - use MX) MF, // a mail forwarder (OBSOLETE - use MX) CNAME, // the canonical name for an alias SOA, // marks the start of a zone of authority MB, // a mailbox domain name (EXPERIMENTAL) MG, // a mail group member (EXPERIMENTAL) MR, // a mail rename domain name (EXPERIMENTAL) NULL, // a null RR (EXPERIMENTAL) WKS, // a well known service description PTR, // a domain name pointer HINFO, // host information MINFO, // mailbox or mail list information MX, // mail exchange TXT, // text strings RP, // for Responsible Person AFSDB, // for AFS Data Base location X25, // for X.25 PSDN address ISDN, // for ISDN address RT, // for Route Through NSAP, // for NSAP address, NSAP style A record NSAPPTR, // for domain name pointer, NSAP style SIG, // for security signature KEY, // for security signature PX, // X.400 mail mapping information GPOS, // Geographical Position AAAA, // IP6 Address LOC, // Location Information NXT, // Next Domain (OBSOLETE) EID, // Endpoint Identifier NIMLOC, // Nimrod Locator SRV, // Server Selection ATMA, // ATM Address NAPTR, // Naming Authority Pointer KX, // Key Exchanger CERT, // CERT A6, // A6 (OBSOLETE - use AAAA) DNAME, // DNAME SINK, // SINK OPT, // OPT APL, // APL DS, // Delegation Signer SSHFP, // SSH Key Fingerprint IPSECKEY, // IPSECKEY RRSIG, // RRSIG NSEC, // NSEC DNSKEY, // DNSKEY DHCID, // DHCID NSEC3, // NSEC3 NSEC3PARAM, // NSEC3PARAM TLSA, // TLSA SMIMEA, // S/MIME cert association HIP, // Host Identity Protocol NINFO, // NINFO RKEY, // RKEY TALINK, // Trust Anchor LINK CDS, // Child DS CDNSKEY, // DNSKEY(s) the Child wants reflected in DS OPENPGPKEY, // OpenPGP Key CSYNC, // Child-To-Parent Synchronization ZONEMD, // Message Digest Over Zone Data SVCB, // General Purpose Service Binding HTTPS, // Service Binding type for use with HTTP SPF, UINFO, UID, GID, UNSPEC, NID, L32, L64, LP, EUI48, // an EUI-48 address EUI64, // an EUI-64 address TKEY, // Transaction Key TSIG, // Transaction Signature IXFR, // incremental transfer AXFR, // transfer of an entire zone MAILB, // mailbox-related RRs (MB, MG or MR) MAILA, // mail agent RRs (OBSOLETE - see MX) ANY, // A request for some or all records the server has available URI, // URI CAA, // Certification Authority Restriction AVC, // Application Visibility and Control DOA, // Digital Object Architecture AMTRELAY, // Automatic Multicast Tunneling Relay TA, // DNSSEC Trust Authorities DLV, // DNSSEC Lookaside Validation (OBSOLETE) Other(u16), } impl DNSQtype { fn from_u16(n: u16) -> DNSQtype { match n { 1 => DNSQtype::A, 2 => DNSQtype::NS, 3 => DNSQtype::MD, 4 => DNSQtype::MF, 5 => DNSQtype::CNAME, 6 => DNSQtype::SOA, 7 => DNSQtype::MB, 8 => DNSQtype::MG, 9 => DNSQtype::MR, 10 => DNSQtype::NULL, 11 => DNSQtype::WKS, 12 => DNSQtype::PTR, 13 => DNSQtype::HINFO, 14 => DNSQtype::MINFO, 15 => DNSQtype::MX, 16 => DNSQtype::TXT, 17 => DNSQtype::RP, 18 => DNSQtype::AFSDB, 19 => DNSQtype::X25, 20 => DNSQtype::ISDN, 21 => DNSQtype::RT, 22 => DNSQtype::NSAP, 23 => DNSQtype::NSAPPTR, 24 => DNSQtype::SIG, 25 => DNSQtype::KEY, 26 => DNSQtype::PX, 27 => DNSQtype::GPOS, 28 => DNSQtype::AAAA, 29 => DNSQtype::LOC, 30 => DNSQtype::NXT, 31 => DNSQtype::EID, 32 => DNSQtype::NIMLOC, 33 => DNSQtype::SRV, 34 => DNSQtype::ATMA, 35 => DNSQtype::NAPTR, 36 => DNSQtype::KX, 37 => DNSQtype::CERT, 38 => DNSQtype::A6, 39 => DNSQtype::DNAME, 40 => DNSQtype::SINK, 41 => DNSQtype::OPT, 42 => DNSQtype::APL, 43 => DNSQtype::DS, 44 => DNSQtype::SSHFP, 45 => DNSQtype::IPSECKEY, 46 => DNSQtype::RRSIG, 47 => DNSQtype::NSEC, 48 => DNSQtype::DNSKEY, 49 => DNSQtype::DHCID, 50 => DNSQtype::NSEC3, 51 => DNSQtype::NSEC3PARAM, 52 => DNSQtype::TLSA, 53 => DNSQtype::SMIMEA, // hole 55 => DNSQtype::HIP, 56 => DNSQtype::NINFO, 57 => DNSQtype::RKEY, 58 => DNSQtype::TALINK, 59 => DNSQtype::CDS, 60 => DNSQtype::CDNSKEY, 61 => DNSQtype::OPENPGPKEY, 62 => DNSQtype::CSYNC, 63 => DNSQtype::ZONEMD, 64 => DNSQtype::SVCB, 65 => DNSQtype::HTTPS, // hole 99 => DNSQtype::SPF, 100 => DNSQtype::UINFO, 101 => DNSQtype::UID, 102 => DNSQtype::GID, 103 => DNSQtype::UNSPEC, 104 => DNSQtype::NID, 105 => DNSQtype::L32, 106 => DNSQtype::L64, 107 => DNSQtype::LP, 108 => DNSQtype::EUI48, 109 => DNSQtype::EUI64, // hole 249 => DNSQtype::TKEY, 250 => DNSQtype::TSIG, 251 => DNSQtype::IXFR, 252 => DNSQtype::AXFR, 253 => DNSQtype::MAILB, 254 => DNSQtype::MAILA, 255 => DNSQtype::ANY, 256 => DNSQtype::URI, 257 => DNSQtype::CAA, 258 => DNSQtype::AVC, 259 => DNSQtype::DOA, 260 => DNSQtype::AMTRELAY, // hole 32768 => DNSQtype::TA, 32769 => DNSQtype::DLV, n => DNSQtype::Other(n), } } } // https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-2 #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum DNSQclass { Internet, Unassigned, Chaos, Hesiod, None, Any, Other(u16), } impl DNSQclass { fn from_u16(n: u16) -> DNSQclass { match n { 1 => DNSQclass::Internet, 2 => DNSQclass::Unassigned, 3 => DNSQclass::Chaos, 4 => DNSQclass::Hesiod, 254 => DNSQclass::None, 255 => DNSQclass::Any, n => DNSQclass::Other(n), } } } #[derive(Clone, Debug, PartialEq, Eq)] pub struct DNSQuestionSection { pub qname: String, pub qtype: DNSQtype, pub qclass: DNSQclass, size: usize, // DNS 原始数据中 DNSQuestionSection 占多少字节 } impl DNSQuestionSection { fn decode<'a, 'b>( input: &'a [u8], labels: &'b mut DNSLabelTable, position: usize, ) -> IResult<&'a [u8], DNSQuestionSection> { let (input, (name, size)) = dname_decode(input, labels, position)?; let (input, qtype) = number::streaming::be_u16(input)?; let (input, qclass) = number::streaming::be_u16(input)?; Ok(( input, DNSQuestionSection { qname: name.to_string(), qtype: DNSQtype::from_u16(qtype), qclass: DNSQclass::from_u16(qclass), size: 4 + size, }, )) } } /****************************************************************************** * DNS Resource Record Section ******************************************************************************/ #[derive(Clone, Debug, PartialEq, Eq)] pub struct DNSResourceRecordHeader { pub qname: String, pub rr_type: DNSQtype, pub rr_class: DNSQclass, pub ttl: i32, pub rd_length: u16, size: usize, // DNS 原始数据中 DNSResourceRecordHeader 占多少字节 } impl DNSResourceRecordHeader { fn decode<'a>( input: &'a [u8], labels: &mut DNSLabelTable, position: usize, ) -> IResult<&'a [u8], DNSResourceRecordHeader> { let (input, (name, size)) = dname_decode(input, labels, position)?; let (input, rr_type) = number::streaming::be_u16(input)?; let (input, rr_class) = number::streaming::be_u16(input)?; let (input, rr_ttl) = number::streaming::be_i32(input)?; let (input, rr_rdlength) = number::streaming::be_u16(input)?; Ok(( input, DNSResourceRecordHeader { qname: name.to_string(), rr_type: DNSQtype::from_u16(rr_type), rr_class: DNSQclass::from_u16(rr_class), ttl: rr_ttl, rd_length: rr_rdlength, size: 10 + size, }, )) } } #[derive(Clone, Debug, PartialEq, Eq)] pub enum DNSResourceRecordData { A { address: String, }, AAAA { address: String, }, CNAME { cname: String, }, HINFO { cpu: Vec, os: Vec, }, MX { preference: u16, exchange: String, }, NS { nsdname: String, }, PTR { ptrdnmae: String, }, SOA { mname: String, rname: String, serial: u32, refresh: i32, retry: i32, expire: i32, minimum: i32, }, TXT { txtdata: Vec, }, Other { qtype: DNSQtype, data: Vec, }, } impl DNSResourceRecordData { fn decode<'a>( input: &'a [u8], labels: &mut DNSLabelTable, position: usize, rr_hdr: &DNSResourceRecordHeader, ) -> IResult<&'a [u8], DNSResourceRecordData> { match (rr_hdr.rr_class, rr_hdr.rr_type) { (DNSQclass::Internet, DNSQtype::A) => { let (input, num) = number::streaming::be_u32(input)?; Ok(( input, DNSResourceRecordData::A { address: address_v4_decode(num), }, )) } (DNSQclass::Internet, DNSQtype::AAAA) => { let (input, num) = number::streaming::be_u128(input)?; Ok(( input, DNSResourceRecordData::AAAA { address: address_v6_decode(num), }, )) } (_, DNSQtype::CNAME) => { let (input, (name, _size)) = dname_decode(input, labels, position)?; Ok(( input, DNSResourceRecordData::CNAME { cname: name.to_string(), }, )) } (_, DNSQtype::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, DNSResourceRecordData::HINFO { cpu: _cpu.to_vec(), os: _os.to_vec(), }, )) } (_, DNSQtype::MX) => { let (input, _preference) = number::streaming::be_u16(input)?; let (input, (name, _size)) = dname_decode(input, labels, position + 2)?; Ok(( input, DNSResourceRecordData::MX { preference: _preference, exchange: name.to_string(), }, )) } (_, DNSQtype::NS) => { let (input, (name, _size)) = dname_decode(input, labels, position)?; Ok(( input, DNSResourceRecordData::NS { nsdname: name.to_string(), }, )) } (_, DNSQtype::PTR) => { let (input, (name, _size)) = dname_decode(input, labels, position)?; Ok(( input, DNSResourceRecordData::PTR { ptrdnmae: name.to_string(), }, )) } (_, DNSQtype::SOA) => { let (input, (_mname, mname_size)) = dname_decode(input, labels, position)?; let mname_string = _mname.to_string(); let (input, (_rname, _rname_size)) = dname_decode(input, labels, position + mname_size)?; let (input, _serial) = number::streaming::be_u32(input)?; let (input, _refresh) = number::streaming::be_i32(input)?; let (input, _retry) = number::streaming::be_i32(input)?; let (input, _expire) = number::streaming::be_i32(input)?; let (input, _minimum) = number::streaming::be_i32(input)?; Ok(( input, DNSResourceRecordData::SOA { mname: mname_string, rname: _rname.to_string(), serial: _serial, refresh: _refresh, retry: _retry, expire: _expire, minimum: _minimum, }, )) } (_, DNSQtype::TXT) => { let mut input = input; let mut nleft = rr_hdr.rd_length; let mut ret = Vec::new(); loop { let (__input, data) = character_string_decode(input)?; ret.push(str::from_utf8(data).unwrap().to_string()); input = __input; nleft = nleft - 1 - (data.len() as u16); if nleft < 2 { break; } } Ok((input, DNSResourceRecordData::TXT { txtdata: ret })) } (_, _) => { let slen: usize = rr_hdr.rd_length.into(); let rdata = &input[0..slen]; Ok(( &input[slen..], DNSResourceRecordData::Other { qtype: rr_hdr.rr_type, data: rdata.to_vec(), }, )) } } } } #[derive(Clone, Debug, PartialEq, Eq)] pub struct DNSResourceRecordSection { pub hdr: DNSResourceRecordHeader, pub data: DNSResourceRecordData, } impl DNSResourceRecordSection { fn decode<'a, 'b>( input: &'a [u8], labels: &'b mut DNSLabelTable, position: usize, ) -> IResult<&'a [u8], DNSResourceRecordSection> { let (left, rr_hdr) = DNSResourceRecordHeader::decode(input, labels, position)?; let (left, rr_data) = DNSResourceRecordData::decode(left, labels, position + rr_hdr.size, &rr_hdr)?; Ok(( left, DNSResourceRecordSection { hdr: rr_hdr, data: rr_data, }, )) } } /****************************************************************************** * DNS Label ******************************************************************************/ // https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-10 #[derive(Clone, Copy, Debug, PartialEq, Eq)] enum DNSLabelType { Normal(usize), // Normal label lower 6 bits is the length of the label Pointer(usize), // Compressed label the lower 6 bits and the 8 bits from next octet form a pointer to the compression target End, Other, } impl DNSLabelType { fn peek(input: &[u8]) -> IResult<&[u8], (u8, u8)> { combinator::peek(bits::<_, _, Error<(&[u8], usize)>, _, _>(sequence::tuple( (bits::streaming::take(1usize), bits::streaming::take(1usize)), )))(input) } fn decode(input: &[u8]) -> IResult<&[u8], DNSLabelType> { match Self::peek(input) { // 域名压缩 Ok((_, (1u8, 1u8))) => bits::<_, _, Error<(&[u8], usize)>, _, _>(sequence::tuple(( bits::streaming::take(1usize), bits::streaming::take(1usize), bits::streaming::take(6usize), bits::streaming::take(8usize), )))(input) .map(|(input, (_, _, left, right)): (&[u8], (u8, u8, u8, u8))| { ( input, DNSLabelType::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, DNSLabelType::End); } else { return (input, DNSLabelType::Normal(size as usize)); } }), _ => Ok((input, DNSLabelType::Other)), } } } #[derive(Clone, Debug, PartialEq, Eq)] pub struct DNSLabelTable { pub labels: Vec<((usize, usize), String)>, } impl DNSLabelTable { fn new() -> DNSLabelTable { DNSLabelTable { labels: vec![] } } /* * position : byte position in message * size : byte size in message (can be smaller than domain's size) * domain : the full domain */ fn set(&mut self, position: usize, size: usize, domain: &str) -> &str { self.labels.push(((position, size), domain.to_string())); &self.labels.last().unwrap().1 } fn get(&self, position: usize) -> Option<&str> { // labels 为空 if self.labels.len() == 0 { return None; } // position 太小 let mut cursor = self.labels.first().unwrap(); if position < cursor.0 .0 { return None; } let mut index: usize = 0; while position >= (cursor.0 .0 + cursor.0 .1) { cursor = match self.labels.get(index + 1) { Some(c) => c, _ => { return None; } }; index += 1; } if position < cursor.0 .0 { // hole return None; } if position == cursor.0 .0 { return Some(&cursor.1); } if cursor.1.as_bytes()[position - cursor.0 .0 - 1] == b'.' { return Some(cursor.1.split_at(position - cursor.0 .0).1); } None } } /****************************************************************************** * DNS Message ******************************************************************************/ #[derive(Clone, Debug, PartialEq, Eq)] pub struct DNSMessage { pub header: DNSHeader, labels: DNSLabelTable, pub qd: Vec, pub an: Vec, pub ns: Vec, pub ar: Vec, } impl DNSMessage { fn new(header: DNSHeader) -> DNSMessage { DNSMessage { header: header, labels: DNSLabelTable::new(), qd: vec![], an: vec![], ns: vec![], ar: vec![], } } } impl Decode for DNSMessage { type Iterm = DNSMessage; fn decode(input: &[u8]) -> IResult<&[u8], DNSMessage> { let (mut input, header) = DNSHeader::decode(input)?; let mut message = DNSMessage::new(header); let mut position = 12; // QDCOUNT for _ in 0..message.header.qd_count { let (left, qd) = DNSQuestionSection::decode(input, &mut message.labels, position)?; input = left; position += qd.size; message.qd.push(qd); } // ANCOUNT for _ in 0..message.header.an_count { let (left, rr) = DNSResourceRecordSection::decode(input, &mut message.labels, position)?; position += rr.hdr.size + rr.hdr.rd_length as usize; message.an.push(rr); input = left; } // NSCOUNT for _ in 0..message.header.ns_count { let (left, rr) = DNSResourceRecordSection::decode(input, &mut message.labels, position)?; position += rr.hdr.size + rr.hdr.rd_length as usize; message.ns.push(rr); input = left; } // ARCOUNT for _ in 0..message.header.ar_count { let (left, rr) = DNSResourceRecordSection::decode(input, &mut message.labels, position)?; position += rr.hdr.size + rr.hdr.rd_length as usize; message.ar.push(rr); input = left; } Ok((input, message)) } } /****************************************************************************** * Utils ******************************************************************************/ fn address_v4_decode(n: u32) -> String { let ipb = n.to_be_bytes(); format!("{}.{}.{}.{}", ipb[0], ipb[1], ipb[2], ipb[3]) } fn address_v6_decode(n: u128) -> String { let ipb = n.to_be_bytes(); format!("{:02X}{:02X}:{:02X}{:02X}:{:02X}{:02X}:{:02X}{:02X}:{:02X}{:02X}:{:02X}{:02X}:{:02X}{:02X}:{:02X}{:02X}", ipb[0], ipb[1], ipb[2], ipb[3], ipb[4], ipb[5], ipb[6], ipb[7], ipb[8], ipb[9], ipb[10], ipb[11], ipb[12], ipb[13], ipb[14], ipb[15]) } /* https://www.ietf.org/rfc/rfc1035.txt * * 3.3. Standard RRs * * 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 character_string_decode(input: &[u8]) -> IResult<&[u8], &[u8]> { multi::length_data(number::streaming::be_u8)(input) } /* * https://www.ietf.org/rfc/rfc1035.txt * * 2.3.1. Preferred name syntax * * The labels must follow the rules for ARPANET host names. They must * start with a letter, end with a letter or digit, and have as interior * characters only letters, digits, and hyphen. There are also some * restrictions on the length. Labels must be 63 characters or less. */ // 为了解析出更多的域名,此处未严格遵守 RFC 中规定的 “字母” “数字” 和 “-” 的顺序,并增加了 ‘_’ 字符 fn label_character_verify(c: u8) -> bool { character::is_alphabetic(c) || character::is_digit(c) || (c == b'-') || (c == b'_') } fn label_decode<'a>( input: &'a [u8], size: usize, name: &mut String, ) -> IResult<&'a [u8], &'static str> { if input.len() < size { return Err(Err::Incomplete(Needed::new(size - input.len()))); } for i in 0..size { if !label_character_verify(input[i]) { return Err(Err::Failure(Error::new(input, ErrorKind::Fail))); } } name.push_str(str::from_utf8(&input[0..size]).unwrap()); return Ok((input.get(size..).unwrap(), "OK")); } /* * https://www.ietf.org/rfc/rfc1035.txt * * 4.1.4. Message compression * * The compression scheme allows a domain name in a message to be represented as either: * * - a sequence of labels ending in a zero octet * - a pointer * - a sequence of labels ending with a pointer */ fn dname_decode<'a, 'b>( data: &'a [u8], labels: &'b mut DNSLabelTable, position: usize, ) -> IResult<&'a [u8], (&'b str, usize)> { let mut input = data; let mut total_size = 0usize; let mut name = String::new(); loop { match DNSLabelType::decode(input) { Ok((inner_input, label_type)) => match label_type { // 域名未压缩 DNSLabelType::Normal(size) => { total_size += 1 + size; if name.len() > 0 { name.push('.'); } let (left, _) = label_decode(inner_input, size, &mut name)?; input = left; } // 域名压缩 DNSLabelType::Pointer(_position) => { total_size += 2; if name.len() > 0 { name.push('.'); } let label = match labels.get(_position) { Some(label) => label, _ => { return Err(Err::Failure(Error::new(inner_input, ErrorKind::Fail))); } }; name.push_str(label); let n = labels.set(position, total_size, &name); return Ok((inner_input, (n, total_size))); } // 域名未压缩 DNSLabelType::End => { total_size += 1; name.push('.'); let n = labels.set(position, total_size, &name); return Ok((inner_input, (n, total_size))); } DNSLabelType::Other => { return Err(Err::Failure(Error::new(inner_input, ErrorKind::Fail))); } }, _ => { return Err(Err::Failure(Error::new(input, ErrorKind::Fail))); } } } } /****************************************************************************** * TEST ******************************************************************************/ #[cfg(test)] mod tests { use super::character_string_decode; use super::dname_decode; use super::label_decode; use super::DNSHeader; use super::DNSHeaderOpcode; use super::DNSHeaderQR; use super::DNSHeaderRcode; use super::DNSLabelTable; use super::DNSMessage; use super::DNSQclass; use super::DNSQtype; use super::DNSQuestionSection; use super::DNSResourceRecordData; use super::DNSResourceRecordHeader; use super::DNSResourceRecordSection; use crate::protocol::codec::Decode; const LAST_SLICE: &'static [u8] = &[]; /************************************************************************** * test util **************************************************************************/ #[test] fn test_character_string_decode() { let input = [0b00000101u8, b'h', b'i', b'!', b'!', 0b11000000u8]; let result = character_string_decode(&input); assert_eq!( result, Ok((&[][..], &[b'h', b'i', b'!', b'!', 0b11000000u8][..])) ); let input = [0b00000101u8, 0b11000000u8]; let result = character_string_decode(&input); assert_eq!(result, Err(nom::Err::Incomplete(nom::Needed::new(4)))); } #[test] fn test_label_table() { let mut table = DNSLabelTable::new(); table.set(30, 16, "www.example.com."); assert_eq!(table.get(29), None); assert_eq!(table.get(30), Some("www.example.com.")); assert_eq!(table.get(31), None); assert_eq!(table.get(32), None); assert_eq!(table.get(33), None); assert_eq!(table.get(34), Some("example.com.")); assert_eq!(table.get(35), None); assert_eq!(table.get(36), None); assert_eq!(table.get(37), None); assert_eq!(table.get(38), None); assert_eq!(table.get(39), None); assert_eq!(table.get(40), None); assert_eq!(table.get(41), None); assert_eq!(table.get(42), Some("com.")); assert_eq!(table.get(43), None); } #[test] fn test_label_decode() { /* * [letter] -> OK * [digit] -> OK * [-] -> OK * [other] -> Err */ let buf1 = &[b'a']; // OK let buf2 = &[b'1']; // OK let buf3 = &[b'-']; // OK let buf4 = &[b'_']; // OK let buf5 = &[b'+']; // Err let mut str = String::new(); let ret = label_decode(buf1, 1, &mut str); assert_eq!(str, "a"); assert_eq!(ret, Ok((LAST_SLICE, "OK"))); let mut str = String::new(); let ret = label_decode(buf2, 1, &mut str); assert_eq!(str, "1"); assert_eq!(ret, Ok((LAST_SLICE, "OK"))); let mut str = String::new(); let ret = label_decode(buf3, 1, &mut str); assert_eq!(str, "-"); assert_eq!(ret, Ok((LAST_SLICE, "OK"))); let mut str = String::new(); let ret = label_decode(buf4, 1, &mut str); assert_eq!(str, "_"); assert_eq!(ret, Ok((LAST_SLICE, "OK"))); let mut str = String::new(); let ret = label_decode(buf5, 1, &mut str); assert_eq!(str, ""); assert_ne!(ret, Ok((LAST_SLICE, "OK"))); } #[test] fn test_dname_decode() { let mut table = DNSLabelTable::new(); // uncompress name: www.example.com let input1 = [ 0x03, // uncompress and lable length 3 0x77, 0x77, 0x77, // www 0x07, // lable length 7 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, // example 0x03, // lable length 3 0x63, 0x6f, 0x6d, // com 0x00, // label length 0 ]; let result = dname_decode(&input1, &mut table, 10); assert_eq!(result, Ok((LAST_SLICE, ("www.example.com.", 17)))); assert_eq!(table.get(10), Some("www.example.com.")); // NOTE: second_msg_position >= first_msg_position + data_length // compress name: www.example.com let input2 = [0b11000000u8, 0b00001010u8]; // Pointer to 10 let result = dname_decode(&input2, &mut table, 50); assert_eq!(result, Ok((LAST_SLICE, ("www.example.com.", 2)))); assert_eq!(table.get(50), Some("www.example.com.")); } /************************************************************************** * test decode query **************************************************************************/ #[test] fn test_query_a_decode() { /* * dig www.baidu.com * * Domain Name System (query) * Transaction ID: 0xa59a * Flags: 0x0120 Standard query * 0... .... .... .... = Response: Message is a query * .000 0... .... .... = Opcode: Standard query (0) * .... ..0. .... .... = Truncated: Message is not truncated * .... ...1 .... .... = Recursion desired: Do query recursively * .... .... .0.. .... = Z: reserved (0) * .... .... ..1. .... = AD bit: Set * .... .... ...0 .... = Non-authenticated data: Unacceptable * Questions: 1 * Answer RRs: 0 * Authority RRs: 0 * Additional RRs: 1 * Queries * www.baidu.com: type A, class IN * Name: www.baidu.com * [Name Length: 13] * [Label Count: 3] * Type: A (Host Address) (1) * Class: IN (0x0001) * Additional records * : 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 = DNSMessage { header: DNSHeader { id: 0xa59a, qr: DNSHeaderQR::Query, op_code: DNSHeaderOpcode::Query, aa: false, tc: false, rd: true, ra: false, ad: true, cd: false, r_code: DNSHeaderRcode::NoError, qd_count: 1, an_count: 0, ns_count: 0, ar_count: 1, }, labels: DNSLabelTable { labels: vec![ ((12, 15), "www.baidu.com.".to_string()), ((31, 1), ".".to_string()), ], }, qd: vec![DNSQuestionSection { qname: "www.baidu.com.".to_string(), qtype: DNSQtype::A, qclass: DNSQclass::Internet, size: 19, }], an: vec![], ns: vec![], ar: vec![ (DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: ".".to_string(), rr_type: DNSQtype::OPT, rr_class: DNSQclass::Other(4096), ttl: 0, rd_length: 0, size: 11, }, data: DNSResourceRecordData::Other { qtype: DNSQtype::OPT, data: vec![], }, }), ], }; assert_eq!(DNSMessage::decode(&bytes), Ok((LAST_SLICE, expectation))); match DNSMessage::decode(&bytes) { Ok((left, message)) => { println!("{:?}; left {:?}", message, left); } Err(err) => { println!("{:?}\n", err); } } // assert_eq!(0, 1); } #[test] fn test_query_aaaa_decode() { /* * dig www.baidu.com aaaa * * Domain Name System (query) * Transaction ID: 0xf086 * Flags: 0x0120 Standard query * 0... .... .... .... = Response: Message is a query * .000 0... .... .... = Opcode: Standard query (0) * .... ..0. .... .... = Truncated: Message is not truncated * .... ...1 .... .... = Recursion desired: Do query recursively * .... .... .0.. .... = Z: reserved (0) * .... .... ..1. .... = AD bit: Set * .... .... ...0 .... = Non-authenticated data: Unacceptable * Questions: 1 * Answer RRs: 0 * Authority RRs: 0 * Additional RRs: 1 * Queries * www.baidu.com: type AAAA, class IN * Name: www.baidu.com * [Name Length: 13] * [Label Count: 3] * Type: AAAA (IPv6 Address) (28) * Class: IN (0x0001) * Additional records * : 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 = DNSMessage { header: DNSHeader { id: 0xf086, qr: DNSHeaderQR::Query, op_code: DNSHeaderOpcode::Query, aa: false, tc: false, rd: true, ra: false, ad: true, cd: false, r_code: DNSHeaderRcode::NoError, qd_count: 1, an_count: 0, ns_count: 0, ar_count: 1, }, labels: DNSLabelTable { labels: vec![ ((12, 15), "www.baidu.com.".to_string()), ((31, 1), ".".to_string()), ], }, qd: vec![DNSQuestionSection { qname: "www.baidu.com.".to_string(), qtype: DNSQtype::AAAA, qclass: DNSQclass::Internet, size: 19, }], an: vec![], ns: vec![], ar: vec![ (DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: ".".to_string(), rr_type: DNSQtype::OPT, rr_class: DNSQclass::Other(4096), ttl: 0, rd_length: 0, size: 11, }, data: DNSResourceRecordData::Other { qtype: DNSQtype::OPT, data: vec![], }, }), ], }; assert_eq!(DNSMessage::decode(&bytes), Ok((LAST_SLICE, expectation))); match DNSMessage::decode(&bytes) { Ok((left, message)) => { println!("{:?}; left {:?}", message, left); } Err(err) => { println!("{:?}\n", err); } } // assert_eq!(0, 1); } #[test] fn test_query_cname_decode() { /* * dig www.baidu.com cname * * Domain Name System (query) * Transaction ID: 0x61a5 * Flags: 0x0120 Standard query * 0... .... .... .... = Response: Message is a query * .000 0... .... .... = Opcode: Standard query (0) * .... ..0. .... .... = Truncated: Message is not truncated * .... ...1 .... .... = Recursion desired: Do query recursively * .... .... .0.. .... = Z: reserved (0) * .... .... ..1. .... = AD bit: Set * .... .... ...0 .... = Non-authenticated data: Unacceptable * Questions: 1 * Answer RRs: 0 * Authority RRs: 0 * Additional RRs: 1 * Queries * www.baidu.com: type CNAME, class IN * Name: www.baidu.com * [Name Length: 13] * [Label Count: 3] * Type: CNAME (Canonical NAME for an alias) (5) * Class: IN (0x0001) * Additional records * : 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 = DNSMessage { header: DNSHeader { id: 0x61a5, qr: DNSHeaderQR::Query, op_code: DNSHeaderOpcode::Query, aa: false, tc: false, rd: true, ra: false, ad: true, cd: false, r_code: DNSHeaderRcode::NoError, qd_count: 1, an_count: 0, ns_count: 0, ar_count: 1, }, labels: DNSLabelTable { labels: vec![ ((12, 15), "www.baidu.com.".to_string()), ((31, 1), ".".to_string()), ], }, qd: vec![DNSQuestionSection { qname: "www.baidu.com.".to_string(), qtype: DNSQtype::CNAME, qclass: DNSQclass::Internet, size: 19, }], an: vec![], ns: vec![], ar: vec![ (DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: ".".to_string(), rr_type: DNSQtype::OPT, rr_class: DNSQclass::Other(4096), ttl: 0, rd_length: 0, size: 11, }, data: DNSResourceRecordData::Other { qtype: DNSQtype::OPT, data: vec![], }, }), ], }; assert_eq!(DNSMessage::decode(&bytes), Ok((LAST_SLICE, expectation))); match DNSMessage::decode(&bytes) { Ok((left, message)) => { println!("{:?}; left {:?}", message, left); } Err(err) => { println!("{:?}\n", err); } } // assert_eq!(0, 1); } // TODO #[test] fn test_query_hinfo_decode() {} #[test] fn test_query_mx_decode() { /* * dig 163.com mx * * Domain Name System (query) * Transaction ID: 0xd35c * Flags: 0x0120 Standard query * 0... .... .... .... = Response: Message is a query * .000 0... .... .... = Opcode: Standard query (0) * .... ..0. .... .... = Truncated: Message is not truncated * .... ...1 .... .... = Recursion desired: Do query recursively * .... .... .0.. .... = Z: reserved (0) * .... .... ..1. .... = AD bit: Set * .... .... ...0 .... = Non-authenticated data: Unacceptable * Questions: 1 * Answer RRs: 0 * Authority RRs: 0 * Additional RRs: 1 * Queries * 163.com: type MX, class IN * Name: 163.com * [Name Length: 7] * [Label Count: 2] * Type: MX (Mail eXchange) (15) * Class: IN (0x0001) * Additional records * : 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 = DNSMessage { header: DNSHeader { id: 0xd35c, qr: DNSHeaderQR::Query, op_code: DNSHeaderOpcode::Query, aa: false, tc: false, rd: true, ra: false, ad: true, cd: false, r_code: DNSHeaderRcode::NoError, qd_count: 1, an_count: 0, ns_count: 0, ar_count: 1, }, labels: DNSLabelTable { labels: vec![ ((12, 9), "163.com.".to_string()), ((25, 1), ".".to_string()), ], }, qd: vec![DNSQuestionSection { qname: "163.com.".to_string(), qtype: DNSQtype::MX, qclass: DNSQclass::Internet, size: 13, }], an: vec![], ns: vec![], ar: vec![ (DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: ".".to_string(), rr_type: DNSQtype::OPT, rr_class: DNSQclass::Other(4096), ttl: 0, rd_length: 0, size: 11, }, data: DNSResourceRecordData::Other { qtype: DNSQtype::OPT, data: vec![], }, }), ], }; assert_eq!(DNSMessage::decode(&bytes), Ok((LAST_SLICE, expectation))); match DNSMessage::decode(&bytes) { Ok((left, message)) => { println!("{:?}; left {:?}", message, left); } Err(err) => { println!("{:?}\n", err); } } // assert_eq!(0, 1); } // TODO #[test] fn test_query_nx_decode() {} // TODO #[test] fn test_query_ptr_decode() {} #[test] fn test_query_soa_decode() { /* * dig www.baidu.com soa * * Domain Name System (query) * Transaction ID: 0xb613 * Flags: 0x0120 Standard query * 0... .... .... .... = Response: Message is a query * .000 0... .... .... = Opcode: Standard query (0) * .... ..0. .... .... = Truncated: Message is not truncated * .... ...1 .... .... = Recursion desired: Do query recursively * .... .... .0.. .... = Z: reserved (0) * .... .... ..1. .... = AD bit: Set * .... .... ...0 .... = Non-authenticated data: Unacceptable * Questions: 1 * Answer RRs: 0 * Authority RRs: 0 * Additional RRs: 1 * Queries * www.baidu.com: type SOA, class IN * Name: www.baidu.com * [Name Length: 13] * [Label Count: 3] * Type: SOA (Start Of a zone of Authority) (6) * Class: IN (0x0001) * Additional records * : 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 = DNSMessage { header: DNSHeader { id: 0xb613, qr: DNSHeaderQR::Query, op_code: DNSHeaderOpcode::Query, aa: false, tc: false, rd: true, ra: false, ad: true, cd: false, r_code: DNSHeaderRcode::NoError, qd_count: 1, an_count: 0, ns_count: 0, ar_count: 1, }, labels: DNSLabelTable { labels: vec![ ((12, 15), "www.baidu.com.".to_string()), ((31, 1), ".".to_string()), ], }, qd: vec![DNSQuestionSection { qname: "www.baidu.com.".to_string(), qtype: DNSQtype::SOA, qclass: DNSQclass::Internet, size: 19, }], an: vec![], ns: vec![], ar: vec![ (DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: ".".to_string(), rr_type: DNSQtype::OPT, rr_class: DNSQclass::Other(4096), ttl: 0, rd_length: 0, size: 11, }, data: DNSResourceRecordData::Other { qtype: DNSQtype::OPT, data: vec![], }, }), ], }; assert_eq!(DNSMessage::decode(&bytes), Ok((LAST_SLICE, expectation))); match DNSMessage::decode(&bytes) { Ok((left, message)) => { println!("{:?}; left {:?}", message, left); } Err(err) => { println!("{:?}\n", err); } } // assert_eq!(0, 1); } #[test] fn test_query_txt_decode() { /* * dig baidu.com txt * * Domain Name System (query) * Transaction ID: 0x6721 * Flags: 0x0120 Standard query * 0... .... .... .... = Response: Message is a query * .000 0... .... .... = Opcode: Standard query (0) * .... ..0. .... .... = Truncated: Message is not truncated * .... ...1 .... .... = Recursion desired: Do query recursively * .... .... .0.. .... = Z: reserved (0) * .... .... ..1. .... = AD bit: Set * .... .... ...0 .... = Non-authenticated data: Unacceptable * Questions: 1 * Answer RRs: 0 * Authority RRs: 0 * Additional RRs: 1 * Queries * baidu.com: type TXT, class IN * Name: baidu.com * [Name Length: 9] * [Label Count: 2] * Type: TXT (Text strings) (16) * Class: IN (0x0001) * Additional records * : 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 = DNSMessage { header: DNSHeader { id: 0x6721, qr: DNSHeaderQR::Query, op_code: DNSHeaderOpcode::Query, aa: false, tc: false, rd: true, ra: false, ad: true, cd: false, r_code: DNSHeaderRcode::NoError, qd_count: 1, an_count: 0, ns_count: 0, ar_count: 1, }, labels: DNSLabelTable { labels: vec![ ((12, 11), "baidu.com.".to_string()), ((27, 1), ".".to_string()), ], }, qd: vec![DNSQuestionSection { qname: "baidu.com.".to_string(), qtype: DNSQtype::TXT, qclass: DNSQclass::Internet, size: 15, }], an: vec![], ns: vec![], ar: vec![ (DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: ".".to_string(), rr_type: DNSQtype::OPT, rr_class: DNSQclass::Other(4096), ttl: 0, rd_length: 0, size: 11, }, data: DNSResourceRecordData::Other { qtype: DNSQtype::OPT, data: vec![], }, }), ], }; assert_eq!(DNSMessage::decode(&bytes), Ok((LAST_SLICE, expectation))); match DNSMessage::decode(&bytes) { Ok((left, message)) => { println!("{:?}; left {:?}", message, left); } Err(err) => { println!("{:?}\n", err); } } // assert_eq!(0, 1); } // TODO #[test] fn test_query_other_decode() {} /************************************************************************** * test decode answer **************************************************************************/ #[test] fn test_answer_a_decode() { // test in test_answer_txt_decode } #[test] fn test_answer_aaaa_decode() { // test in test_answer_txt_decode } #[test] fn test_answer_ns_decode() { // test in test_answer_txt_decode } // TODO #[test] fn test_answer_hinfo_decode() {} #[test] fn test_answer_mx_decode() { /* * Domain Name System (response) * Transaction ID: 0xd35c * Flags: 0x8180 Standard query response, No error * 1... .... .... .... = Response: Message is a response * .000 0... .... .... = Opcode: Standard query (0) * .... .0.. .... .... = Authoritative: Server is not an authority for domain * .... ..0. .... .... = Truncated: Message is not truncated * .... ...1 .... .... = Recursion desired: Do query recursively * .... .... 1... .... = Recursion available: Server can do recursive queries * .... .... .0.. .... = Z: reserved (0) * .... .... ..0. .... = Answer authenticated: Answer/authority portion was not authenticated by the server * .... .... ...0 .... = Non-authenticated data: Unacceptable * .... .... .... 0000 = Reply code: No error (0) * Questions: 1 * Answer RRs: 4 * Authority RRs: 7 * Additional RRs: 13 * Queries * 163.com: type MX, class IN * Name: 163.com * [Name Length: 7] * [Label Count: 2] * Type: MX (Mail eXchange) (15) * Class: IN (0x0001) * Answers * 163.com: type MX, class IN, preference 10, mx 163mx02.mxmail.netease.com * Name: 163.com * Type: MX (Mail eXchange) (15) * Class: IN (0x0001) * Time to live: 12492 (3 hours, 28 minutes, 12 seconds) * Data length: 27 * Preference: 10 * Mail Exchange: 163mx02.mxmail.netease.com * 163.com: type MX, class IN, preference 50, mx 163mx00.mxmail.netease.com * Name: 163.com * Type: MX (Mail eXchange) (15) * Class: IN (0x0001) * Time to live: 12492 (3 hours, 28 minutes, 12 seconds) * Data length: 12 * Preference: 50 * Mail Exchange: 163mx00.mxmail.netease.com * 163.com: type MX, class IN, preference 10, mx 163mx03.mxmail.netease.com * Name: 163.com * Type: MX (Mail eXchange) (15) * Class: IN (0x0001) * Time to live: 12492 (3 hours, 28 minutes, 12 seconds) * Data length: 12 * Preference: 10 * Mail Exchange: 163mx03.mxmail.netease.com * 163.com: type MX, class IN, preference 10, mx 163mx01.mxmail.netease.com * Name: 163.com * Type: MX (Mail eXchange) (15) * Class: IN (0x0001) * Time to live: 12492 (3 hours, 28 minutes, 12 seconds) * Data length: 12 * Preference: 10 * Mail Exchange: 163mx01.mxmail.netease.com * Authoritative nameservers * 163.com: type NS, class IN, ns ns5.nease.net * Name: 163.com * Type: NS (authoritative Name Server) (2) * Class: IN (0x0001) * Time to live: 34967 (9 hours, 42 minutes, 47 seconds) * Data length: 15 * Name Server: ns5.nease.net * 163.com: type NS, class IN, ns ns3.nease.net * Name: 163.com * Type: NS (authoritative Name Server) (2) * Class: IN (0x0001) * Time to live: 34967 (9 hours, 42 minutes, 47 seconds) * Data length: 6 * Name Server: ns3.nease.net * 163.com: type NS, class IN, ns ns1.nease.net * Name: 163.com * Type: NS (authoritative Name Server) (2) * Class: IN (0x0001) * Time to live: 34967 (9 hours, 42 minutes, 47 seconds) * Data length: 6 * Name Server: ns1.nease.net * 163.com: type NS, class IN, ns ns8.166.com * Name: 163.com * Type: NS (authoritative Name Server) (2) * Class: IN (0x0001) * Time to live: 34967 (9 hours, 42 minutes, 47 seconds) * Data length: 10 * Name Server: ns8.166.com * 163.com: type NS, class IN, ns ns4.nease.net * Name: 163.com * Type: NS (authoritative Name Server) (2) * Class: IN (0x0001) * Time to live: 34967 (9 hours, 42 minutes, 47 seconds) * Data length: 6 * Name Server: ns4.nease.net * 163.com: type NS, class IN, ns ns2.166.com * Name: 163.com * Type: NS (authoritative Name Server) (2) * Class: IN (0x0001) * Time to live: 34967 (9 hours, 42 minutes, 47 seconds) * Data length: 6 * Name Server: ns2.166.com * 163.com: type NS, class IN, ns ns6.nease.net * Name: 163.com * Type: NS (authoritative Name Server) (2) * Class: IN (0x0001) * Time to live: 34967 (9 hours, 42 minutes, 47 seconds) * Data length: 6 * Name Server: ns6.nease.net * Additional records * 163mx03.mxmail.netease.com: type A, class IN, addr 220.181.12.119 * Name: 163mx03.mxmail.netease.com * Type: A (Host Address) (1) * Class: IN (0x0001) * Time to live: 442 (7 minutes, 22 seconds) * Data length: 4 * Address: 220.181.12.119 * 163mx01.mxmail.netease.com: type A, class IN, addr 220.181.12.117 * Name: 163mx01.mxmail.netease.com * Type: A (Host Address) (1) * Class: IN (0x0001) * Time to live: 442 (7 minutes, 22 seconds) * Data length: 4 * Address: 220.181.12.117 * 163mx02.mxmail.netease.com: type A, class IN, addr 220.181.12.118 * Name: 163mx02.mxmail.netease.com * Type: A (Host Address) (1) * Class: IN (0x0001) * Time to live: 442 (7 minutes, 22 seconds) * Data length: 4 * Address: 220.181.12.118 * 163mx00.mxmail.netease.com: type A, class IN, addr 220.181.12.180 * Name: 163mx00.mxmail.netease.com * Type: A (Host Address) (1) * Class: IN (0x0001) * Time to live: 442 (7 minutes, 22 seconds) * Data length: 4 * Address: 220.181.12.180 * ns6.nease.net: type A, class IN, addr 54.228.156.72 * Name: ns6.nease.net * Type: A (Host Address) (1) * Class: IN (0x0001) * Time to live: 5583 (1 hour, 33 minutes, 3 seconds) * Data length: 4 * Address: 54.228.156.72 * ns1.nease.net: type A, class IN, addr 42.186.35.222 * Name: ns1.nease.net * Type: A (Host Address) (1) * Class: IN (0x0001) * Time to live: 32644 (9 hours, 4 minutes, 4 seconds) * Data length: 4 * Address: 42.186.35.222 * ns4.nease.net: type A, class IN, addr 103.72.16.81 * Name: ns4.nease.net * Type: A (Host Address) (1) * Class: IN (0x0001) * Time to live: 51974 (14 hours, 26 minutes, 14 seconds) * Data length: 4 * Address: 103.72.16.81 * ns3.nease.net: type A, class IN, addr 220.181.36.234 * Name: ns3.nease.net * Type: A (Host Address) (1) * Class: IN (0x0001) * Time to live: 41374 (11 hours, 29 minutes, 34 seconds) * Data length: 4 * Address: 220.181.36.234 * ns2.166.com: type A, class IN, addr 103.71.201.3 * Name: ns2.166.com * Type: A (Host Address) (1) * Class: IN (0x0001) * Time to live: 73500 (20 hours, 25 minutes) * Data length: 4 * Address: 103.71.201.3 * ns5.nease.net: type A, class IN, addr 121.195.179.18 * Name: ns5.nease.net * Type: A (Host Address) (1) * Class: IN (0x0001) * Time to live: 34614 (9 hours, 36 minutes, 54 seconds) * Data length: 4 * Address: 121.195.179.18 * ns8.166.com: type A, class IN, addr 18.182.82.158 * Name: ns8.166.com * Type: A (Host Address) (1) * Class: IN (0x0001) * Time to live: 64949 (18 hours, 2 minutes, 29 seconds) * Data length: 4 * Address: 18.182.82.158 * ns8.166.com: type A, class IN, addr 44.228.163.69 * Name: ns8.166.com * Type: A (Host Address) (1) * Class: IN (0x0001) * Time to live: 64949 (18 hours, 2 minutes, 29 seconds) * Data length: 4 * Address: 44.228.163.69 * : 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 = DNSMessage { header: DNSHeader { id: 0xd35c, qr: DNSHeaderQR::Response, op_code: DNSHeaderOpcode::Query, aa: false, tc: false, rd: true, ra: true, ad: false, cd: false, r_code: DNSHeaderRcode::NoError, qd_count: 1, an_count: 4, ns_count: 7, ar_count: 13, }, labels: DNSLabelTable { 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![DNSQuestionSection { qname: "163.com.".to_string(), qtype: DNSQtype::MX, qclass: DNSQclass::Internet, size: 13, }], an: vec![ (DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: "163.com.".to_string(), rr_type: DNSQtype::MX, rr_class: DNSQclass::Internet, ttl: 12492, rd_length: 27, size: 12, }, data: DNSResourceRecordData::MX { preference: 10, exchange: "163mx02.mxmail.netease.com.".to_string(), }, }), (DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: "163.com.".to_string(), rr_type: DNSQtype::MX, rr_class: DNSQclass::Internet, ttl: 12492, rd_length: 12, size: 12, }, data: DNSResourceRecordData::MX { preference: 50, exchange: "163mx00.mxmail.netease.com.".to_string(), }, }), (DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: "163.com.".to_string(), rr_type: DNSQtype::MX, rr_class: DNSQclass::Internet, ttl: 12492, rd_length: 12, size: 12, }, data: DNSResourceRecordData::MX { preference: 10, exchange: "163mx03.mxmail.netease.com.".to_string(), }, }), (DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: "163.com.".to_string(), rr_type: DNSQtype::MX, rr_class: DNSQclass::Internet, ttl: 12492, rd_length: 12, size: 12, }, data: DNSResourceRecordData::MX { preference: 10, exchange: "163mx01.mxmail.netease.com.".to_string(), }, }), ], ns: vec![ (DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: "163.com.".to_string(), rr_type: DNSQtype::NS, rr_class: DNSQclass::Internet, ttl: 34967, rd_length: 15, size: 12, }, data: DNSResourceRecordData::NS { nsdname: "ns5.nease.net.".to_string(), }, }), (DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: "163.com.".to_string(), rr_type: DNSQtype::NS, rr_class: DNSQclass::Internet, ttl: 34967, rd_length: 6, size: 12, }, data: DNSResourceRecordData::NS { nsdname: "ns3.nease.net.".to_string(), }, }), (DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: "163.com.".to_string(), rr_type: DNSQtype::NS, rr_class: DNSQclass::Internet, ttl: 34967, rd_length: 6, size: 12, }, data: DNSResourceRecordData::NS { nsdname: "ns1.nease.net.".to_string(), }, }), (DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: "163.com.".to_string(), rr_type: DNSQtype::NS, rr_class: DNSQclass::Internet, ttl: 34967, rd_length: 10, size: 12, }, data: DNSResourceRecordData::NS { nsdname: "ns8.166.com.".to_string(), }, }), (DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: "163.com.".to_string(), rr_type: DNSQtype::NS, rr_class: DNSQclass::Internet, ttl: 34967, rd_length: 6, size: 12, }, data: DNSResourceRecordData::NS { nsdname: "ns4.nease.net.".to_string(), }, }), (DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: "163.com.".to_string(), rr_type: DNSQtype::NS, rr_class: DNSQclass::Internet, ttl: 34967, rd_length: 6, size: 12, }, data: DNSResourceRecordData::NS { nsdname: "ns2.166.com.".to_string(), }, }), (DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: "163.com.".to_string(), rr_type: DNSQtype::NS, rr_class: DNSQclass::Internet, ttl: 34967, rd_length: 6, size: 12, }, data: DNSResourceRecordData::NS { nsdname: "ns6.nease.net.".to_string(), }, }), ], ar: vec![ (DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: "163mx03.mxmail.netease.com.".to_string(), rr_type: DNSQtype::A, rr_class: DNSQclass::Internet, ttl: 442, rd_length: 4, size: 12, }, data: DNSResourceRecordData::A { address: "220.181.12.119".to_string(), }, }), (DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: "163mx01.mxmail.netease.com.".to_string(), rr_type: DNSQtype::A, rr_class: DNSQclass::Internet, ttl: 442, rd_length: 4, size: 12, }, data: DNSResourceRecordData::A { address: "220.181.12.117".to_string(), }, }), (DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: "163mx02.mxmail.netease.com.".to_string(), rr_type: DNSQtype::A, rr_class: DNSQclass::Internet, ttl: 442, rd_length: 4, size: 12, }, data: DNSResourceRecordData::A { address: "220.181.12.118".to_string(), }, }), (DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: "163mx00.mxmail.netease.com.".to_string(), rr_type: DNSQtype::A, rr_class: DNSQclass::Internet, ttl: 442, rd_length: 4, size: 12, }, data: DNSResourceRecordData::A { address: "220.181.12.180".to_string(), }, }), (DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: "ns6.nease.net.".to_string(), rr_type: DNSQtype::A, rr_class: DNSQclass::Internet, ttl: 5583, rd_length: 4, size: 12, }, data: DNSResourceRecordData::A { address: "54.228.156.72".to_string(), }, }), (DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: "ns1.nease.net.".to_string(), rr_type: DNSQtype::A, rr_class: DNSQclass::Internet, ttl: 32644, rd_length: 4, size: 12, }, data: DNSResourceRecordData::A { address: "42.186.35.222".to_string(), }, }), (DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: "ns4.nease.net.".to_string(), rr_type: DNSQtype::A, rr_class: DNSQclass::Internet, ttl: 51974, rd_length: 4, size: 12, }, data: DNSResourceRecordData::A { address: "103.72.16.81".to_string(), }, }), (DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: "ns3.nease.net.".to_string(), rr_type: DNSQtype::A, rr_class: DNSQclass::Internet, ttl: 41374, rd_length: 4, size: 12, }, data: DNSResourceRecordData::A { address: "220.181.36.234".to_string(), }, }), (DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: "ns2.166.com.".to_string(), rr_type: DNSQtype::A, rr_class: DNSQclass::Internet, ttl: 73500, rd_length: 4, size: 12, }, data: DNSResourceRecordData::A { address: "103.71.201.3".to_string(), }, }), (DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: "ns5.nease.net.".to_string(), rr_type: DNSQtype::A, rr_class: DNSQclass::Internet, ttl: 34614, rd_length: 4, size: 12, }, data: DNSResourceRecordData::A { address: "121.195.179.18".to_string(), }, }), (DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: "ns8.166.com.".to_string(), rr_type: DNSQtype::A, rr_class: DNSQclass::Internet, ttl: 64949, rd_length: 4, size: 12, }, data: DNSResourceRecordData::A { address: "18.182.82.158".to_string(), }, }), (DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: "ns8.166.com.".to_string(), rr_type: DNSQtype::A, rr_class: DNSQclass::Internet, ttl: 64949, rd_length: 4, size: 12, }, data: DNSResourceRecordData::A { address: "44.228.163.69".to_string(), }, }), (DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: ".".to_string(), rr_type: DNSQtype::OPT, rr_class: DNSQclass::Other(4096), ttl: 0, rd_length: 0, size: 11, }, data: DNSResourceRecordData::Other { qtype: DNSQtype::OPT, data: vec![], }, }), ], }; assert_eq!(DNSMessage::decode(&bytes), Ok((LAST_SLICE, expectation))); match DNSMessage::decode(&bytes) { Ok((left, message)) => { println!("{:?}; left {:?}", message, left); } Err(err) => { println!("{:?}\n", err); } } // assert_eq!(0, 1); } // TODO #[test] fn dns_answer_nx() {} // TODO #[test] fn test_answer_ptr_decode() {} #[test] fn test_answer_cname_decode() { // test in test_answer_soa_decode } #[test] fn test_answer_soa_decode() { /* * Domain Name System (response) * Transaction ID: 0xb613 * Flags: 0x8180 Standard query response, No error * 1... .... .... .... = Response: Message is a response * .000 0... .... .... = Opcode: Standard query (0) * .... .0.. .... .... = Authoritative: Server is not an authority for domain * .... ..0. .... .... = Truncated: Message is not truncated * .... ...1 .... .... = Recursion desired: Do query recursively * .... .... 1... .... = Recursion available: Server can do recursive queries * .... .... .0.. .... = Z: reserved (0) * .... .... ..0. .... = Answer authenticated: Answer/authority portion was not authenticated by the server * .... .... ...0 .... = Non-authenticated data: Unacceptable * .... .... .... 0000 = Reply code: No error (0) * Questions: 1 * Answer RRs: 1 * Authority RRs: 1 * Additional RRs: 1 * Queries * www.baidu.com: type SOA, class IN * Name: www.baidu.com * [Name Length: 13] * [Label Count: 3] * Type: SOA (Start Of a zone of Authority) (6) * Class: IN (0x0001) * Answers * www.baidu.com: type CNAME, class IN, cname www.a.shifen.com * Name: www.baidu.com * Type: CNAME (Canonical NAME for an alias) (5) * Class: IN (0x0001) * Time to live: 221 (3 minutes, 41 seconds) * Data length: 15 * CNAME: www.a.shifen.com * Authoritative nameservers * a.shifen.com: type SOA, class IN, mname ns1.a.shifen.com * Name: a.shifen.com * Type: SOA (Start Of a zone of Authority) (6) * Class: IN (0x0001) * Time to live: 296 (4 minutes, 56 seconds) * Data length: 45 * Primary name server: ns1.a.shifen.com * Responsible authority's mailbox: baidu_dns_master.baidu.com * Serial Number: 2211240019 * Refresh Interval: 5 (5 seconds) * Retry Interval: 5 (5 seconds) * Expire limit: 2592000 (30 days) * Minimum TTL: 3600 (1 hour) * Additional records * : 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 = DNSMessage { header: DNSHeader { id: 0xb613, qr: DNSHeaderQR::Response, op_code: DNSHeaderOpcode::Query, aa: false, tc: false, rd: true, ra: true, ad: false, cd: false, r_code: DNSHeaderRcode::NoError, qd_count: 1, an_count: 1, ns_count: 1, ar_count: 1, }, labels: DNSLabelTable { 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![DNSQuestionSection { qname: "www.baidu.com.".to_string(), qtype: DNSQtype::SOA, qclass: DNSQclass::Internet, size: 19, }], an: vec![ (DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: "www.baidu.com.".to_string(), rr_type: DNSQtype::CNAME, rr_class: DNSQclass::Internet, ttl: 221, rd_length: 15, size: 12, }, data: DNSResourceRecordData::CNAME { cname: "www.a.shifen.com.".to_string(), }, }), ], ns: vec![ (DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: "a.shifen.com.".to_string(), rr_type: DNSQtype::SOA, rr_class: DNSQclass::Internet, ttl: 296, rd_length: 45, size: 12, }, data: DNSResourceRecordData::SOA { mname: "ns1.a.shifen.com.".to_string(), rname: "baidu_dns_master.baidu.com.".to_string(), serial: 2211240019, refresh: 5, retry: 5, expire: 2592000, minimum: 3600, }, }), ], ar: vec![ (DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: ".".to_string(), rr_type: DNSQtype::OPT, rr_class: DNSQclass::Other(4096), ttl: 0, rd_length: 0, size: 11, }, data: DNSResourceRecordData::Other { qtype: DNSQtype::OPT, data: vec![], }, }), ], }; assert_eq!(DNSMessage::decode(&bytes), Ok((LAST_SLICE, expectation))); match DNSMessage::decode(&bytes) { Ok((left, message)) => { println!("{:?}; left {:?}", message, left); } Err(err) => { println!("{:?}\n", err); } } // assert_eq!(0, 1); } #[test] fn test_answer_txt_decode() { /* * Domain Name System (response) * Transaction ID: 0x6721 * Flags: 0x8180 Standard query response, No error * 1... .... .... .... = Response: Message is a response * .000 0... .... .... = Opcode: Standard query (0) * .... .0.. .... .... = Authoritative: Server is not an authority for domain * .... ..0. .... .... = Truncated: Message is not truncated * .... ...1 .... .... = Recursion desired: Do query recursively * .... .... 1... .... = Recursion available: Server can do recursive queries * .... .... .0.. .... = Z: reserved (0) * .... .... ..0. .... = Answer authenticated: Answer/authority portion was not authenticated by the server * .... .... ...0 .... = Non-authenticated data: Unacceptable * .... .... .... 0000 = Reply code: No error (0) * Questions: 1 * Answer RRs: 3 * Authority RRs: 5 * Additional RRs: 10 * Queries * baidu.com: type TXT, class IN * Name: baidu.com * [Name Length: 9] * [Label Count: 2] * Type: TXT (Text strings) (16) * Class: IN (0x0001) * Answers * baidu.com: type TXT, class IN * Name: baidu.com * Type: TXT (Text strings) (16) * Class: IN (0x0001) * Time to live: 7200 (2 hours) * Data length: 75 * TXT Length: 74 * TXT: _globalsign-domain-verification=qjb28W2jJSrWj04NHpB0CvgK9tle5JkOq-EcyWBgnE * baidu.com: type TXT, class IN * Name: baidu.com * Type: TXT (Text strings) (16) * Class: IN (0x0001) * Time to live: 7200 (2 hours) * Data length: 69 * TXT Length: 68 * TXT: google-site-verification=GHb98-6msqyx_qqjGl5eRatD3QTHyVB6-xQ3gJB5UwM * baidu.com: type TXT, class IN * Name: baidu.com * Type: TXT (Text strings) (16) * Class: IN (0x0001) * Time to live: 7200 (2 hours) * Data length: 113 * TXT Length: 112 * TXT: v=spf1 include:spf1.baidu.com include:spf2.baidu.com include:spf3.baidu.com include:spf4.baidu.com a mx ptr -all * Authoritative nameservers * baidu.com: type NS, class IN, ns ns7.baidu.com * Name: baidu.com * Type: NS (authoritative Name Server) (2) * Class: IN (0x0001) * Time to live: 53898 (14 hours, 58 minutes, 18 seconds) * Data length: 6 * Name Server: ns7.baidu.com * baidu.com: type NS, class IN, ns dns.baidu.com * Name: baidu.com * Type: NS (authoritative Name Server) (2) * Class: IN (0x0001) * Time to live: 53898 (14 hours, 58 minutes, 18 seconds) * Data length: 6 * Name Server: dns.baidu.com * baidu.com: type NS, class IN, ns ns3.baidu.com * Name: baidu.com * Type: NS (authoritative Name Server) (2) * Class: IN (0x0001) * Time to live: 53898 (14 hours, 58 minutes, 18 seconds) * Data length: 6 * Name Server: ns3.baidu.com * baidu.com: type NS, class IN, ns ns2.baidu.com * Name: baidu.com * Type: NS (authoritative Name Server) (2) * Class: IN (0x0001) * Time to live: 53898 (14 hours, 58 minutes, 18 seconds) * Data length: 6 * Name Server: ns2.baidu.com * baidu.com: type NS, class IN, ns ns4.baidu.com * Name: baidu.com * Type: NS (authoritative Name Server) (2) * Class: IN (0x0001) * Time to live: 53898 (14 hours, 58 minutes, 18 seconds) * Data length: 6 * Name Server: ns4.baidu.com * Additional records * ns3.baidu.com: type A, class IN, addr 36.152.45.193 * Name: ns3.baidu.com * Type: A (Host Address) (1) * Class: IN (0x0001) * Time to live: 25922 (7 hours, 12 minutes, 2 seconds) * Data length: 4 * Address: 36.152.45.193 * ns3.baidu.com: type A, class IN, addr 112.80.248.64 * Name: ns3.baidu.com * Type: A (Host Address) (1) * Class: IN (0x0001) * Time to live: 25922 (7 hours, 12 minutes, 2 seconds) * Data length: 4 * Address: 112.80.248.64 * ns2.baidu.com: type A, class IN, addr 220.181.33.31 * Name: ns2.baidu.com * Type: A (Host Address) (1) * Class: IN (0x0001) * Time to live: 23967 (6 hours, 39 minutes, 27 seconds) * Data length: 4 * Address: 220.181.33.31 * ns7.baidu.com: type A, class IN, addr 180.76.76.92 * Name: ns7.baidu.com * Type: A (Host Address) (1) * Class: IN (0x0001) * Time to live: 50794 (14 hours, 6 minutes, 34 seconds) * Data length: 4 * Address: 180.76.76.92 * ns4.baidu.com: type A, class IN, addr 14.215.178.80 * Name: ns4.baidu.com * Type: A (Host Address) (1) * Class: IN (0x0001) * Time to live: 58399 (16 hours, 13 minutes, 19 seconds) * Data length: 4 * Address: 14.215.178.80 * ns4.baidu.com: type A, class IN, addr 111.45.3.226 * Name: ns4.baidu.com * Type: A (Host Address) (1) * Class: IN (0x0001) * Time to live: 58399 (16 hours, 13 minutes, 19 seconds) * Data length: 4 * Address: 111.45.3.226 * dns.baidu.com: type A, class IN, addr 110.242.68.134 * Name: dns.baidu.com * Type: A (Host Address) (1) * Class: IN (0x0001) * Time to live: 114889 (1 day, 7 hours, 54 minutes, 49 seconds) * Data length: 4 * Address: 110.242.68.134 * ns7.baidu.com: type AAAA, class IN, addr 240e:940:603:4:0:ff:b01b:589a * Name: ns7.baidu.com * Type: AAAA (IPv6 Address) (28) * Class: IN (0x0001) * Time to live: 80536 (22 hours, 22 minutes, 16 seconds) * Data length: 16 * AAAA Address: 240e:940:603:4:0:ff:b01b:589a * ns7.baidu.com: type AAAA, class IN, addr 240e:bf:b801:1002:0:ff:b024:26de * Name: ns7.baidu.com * Type: AAAA (IPv6 Address) (28) * Class: IN (0x0001) * Time to live: 80536 (22 hours, 22 minutes, 16 seconds) * Data length: 16 * AAAA Address: 240e:bf:b801:1002:0:ff:b024:26de * : 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 = DNSMessage { header: DNSHeader { id: 0x6721, qr: DNSHeaderQR::Response, op_code: DNSHeaderOpcode::Query, aa: false, tc: false, rd: true, ra: true, ad: false, cd: false, r_code: DNSHeaderRcode::NoError, qd_count: 1, an_count: 3, ns_count: 5, ar_count: 10, }, labels: DNSLabelTable { 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![ DNSQuestionSection { qname: "baidu.com.".to_string(), qtype: DNSQtype::TXT, qclass: DNSQclass::Internet, size: 15 } ], an: vec![ (DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: "baidu.com.".to_string(), rr_type: DNSQtype::TXT, rr_class: DNSQclass::Internet, ttl: 7200, rd_length: 75, size: 12 }, data:DNSResourceRecordData::TXT{ txtdata:vec!["_globalsign-domain-verification=qjb28W2jJSrWj04NHpB0CvgK9tle5JkOq-EcyWBgnE".to_string()]},} ), (DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: "baidu.com.".to_string(), rr_type: DNSQtype::TXT, rr_class: DNSQclass::Internet, ttl: 7200, rd_length: 69, size: 12 }, data:DNSResourceRecordData::TXT{ txtdata:vec!["google-site-verification=GHb98-6msqyx_qqjGl5eRatD3QTHyVB6-xQ3gJB5UwM".to_string()]},} ), (DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: "baidu.com.".to_string(), rr_type: DNSQtype::TXT, rr_class: DNSQclass::Internet, ttl: 7200, rd_length: 113, size: 12 }, data: DNSResourceRecordData::TXT{ txtdata:vec!["v=spf1 include:spf1.baidu.com include:spf2.baidu.com include:spf3.baidu.com include:spf4.baidu.com a mx ptr -all".to_string()]}, } )], ns: vec![ (DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: "baidu.com.".to_string(), rr_type: DNSQtype::NS, rr_class: DNSQclass::Internet, ttl: 53898, rd_length: 6, size: 12 }, data: DNSResourceRecordData::NS{nsdname: "ns7.baidu.com.".to_string()},} ), (DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: "baidu.com.".to_string(), rr_type: DNSQtype::NS, rr_class: DNSQclass::Internet, ttl: 53898, rd_length: 6, size: 12 }, data: DNSResourceRecordData::NS{nsdname: "dns.baidu.com.".to_string()},} ), (DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: "baidu.com.".to_string(), rr_type: DNSQtype::NS, rr_class: DNSQclass::Internet, ttl: 53898, rd_length: 6, size: 12 }, data: DNSResourceRecordData::NS{nsdname: "ns3.baidu.com.".to_string()},} ), (DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: "baidu.com.".to_string(), rr_type: DNSQtype::NS, rr_class: DNSQclass::Internet, ttl: 53898, rd_length: 6, size: 12 }, data: DNSResourceRecordData:: NS{ nsdname: "ns2.baidu.com.".to_string()},} ), (DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: "baidu.com.".to_string(), rr_type: DNSQtype::NS, rr_class: DNSQclass::Internet, ttl: 53898, rd_length: 6, size: 12 }, data: DNSResourceRecordData::NS{nsdname: "ns4.baidu.com.".to_string()},} ) ], ar: vec![ (DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: "ns3.baidu.com.".to_string(), rr_type: DNSQtype::A, rr_class: DNSQclass::Internet, ttl: 25922, rd_length: 4, size: 12 }, data: DNSResourceRecordData::A{address: "36.152.45.193".to_string()}, } ), (DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: "ns3.baidu.com.".to_string(), rr_type: DNSQtype::A, rr_class: DNSQclass::Internet, ttl: 25922, rd_length: 4, size: 12 }, data: DNSResourceRecordData::A{address: "112.80.248.64".to_string()}, } ), ( DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: "ns2.baidu.com.".to_string(), rr_type: DNSQtype::A, rr_class: DNSQclass::Internet, ttl: 23967, rd_length: 4, size: 12 }, data: DNSResourceRecordData::A{address: "220.181.33.31".to_string()}, } ), ( DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: "ns7.baidu.com.".to_string(), rr_type: DNSQtype::A, rr_class: DNSQclass::Internet, ttl: 50794, rd_length: 4, size: 12 }, data: DNSResourceRecordData::A{address: "180.76.76.92".to_string()}, } ), ( DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: "ns4.baidu.com.".to_string(), rr_type: DNSQtype::A, rr_class: DNSQclass::Internet, ttl: 58399, rd_length: 4, size: 12 }, data:DNSResourceRecordData::A{address: "14.215.178.80".to_string()}, } ), ( DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: "ns4.baidu.com.".to_string(), rr_type: DNSQtype::A, rr_class: DNSQclass::Internet, ttl: 58399, rd_length: 4, size: 12 }, data:DNSResourceRecordData::A{address: "111.45.3.226".to_string()},} ), ( DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: "dns.baidu.com.".to_string(), rr_type: DNSQtype::A, rr_class: DNSQclass::Internet, ttl: 114889, rd_length: 4, size: 12 }, data: DNSResourceRecordData::A{address: "110.242.68.134".to_string()},} ), (DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: "ns7.baidu.com.".to_string(), rr_type: DNSQtype::AAAA, rr_class: DNSQclass::Internet, ttl: 80536, rd_length: 16, size: 12 }, data: DNSResourceRecordData::AAAA{ address : "240E:0940:0603:0004:0000:00FF:B01B:589A".to_string()},} ), (DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: "ns7.baidu.com.".to_string(), rr_type: DNSQtype::AAAA, rr_class: DNSQclass::Internet, ttl: 80536, rd_length: 16, size: 12 }, data: DNSResourceRecordData::AAAA{ address: "240E:00BF:B801:1002:0000:00FF:B024:26DE".to_string()},} ), (DNSResourceRecordSection { hdr: DNSResourceRecordHeader { qname: ".".to_string(), rr_type: DNSQtype::OPT, rr_class: DNSQclass::Other(4096), ttl: 0, rd_length: 0, size: 11 }, data:DNSResourceRecordData::Other{ qtype: DNSQtype::OPT,data:vec![]},} ) ] }; assert_eq!(DNSMessage::decode(&bytes), Ok((LAST_SLICE, expectation))); match DNSMessage::decode(&bytes) { Ok((left, message)) => { println!("{:?}; left {:?}", message, left); } Err(err) => { println!("{:?}\n", err); } } // assert_eq!(0, 1); } // TODO #[test] fn test_answer_other_decode() {} }