|  | // Copyright 2009 The Go Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style | 
|  | // license that can be found in the LICENSE file. | 
|  |  | 
|  | // DNS packet assembly. See RFC 1035. | 
|  | // | 
|  | // This is intended to support name resolution during Dial. | 
|  | // It doesn't have to be blazing fast. | 
|  | // | 
|  | // Each message structure has a Walk method that is used by | 
|  | // a generic pack/unpack routine. Thus, if in the future we need | 
|  | // to define new message structs, no new pack/unpack/printing code | 
|  | // needs to be written. | 
|  | // | 
|  | // The first half of this file defines the DNS message formats. | 
|  | // The second half implements the conversion to and from wire format. | 
|  | // A few of the structure elements have string tags to aid the | 
|  | // generic pack/unpack routines. | 
|  | // | 
|  | // TODO(rsc):  There are enough names defined in this file that they're all | 
|  | // prefixed with dns. Perhaps put this in its own package later. | 
|  |  | 
|  | package net | 
|  |  | 
|  | // Packet formats | 
|  |  | 
|  | // Wire constants. | 
|  | const ( | 
|  | // valid dnsRR_Header.Rrtype and dnsQuestion.qtype | 
|  | dnsTypeA     = 1 | 
|  | dnsTypeNS    = 2 | 
|  | dnsTypeMD    = 3 | 
|  | dnsTypeMF    = 4 | 
|  | dnsTypeCNAME = 5 | 
|  | dnsTypeSOA   = 6 | 
|  | dnsTypeMB    = 7 | 
|  | dnsTypeMG    = 8 | 
|  | dnsTypeMR    = 9 | 
|  | dnsTypeNULL  = 10 | 
|  | dnsTypeWKS   = 11 | 
|  | dnsTypePTR   = 12 | 
|  | dnsTypeHINFO = 13 | 
|  | dnsTypeMINFO = 14 | 
|  | dnsTypeMX    = 15 | 
|  | dnsTypeTXT   = 16 | 
|  | dnsTypeAAAA  = 28 | 
|  | dnsTypeSRV   = 33 | 
|  |  | 
|  | // valid dnsQuestion.qtype only | 
|  | dnsTypeAXFR  = 252 | 
|  | dnsTypeMAILB = 253 | 
|  | dnsTypeMAILA = 254 | 
|  | dnsTypeALL   = 255 | 
|  |  | 
|  | // valid dnsQuestion.qclass | 
|  | dnsClassINET   = 1 | 
|  | dnsClassCSNET  = 2 | 
|  | dnsClassCHAOS  = 3 | 
|  | dnsClassHESIOD = 4 | 
|  | dnsClassANY    = 255 | 
|  |  | 
|  | // dnsMsg.rcode | 
|  | dnsRcodeSuccess        = 0 | 
|  | dnsRcodeFormatError    = 1 | 
|  | dnsRcodeServerFailure  = 2 | 
|  | dnsRcodeNameError      = 3 | 
|  | dnsRcodeNotImplemented = 4 | 
|  | dnsRcodeRefused        = 5 | 
|  | ) | 
|  |  | 
|  | // A dnsStruct describes how to iterate over its fields to emulate | 
|  | // reflective marshaling. | 
|  | type dnsStruct interface { | 
|  | // Walk iterates over fields of a structure and calls f | 
|  | // with a reference to that field, the name of the field | 
|  | // and a tag ("", "domain", "ipv4", "ipv6") specifying | 
|  | // particular encodings. Possible concrete types | 
|  | // for v are *uint16, *uint32, *string, or []byte, and | 
|  | // *int, *bool in the case of dnsMsgHdr. | 
|  | // Whenever f returns false, Walk must stop and return | 
|  | // false, and otherwise return true. | 
|  | Walk(f func(v interface{}, name, tag string) (ok bool)) (ok bool) | 
|  | } | 
|  |  | 
|  | // The wire format for the DNS packet header. | 
|  | type dnsHeader struct { | 
|  | Id                                 uint16 | 
|  | Bits                               uint16 | 
|  | Qdcount, Ancount, Nscount, Arcount uint16 | 
|  | } | 
|  |  | 
|  | func (h *dnsHeader) Walk(f func(v interface{}, name, tag string) bool) bool { | 
|  | return f(&h.Id, "Id", "") && | 
|  | f(&h.Bits, "Bits", "") && | 
|  | f(&h.Qdcount, "Qdcount", "") && | 
|  | f(&h.Ancount, "Ancount", "") && | 
|  | f(&h.Nscount, "Nscount", "") && | 
|  | f(&h.Arcount, "Arcount", "") | 
|  | } | 
|  |  | 
|  | const ( | 
|  | // dnsHeader.Bits | 
|  | _QR = 1 << 15 // query/response (response=1) | 
|  | _AA = 1 << 10 // authoritative | 
|  | _TC = 1 << 9  // truncated | 
|  | _RD = 1 << 8  // recursion desired | 
|  | _RA = 1 << 7  // recursion available | 
|  | ) | 
|  |  | 
|  | // DNS queries. | 
|  | type dnsQuestion struct { | 
|  | Name   string | 
|  | Qtype  uint16 | 
|  | Qclass uint16 | 
|  | } | 
|  |  | 
|  | func (q *dnsQuestion) Walk(f func(v interface{}, name, tag string) bool) bool { | 
|  | return f(&q.Name, "Name", "domain") && | 
|  | f(&q.Qtype, "Qtype", "") && | 
|  | f(&q.Qclass, "Qclass", "") | 
|  | } | 
|  |  | 
|  | // DNS responses (resource records). | 
|  | // There are many types of messages, | 
|  | // but they all share the same header. | 
|  | type dnsRR_Header struct { | 
|  | Name     string | 
|  | Rrtype   uint16 | 
|  | Class    uint16 | 
|  | Ttl      uint32 | 
|  | Rdlength uint16 // length of data after header | 
|  | } | 
|  |  | 
|  | func (h *dnsRR_Header) Header() *dnsRR_Header { | 
|  | return h | 
|  | } | 
|  |  | 
|  | func (h *dnsRR_Header) Walk(f func(v interface{}, name, tag string) bool) bool { | 
|  | return f(&h.Name, "Name", "domain") && | 
|  | f(&h.Rrtype, "Rrtype", "") && | 
|  | f(&h.Class, "Class", "") && | 
|  | f(&h.Ttl, "Ttl", "") && | 
|  | f(&h.Rdlength, "Rdlength", "") | 
|  | } | 
|  |  | 
|  | type dnsRR interface { | 
|  | dnsStruct | 
|  | Header() *dnsRR_Header | 
|  | } | 
|  |  | 
|  | // Specific DNS RR formats for each query type. | 
|  |  | 
|  | type dnsRR_CNAME struct { | 
|  | Hdr   dnsRR_Header | 
|  | Cname string | 
|  | } | 
|  |  | 
|  | func (rr *dnsRR_CNAME) Header() *dnsRR_Header { | 
|  | return &rr.Hdr | 
|  | } | 
|  |  | 
|  | func (rr *dnsRR_CNAME) Walk(f func(v interface{}, name, tag string) bool) bool { | 
|  | return rr.Hdr.Walk(f) && f(&rr.Cname, "Cname", "domain") | 
|  | } | 
|  |  | 
|  | type dnsRR_MX struct { | 
|  | Hdr  dnsRR_Header | 
|  | Pref uint16 | 
|  | Mx   string | 
|  | } | 
|  |  | 
|  | func (rr *dnsRR_MX) Header() *dnsRR_Header { | 
|  | return &rr.Hdr | 
|  | } | 
|  |  | 
|  | func (rr *dnsRR_MX) Walk(f func(v interface{}, name, tag string) bool) bool { | 
|  | return rr.Hdr.Walk(f) && f(&rr.Pref, "Pref", "") && f(&rr.Mx, "Mx", "domain") | 
|  | } | 
|  |  | 
|  | type dnsRR_NS struct { | 
|  | Hdr dnsRR_Header | 
|  | Ns  string | 
|  | } | 
|  |  | 
|  | func (rr *dnsRR_NS) Header() *dnsRR_Header { | 
|  | return &rr.Hdr | 
|  | } | 
|  |  | 
|  | func (rr *dnsRR_NS) Walk(f func(v interface{}, name, tag string) bool) bool { | 
|  | return rr.Hdr.Walk(f) && f(&rr.Ns, "Ns", "domain") | 
|  | } | 
|  |  | 
|  | type dnsRR_PTR struct { | 
|  | Hdr dnsRR_Header | 
|  | Ptr string | 
|  | } | 
|  |  | 
|  | func (rr *dnsRR_PTR) Header() *dnsRR_Header { | 
|  | return &rr.Hdr | 
|  | } | 
|  |  | 
|  | func (rr *dnsRR_PTR) Walk(f func(v interface{}, name, tag string) bool) bool { | 
|  | return rr.Hdr.Walk(f) && f(&rr.Ptr, "Ptr", "domain") | 
|  | } | 
|  |  | 
|  | type dnsRR_SOA struct { | 
|  | Hdr     dnsRR_Header | 
|  | Ns      string | 
|  | Mbox    string | 
|  | Serial  uint32 | 
|  | Refresh uint32 | 
|  | Retry   uint32 | 
|  | Expire  uint32 | 
|  | Minttl  uint32 | 
|  | } | 
|  |  | 
|  | func (rr *dnsRR_SOA) Header() *dnsRR_Header { | 
|  | return &rr.Hdr | 
|  | } | 
|  |  | 
|  | func (rr *dnsRR_SOA) Walk(f func(v interface{}, name, tag string) bool) bool { | 
|  | return rr.Hdr.Walk(f) && | 
|  | f(&rr.Ns, "Ns", "domain") && | 
|  | f(&rr.Mbox, "Mbox", "domain") && | 
|  | f(&rr.Serial, "Serial", "") && | 
|  | f(&rr.Refresh, "Refresh", "") && | 
|  | f(&rr.Retry, "Retry", "") && | 
|  | f(&rr.Expire, "Expire", "") && | 
|  | f(&rr.Minttl, "Minttl", "") | 
|  | } | 
|  |  | 
|  | type dnsRR_TXT struct { | 
|  | Hdr dnsRR_Header | 
|  | Txt string // not domain name | 
|  | } | 
|  |  | 
|  | func (rr *dnsRR_TXT) Header() *dnsRR_Header { | 
|  | return &rr.Hdr | 
|  | } | 
|  |  | 
|  | func (rr *dnsRR_TXT) Walk(f func(v interface{}, name, tag string) bool) bool { | 
|  | if !rr.Hdr.Walk(f) { | 
|  | return false | 
|  | } | 
|  | var n uint16 = 0 | 
|  | for n < rr.Hdr.Rdlength { | 
|  | var txt string | 
|  | if !f(&txt, "Txt", "") { | 
|  | return false | 
|  | } | 
|  | // more bytes than rr.Hdr.Rdlength said there would be | 
|  | if rr.Hdr.Rdlength-n < uint16(len(txt))+1 { | 
|  | return false | 
|  | } | 
|  | n += uint16(len(txt)) + 1 | 
|  | rr.Txt += txt | 
|  | } | 
|  | return true | 
|  | } | 
|  |  | 
|  | type dnsRR_SRV struct { | 
|  | Hdr      dnsRR_Header | 
|  | Priority uint16 | 
|  | Weight   uint16 | 
|  | Port     uint16 | 
|  | Target   string | 
|  | } | 
|  |  | 
|  | func (rr *dnsRR_SRV) Header() *dnsRR_Header { | 
|  | return &rr.Hdr | 
|  | } | 
|  |  | 
|  | func (rr *dnsRR_SRV) Walk(f func(v interface{}, name, tag string) bool) bool { | 
|  | return rr.Hdr.Walk(f) && | 
|  | f(&rr.Priority, "Priority", "") && | 
|  | f(&rr.Weight, "Weight", "") && | 
|  | f(&rr.Port, "Port", "") && | 
|  | f(&rr.Target, "Target", "domain") | 
|  | } | 
|  |  | 
|  | type dnsRR_A struct { | 
|  | Hdr dnsRR_Header | 
|  | A   uint32 | 
|  | } | 
|  |  | 
|  | func (rr *dnsRR_A) Header() *dnsRR_Header { | 
|  | return &rr.Hdr | 
|  | } | 
|  |  | 
|  | func (rr *dnsRR_A) Walk(f func(v interface{}, name, tag string) bool) bool { | 
|  | return rr.Hdr.Walk(f) && f(&rr.A, "A", "ipv4") | 
|  | } | 
|  |  | 
|  | type dnsRR_AAAA struct { | 
|  | Hdr  dnsRR_Header | 
|  | AAAA [16]byte | 
|  | } | 
|  |  | 
|  | func (rr *dnsRR_AAAA) Header() *dnsRR_Header { | 
|  | return &rr.Hdr | 
|  | } | 
|  |  | 
|  | func (rr *dnsRR_AAAA) Walk(f func(v interface{}, name, tag string) bool) bool { | 
|  | return rr.Hdr.Walk(f) && f(rr.AAAA[:], "AAAA", "ipv6") | 
|  | } | 
|  |  | 
|  | // Packing and unpacking. | 
|  | // | 
|  | // All the packers and unpackers take a (msg []byte, off int) | 
|  | // and return (off1 int, ok bool).  If they return ok==false, they | 
|  | // also return off1==len(msg), so that the next unpacker will | 
|  | // also fail. This lets us avoid checks of ok until the end of a | 
|  | // packing sequence. | 
|  |  | 
|  | // Map of constructors for each RR wire type. | 
|  | var rr_mk = map[int]func() dnsRR{ | 
|  | dnsTypeCNAME: func() dnsRR { return new(dnsRR_CNAME) }, | 
|  | dnsTypeMX:    func() dnsRR { return new(dnsRR_MX) }, | 
|  | dnsTypeNS:    func() dnsRR { return new(dnsRR_NS) }, | 
|  | dnsTypePTR:   func() dnsRR { return new(dnsRR_PTR) }, | 
|  | dnsTypeSOA:   func() dnsRR { return new(dnsRR_SOA) }, | 
|  | dnsTypeTXT:   func() dnsRR { return new(dnsRR_TXT) }, | 
|  | dnsTypeSRV:   func() dnsRR { return new(dnsRR_SRV) }, | 
|  | dnsTypeA:     func() dnsRR { return new(dnsRR_A) }, | 
|  | dnsTypeAAAA:  func() dnsRR { return new(dnsRR_AAAA) }, | 
|  | } | 
|  |  | 
|  | // Pack a domain name s into msg[off:]. | 
|  | // Domain names are a sequence of counted strings | 
|  | // split at the dots. They end with a zero-length string. | 
|  | func packDomainName(s string, msg []byte, off int) (off1 int, ok bool) { | 
|  | // Add trailing dot to canonicalize name. | 
|  | if n := len(s); n == 0 || s[n-1] != '.' { | 
|  | s += "." | 
|  | } | 
|  |  | 
|  | // Allow root domain. | 
|  | if s == "." { | 
|  | msg[off] = 0 | 
|  | off++ | 
|  | return off, true | 
|  | } | 
|  |  | 
|  | // Each dot ends a segment of the name. | 
|  | // We trade each dot byte for a length byte. | 
|  | // There is also a trailing zero. | 
|  | // Check that we have all the space we need. | 
|  | tot := len(s) + 1 | 
|  | if off+tot > len(msg) { | 
|  | return len(msg), false | 
|  | } | 
|  |  | 
|  | // Emit sequence of counted strings, chopping at dots. | 
|  | begin := 0 | 
|  | for i := 0; i < len(s); i++ { | 
|  | if s[i] == '.' { | 
|  | if i-begin >= 1<<6 { // top two bits of length must be clear | 
|  | return len(msg), false | 
|  | } | 
|  | if i-begin == 0 { | 
|  | return len(msg), false | 
|  | } | 
|  |  | 
|  | msg[off] = byte(i - begin) | 
|  | off++ | 
|  |  | 
|  | for j := begin; j < i; j++ { | 
|  | msg[off] = s[j] | 
|  | off++ | 
|  | } | 
|  | begin = i + 1 | 
|  | } | 
|  | } | 
|  | msg[off] = 0 | 
|  | off++ | 
|  | return off, true | 
|  | } | 
|  |  | 
|  | // Unpack a domain name. | 
|  | // In addition to the simple sequences of counted strings above, | 
|  | // domain names are allowed to refer to strings elsewhere in the | 
|  | // packet, to avoid repeating common suffixes when returning | 
|  | // many entries in a single domain. The pointers are marked | 
|  | // by a length byte with the top two bits set. Ignoring those | 
|  | // two bits, that byte and the next give a 14 bit offset from msg[0] | 
|  | // where we should pick up the trail. | 
|  | // Note that if we jump elsewhere in the packet, | 
|  | // we return off1 == the offset after the first pointer we found, | 
|  | // which is where the next record will start. | 
|  | // In theory, the pointers are only allowed to jump backward. | 
|  | // We let them jump anywhere and stop jumping after a while. | 
|  | func unpackDomainName(msg []byte, off int) (s string, off1 int, ok bool) { | 
|  | s = "" | 
|  | ptr := 0 // number of pointers followed | 
|  | Loop: | 
|  | for { | 
|  | if off >= len(msg) { | 
|  | return "", len(msg), false | 
|  | } | 
|  | c := int(msg[off]) | 
|  | off++ | 
|  | switch c & 0xC0 { | 
|  | case 0x00: | 
|  | if c == 0x00 { | 
|  | // end of name | 
|  | break Loop | 
|  | } | 
|  | // literal string | 
|  | if off+c > len(msg) { | 
|  | return "", len(msg), false | 
|  | } | 
|  | s += string(msg[off:off+c]) + "." | 
|  | off += c | 
|  | case 0xC0: | 
|  | // pointer to somewhere else in msg. | 
|  | // remember location after first ptr, | 
|  | // since that's how many bytes we consumed. | 
|  | // also, don't follow too many pointers -- | 
|  | // maybe there's a loop. | 
|  | if off >= len(msg) { | 
|  | return "", len(msg), false | 
|  | } | 
|  | c1 := msg[off] | 
|  | off++ | 
|  | if ptr == 0 { | 
|  | off1 = off | 
|  | } | 
|  | if ptr++; ptr > 10 { | 
|  | return "", len(msg), false | 
|  | } | 
|  | off = (c^0xC0)<<8 | int(c1) | 
|  | default: | 
|  | // 0x80 and 0x40 are reserved | 
|  | return "", len(msg), false | 
|  | } | 
|  | } | 
|  | if len(s) == 0 { | 
|  | s = "." | 
|  | } | 
|  | if ptr == 0 { | 
|  | off1 = off | 
|  | } | 
|  | return s, off1, true | 
|  | } | 
|  |  | 
|  | // packStruct packs a structure into msg at specified offset off, and | 
|  | // returns off1 such that msg[off:off1] is the encoded data. | 
|  | func packStruct(any dnsStruct, msg []byte, off int) (off1 int, ok bool) { | 
|  | ok = any.Walk(func(field interface{}, name, tag string) bool { | 
|  | switch fv := field.(type) { | 
|  | default: | 
|  | println("net: dns: unknown packing type") | 
|  | return false | 
|  | case *uint16: | 
|  | i := *fv | 
|  | if off+2 > len(msg) { | 
|  | return false | 
|  | } | 
|  | msg[off] = byte(i >> 8) | 
|  | msg[off+1] = byte(i) | 
|  | off += 2 | 
|  | case *uint32: | 
|  | i := *fv | 
|  | msg[off] = byte(i >> 24) | 
|  | msg[off+1] = byte(i >> 16) | 
|  | msg[off+2] = byte(i >> 8) | 
|  | msg[off+3] = byte(i) | 
|  | off += 4 | 
|  | case []byte: | 
|  | n := len(fv) | 
|  | if off+n > len(msg) { | 
|  | return false | 
|  | } | 
|  | copy(msg[off:off+n], fv) | 
|  | off += n | 
|  | case *string: | 
|  | s := *fv | 
|  | switch tag { | 
|  | default: | 
|  | println("net: dns: unknown string tag", tag) | 
|  | return false | 
|  | case "domain": | 
|  | off, ok = packDomainName(s, msg, off) | 
|  | if !ok { | 
|  | return false | 
|  | } | 
|  | case "": | 
|  | // Counted string: 1 byte length. | 
|  | if len(s) > 255 || off+1+len(s) > len(msg) { | 
|  | return false | 
|  | } | 
|  | msg[off] = byte(len(s)) | 
|  | off++ | 
|  | off += copy(msg[off:], s) | 
|  | } | 
|  | } | 
|  | return true | 
|  | }) | 
|  | if !ok { | 
|  | return len(msg), false | 
|  | } | 
|  | return off, true | 
|  | } | 
|  |  | 
|  | // unpackStruct decodes msg[off:] into the given structure, and | 
|  | // returns off1 such that msg[off:off1] is the encoded data. | 
|  | func unpackStruct(any dnsStruct, msg []byte, off int) (off1 int, ok bool) { | 
|  | ok = any.Walk(func(field interface{}, name, tag string) bool { | 
|  | switch fv := field.(type) { | 
|  | default: | 
|  | println("net: dns: unknown packing type") | 
|  | return false | 
|  | case *uint16: | 
|  | if off+2 > len(msg) { | 
|  | return false | 
|  | } | 
|  | *fv = uint16(msg[off])<<8 | uint16(msg[off+1]) | 
|  | off += 2 | 
|  | case *uint32: | 
|  | if off+4 > len(msg) { | 
|  | return false | 
|  | } | 
|  | *fv = uint32(msg[off])<<24 | uint32(msg[off+1])<<16 | | 
|  | uint32(msg[off+2])<<8 | uint32(msg[off+3]) | 
|  | off += 4 | 
|  | case []byte: | 
|  | n := len(fv) | 
|  | if off+n > len(msg) { | 
|  | return false | 
|  | } | 
|  | copy(fv, msg[off:off+n]) | 
|  | off += n | 
|  | case *string: | 
|  | var s string | 
|  | switch tag { | 
|  | default: | 
|  | println("net: dns: unknown string tag", tag) | 
|  | return false | 
|  | case "domain": | 
|  | s, off, ok = unpackDomainName(msg, off) | 
|  | if !ok { | 
|  | return false | 
|  | } | 
|  | case "": | 
|  | if off >= len(msg) || off+1+int(msg[off]) > len(msg) { | 
|  | return false | 
|  | } | 
|  | n := int(msg[off]) | 
|  | off++ | 
|  | b := make([]byte, n) | 
|  | for i := 0; i < n; i++ { | 
|  | b[i] = msg[off+i] | 
|  | } | 
|  | off += n | 
|  | s = string(b) | 
|  | } | 
|  | *fv = s | 
|  | } | 
|  | return true | 
|  | }) | 
|  | if !ok { | 
|  | return len(msg), false | 
|  | } | 
|  | return off, true | 
|  | } | 
|  |  | 
|  | // Generic struct printer. Prints fields with tag "ipv4" or "ipv6" | 
|  | // as IP addresses. | 
|  | func printStruct(any dnsStruct) string { | 
|  | s := "{" | 
|  | i := 0 | 
|  | any.Walk(func(val interface{}, name, tag string) bool { | 
|  | i++ | 
|  | if i > 1 { | 
|  | s += ", " | 
|  | } | 
|  | s += name + "=" | 
|  | switch tag { | 
|  | case "ipv4": | 
|  | i := *val.(*uint32) | 
|  | s += IPv4(byte(i>>24), byte(i>>16), byte(i>>8), byte(i)).String() | 
|  | case "ipv6": | 
|  | i := val.([]byte) | 
|  | s += IP(i).String() | 
|  | default: | 
|  | var i int64 | 
|  | switch v := val.(type) { | 
|  | default: | 
|  | // can't really happen. | 
|  | s += "<unknown type>" | 
|  | return true | 
|  | case *string: | 
|  | s += *v | 
|  | return true | 
|  | case []byte: | 
|  | s += string(v) | 
|  | return true | 
|  | case *bool: | 
|  | if *v { | 
|  | s += "true" | 
|  | } else { | 
|  | s += "false" | 
|  | } | 
|  | return true | 
|  | case *int: | 
|  | i = int64(*v) | 
|  | case *uint: | 
|  | i = int64(*v) | 
|  | case *uint8: | 
|  | i = int64(*v) | 
|  | case *uint16: | 
|  | i = int64(*v) | 
|  | case *uint32: | 
|  | i = int64(*v) | 
|  | case *uint64: | 
|  | i = int64(*v) | 
|  | case *uintptr: | 
|  | i = int64(*v) | 
|  | } | 
|  | s += itoa(int(i)) | 
|  | } | 
|  | return true | 
|  | }) | 
|  | s += "}" | 
|  | return s | 
|  | } | 
|  |  | 
|  | // Resource record packer. | 
|  | func packRR(rr dnsRR, msg []byte, off int) (off2 int, ok bool) { | 
|  | var off1 int | 
|  | // pack twice, once to find end of header | 
|  | // and again to find end of packet. | 
|  | // a bit inefficient but this doesn't need to be fast. | 
|  | // off1 is end of header | 
|  | // off2 is end of rr | 
|  | off1, ok = packStruct(rr.Header(), msg, off) | 
|  | if !ok { | 
|  | return len(msg), false | 
|  | } | 
|  | off2, ok = packStruct(rr, msg, off) | 
|  | if !ok { | 
|  | return len(msg), false | 
|  | } | 
|  | // pack a third time; redo header with correct data length | 
|  | rr.Header().Rdlength = uint16(off2 - off1) | 
|  | packStruct(rr.Header(), msg, off) | 
|  | return off2, true | 
|  | } | 
|  |  | 
|  | // Resource record unpacker. | 
|  | func unpackRR(msg []byte, off int) (rr dnsRR, off1 int, ok bool) { | 
|  | // unpack just the header, to find the rr type and length | 
|  | var h dnsRR_Header | 
|  | off0 := off | 
|  | if off, ok = unpackStruct(&h, msg, off); !ok { | 
|  | return nil, len(msg), false | 
|  | } | 
|  | end := off + int(h.Rdlength) | 
|  |  | 
|  | // make an rr of that type and re-unpack. | 
|  | // again inefficient but doesn't need to be fast. | 
|  | mk, known := rr_mk[int(h.Rrtype)] | 
|  | if !known { | 
|  | return &h, end, true | 
|  | } | 
|  | rr = mk() | 
|  | off, ok = unpackStruct(rr, msg, off0) | 
|  | if off != end { | 
|  | return &h, end, true | 
|  | } | 
|  | return rr, off, ok | 
|  | } | 
|  |  | 
|  | // Usable representation of a DNS packet. | 
|  |  | 
|  | // A manually-unpacked version of (id, bits). | 
|  | // This is in its own struct for easy printing. | 
|  | type dnsMsgHdr struct { | 
|  | id                  uint16 | 
|  | response            bool | 
|  | opcode              int | 
|  | authoritative       bool | 
|  | truncated           bool | 
|  | recursion_desired   bool | 
|  | recursion_available bool | 
|  | rcode               int | 
|  | } | 
|  |  | 
|  | func (h *dnsMsgHdr) Walk(f func(v interface{}, name, tag string) bool) bool { | 
|  | return f(&h.id, "id", "") && | 
|  | f(&h.response, "response", "") && | 
|  | f(&h.opcode, "opcode", "") && | 
|  | f(&h.authoritative, "authoritative", "") && | 
|  | f(&h.truncated, "truncated", "") && | 
|  | f(&h.recursion_desired, "recursion_desired", "") && | 
|  | f(&h.recursion_available, "recursion_available", "") && | 
|  | f(&h.rcode, "rcode", "") | 
|  | } | 
|  |  | 
|  | type dnsMsg struct { | 
|  | dnsMsgHdr | 
|  | question []dnsQuestion | 
|  | answer   []dnsRR | 
|  | ns       []dnsRR | 
|  | extra    []dnsRR | 
|  | } | 
|  |  | 
|  | func (dns *dnsMsg) Pack() (msg []byte, ok bool) { | 
|  | var dh dnsHeader | 
|  |  | 
|  | // Convert convenient dnsMsg into wire-like dnsHeader. | 
|  | dh.Id = dns.id | 
|  | dh.Bits = uint16(dns.opcode)<<11 | uint16(dns.rcode) | 
|  | if dns.recursion_available { | 
|  | dh.Bits |= _RA | 
|  | } | 
|  | if dns.recursion_desired { | 
|  | dh.Bits |= _RD | 
|  | } | 
|  | if dns.truncated { | 
|  | dh.Bits |= _TC | 
|  | } | 
|  | if dns.authoritative { | 
|  | dh.Bits |= _AA | 
|  | } | 
|  | if dns.response { | 
|  | dh.Bits |= _QR | 
|  | } | 
|  |  | 
|  | // Prepare variable sized arrays. | 
|  | question := dns.question | 
|  | answer := dns.answer | 
|  | ns := dns.ns | 
|  | extra := dns.extra | 
|  |  | 
|  | dh.Qdcount = uint16(len(question)) | 
|  | dh.Ancount = uint16(len(answer)) | 
|  | dh.Nscount = uint16(len(ns)) | 
|  | dh.Arcount = uint16(len(extra)) | 
|  |  | 
|  | // Could work harder to calculate message size, | 
|  | // but this is far more than we need and not | 
|  | // big enough to hurt the allocator. | 
|  | msg = make([]byte, 2000) | 
|  |  | 
|  | // Pack it in: header and then the pieces. | 
|  | off := 0 | 
|  | off, ok = packStruct(&dh, msg, off) | 
|  | if !ok { | 
|  | return nil, false | 
|  | } | 
|  | for i := 0; i < len(question); i++ { | 
|  | off, ok = packStruct(&question[i], msg, off) | 
|  | if !ok { | 
|  | return nil, false | 
|  | } | 
|  | } | 
|  | for i := 0; i < len(answer); i++ { | 
|  | off, ok = packRR(answer[i], msg, off) | 
|  | if !ok { | 
|  | return nil, false | 
|  | } | 
|  | } | 
|  | for i := 0; i < len(ns); i++ { | 
|  | off, ok = packRR(ns[i], msg, off) | 
|  | if !ok { | 
|  | return nil, false | 
|  | } | 
|  | } | 
|  | for i := 0; i < len(extra); i++ { | 
|  | off, ok = packRR(extra[i], msg, off) | 
|  | if !ok { | 
|  | return nil, false | 
|  | } | 
|  | } | 
|  | return msg[0:off], true | 
|  | } | 
|  |  | 
|  | func (dns *dnsMsg) Unpack(msg []byte) bool { | 
|  | // Header. | 
|  | var dh dnsHeader | 
|  | off := 0 | 
|  | var ok bool | 
|  | if off, ok = unpackStruct(&dh, msg, off); !ok { | 
|  | return false | 
|  | } | 
|  | dns.id = dh.Id | 
|  | dns.response = (dh.Bits & _QR) != 0 | 
|  | dns.opcode = int(dh.Bits>>11) & 0xF | 
|  | dns.authoritative = (dh.Bits & _AA) != 0 | 
|  | dns.truncated = (dh.Bits & _TC) != 0 | 
|  | dns.recursion_desired = (dh.Bits & _RD) != 0 | 
|  | dns.recursion_available = (dh.Bits & _RA) != 0 | 
|  | dns.rcode = int(dh.Bits & 0xF) | 
|  |  | 
|  | // Arrays. | 
|  | dns.question = make([]dnsQuestion, dh.Qdcount) | 
|  | dns.answer = make([]dnsRR, 0, dh.Ancount) | 
|  | dns.ns = make([]dnsRR, 0, dh.Nscount) | 
|  | dns.extra = make([]dnsRR, 0, dh.Arcount) | 
|  |  | 
|  | var rec dnsRR | 
|  |  | 
|  | for i := 0; i < len(dns.question); i++ { | 
|  | off, ok = unpackStruct(&dns.question[i], msg, off) | 
|  | if !ok { | 
|  | return false | 
|  | } | 
|  | } | 
|  | for i := 0; i < int(dh.Ancount); i++ { | 
|  | rec, off, ok = unpackRR(msg, off) | 
|  | if !ok { | 
|  | return false | 
|  | } | 
|  | dns.answer = append(dns.answer, rec) | 
|  | } | 
|  | for i := 0; i < int(dh.Nscount); i++ { | 
|  | rec, off, ok = unpackRR(msg, off) | 
|  | if !ok { | 
|  | return false | 
|  | } | 
|  | dns.ns = append(dns.ns, rec) | 
|  | } | 
|  | for i := 0; i < int(dh.Arcount); i++ { | 
|  | rec, off, ok = unpackRR(msg, off) | 
|  | if !ok { | 
|  | return false | 
|  | } | 
|  | dns.extra = append(dns.extra, rec) | 
|  | } | 
|  | //	if off != len(msg) { | 
|  | //		println("extra bytes in dns packet", off, "<", len(msg)); | 
|  | //	} | 
|  | return true | 
|  | } | 
|  |  | 
|  | func (dns *dnsMsg) String() string { | 
|  | s := "DNS: " + printStruct(&dns.dnsMsgHdr) + "\n" | 
|  | if len(dns.question) > 0 { | 
|  | s += "-- Questions\n" | 
|  | for i := 0; i < len(dns.question); i++ { | 
|  | s += printStruct(&dns.question[i]) + "\n" | 
|  | } | 
|  | } | 
|  | if len(dns.answer) > 0 { | 
|  | s += "-- Answers\n" | 
|  | for i := 0; i < len(dns.answer); i++ { | 
|  | s += printStruct(dns.answer[i]) + "\n" | 
|  | } | 
|  | } | 
|  | if len(dns.ns) > 0 { | 
|  | s += "-- Name servers\n" | 
|  | for i := 0; i < len(dns.ns); i++ { | 
|  | s += printStruct(dns.ns[i]) + "\n" | 
|  | } | 
|  | } | 
|  | if len(dns.extra) > 0 { | 
|  | s += "-- Extra\n" | 
|  | for i := 0; i < len(dns.extra); i++ { | 
|  | s += printStruct(dns.extra[i]) + "\n" | 
|  | } | 
|  | } | 
|  | return s | 
|  | } | 
|  |  | 
|  | // IsResponseTo reports whether m is an acceptable response to query. | 
|  | func (m *dnsMsg) IsResponseTo(query *dnsMsg) bool { | 
|  | if !m.response { | 
|  | return false | 
|  | } | 
|  | if m.id != query.id { | 
|  | return false | 
|  | } | 
|  | if len(m.question) != len(query.question) { | 
|  | return false | 
|  | } | 
|  | for i, q := range m.question { | 
|  | q2 := query.question[i] | 
|  | if !equalASCIILabel(q.Name, q2.Name) || q.Qtype != q2.Qtype || q.Qclass != q2.Qclass { | 
|  | return false | 
|  | } | 
|  | } | 
|  | return true | 
|  | } |