diff options
Diffstat (limited to 'src/net/dnsmsg.go')
-rw-r--r-- | src/net/dnsmsg.go | 887 |
1 files changed, 887 insertions, 0 deletions
diff --git a/src/net/dnsmsg.go b/src/net/dnsmsg.go new file mode 100644 index 000000000..161afb2a5 --- /dev/null +++ b/src/net/dnsmsg.go @@ -0,0 +1,887 @@ +// 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 marshalling. +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 `net:"domain-name"` // `net:"domain-name"` specifies encoding; see packers below + 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 `net:"domain-name"` + 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 `net:"domain-name"` +} + +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_HINFO struct { + Hdr dnsRR_Header + Cpu string + Os string +} + +func (rr *dnsRR_HINFO) Header() *dnsRR_Header { + return &rr.Hdr +} + +func (rr *dnsRR_HINFO) Walk(f func(v interface{}, name, tag string) bool) bool { + return rr.Hdr.Walk(f) && f(&rr.Cpu, "Cpu", "") && f(&rr.Os, "Os", "") +} + +type dnsRR_MB struct { + Hdr dnsRR_Header + Mb string `net:"domain-name"` +} + +func (rr *dnsRR_MB) Header() *dnsRR_Header { + return &rr.Hdr +} + +func (rr *dnsRR_MB) Walk(f func(v interface{}, name, tag string) bool) bool { + return rr.Hdr.Walk(f) && f(&rr.Mb, "Mb", "domain") +} + +type dnsRR_MG struct { + Hdr dnsRR_Header + Mg string `net:"domain-name"` +} + +func (rr *dnsRR_MG) Header() *dnsRR_Header { + return &rr.Hdr +} + +func (rr *dnsRR_MG) Walk(f func(v interface{}, name, tag string) bool) bool { + return rr.Hdr.Walk(f) && f(&rr.Mg, "Mg", "domain") +} + +type dnsRR_MINFO struct { + Hdr dnsRR_Header + Rmail string `net:"domain-name"` + Email string `net:"domain-name"` +} + +func (rr *dnsRR_MINFO) Header() *dnsRR_Header { + return &rr.Hdr +} + +func (rr *dnsRR_MINFO) Walk(f func(v interface{}, name, tag string) bool) bool { + return rr.Hdr.Walk(f) && f(&rr.Rmail, "Rmail", "domain") && f(&rr.Email, "Email", "domain") +} + +type dnsRR_MR struct { + Hdr dnsRR_Header + Mr string `net:"domain-name"` +} + +func (rr *dnsRR_MR) Header() *dnsRR_Header { + return &rr.Hdr +} + +func (rr *dnsRR_MR) Walk(f func(v interface{}, name, tag string) bool) bool { + return rr.Hdr.Walk(f) && f(&rr.Mr, "Mr", "domain") +} + +type dnsRR_MX struct { + Hdr dnsRR_Header + Pref uint16 + Mx string `net:"domain-name"` +} + +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 `net:"domain-name"` +} + +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 `net:"domain-name"` +} + +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 `net:"domain-name"` + Mbox string `net:"domain-name"` + 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 { + return rr.Hdr.Walk(f) && f(&rr.Txt, "Txt", "") +} + +type dnsRR_SRV struct { + Hdr dnsRR_Header + Priority uint16 + Weight uint16 + Port uint16 + Target string `net:"domain-name"` +} + +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 `net:"ipv4"` +} + +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 `net:"ipv6"` +} + +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) }, + dnsTypeHINFO: func() dnsRR { return new(dnsRR_HINFO) }, + dnsTypeMB: func() dnsRR { return new(dnsRR_MB) }, + dnsTypeMG: func() dnsRR { return new(dnsRR_MG) }, + dnsTypeMINFO: func() dnsRR { return new(dnsRR_MINFO) }, + dnsTypeMR: func() dnsRR { return new(dnsRR_MR) }, + 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 += "." + } + + // 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 + } + 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 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) + 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) + for i := 0; i < len(question); i++ { + off, ok = packStruct(&question[i], msg, off) + } + for i := 0; i < len(answer); i++ { + off, ok = packRR(answer[i], msg, off) + } + for i := 0; i < len(ns); i++ { + off, ok = packRR(ns[i], msg, off) + } + 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) + } + 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 +} |