diff options
Diffstat (limited to 'vendor/github.com/google/gopacket/layers/dns.go')
-rw-r--r-- | vendor/github.com/google/gopacket/layers/dns.go | 894 |
1 files changed, 0 insertions, 894 deletions
diff --git a/vendor/github.com/google/gopacket/layers/dns.go b/vendor/github.com/google/gopacket/layers/dns.go deleted file mode 100644 index 2368a28..0000000 --- a/vendor/github.com/google/gopacket/layers/dns.go +++ /dev/null @@ -1,894 +0,0 @@ -// 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 // Resrved 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) + 14 - 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 -} |