123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894 |
- // Copyright 2014 Google, Inc. All rights reserved.
- //
- // Use of this source code is governed by a BSD-style license
- // that can be found in the LICENSE file in the root of the source
- // tree.
- package layers
- import (
- "encoding/binary"
- "errors"
- "fmt"
- "net"
- "github.com/google/gopacket"
- )
- // DNSClass defines the class associated with a request/response. Different DNS
- // classes can be thought of as an array of parallel namespace trees.
- type DNSClass uint16
- // DNSClass known values.
- const (
- DNSClassIN DNSClass = 1 // Internet
- DNSClassCS DNSClass = 2 // the CSNET class (Obsolete)
- DNSClassCH DNSClass = 3 // the CHAOS class
- DNSClassHS DNSClass = 4 // Hesiod [Dyer 87]
- DNSClassAny DNSClass = 255 // AnyClass
- )
- func (dc DNSClass) String() string {
- switch dc {
- default:
- return "Unknown"
- case DNSClassIN:
- return "IN"
- case DNSClassCS:
- return "CS"
- case DNSClassCH:
- return "CH"
- case DNSClassHS:
- return "HS"
- case DNSClassAny:
- return "Any"
- }
- }
- // DNSType defines the type of data being requested/returned in a
- // question/answer.
- type DNSType uint16
- // DNSType known values.
- const (
- DNSTypeA DNSType = 1 // a host address
- DNSTypeNS DNSType = 2 // an authoritative name server
- DNSTypeMD DNSType = 3 // a mail destination (Obsolete - use MX)
- DNSTypeMF DNSType = 4 // a mail forwarder (Obsolete - use MX)
- DNSTypeCNAME DNSType = 5 // the canonical name for an alias
- DNSTypeSOA DNSType = 6 // marks the start of a zone of authority
- DNSTypeMB DNSType = 7 // a mailbox domain name (EXPERIMENTAL)
- DNSTypeMG DNSType = 8 // a mail group member (EXPERIMENTAL)
- DNSTypeMR DNSType = 9 // a mail rename domain name (EXPERIMENTAL)
- DNSTypeNULL DNSType = 10 // a null RR (EXPERIMENTAL)
- DNSTypeWKS DNSType = 11 // a well known service description
- DNSTypePTR DNSType = 12 // a domain name pointer
- DNSTypeHINFO DNSType = 13 // host information
- DNSTypeMINFO DNSType = 14 // mailbox or mail list information
- DNSTypeMX DNSType = 15 // mail exchange
- DNSTypeTXT DNSType = 16 // text strings
- DNSTypeAAAA DNSType = 28 // a IPv6 host address [RFC3596]
- DNSTypeSRV DNSType = 33 // server discovery [RFC2782] [RFC6195]
- )
- func (dt DNSType) String() string {
- switch dt {
- default:
- return "Unknown"
- case DNSTypeA:
- return "A"
- case DNSTypeNS:
- return "NS"
- case DNSTypeMD:
- return "MD"
- case DNSTypeMF:
- return "MF"
- case DNSTypeCNAME:
- return "CNAME"
- case DNSTypeSOA:
- return "SOA"
- case DNSTypeMB:
- return "MB"
- case DNSTypeMG:
- return "MG"
- case DNSTypeMR:
- return "MR"
- case DNSTypeNULL:
- return "NULL"
- case DNSTypeWKS:
- return "WKS"
- case DNSTypePTR:
- return "PTR"
- case DNSTypeHINFO:
- return "HINFO"
- case DNSTypeMINFO:
- return "MINFO"
- case DNSTypeMX:
- return "MX"
- case DNSTypeTXT:
- return "TXT"
- case DNSTypeAAAA:
- return "AAAA"
- case DNSTypeSRV:
- return "SRV"
- }
- }
- // DNSResponseCode provides response codes for question answers.
- type DNSResponseCode uint8
- // DNSResponseCode known values.
- const (
- DNSResponseCodeNoErr DNSResponseCode = 0 // No error
- DNSResponseCodeFormErr DNSResponseCode = 1 // Format Error [RFC1035]
- DNSResponseCodeServFail DNSResponseCode = 2 // Server Failure [RFC1035]
- DNSResponseCodeNXDomain DNSResponseCode = 3 // Non-Existent Domain [RFC1035]
- DNSResponseCodeNotImp DNSResponseCode = 4 // Not Implemented [RFC1035]
- DNSResponseCodeRefused DNSResponseCode = 5 // Query Refused [RFC1035]
- DNSResponseCodeYXDomain DNSResponseCode = 6 // Name Exists when it should not [RFC2136]
- DNSResponseCodeYXRRSet DNSResponseCode = 7 // RR Set Exists when it should not [RFC2136]
- DNSResponseCodeNXRRSet DNSResponseCode = 8 // RR Set that should exist does not [RFC2136]
- DNSResponseCodeNotAuth DNSResponseCode = 9 // Server Not Authoritative for zone [RFC2136]
- DNSResponseCodeNotZone DNSResponseCode = 10 // Name not contained in zone [RFC2136]
- DNSResponseCodeBadVers DNSResponseCode = 16 // Bad OPT Version [RFC2671]
- DNSResponseCodeBadSig DNSResponseCode = 16 // TSIG Signature Failure [RFC2845]
- DNSResponseCodeBadKey DNSResponseCode = 17 // Key not recognized [RFC2845]
- DNSResponseCodeBadTime DNSResponseCode = 18 // Signature out of time window [RFC2845]
- DNSResponseCodeBadMode DNSResponseCode = 19 // Bad TKEY Mode [RFC2930]
- DNSResponseCodeBadName DNSResponseCode = 20 // Duplicate key name [RFC2930]
- DNSResponseCodeBadAlg DNSResponseCode = 21 // Algorithm not supported [RFC2930]
- DNSResponseCodeBadTruc DNSResponseCode = 22 // Bad Truncation [RFC4635]
- )
- func (drc DNSResponseCode) String() string {
- switch drc {
- default:
- return "Unknown"
- case DNSResponseCodeNoErr:
- return "No Error"
- case DNSResponseCodeFormErr:
- return "Format Error"
- case DNSResponseCodeServFail:
- return "Server Failure "
- case DNSResponseCodeNXDomain:
- return "Non-Existent Domain"
- case DNSResponseCodeNotImp:
- return "Not Implemented"
- case DNSResponseCodeRefused:
- return "Query Refused"
- case DNSResponseCodeYXDomain:
- return "Name Exists when it should not"
- case DNSResponseCodeYXRRSet:
- return "RR Set Exists when it should not"
- case DNSResponseCodeNXRRSet:
- return "RR Set that should exist does not"
- case DNSResponseCodeNotAuth:
- return "Server Not Authoritative for zone"
- case DNSResponseCodeNotZone:
- return "Name not contained in zone"
- case DNSResponseCodeBadVers:
- return "Bad OPT Version"
- case DNSResponseCodeBadKey:
- return "Key not recognized"
- case DNSResponseCodeBadTime:
- return "Signature out of time window"
- case DNSResponseCodeBadMode:
- return "Bad TKEY Mode"
- case DNSResponseCodeBadName:
- return "Duplicate key name"
- case DNSResponseCodeBadAlg:
- return "Algorithm not supported"
- case DNSResponseCodeBadTruc:
- return "Bad Truncation"
- }
- }
- // DNSOpCode defines a set of different operation types.
- type DNSOpCode uint8
- // DNSOpCode known values.
- const (
- DNSOpCodeQuery DNSOpCode = 0 // Query [RFC1035]
- DNSOpCodeIQuery DNSOpCode = 1 // Inverse Query Obsolete [RFC3425]
- DNSOpCodeStatus DNSOpCode = 2 // Status [RFC1035]
- DNSOpCodeNotify DNSOpCode = 4 // Notify [RFC1996]
- DNSOpCodeUpdate DNSOpCode = 5 // Update [RFC2136]
- )
- func (doc DNSOpCode) String() string {
- switch doc {
- default:
- return "Unknown"
- case DNSOpCodeQuery:
- return "Query"
- case DNSOpCodeIQuery:
- return "Inverse Query"
- case DNSOpCodeStatus:
- return "Status"
- case DNSOpCodeNotify:
- return "Notify"
- case DNSOpCodeUpdate:
- return "Update"
- }
- }
- // DNS is specified in RFC 1034 / RFC 1035
- // +---------------------+
- // | Header |
- // +---------------------+
- // | Question | the question for the name server
- // +---------------------+
- // | Answer | RRs answering the question
- // +---------------------+
- // | Authority | RRs pointing toward an authority
- // +---------------------+
- // | Additional | RRs holding additional information
- // +---------------------+
- //
- // DNS Header
- // 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 contains data from a single Domain Name Service packet.
- type DNS struct {
- BaseLayer
- // Header fields
- ID uint16
- QR bool
- OpCode DNSOpCode
- AA bool // Authoritative answer
- TC bool // Truncated
- RD bool // Recursion desired
- RA bool // Recursion available
- Z uint8 // Reserved for future use
- ResponseCode DNSResponseCode
- QDCount uint16 // Number of questions to expect
- ANCount uint16 // Number of answers to expect
- NSCount uint16 // Number of authorities to expect
- ARCount uint16 // Number of additional records to expect
- // Entries
- Questions []DNSQuestion
- Answers []DNSResourceRecord
- Authorities []DNSResourceRecord
- Additionals []DNSResourceRecord
- // buffer for doing name decoding. We use a single reusable buffer to avoid
- // name decoding on a single object via multiple DecodeFromBytes calls
- // requiring constant allocation of small byte slices.
- buffer []byte
- }
- // LayerType returns gopacket.LayerTypeDNS.
- func (d *DNS) LayerType() gopacket.LayerType { return LayerTypeDNS }
- // decodeDNS decodes the byte slice into a DNS type. It also
- // setups the application Layer in PacketBuilder.
- func decodeDNS(data []byte, p gopacket.PacketBuilder) error {
- d := &DNS{}
- err := d.DecodeFromBytes(data, p)
- if err != nil {
- return err
- }
- p.AddLayer(d)
- p.SetApplicationLayer(d)
- return nil
- }
- // DecodeFromBytes decodes the slice into the DNS struct.
- func (d *DNS) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
- d.buffer = d.buffer[:0]
- if len(data) < 12 {
- df.SetTruncated()
- return errors.New("DNS packet too short")
- }
- // since there are no further layers, the baselayer's content is
- // pointing to this layer
- d.BaseLayer = BaseLayer{Contents: data[:len(data)]}
- d.ID = binary.BigEndian.Uint16(data[:2])
- d.QR = data[2]&0x80 != 0
- d.OpCode = DNSOpCode(data[2]>>3) & 0x0F
- d.AA = data[2]&0x04 != 0
- d.TC = data[2]&0x02 != 0
- d.RD = data[2]&0x01 != 0
- d.RA = data[3]&0x80 != 0
- d.Z = uint8(data[3]>>4) & 0x7
- d.ResponseCode = DNSResponseCode(data[3] & 0xF)
- d.QDCount = binary.BigEndian.Uint16(data[4:6])
- d.ANCount = binary.BigEndian.Uint16(data[6:8])
- d.NSCount = binary.BigEndian.Uint16(data[8:10])
- d.ARCount = binary.BigEndian.Uint16(data[10:12])
- d.Questions = d.Questions[:0]
- d.Answers = d.Answers[:0]
- d.Authorities = d.Authorities[:0]
- d.Additionals = d.Additionals[:0]
- offset := 12
- var err error
- for i := 0; i < int(d.QDCount); i++ {
- var q DNSQuestion
- if offset, err = q.decode(data, offset, df, &d.buffer); err != nil {
- return err
- }
- d.Questions = append(d.Questions, q)
- }
- // For some horrible reason, if we do the obvious thing in this loop:
- // var r DNSResourceRecord
- // if blah := r.decode(blah); err != nil {
- // return err
- // }
- // d.Foo = append(d.Foo, r)
- // the Go compiler thinks that 'r' escapes to the heap, causing a malloc for
- // every Answer, Authority, and Additional. To get around this, we do
- // something really silly: we append an empty resource record to our slice,
- // then use the last value in the slice to call decode. Since the value is
- // already in the slice, there's no WAY it can escape... on the other hand our
- // code is MUCH uglier :(
- for i := 0; i < int(d.ANCount); i++ {
- d.Answers = append(d.Answers, DNSResourceRecord{})
- if offset, err = d.Answers[i].decode(data, offset, df, &d.buffer); err != nil {
- d.Answers = d.Answers[:i] // strip off erroneous value
- return err
- }
- }
- for i := 0; i < int(d.NSCount); i++ {
- d.Authorities = append(d.Authorities, DNSResourceRecord{})
- if offset, err = d.Authorities[i].decode(data, offset, df, &d.buffer); err != nil {
- d.Authorities = d.Authorities[:i] // strip off erroneous value
- return err
- }
- }
- for i := 0; i < int(d.ARCount); i++ {
- d.Additionals = append(d.Additionals, DNSResourceRecord{})
- if offset, err = d.Additionals[i].decode(data, offset, df, &d.buffer); err != nil {
- d.Additionals = d.Additionals[:i] // strip off erroneous value
- return err
- }
- }
- if uint16(len(d.Questions)) != d.QDCount {
- return errors.New("Invalid query decoding, not the right number of questions")
- } else if uint16(len(d.Answers)) != d.ANCount {
- return errors.New("Invalid query decoding, not the right number of answers")
- } else if uint16(len(d.Authorities)) != d.NSCount {
- return errors.New("Invalid query decoding, not the right number of authorities")
- } else if uint16(len(d.Additionals)) != d.ARCount {
- return errors.New("Invalid query decoding, not the right number of additionals info")
- }
- return nil
- }
- // CanDecode implements gopacket.DecodingLayer.
- func (d *DNS) CanDecode() gopacket.LayerClass {
- return LayerTypeDNS
- }
- // NextLayerType implements gopacket.DecodingLayer.
- func (d *DNS) NextLayerType() gopacket.LayerType {
- return gopacket.LayerTypePayload
- }
- // Payload returns nil.
- func (d *DNS) Payload() []byte {
- return nil
- }
- func b2i(b bool) int {
- if b {
- return 1
- }
- return 0
- }
- func recSize(rr *DNSResourceRecord) int {
- switch rr.Type {
- case DNSTypeA:
- return 4
- case DNSTypeAAAA:
- return 16
- case DNSTypeNS:
- return len(rr.NS) + 2
- case DNSTypeCNAME:
- return len(rr.CNAME) + 2
- case DNSTypePTR:
- return len(rr.PTR) + 2
- case DNSTypeSOA:
- return len(rr.SOA.MName) + 2 + len(rr.SOA.RName) + 2 + 20
- case DNSTypeMX:
- return 2 + len(rr.MX.Name) + 2
- case DNSTypeTXT:
- l := len(rr.TXTs)
- for _, txt := range rr.TXTs {
- l += len(txt)
- }
- return l
- case DNSTypeSRV:
- return 6 + len(rr.SRV.Name) + 2
- }
- return 0
- }
- func computeSize(recs []DNSResourceRecord) int {
- sz := 0
- for _, rr := range recs {
- sz += len(rr.Name) + 12
- sz += recSize(&rr)
- }
- return sz
- }
- // SerializeTo writes the serialized form of this layer into the
- // SerializationBuffer, implementing gopacket.SerializableLayer.
- func (d *DNS) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
- dsz := 0
- for _, q := range d.Questions {
- dsz += len(q.Name) + 6
- }
- dsz += computeSize(d.Answers)
- dsz += computeSize(d.Authorities)
- dsz += computeSize(d.Additionals)
- bytes, err := b.PrependBytes(12 + dsz)
- if err != nil {
- return err
- }
- binary.BigEndian.PutUint16(bytes, d.ID)
- bytes[2] = byte((b2i(d.QR) << 7) | (int(d.OpCode) << 3) | (b2i(d.AA) << 2) | (b2i(d.TC) << 1) | b2i(d.RD))
- bytes[3] = byte((b2i(d.RA) << 7) | (int(d.Z) << 4) | int(d.ResponseCode))
- if opts.FixLengths {
- d.QDCount = uint16(len(d.Questions))
- d.ANCount = uint16(len(d.Answers))
- d.NSCount = uint16(len(d.Authorities))
- d.ARCount = uint16(len(d.Additionals))
- }
- binary.BigEndian.PutUint16(bytes[4:], d.QDCount)
- binary.BigEndian.PutUint16(bytes[6:], d.ANCount)
- binary.BigEndian.PutUint16(bytes[8:], d.NSCount)
- binary.BigEndian.PutUint16(bytes[10:], d.ARCount)
- off := 12
- for _, qd := range d.Questions {
- n := qd.encode(bytes, off)
- off += n
- }
- for i := range d.Answers {
- // done this way so we can modify DNSResourceRecord to fix
- // lengths if requested
- qa := &d.Answers[i]
- n, err := qa.encode(bytes, off, opts)
- if err != nil {
- return err
- }
- off += n
- }
- for i := range d.Authorities {
- qa := &d.Authorities[i]
- n, err := qa.encode(bytes, off, opts)
- if err != nil {
- return err
- }
- off += n
- }
- for i := range d.Additionals {
- qa := &d.Additionals[i]
- n, err := qa.encode(bytes, off, opts)
- if err != nil {
- return err
- }
- off += n
- }
- return nil
- }
- var errMaxRecursion = errors.New("max DNS recursion level hit")
- const maxRecursionLevel = 255
- func decodeName(data []byte, offset int, buffer *[]byte, level int) ([]byte, int, error) {
- if level > maxRecursionLevel {
- return nil, 0, errMaxRecursion
- } else if offset >= len(data) {
- return nil, 0, errors.New("dns name offset too high")
- } else if offset < 0 {
- return nil, 0, errors.New("dns name offset is negative")
- }
- start := len(*buffer)
- index := offset
- if data[index] == 0x00 {
- return nil, index + 1, nil
- }
- loop:
- for data[index] != 0x00 {
- switch data[index] & 0xc0 {
- default:
- /* RFC 1035
- A domain name represented as a sequence of labels, where
- each label consists of a length octet followed by that
- number of octets. The domain name terminates with the
- zero length octet for the null label of the root. Note
- that this field may be an odd number of octets; no
- padding is used.
- */
- index2 := index + int(data[index]) + 1
- if index2-offset > 255 {
- return nil, 0, errors.New("dns name is too long")
- } else if index2 < index+1 || index2 > len(data) {
- return nil, 0, errors.New("dns name uncomputable: invalid index")
- }
- *buffer = append(*buffer, '.')
- *buffer = append(*buffer, data[index+1:index2]...)
- index = index2
- case 0xc0:
- /* RFC 1035
- The pointer takes the form of a two octet sequence.
- The first two bits are ones. This allows a pointer to
- be distinguished from a label, since the label must
- begin with two zero bits because labels are restricted
- to 63 octets or less. (The 10 and 01 combinations are
- reserved for future use.) The OFFSET field specifies
- an offset from the start of the message (i.e., the
- first octet of the ID field in the domain header). A
- zero offset specifies the first byte of the ID field,
- etc.
- 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
- */
- if index+2 > len(data) {
- return nil, 0, errors.New("dns offset pointer too high")
- }
- offsetp := int(binary.BigEndian.Uint16(data[index:index+2]) & 0x3fff)
- if offsetp > len(data) {
- return nil, 0, errors.New("dns offset pointer too high")
- }
- // This looks a little tricky, but actually isn't. Because of how
- // decodeName is written, calling it appends the decoded name to the
- // current buffer. We already have the start of the buffer, then, so
- // once this call is done buffer[start:] will contain our full name.
- _, _, err := decodeName(data, offsetp, buffer, level+1)
- if err != nil {
- return nil, 0, err
- }
- index++ // pointer is two bytes, so add an extra byte here.
- break loop
- /* EDNS, or other DNS option ? */
- case 0x40: // RFC 2673
- return nil, 0, fmt.Errorf("qname '0x40' - RFC 2673 unsupported yet (data=%x index=%d)",
- data[index], index)
- case 0x80:
- return nil, 0, fmt.Errorf("qname '0x80' unsupported yet (data=%x index=%d)",
- data[index], index)
- }
- if index >= len(data) {
- return nil, 0, errors.New("dns index walked out of range")
- }
- }
- if len(*buffer) <= start {
- return nil, 0, errors.New("no dns data found for name")
- }
- return (*buffer)[start+1:], index + 1, nil
- }
- // DNSQuestion wraps a single request (question) within a DNS query.
- type DNSQuestion struct {
- Name []byte
- Type DNSType
- Class DNSClass
- }
- func (q *DNSQuestion) decode(data []byte, offset int, df gopacket.DecodeFeedback, buffer *[]byte) (int, error) {
- name, endq, err := decodeName(data, offset, buffer, 1)
- if err != nil {
- return 0, err
- }
- q.Name = name
- q.Type = DNSType(binary.BigEndian.Uint16(data[endq : endq+2]))
- q.Class = DNSClass(binary.BigEndian.Uint16(data[endq+2 : endq+4]))
- return endq + 4, nil
- }
- func (q *DNSQuestion) encode(data []byte, offset int) int {
- noff := encodeName(q.Name, data, offset)
- binary.BigEndian.PutUint16(data[noff:], uint16(q.Type))
- binary.BigEndian.PutUint16(data[noff+2:], uint16(q.Class))
- return len(q.Name) + 6
- }
- // DNSResourceRecord
- // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
- // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- // | |
- // / /
- // / NAME /
- // | |
- // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- // | TYPE |
- // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- // | CLASS |
- // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- // | TTL |
- // | |
- // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- // | RDLENGTH |
- // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
- // / RDATA /
- // / /
- // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- // DNSResourceRecord wraps the data from a single DNS resource within a
- // response.
- type DNSResourceRecord struct {
- // Header
- Name []byte
- Type DNSType
- Class DNSClass
- TTL uint32
- // RDATA Raw Values
- DataLength uint16
- Data []byte
- // RDATA Decoded Values
- IP net.IP
- NS, CNAME, PTR []byte
- TXTs [][]byte
- SOA DNSSOA
- SRV DNSSRV
- MX DNSMX
- // Undecoded TXT for backward compatibility
- TXT []byte
- }
- // decode decodes the resource record, returning the total length of the record.
- func (rr *DNSResourceRecord) decode(data []byte, offset int, df gopacket.DecodeFeedback, buffer *[]byte) (int, error) {
- name, endq, err := decodeName(data, offset, buffer, 1)
- if err != nil {
- return 0, err
- }
- rr.Name = name
- rr.Type = DNSType(binary.BigEndian.Uint16(data[endq : endq+2]))
- rr.Class = DNSClass(binary.BigEndian.Uint16(data[endq+2 : endq+4]))
- rr.TTL = binary.BigEndian.Uint32(data[endq+4 : endq+8])
- rr.DataLength = binary.BigEndian.Uint16(data[endq+8 : endq+10])
- end := endq + 10 + int(rr.DataLength)
- if end > len(data) {
- return 0, fmt.Errorf("resource record length exceeds data")
- }
- rr.Data = data[endq+10 : end]
- if err = rr.decodeRData(data, endq+10, buffer); err != nil {
- return 0, err
- }
- return endq + 10 + int(rr.DataLength), nil
- }
- func encodeName(name []byte, data []byte, offset int) int {
- l := 0
- for i := range name {
- if name[i] == '.' {
- data[offset+i-l] = byte(l)
- l = 0
- } else {
- // skip one to write the length
- data[offset+i+1] = name[i]
- l++
- }
- }
- // length for final portion
- data[offset+len(name)-l] = byte(l)
- data[offset+len(name)+1] = 0x00 // terminal
- return offset + len(name) + 2
- }
- func (rr *DNSResourceRecord) encode(data []byte, offset int, opts gopacket.SerializeOptions) (int, error) {
- noff := encodeName(rr.Name, data, offset)
- binary.BigEndian.PutUint16(data[noff:], uint16(rr.Type))
- binary.BigEndian.PutUint16(data[noff+2:], uint16(rr.Class))
- binary.BigEndian.PutUint32(data[noff+4:], uint32(rr.TTL))
- switch rr.Type {
- case DNSTypeA:
- copy(data[noff+10:], rr.IP.To4())
- case DNSTypeAAAA:
- copy(data[noff+10:], rr.IP)
- case DNSTypeNS:
- encodeName(rr.NS, data, noff+10)
- case DNSTypeCNAME:
- encodeName(rr.CNAME, data, noff+10)
- case DNSTypePTR:
- encodeName(rr.PTR, data, noff+10)
- case DNSTypeSOA:
- noff2 := encodeName(rr.SOA.MName, data, noff+10)
- noff2 = encodeName(rr.SOA.RName, data, noff2)
- binary.BigEndian.PutUint32(data[noff2:], rr.SOA.Serial)
- binary.BigEndian.PutUint32(data[noff2+4:], rr.SOA.Refresh)
- binary.BigEndian.PutUint32(data[noff2+8:], rr.SOA.Retry)
- binary.BigEndian.PutUint32(data[noff2+12:], rr.SOA.Expire)
- binary.BigEndian.PutUint32(data[noff2+16:], rr.SOA.Minimum)
- case DNSTypeMX:
- binary.BigEndian.PutUint16(data[noff+10:], rr.MX.Preference)
- encodeName(rr.MX.Name, data, noff+12)
- case DNSTypeTXT:
- noff2 := noff + 10
- for _, txt := range rr.TXTs {
- data[noff2] = byte(len(txt))
- copy(data[noff2+1:], txt)
- noff2 += 1 + len(txt)
- }
- case DNSTypeSRV:
- binary.BigEndian.PutUint16(data[noff+10:], rr.SRV.Priority)
- binary.BigEndian.PutUint16(data[noff+12:], rr.SRV.Weight)
- binary.BigEndian.PutUint16(data[noff+14:], rr.SRV.Port)
- encodeName(rr.SRV.Name, data, noff+16)
- default:
- return 0, fmt.Errorf("serializing resource record of type %v not supported", rr.Type)
- }
- // DataLength
- dSz := recSize(rr)
- binary.BigEndian.PutUint16(data[noff+8:], uint16(dSz))
- if opts.FixLengths {
- rr.DataLength = uint16(dSz)
- }
- return len(rr.Name) + 1 + 11 + dSz, nil
- }
- func (rr *DNSResourceRecord) String() string {
- if rr.Class == DNSClassIN {
- switch rr.Type {
- case DNSTypeA, DNSTypeAAAA:
- return rr.IP.String()
- case DNSTypeNS:
- return "NS " + string(rr.NS)
- case DNSTypeCNAME:
- return "CNAME " + string(rr.CNAME)
- case DNSTypePTR:
- return "PTR " + string(rr.PTR)
- case DNSTypeTXT:
- return "TXT " + string(rr.TXT)
- }
- }
- return fmt.Sprintf("<%v, %v>", rr.Class, rr.Type)
- }
- func decodeCharacterStrings(data []byte) ([][]byte, error) {
- strings := make([][]byte, 0, 1)
- end := len(data)
- for index, index2 := 0, 0; index != end; index = index2 {
- index2 = index + 1 + int(data[index]) // index increases by 1..256 and does not overflow
- if index2 > end {
- return nil, errors.New("Insufficient data for a <character-string>")
- }
- strings = append(strings, data[index+1:index2])
- }
- return strings, nil
- }
- func (rr *DNSResourceRecord) decodeRData(data []byte, offset int, buffer *[]byte) error {
- switch rr.Type {
- case DNSTypeA:
- rr.IP = rr.Data
- case DNSTypeAAAA:
- rr.IP = rr.Data
- case DNSTypeTXT, DNSTypeHINFO:
- rr.TXT = rr.Data
- txts, err := decodeCharacterStrings(rr.Data)
- if err != nil {
- return err
- }
- rr.TXTs = txts
- case DNSTypeNS:
- name, _, err := decodeName(data, offset, buffer, 1)
- if err != nil {
- return err
- }
- rr.NS = name
- case DNSTypeCNAME:
- name, _, err := decodeName(data, offset, buffer, 1)
- if err != nil {
- return err
- }
- rr.CNAME = name
- case DNSTypePTR:
- name, _, err := decodeName(data, offset, buffer, 1)
- if err != nil {
- return err
- }
- rr.PTR = name
- case DNSTypeSOA:
- name, endq, err := decodeName(data, offset, buffer, 1)
- if err != nil {
- return err
- }
- rr.SOA.MName = name
- name, endq, err = decodeName(data, endq, buffer, 1)
- if err != nil {
- return err
- }
- rr.SOA.RName = name
- rr.SOA.Serial = binary.BigEndian.Uint32(data[endq : endq+4])
- rr.SOA.Refresh = binary.BigEndian.Uint32(data[endq+4 : endq+8])
- rr.SOA.Retry = binary.BigEndian.Uint32(data[endq+8 : endq+12])
- rr.SOA.Expire = binary.BigEndian.Uint32(data[endq+12 : endq+16])
- rr.SOA.Minimum = binary.BigEndian.Uint32(data[endq+16 : endq+20])
- case DNSTypeMX:
- rr.MX.Preference = binary.BigEndian.Uint16(data[offset : offset+2])
- name, _, err := decodeName(data, offset+2, buffer, 1)
- if err != nil {
- return err
- }
- rr.MX.Name = name
- case DNSTypeSRV:
- rr.SRV.Priority = binary.BigEndian.Uint16(data[offset : offset+2])
- rr.SRV.Weight = binary.BigEndian.Uint16(data[offset+2 : offset+4])
- rr.SRV.Port = binary.BigEndian.Uint16(data[offset+4 : offset+6])
- name, _, err := decodeName(data, offset+6, buffer, 1)
- if err != nil {
- return err
- }
- rr.SRV.Name = name
- }
- return nil
- }
- // DNSSOA is a Start of Authority record. Each domain requires a SOA record at
- // the cutover where a domain is delegated from its parent.
- type DNSSOA struct {
- MName, RName []byte
- Serial, Refresh, Retry, Expire, Minimum uint32
- }
- // DNSSRV is a Service record, defining a location (hostname/port) of a
- // server/service.
- type DNSSRV struct {
- Priority, Weight, Port uint16
- Name []byte
- }
- // DNSMX is a mail exchange record, defining a mail server for a recipient's
- // domain.
- type DNSMX struct {
- Preference uint16
- Name []byte
- }
|