[net] x/net/dns: add support for parsing and packing of DNS packets

277 views
Skip to first unread message

Ian Gudger (Gerrit)

unread,
Jan 15, 2017, 7:57:14 PM1/15/17
to Ian Lance Taylor, Matthew Dempsky, Brad Fitzpatrick, golang-co...@googlegroups.com

Ian Gudger has uploaded this change for review.

View Change

x/net/dns: add support for parsing and packing of DNS packets

The Go standard library contains support for parsing and packing DNS packets,
but it is not exported, doesn't follow Go style and is not very well optimized.
Low level DNS functionality is clearly useful to the Go community as evidenced
by the success of github.com/miekg/dns. This implementation endeavors to avoid
the limitations of both the standard library and github.com/miekg/dns
implementations and is an almost complete rewrite of the code currently found
in on net/dnsmsg.go and net/dnsmsg_test.go.

Goals:
* Minimize heap allocations.
* Allow parsing only what is needed. Avoid unnecessary parsing and
  heap allocations for parts of the message that you don't care about. Parsing
  should be allowed on as small of a granularity as is useful, but no smaller
  as to avoid complicating the interface.
* Parse and pack each byte of the message at most one time.
* Support parsing and packing as many valid DNS packets as possible.

Updates golang/go#16218

Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
---
A dns/dns.go
A dns/message.go
A dns/message_test.go
3 files changed, 1,645 insertions(+), 0 deletions(-)

diff --git a/dns/dns.go b/dns/dns.go
new file mode 100644
index 0000000..4d96974
--- /dev/null
+++ b/dns/dns.go
@@ -0,0 +1,6 @@
+// Copyright 2017 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.
+//
+// Package dns is an experimental DNS library. See RFC 1035.
+package dns
diff --git a/dns/message.go b/dns/message.go
new file mode 100644
index 0000000..854f727
--- /dev/null
+++ b/dns/message.go
@@ -0,0 +1,1306 @@
+// 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.
+package dns
+
+import (
+	"errors"
+	"fmt"
+)
+
+// Packet formats
+
+// Wire constants.
+const (
+	// ResponseHeader.Type and Question.Type
+	TypeA     = 1
+	TypeNS    = 2
+	TypeCNAME = 5
+	TypeSOA   = 6
+	TypeNULL  = 10
+	TypeTXT   = 16
+	TypeAAAA  = 28
+	TypeSRV   = 33
+
+	// Question.Type
+	TypeMD    = 3
+	TypeMF    = 4
+	TypeMB    = 7
+	TypeMG    = 8
+	TypeMR    = 9
+	TypePTR   = 12
+	TypeWKS   = 11
+	TypeMX    = 15
+	TypeHINFO = 13
+	TypeMINFO = 14
+	TypeAXFR  = 252
+	TypeMAILB = 253
+	TypeMAILA = 254
+	TypeALL   = 255
+
+	// ResponseHeader.Class and Question.Class
+	ClassINET   = 1
+	ClassCSNET  = 2
+	ClassCHAOS  = 3
+	ClassHESIOD = 4
+
+	// Question.Class
+	ClassANY = 255
+
+	// Msg.Rcode
+	RCodeSuccess        = 0
+	RCodeFormatError    = 1
+	RCodeServerFailure  = 2
+	RCodeNameError      = 3
+	RCodeNotImplemented = 4
+	RCodeRefused        = 5
+)
+
+var (
+	// ErrNotStarted indicates that the prerequisite information isn't
+	// available yet because the previous records haven't been appropriately
+	// parsed or skipped.
+	ErrNotStarted = errors.New("parsing of this type isn't available yet")
+
+	errBaseLen      = errors.New("insufficent data for base length type")
+	errCalcLen      = errors.New("insufficent data for calculated length type")
+	errReserved     = errors.New("segment prefix is reserved")
+	errTooManyPtr   = errors.New("too many pointers (>10)")
+	errInvalidPtr   = errors.New("invalid pointer")
+	errResponseLen  = errors.New("insufficent data for response body length")
+	errSegTooLong   = errors.New("segment length too long")
+	errZeroSegLen   = errors.New("zero length segment")
+	errRespTooLong  = errors.New("response length too long")
+	errTooManyQuest = errors.New("too many questions to pack (>65535)")
+	errTooManyAns   = errors.New("too many answers to pack (>65535)")
+	errTooManyNS    = errors.New("too many name servers to pack (>65535)")
+	errTooManyExtra = errors.New("too many extras to pack (>65535)")
+)
+
+// MessageHeader is a representation of a DNS packet header.
+type MessageHeader struct {
+	ID                 uint16
+	Response           bool
+	OpCode             int
+	Authoritative      bool
+	Truncated          bool
+	RecursionDesired   bool
+	RecursionAvailable bool
+	RCode              int
+}
+
+func (m *MessageHeader) pack() (id uint16, bits uint16) {
+	id = m.ID
+	bits = uint16(m.OpCode)<<11 | uint16(m.RCode)
+	if m.RecursionAvailable {
+		bits |= headerBitRA
+	}
+	if m.RecursionDesired {
+		bits |= headerBitRD
+	}
+	if m.Truncated {
+		bits |= headerBitTC
+	}
+	if m.Authoritative {
+		bits |= headerBitAA
+	}
+	if m.Response {
+		bits |= headerBitQR
+	}
+	return
+}
+
+// Message is a representation of a DNS packet.
+type Message struct {
+	MessageHeader
+	Questions   []*Question
+	Answers     []Response
+	NameServers []Response
+	Extras      []Response
+}
+
+func (m *Message) String() string {
+	s := fmt.Sprintf("Message: %#v\n", &m.MessageHeader)
+	if len(m.Questions) > 0 {
+		s += "-- Questions\n"
+		for _, q := range m.Questions {
+			s += fmt.Sprintf("%#v\n", q)
+		}
+	}
+	if len(m.Answers) > 0 {
+		s += "-- Answers\n"
+		for _, a := range m.Answers {
+			s += fmt.Sprintf("%#v\n", a)
+		}
+	}
+	if len(m.NameServers) > 0 {
+		s += "-- NameServers\n"
+		for _, ns := range m.NameServers {
+			s += fmt.Sprintf("%#v\n", ns)
+		}
+	}
+	if len(m.Extras) > 0 {
+		s += "-- Extras\n"
+		for _, e := range m.Extras {
+			s += fmt.Sprintf("%#v\n", e)
+		}
+	}
+	return s
+}
+
+const (
+	headerBitQR = 1 << 15 // query/response (response=1)
+	headerBitAA = 1 << 10 // authoritative
+	headerBitTC = 1 << 9  // truncated
+	headerBitRD = 1 << 8  // recursion desired
+	headerBitRA = 1 << 7  // recursion available
+)
+
+// header is the wire format for a DNS packet header.
+type header struct {
+	id          uint16
+	bits        uint16
+	questions   uint16
+	answers     uint16
+	nameServers uint16
+	extras      uint16
+}
+
+func (h *header) pack(msg []byte) []byte {
+	msg = packUint16(msg, h.id)
+	msg = packUint16(msg, h.bits)
+	msg = packUint16(msg, h.questions)
+	msg = packUint16(msg, h.answers)
+	msg = packUint16(msg, h.nameServers)
+	return packUint16(msg, h.extras)
+}
+
+func (h *header) unpack(msg []byte, off int) (int, error) {
+	newOff := off
+	var err error
+	if h.id, newOff, err = unpackUint16(msg, newOff); err != nil {
+		return off, fmt.Errorf("id: %v", err)
+	}
+	if h.bits, newOff, err = unpackUint16(msg, newOff); err != nil {
+		return off, fmt.Errorf("bits: %v", err)
+	}
+	if h.questions, newOff, err = unpackUint16(msg, newOff); err != nil {
+		return off, fmt.Errorf("questions: %v", err)
+	}
+	if h.answers, newOff, err = unpackUint16(msg, newOff); err != nil {
+		return off, fmt.Errorf("answers: %v", err)
+	}
+	if h.nameServers, newOff, err = unpackUint16(msg, newOff); err != nil {
+		return off, fmt.Errorf("nameServers: %v", err)
+	}
+	if h.extras, newOff, err = unpackUint16(msg, newOff); err != nil {
+		return off, fmt.Errorf("extras: %v", err)
+	}
+	return newOff, nil
+}
+
+func (h *header) messageHeader() MessageHeader {
+	return MessageHeader{
+		ID:                 h.id,
+		Response:           (h.bits & headerBitQR) != 0,
+		OpCode:             int(h.bits>>11) & 0xF,
+		Authoritative:      (h.bits & headerBitAA) != 0,
+		Truncated:          (h.bits & headerBitTC) != 0,
+		RecursionDesired:   (h.bits & headerBitRD) != 0,
+		RecursionAvailable: (h.bits & headerBitRA) != 0,
+		RCode:              int(h.bits & 0xF),
+	}
+}
+
+// tracker tracker keep track of the index and offset for parsing a particular
+// type of Question or Response.
+type tracker struct {
+	off   int
+	index int
+}
+
+// A Response is a DNS response resource record.
+type Response interface {
+	// Header return's the Response's ResponseHeader.
+	Header() *ResponseHeader
+
+	// pack packs a response except for its header.
+	pack(msg []byte, compression map[string]int) ([]byte, error)
+
+	// realType returns the actual type of the Response. This is used to fill
+	// in the header Type field.
+	realType() uint16
+}
+
+func packResponse(msg []byte, response Response, compression map[string]int) ([]byte, error) {
+	oldMsg := msg
+	response.Header().Type = response.realType()
+	msg, length, err := response.Header().pack(msg, compression)
+	if err != nil {
+		return msg, fmt.Errorf("ResponseHeader: %v", err)
+	}
+	preLen := len(msg)
+	msg, err = response.pack(msg, compression)
+	if err != nil {
+		return msg, fmt.Errorf("content: %v", err)
+	}
+	conLen := len(msg) - preLen
+	if conLen > int(^uint16(0)) {
+		return oldMsg, errRespTooLong
+	}
+	// Fill in the length now that we know how long the content is.
+	packUint16(length[:0], uint16(conLen))
+	response.Header().Length = uint16(conLen)
+	return msg, nil
+}
+
+// A Parser allows incrementally parsing a DNS message.
+//
+// When parsing is started, the MessageHeader is parsed. Next, each Question
+// can be either parsed or skipped. Alternatively, all Questions can be skipped
+// at once. When all Questions have been parsed, attempting to parse Questions
+// will return (nil, nil) and attempting to skip Questions will return
+// (true, nil). After all Questions have been either parsed or skipped, all
+// Answers, NameServers and Extras can be either parsed or skipped in the same
+// way, and each type of Response must be fully parsed or skipped before
+// proceeding to the next type of Response.
+//
+// Note that there is no requirement to fully skip or parse the message.
+type Parser struct {
+	msg        []byte
+	header     header
+	question   tracker
+	answer     tracker
+	nameServer tracker
+	extra      tracker
+}
+
+// Reset resets the Parser so that it is ready to parse another message.
+func (p *Parser) Reset() {
+	*p = Parser{}
+}
+
+// Start parses the header and enables the parsing of Questions. Start can only
+// be called on a Parser that is either new or has been Reset.
+func (p *Parser) Start(msg []byte) (MessageHeader, error) {
+	p.msg = msg
+	var err error
+	if p.question.off, err = p.header.unpack(msg, 0); err != nil {
+		return MessageHeader{}, fmt.Errorf("unpacking header: %v", err)
+	}
+	return p.header.messageHeader(), nil
+}
+
+func (t tracker) check(count uint16, next *tracker) (done bool, err error) {
+	if t.off == 0 {
+		return false, ErrNotStarted
+	}
+	if t.index == int(count) {
+		if next != nil && next.off == 0 {
+			next.off = t.off
+		}
+		return true, nil
+	}
+	return false, nil
+}
+
+func (t *tracker) parseResponse(msg []byte, count uint16, name string, next *tracker) (Response, error) {
+	if done, err := t.check(count, next); done {
+		return nil, nil
+	} else if err != nil {
+		return nil, fmt.Errorf("unpacking %s: %v", name, err)
+	}
+	var r Response
+	var err error
+	r, t.off, err = unpackResponse(msg, t.off)
+	if err != nil {
+		return nil, fmt.Errorf("unpacking %s: %v", name, err)
+	}
+	t.index++
+	return r, nil
+}
+
+func (t *tracker) skipResponse(msg []byte, count uint16, name string, next *tracker) (done bool, err error) {
+	if done, err = t.check(count, next); done {
+		return
+	} else if err != nil {
+		return false, fmt.Errorf("skipping %s: %v", name, err)
+	}
+	t.off, err = skipResponse(msg, t.off)
+	if err != nil {
+		return false, fmt.Errorf("skipping %s: %v", name, err)
+	}
+	t.index++
+	return false, nil
+}
+
+// Question parses a single Question.
+func (p *Parser) Question() (*Question, error) {
+	if done, err := p.question.check(p.header.questions, &p.answer); done || err != nil {
+		return nil, err
+	}
+	name, off, err := unpackName(p.msg, p.question.off)
+	if err != nil {
+		return nil, fmt.Errorf("unpacking Question # Name: %v", p.question.index+1, err)
+	}
+	typ, off, err := unpackUint16(p.msg, off)
+	if err != nil {
+		return nil, fmt.Errorf("unpacking Question # Type: %v", p.question.index+1, err)
+	}
+	class, off, err := unpackUint16(p.msg, off)
+	if err != nil {
+		return nil, fmt.Errorf("unpacking Question # Class: %v", p.question.index+1, err)
+	}
+	p.question.off = off
+	p.question.index++
+	return &Question{name, typ, class}, nil
+}
+
+// AllQuestions parses all Questions.
+func (p *Parser) AllQuestions() ([]*Question, error) {
+	qs := make([]*Question, 0, p.header.questions)
+	for {
+		q, err := p.Question()
+		if err != nil {
+			return nil, err
+		}
+		if q == nil {
+			break
+		}
+		qs = append(qs, q)
+	}
+	return qs, nil
+}
+
+// SkipQuestion skips a single Question.
+func (p *Parser) SkipQuestion() (done bool, err error) {
+	if done, err = p.question.check(p.header.questions, &p.answer); done || err != nil {
+		return
+	}
+	off, err := skipName(p.msg, p.question.off)
+	if err != nil {
+		return false, fmt.Errorf("skipping Question Name: %v", err)
+	}
+	if off, err = skipUint16(p.msg, off); err != nil {
+		return false, fmt.Errorf("skipping Question Type: %v", err)
+	}
+	if off, err = skipUint16(p.msg, off); err != nil {
+		return false, fmt.Errorf("skipping Question Class: %v", err)
+	}
+	p.question.off = off
+	p.question.index++
+	return false, nil
+}
+
+// SkipAllQuestions skips all Questions.
+func (p *Parser) SkipAllQuestions() error {
+	for {
+		if done, err := p.SkipQuestion(); done || err != nil {
+			return err
+		}
+	}
+}
+
+// Answer parses a single Answer response.
+func (p *Parser) Answer() (Response, error) {
+	return p.answer.parseResponse(p.msg, p.header.answers, "Answer", &p.nameServer)
+}
+
+// AllAnswers parses all Answer responses.
+func (p *Parser) AllAnswers() ([]Response, error) {
+	as := make([]Response, 0, p.header.answers)
+	for {
+		a, err := p.Answer()
+		if err != nil {
+			return nil, err
+		}
+		if a == nil {
+			break
+		}
+		as = append(as, a)
+	}
+	return as, nil
+}
+
+// SkipAnswer skips a single Answer response.
+func (p *Parser) SkipAnswer() (done bool, err error) {
+	return p.answer.skipResponse(p.msg, p.header.answers, "Answer", &p.nameServer)
+}
+
+// SkipAllAnswers skips all Answer responses.
+func (p *Parser) SkipAllAnswers() error {
+	for {
+		if done, err := p.SkipAnswer(); done || err != nil {
+			return err
+		}
+	}
+}
+
+// NameServer parses a single NameServer response.
+func (p *Parser) NameServer() (Response, error) {
+	return p.nameServer.parseResponse(p.msg, p.header.nameServers, "NameServer", &p.extra)
+}
+
+// AllNameServers parses all NameServer responses.
+func (p *Parser) AllNameServers() ([]Response, error) {
+	nss := make([]Response, 0, p.header.nameServers)
+	for {
+		ns, err := p.NameServer()
+		if err != nil {
+			return nil, err
+		}
+		if ns == nil {
+			break
+		}
+		nss = append(nss, ns)
+	}
+	return nss, nil
+}
+
+// SkipNameServer skips a single NameServer response.
+func (p *Parser) SkipNameServer() (done bool, err error) {
+	return p.nameServer.skipResponse(p.msg, p.header.nameServers, "NameServer", &p.extra)
+}
+
+// SkipAllNameServers skips all NameServer responses.
+func (p *Parser) SkipAllNameServers() error {
+	for {
+		if done, err := p.SkipNameServer(); done || err != nil {
+			return err
+		}
+	}
+}
+
+// Extra parses a single Extra response.
+func (p *Parser) Extra() (Response, error) {
+	return p.extra.parseResponse(p.msg, p.header.extras, "Extra", nil)
+}
+
+// AllExtras parses all Extra responses.
+func (p *Parser) AllExtras() ([]Response, error) {
+	es := make([]Response, 0, p.header.extras)
+	for {
+		e, err := p.Extra()
+		if err != nil {
+			return nil, err
+		}
+		if e == nil {
+			break
+		}
+		es = append(es, e)
+	}
+	return es, nil
+}
+
+// SkipExtra skips a single Extra response.
+func (p *Parser) SkipExtra() (done bool, err error) {
+	return p.extra.skipResponse(p.msg, p.header.extras, "Extra", nil)
+}
+
+// SkipAllExtras skips all Extra responses.
+func (p *Parser) SkipAllExtras() error {
+	for {
+		if done, err := p.SkipExtra(); done || err != nil {
+			return err
+		}
+	}
+}
+
+// Parse parses a full Message.
+func (m *Message) Parse(msg []byte) error {
+	var p Parser
+	var err error
+	if m.MessageHeader, err = p.Start(msg); err != nil {
+		return err
+	}
+	if m.Questions, err = p.AllQuestions(); err != nil {
+		return err
+	}
+	if m.Answers, err = p.AllAnswers(); err != nil {
+		return err
+	}
+	if m.NameServers, err = p.AllNameServers(); err != nil {
+		return err
+	}
+	if m.Extras, err = p.AllExtras(); err != nil {
+		return err
+	}
+	return nil
+}
+
+// Pack packs a full Message.
+func (m *Message) Pack() ([]byte, error) {
+	// Validate the lengths. It is very unlikely that anyone will try to pack
+	// more than 65535 of any particular type, but it is possible and we
+	// should fail gracefully.
+	if len(m.Questions) > int(^uint16(0)) {
+		return nil, errTooManyQuest
+	}
+	if len(m.Answers) > int(^uint16(0)) {
+		return nil, errTooManyAns
+	}
+	if len(m.NameServers) > int(^uint16(0)) {
+		return nil, errTooManyNS
+	}
+	if len(m.Extras) > int(^uint16(0)) {
+		return nil, errTooManyExtra
+	}
+
+	var h header
+	h.id, h.bits = m.MessageHeader.pack()
+
+	h.questions = uint16(len(m.Questions))
+	h.answers = uint16(len(m.Answers))
+	h.nameServers = uint16(len(m.NameServers))
+	h.extras = uint16(len(m.Extras))
+
+	// The starting capacity doesn't matter too much, but most DNS responses
+	// Will be <= 512 bytes as it is the limit for DNS over UDP.
+	msg := make([]byte, 0, 512)
+
+	msg = h.pack(msg)
+
+	// RFC 1035 allows (but does not require) compression for packing. RFC
+	// 1035 requires unpacking implementations to support compression, so
+	// unconditionally enabling it is fine.
+	//
+	// DNS lookups are typically done over UDP, and RFC 1035 states that UDP
+	// DNS packets can be a maximum of 512 bytes long. Without compression,
+	// many DNS response packets are over this limit, so enabling compression
+	// will help ensure compliance.
+	compression := map[string]int{}
+
+	for _, q := range m.Questions {
+		var err error
+		msg, err = q.pack(msg, compression)
+		if err != nil {
+			return nil, fmt.Errorf("packing Question: %v", err)
+		}
+	}
+	for _, a := range m.Answers {
+		var err error
+		msg, err = packResponse(msg, a, compression)
+		if err != nil {
+			return nil, fmt.Errorf("packing Answer: %v", err)
+		}
+	}
+	for _, ns := range m.NameServers {
+		var err error
+		msg, err = packResponse(msg, ns, compression)
+		if err != nil {
+			return nil, fmt.Errorf("packing NameServer: %v", err)
+		}
+	}
+	for _, e := range m.Extras {
+		var err error
+		msg, err = packResponse(msg, e, compression)
+		if err != nil {
+			return nil, fmt.Errorf("packing Extra: %v", err)
+		}
+	}
+
+	return msg, nil
+}
+
+// An ResponseHeader is the header of a DNS response resource record. There are
+// many types of DNS responses, but they all share the same header.
+type ResponseHeader struct {
+	// Name is the domain name for which this response pertains.
+	Name string
+
+	// Type is the type of DNS response.
+	//
+	// This field will be set automaticlly during packing.
+	Type uint16
+
+	// Class is the class of network to which this DNS response pertains.
+	Class uint16
+
+	// TTL is the length of time (measured in seconds) which this response is
+	// valid for (time to live).
+	TTL uint32
+
+	// Length is the length of data in the response after the header.
+	//
+	// This field will be set automaticlly during packing.
+	Length uint16
+}
+
+// Header implements Response.Header.
+func (h *ResponseHeader) Header() *ResponseHeader {
+	return h
+}
+
+// pack packs all of the fields in a ResponseHeader except for the length. The
+// length bytes are returned as a slice so they can be filled in after the rest
+// of the response has been packed.
+func (h *ResponseHeader) pack(oldMsg []byte, compression map[string]int) (msg []byte, length []byte, err error) {
+	msg = oldMsg
+	if msg, err = packName(msg, h.Name, compression); err != nil {
+		return oldMsg, nil, fmt.Errorf("Name: %v", err)
+	}
+	msg = packUint16(msg, h.Type)
+	msg = packUint16(msg, h.Class)
+	msg = packUint32(msg, h.TTL)
+	lenBegin := len(msg)
+	msg = packUint16(msg, h.Length)
+	return msg, msg[lenBegin:len(msg)], nil
+}
+
+func (h *ResponseHeader) unpack(msg []byte, off int) (int, error) {
+	newOff := off
+	var err error
+	if h.Name, newOff, err = unpackName(msg, newOff); err != nil {
+		return off, fmt.Errorf("Name: %v", err)
+	}
+	if h.Type, newOff, err = unpackUint16(msg, newOff); err != nil {
+		return off, fmt.Errorf("Type: %v", err)
+	}
+	if h.Class, newOff, err = unpackUint16(msg, newOff); err != nil {
+		return off, fmt.Errorf("Class: %v", err)
+	}
+	if h.TTL, newOff, err = unpackUint32(msg, newOff); err != nil {
+		return off, fmt.Errorf("TTL: %v", err)
+	}
+	if h.Length, newOff, err = unpackUint16(msg, newOff); err != nil {
+		return off, fmt.Errorf("Length: %v", err)
+	}
+	return newOff, nil
+}
+
+func skipResponse(msg []byte, off int) (int, error) {
+	newOff, err := skipName(msg, off)
+	if err != nil {
+		return off, fmt.Errorf("Name: %v", err)
+	}
+	if newOff, err = skipUint16(msg, newOff); err != nil {
+		return off, fmt.Errorf("Type: %v", err)
+	}
+	if newOff, err = skipUint16(msg, newOff); err != nil {
+		return off, fmt.Errorf("Class: %v", err)
+	}
+	if newOff, err = skipUint32(msg, newOff); err != nil {
+		return off, fmt.Errorf("TTL: %v", err)
+	}
+	length, newOff, err := unpackUint16(msg, newOff)
+	if err != nil {
+		return off, fmt.Errorf("Length: %v", err)
+	}
+	if newOff += int(length); newOff > len(msg) {
+		return off, errResponseLen
+	}
+	return newOff, nil
+}
+
+func packUint16(msg []byte, field uint16) []byte {
+	return append(msg, byte(field>>8), byte(field))
+}
+
+func unpackUint16(msg []byte, off int) (uint16, int, error) {
+	if off+2 > len(msg) {
+		return 0, off, errBaseLen
+	}
+	return uint16(msg[off])<<8 | uint16(msg[off+1]), off + 2, nil
+}
+
+func skipUint16(msg []byte, off int) (int, error) {
+	if off+2 > len(msg) {
+		return off, errBaseLen
+	}
+	return off + 2, nil
+}
+
+func packUint32(msg []byte, field uint32) []byte {
+	return append(
+		msg,
+		byte(field>>24),
+		byte(field>>16),
+		byte(field>>8),
+		byte(field),
+	)
+}
+
+func unpackUint32(msg []byte, off int) (uint32, int, error) {
+	if off+4 > len(msg) {
+		return 0, off, errBaseLen
+	}
+	v := uint32(msg[off])<<24 | uint32(msg[off+1])<<16 | uint32(msg[off+2])<<8 | uint32(msg[off+3])
+	return v, off + 4, nil
+}
+
+func skipUint32(msg []byte, off int) (int, error) {
+	if off+4 > len(msg) {
+		return off, errBaseLen
+	}
+	return off + 4, nil
+}
+
+func packText(msg []byte, field string) []byte {
+	for len(field) > 0 {
+		l := len(field)
+		if l > 255 {
+			l = 255
+		}
+		msg = append(msg, byte(l))
+		msg = append(msg, field[:l]...)
+		field = field[l:]
+	}
+	return msg
+}
+
+func unpackText(msg []byte, off int) (string, int, error) {
+	if off >= len(msg) {
+		return "", off, errBaseLen
+	}
+	beginOff := off + 1
+	endOff := beginOff + int(msg[off])
+	if endOff > len(msg) {
+		return "", off, errCalcLen
+	}
+	return string(msg[beginOff:endOff]), endOff, nil
+}
+
+func skipText(msg []byte, off int) (int, error) {
+	if off >= len(msg) {
+		return off, errBaseLen
+	}
+	endOff := off + 1 + int(msg[off])
+	if endOff > len(msg) {
+		return off, errCalcLen
+	}
+	return endOff, nil
+}
+
+func packBytes(msg []byte, field []byte) []byte {
+	return append(msg, field...)
+}
+
+func unpackBytes(msg []byte, off int, field []byte) (int, error) {
+	newOff := off + len(field)
+	if newOff > len(msg) {
+		return off, errBaseLen
+	}
+	copy(field, msg[off:newOff])
+	return newOff, nil
+}
+
+func skipBytes(msg []byte, off int, field []byte) (int, error) {
+	newOff := off + len(field)
+	if newOff > len(msg) {
+		return off, errBaseLen
+	}
+	return newOff, nil
+}
+
+// packName packs a domain name.
+//
+// Domain names are a sequence of counted strings split at the dots. They end
+// with a zero-length string. Compression can be used to reuse domain suffixes.
+//
+// The compression map will be updated with new domain suffixes.
+func packName(msg []byte, name string, compression map[string]int) ([]byte, error) {
+	oldMsg := msg
+
+	// Add a trailing dot to canonicalize name.
+	if n := len(name); n == 0 || name[n-1] != '.' {
+		name += "."
+	}
+
+	// Allow root domain.
+	if name == "." {
+		return append(msg, 0), nil
+	}
+
+	// Emit sequence of counted strings, chopping at dots.
+	for i, begin := 0, 0; i < len(name); i++ {
+		// Check for the end of the segment.
+		if name[i] == '.' {
+			// The two most significant bits have special meaning. It isn't
+			// allowed for segments to be long enough to need them.
+			if i-begin >= 1<<6 {
+				return oldMsg, errSegTooLong
+			}
+
+			// Segments must have a non-zero length.
+			if i-begin == 0 {
+				return oldMsg, errZeroSegLen
+			}
+
+			msg = append(msg, byte(i-begin))
+
+			for j := begin; j < i; j++ {
+				msg = append(msg, name[j])
+			}
+
+			begin = i + 1
+			continue
+		}
+
+		// We can only compress domain suffixes starting with a new segment. A
+		// pointer is two bytes with the two most significant bits set to 1 to
+		// indicate that it is a pointer.
+		if i == 0 || name[i-1] == '.' {
+			if ptr, ok := compression[name[i:]]; ok {
+				// Hit. Emit a pointer instead of the rest of the domain.
+				return append(msg, byte(ptr>>8|0xC0), byte(ptr)), nil
+			}
+
+			// Miss. Add the suffix to the compression table if the offset can
+			// be stored in the available 14 bytes.
+			if len(msg) <= int(^uint16(0)>>2) {
+				compression[name[i:]] = len(msg)
+			}
+		}
+	}
+	return append(msg, 0), nil
+}
+
+// unpackName unpacks a domain name.
+func unpackName(msg []byte, off int) (string, int, error) {
+	// currOff is the current working offset.
+	currOff := off
+
+	// newOff is the offset where the next record will start. Pointers lead to
+	// data that belongs to other names and thus doesn't count towards to the
+	// usage of this name.
+	newOff := off
+
+	// name is the domain name being unpacked.
+	var name string
+
+	// ptr is the number of pointers followed.
+	var ptr int
+Loop:
+	for {
+		if currOff >= len(msg) {
+			return "", off, errBaseLen
+		}
+		c := int(msg[currOff])
+		currOff++
+		switch c & 0xC0 {
+		case 0x00: // String segment
+			if c == 0x00 {
+				// A zero length signals the end of the name.
+				break Loop
+			}
+			endOff := currOff + c
+			if endOff > len(msg) {
+				return "", off, errCalcLen
+			}
+			name += string(msg[currOff:endOff]) + "."
+			currOff = endOff
+		case 0xC0: // Pointer
+			if currOff >= len(msg) {
+				return "", off, errInvalidPtr
+			}
+			c1 := msg[currOff]
+			currOff++
+			if ptr == 0 {
+				newOff = currOff
+			}
+			// Don't follow too many pointers, maybe there's a loop.
+			if ptr++; ptr > 10 {
+				return "", off, errTooManyPtr
+			}
+			currOff = (c^0xC0)<<8 | int(c1)
+		default:
+			// Prefixes 0x80 and 0x40 are reserved.
+			return "", off, errReserved
+		}
+	}
+	if len(name) == 0 {
+		name = "."
+	}
+	if ptr == 0 {
+		newOff = currOff
+	}
+	return name, newOff, nil
+}
+
+func skipName(msg []byte, off int) (int, error) {
+	// newOff is the offset where the next record will start. Pointers lead to
+	// data that belongs to other names and thus doesn't count towards to the
+	// usage of this name.
+	newOff := off
+
+	if newOff >= len(msg) {
+		return off, errBaseLen
+	}
+	c := int(msg[newOff])
+	newOff++
+	switch c & 0xC0 {
+	case 0x00:
+		if c == 0x00 {
+			// A zero length signals the end of the name.
+			break
+		}
+		// literal string
+		newOff += c
+		if newOff > len(msg) {
+			return off, errCalcLen
+		}
+	case 0xC0:
+		// Pointer to somewhere else in msg.
+	default:
+		// Prefixes 0x80 and 0x40 are reserved.
+		return off, errReserved
+	}
+
+	return newOff, nil
+}
+
+// A Question is a DNS query.
+type Question struct {
+	Name  string
+	Type  uint16
+	Class uint16
+}
+
+func (q *Question) pack(msg []byte, compression map[string]int) ([]byte, error) {
+	msg, err := packName(msg, q.Name, compression)
+	if err != nil {
+		return msg, fmt.Errorf("Name: %v", err)
+	}
+	msg = packUint16(msg, q.Type)
+	return packUint16(msg, q.Class), nil
+}
+
+func unpackResponse(msg []byte, off int) (Response, int, error) {
+	var hdr ResponseHeader
+	off, err := hdr.unpack(msg, off)
+	if err != nil {
+		return nil, off, fmt.Errorf("ResponseHeader: %v", err)
+	}
+	var r Response
+	var name string
+	switch hdr.Type {
+	case TypeA:
+		r, err = unpackAResponse(hdr, msg, off)
+		name = "A"
+	case TypeNS:
+		r, err = unpackNSResponse(hdr, msg, off)
+		name = "NS"
+	case TypeCNAME:
+		r, err = unpackCNAMEResponse(hdr, msg, off)
+		name = "CNAME"
+	case TypeSOA:
+		r, err = unpackSOAResponse(hdr, msg, off)
+		name = "SOA"
+	case TypePTR:
+		r, err = unpackPTRResponse(hdr, msg, off)
+		name = "PTR"
+	case TypeMX:
+		r, err = unpackMXResponse(hdr, msg, off)
+		name = "MX"
+	case TypeTXT:
+		r, err = unpackTXTResponse(hdr, msg, off)
+		name = "TXT"
+	case TypeAAAA:
+		r, err = unpackAAAAResponse(hdr, msg, off)
+		name = "AAAA"
+	case TypeSRV:
+		r, err = unpackSRVResponse(hdr, msg, off)
+		name = "SRV"
+	}
+	if err != nil {
+		return nil, off, fmt.Errorf("%s record: %v", name, err)
+	}
+	if r != nil {
+		return r, off + int(hdr.Length), nil
+	}
+	return nil, off, fmt.Errorf("invalid response type: %d", hdr.Type)
+}
+
+// A CNAMEResponse is a CNAME Response record.
+type CNAMEResponse struct {
+	ResponseHeader
+
+	CNAME string
+}
+
+func (r *CNAMEResponse) realType() uint16 {
+	return TypeCNAME
+}
+
+func (r *CNAMEResponse) pack(msg []byte, compression map[string]int) ([]byte, error) {
+	return packName(msg, r.CNAME, compression)
+}
+
+func unpackCNAMEResponse(hdr ResponseHeader, msg []byte, off int) (*CNAMEResponse, error) {
+	cname, _, err := unpackName(msg, off)
+	if err != nil {
+		return nil, err
+	}
+	return &CNAMEResponse{hdr, cname}, nil
+}
+
+// An MXResponse is an MX Response record.
+type MXResponse struct {
+	ResponseHeader
+
+	Pref uint16
+	MX   string
+}
+
+func (r *MXResponse) realType() uint16 {
+	return TypeMX
+}
+
+func (r *MXResponse) pack(msg []byte, compression map[string]int) ([]byte, error) {
+	oldMsg := msg
+	msg = packUint16(msg, r.Pref)
+	msg, err := packName(msg, r.MX, compression)
+	if err != nil {
+		return oldMsg, fmt.Errorf("MXResponse.MX: %v", err)
+	}
+	return msg, nil
+}
+
+func unpackMXResponse(hdr ResponseHeader, msg []byte, off int) (*MXResponse, error) {
+	pref, off, err := unpackUint16(msg, off)
+	if err != nil {
+		return nil, fmt.Errorf("Pref: %v", err)
+	}
+	mx, _, err := unpackName(msg, off)
+	if err != nil {
+		return nil, fmt.Errorf("MX: %v", err)
+	}
+	return &MXResponse{hdr, pref, mx}, nil
+}
+
+// An NSResponse is an NS Response record.
+type NSResponse struct {
+	ResponseHeader
+
+	NS string
+}
+
+func (r *NSResponse) realType() uint16 {
+	return TypeNS
+}
+
+func (r *NSResponse) pack(msg []byte, compression map[string]int) ([]byte, error) {
+	return packName(msg, r.NS, compression)
+}
+
+func unpackNSResponse(hdr ResponseHeader, msg []byte, off int) (*NSResponse, error) {
+	ns, _, err := unpackName(msg, off)
+	if err != nil {
+		return nil, err
+	}
+	return &NSResponse{hdr, ns}, nil
+}
+
+// A PTRResponse is a PTR Response record.
+type PTRResponse struct {
+	ResponseHeader
+
+	PTR string
+}
+
+func (r *PTRResponse) realType() uint16 {
+	return TypePTR
+}
+
+func (r *PTRResponse) pack(msg []byte, compression map[string]int) ([]byte, error) {
+	return packName(msg, r.PTR, compression)
+}
+
+func unpackPTRResponse(hdr ResponseHeader, msg []byte, off int) (*PTRResponse, error) {
+	ptr, _, err := unpackName(msg, off)
+	if err != nil {
+		return nil, err
+	}
+	return &PTRResponse{hdr, ptr}, nil
+}
+
+// An SOAResponse is an SOA Response record.
+type SOAResponse struct {
+	ResponseHeader
+
+	NS      string
+	MBox    string
+	Serial  uint32
+	Refresh uint32
+	Retry   uint32
+	Expire  uint32
+	MinTTL  uint32
+}
+
+func (r *SOAResponse) realType() uint16 {
+	return TypeSOA
+}
+
+func (r *SOAResponse) pack(msg []byte, compression map[string]int) ([]byte, error) {
+	oldMsg := msg
+	msg, err := packName(msg, r.NS, compression)
+	if err != nil {
+		return oldMsg, fmt.Errorf("SOAResponse.NS: %v", err)
+	}
+	msg, err = packName(msg, r.MBox, compression)
+	if err != nil {
+		return oldMsg, fmt.Errorf("SOAResponse.MBox: %v", err)
+	}
+	msg = packUint32(msg, r.Serial)
+	msg = packUint32(msg, r.Refresh)
+	msg = packUint32(msg, r.Retry)
+	msg = packUint32(msg, r.Expire)
+	return packUint32(msg, r.MinTTL), nil
+}
+
+func unpackSOAResponse(hdr ResponseHeader, msg []byte, off int) (*SOAResponse, error) {
+	ns, off, err := unpackName(msg, off)
+	if err != nil {
+		return nil, fmt.Errorf("NS: %v", err)
+	}
+	mbox, off, err := unpackName(msg, off)
+	if err != nil {
+		return nil, fmt.Errorf("MBox: %v", err)
+	}
+	serial, off, err := unpackUint32(msg, off)
+	if err != nil {
+		return nil, fmt.Errorf("Serial: %v", err)
+	}
+	refresh, off, err := unpackUint32(msg, off)
+	if err != nil {
+		return nil, fmt.Errorf("Refresh: %v", err)
+	}
+	retry, off, err := unpackUint32(msg, off)
+	if err != nil {
+		return nil, fmt.Errorf("Retry: %v", err)
+	}
+	expire, off, err := unpackUint32(msg, off)
+	if err != nil {
+		return nil, fmt.Errorf("Expire: %v", err)
+	}
+	minTTL, _, err := unpackUint32(msg, off)
+	if err != nil {
+		return nil, fmt.Errorf("MinTTL: %v", err)
+	}
+	return &SOAResponse{hdr, ns, mbox, serial, refresh, retry, expire, minTTL}, nil
+}
+
+// A TXTResponse is a TXT Response record.
+type TXTResponse struct {
+	ResponseHeader
+
+	Txt string // Not a domain name.
+}
+
+func (r *TXTResponse) realType() uint16 {
+	return TypeTXT
+}
+
+func (r *TXTResponse) pack(msg []byte, compression map[string]int) ([]byte, error) {
+	return packText(msg, r.Txt), nil
+}
+
+func unpackTXTResponse(hdr ResponseHeader, msg []byte, off int) (*TXTResponse, error) {
+	var txt string
+	for n := uint16(0); n < hdr.Length; {
+		var t string
+		var err error
+		if t, off, err = unpackText(msg, off); err != nil {
+			return nil, fmt.Errorf("text: %v", err)
+		}
+		// Check if we got too many bytes.
+		if hdr.Length-n < uint16(len(t))+1 {
+			return nil, errCalcLen
+		}
+		n += uint16(len(t)) + 1
+		txt += t
+	}
+	return &TXTResponse{hdr, txt}, nil
+}
+
+// An SRVResponse is an SRV Response record.
+type SRVResponse struct {
+	ResponseHeader
+
+	Priority uint16
+	Weight   uint16
+	Port     uint16
+	Target   string
+}
+
+func (r *SRVResponse) realType() uint16 {
+	return TypeSRV
+}
+
+func (r *SRVResponse) pack(msg []byte, compression map[string]int) ([]byte, error) {
+	oldMsg := msg
+	msg = packUint16(msg, r.Priority)
+	msg = packUint16(msg, r.Weight)
+	msg = packUint16(msg, r.Port)
+	msg, err := packName(msg, r.Target, compression)
+	if err != nil {
+		return oldMsg, fmt.Errorf("SRVResponse.Target: %v", err)
+	}
+	return msg, nil
+}
+
+func unpackSRVResponse(hdr ResponseHeader, msg []byte, off int) (*SRVResponse, error) {
+	priority, off, err := unpackUint16(msg, off)
+	if err != nil {
+		return nil, fmt.Errorf("Priority: %v", err)
+	}
+	weight, off, err := unpackUint16(msg, off)
+	if err != nil {
+		return nil, fmt.Errorf("Weight: %v", err)
+	}
+	port, off, err := unpackUint16(msg, off)
+	if err != nil {
+		return nil, fmt.Errorf("Port: %v", err)
+	}
+	target, _, err := unpackName(msg, off)
+	if err != nil {
+		return nil, fmt.Errorf("Target: %v", err)
+	}
+	return &SRVResponse{hdr, priority, weight, port, target}, nil
+}
+
+// An AResponse is an A Response record.
+type AResponse struct {
+	ResponseHeader
+
+	A uint32
+}
+
+func (r *AResponse) realType() uint16 {
+	return TypeA
+}
+
+func (r *AResponse) pack(msg []byte, compression map[string]int) ([]byte, error) {
+	return packUint32(msg, r.A), nil
+}
+
+func unpackAResponse(hdr ResponseHeader, msg []byte, off int) (*AResponse, error) {
+	a, _, err := unpackUint32(msg, off)
+	if err != nil {
+		return nil, err
+	}
+	return &AResponse{hdr, a}, nil
+}
+
+// An AAAAResponse is an AAAA Response record.
+type AAAAResponse struct {
+	ResponseHeader
+
+	AAAA [16]byte
+}
+
+func (r *AAAAResponse) realType() uint16 {
+	return TypeAAAA
+}
+
+func (r *AAAAResponse) pack(msg []byte, compression map[string]int) ([]byte, error) {
+	return packBytes(msg, r.AAAA[:]), nil
+}
+
+func unpackAAAAResponse(hdr ResponseHeader, msg []byte, off int) (*AAAAResponse, error) {
+	var aaaa [16]byte
+	if _, err := unpackBytes(msg, off, aaaa[:]); err != nil {
+		return nil, err
+	}
+	return &AAAAResponse{hdr, aaaa}, nil
+}
diff --git a/dns/message_test.go b/dns/message_test.go
new file mode 100644
index 0000000..33baf7b
--- /dev/null
+++ b/dns/message_test.go
@@ -0,0 +1,333 @@
+// Copyright 2016 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.
+
+package dns
+
+import (
+	"encoding/binary"
+	"reflect"
+	"testing"
+)
+
+func TestQuestionPackUnpack(t *testing.T) {
+	want := Question{
+		Name:  ".",
+		Type:  TypeA,
+		Class: ClassINET,
+	}
+	buf, err := want.pack(make([]byte, 1, 50), map[string]int{})
+	if err != nil {
+		t.Fatal("Packing failed:", err)
+	}
+	var p Parser
+	p.msg = buf
+	p.header.questions = 1
+	p.question.off = 1
+	got, err := p.Question()
+	if err != nil {
+		t.Fatalf("Unpacking failed: %v\n%s", err, string(buf[1:]))
+	}
+	if p.question.off != len(buf) {
+		t.Errorf("Unpacked different amount than packed: got n = %d, want = %d", p.question.off, len(buf))
+	}
+	if !reflect.DeepEqual(*got, want) {
+		t.Errorf("Got = %+v, want = %+v", *got, want)
+	}
+}
+
+func TestNamePackUnpack(t *testing.T) {
+	tests := []struct {
+		in   string
+		want string
+		err  error
+	}{
+		{"", ".", nil},
+		{".", ".", nil},
+		{"google..com", "", errZeroSegLen},
+		{"google.com", "google.com.", nil},
+		{"google..com.", "", errZeroSegLen},
+		{"google.com.", "google.com.", nil},
+		{".google.com.", "", errZeroSegLen},
+		{"www..google.com.", "", errZeroSegLen},
+		{"www.google.com.", "www.google.com.", nil},
+	}
+
+	for _, test := range tests {
+		buf, err := packName(make([]byte, 0, 30), test.in, map[string]int{})
+		if err != test.err {
+			t.Errorf("Packing of %s: got err = %v, want err = %v", test.in, err, test.err)
+			continue
+		}
+		if test.err != nil {
+			continue
+		}
+		got, n, err := unpackName(buf, 0)
+		if err != nil {
+			t.Errorf("Unpacking for %s failed: %v", test.in, err)
+			continue
+		}
+		if n != len(buf) {
+			t.Errorf(
+				"Unpacked different amount than packed for %s: got n = %d, want = %d",
+				test.in,
+				n,
+				len(buf),
+			)
+		}
+		if got != test.want {
+			t.Errorf("Unpacking packing of %s: got = %s, want = %s", test.in, got, test.want)
+		}
+	}
+}
+
+func TestDNSPackUnpack(t *testing.T) {
+	wants := []Message{
+		{
+			Questions: []*Question{
+				&Question{
+					Name:  ".",
+					Type:  TypeAAAA,
+					Class: ClassINET,
+				},
+			},
+			Answers:     []Response{},
+			NameServers: []Response{},
+			Extras:      []Response{},
+		},
+		{
+			MessageHeader: MessageHeader{Response: true, Authoritative: true},
+			Questions: []*Question{
+				&Question{
+					Name:  "foo.bar.example.com.",
+					Type:  TypeA,
+					Class: ClassINET,
+				},
+			},
+			Answers: []Response{
+				&AResponse{
+					ResponseHeader: ResponseHeader{
+						Name:  "foo.bar.example.com.",
+						Type:  TypeA,
+						Class: ClassINET,
+					},
+					A: binary.BigEndian.Uint32([]byte{127, 0, 0, 1}),
+				},
+				&AResponse{
+					ResponseHeader: ResponseHeader{
+						Name:  "foo.bar.example.com.",
+						Type:  TypeA,
+						Class: ClassINET,
+					},
+					A: binary.BigEndian.Uint32([]byte{127, 0, 0, 2}),
+				},
+			},
+			NameServers: []Response{
+				&NSResponse{
+					ResponseHeader: ResponseHeader{
+						Name:  "foo.bar.example.com.",
+						Type:  TypeNS,
+						Class: ClassINET,
+					},
+					NS: "ns1.example.com.",
+				},
+				&NSResponse{
+					ResponseHeader: ResponseHeader{
+						Name:  "foo.bar.example.com.",
+						Type:  TypeNS,
+						Class: ClassINET,
+					},
+					NS: "ns2.example.com.",
+				},
+			},
+			Extras: []Response{
+				&TXTResponse{
+					ResponseHeader: ResponseHeader{
+						Name:  "foo.bar.example.com.",
+						Type:  TypeTXT,
+						Class: ClassINET,
+					},
+					Txt: "So Long, and Thanks for All the Fish",
+				},
+				&TXTResponse{
+					ResponseHeader: ResponseHeader{
+						Name:  "foo.bar.example.com.",
+						Type:  TypeTXT,
+						Class: ClassINET,
+					},
+					Txt: "Hamster Huey and the Gooey Kablooie",
+				},
+			},
+		},
+	}
+	for i, want := range wants {
+		b, err := want.Pack()
+		if err != nil {
+			t.Fatalf("%d: packing failed: %v", i, err)
+		}
+		var got Message
+		err = got.Parse(b)
+		if err != nil {
+			t.Fatalf("%d: unpacking failed: %v", i, err)
+		}
+		if !reflect.DeepEqual(got, want) {
+			t.Errorf("%d: got = %+v, want = %+v", i, &got, &want)
+		}
+	}
+}
+
+func TestVeryLongTxt(t *testing.T) {
+	want := &TXTResponse{
+		ResponseHeader: ResponseHeader{
+			Name:  "foo.bar.example.com.",
+			Type:  TypeTXT,
+			Class: ClassINET,
+		},
+		Txt: loremIpsum,
+	}
+	buf, err := packResponse(make([]byte, 0, 8000), want, map[string]int{})
+	if err != nil {
+		t.Fatal("Packing failed:", err)
+	}
+	got, n, err := unpackResponse(buf, 0)
+	if err != nil {
+		t.Fatal("Unpacking failed:", err)
+	}
+	if n != len(buf) {
+		t.Errorf("Unpacked different amount than packed: got n = %d, want = %d", n, len(buf))
+	}
+	if !reflect.DeepEqual(got, want) {
+		t.Errorf("Got = %+v, want = %+v", got, want)
+	}
+}
+
+const loremIpsum = `
+Lorem ipsum dolor sit amet, nec enim antiopam id, an ullum choro
+nonumes qui, pro eu debet honestatis mediocritatem. No alia enim eos,
+magna signiferumque ex vis. Mei no aperiri dissentias, cu vel quas
+regione. Malorum quaeque vim ut, eum cu semper aliquid invidunt, ei
+nam ipsum assentior.
+
+Nostrum appellantur usu no, vis ex probatus adipiscing. Cu usu illum
+facilis eleifend. Iusto conceptam complectitur vim id. Tale omnesque
+no usu, ei oblique sadipscing vim. At nullam voluptua usu, mei laudem
+reformidans et. Qui ei eros porro reformidans, ius suas veritus
+torquatos ex. Mea te facer alterum consequat.
+
+Soleat torquatos democritum sed et, no mea congue appareat, facer
+aliquam nec in. Has te ipsum tritani. At justo dicta option nec, movet
+phaedrum ad nam. Ea detracto verterem liberavisse has, delectus
+suscipiantur in mei. Ex nam meliore complectitur. Ut nam omnis
+honestatis quaerendum, ea mea nihil affert detracto, ad vix rebum
+mollis.
+
+Ut epicurei praesent neglegentur pri, prima fuisset intellegebat ad
+vim. An habemus comprehensam usu, at enim dignissim pro. Eam reque
+vivendum adipisci ea. Vel ne odio choro minimum. Sea admodum
+dissentiet ex. Mundi tamquam evertitur ius cu. Homero postea iisque ut
+pro, vel ne saepe senserit consetetur.
+
+Nulla utamur facilisis ius ea, in viderer diceret pertinax eum. Mei no
+enim quodsi facilisi, ex sed aeterno appareat mediocritatem, eum
+sententiae deterruisset ut. At suas timeam euismod cum, offendit
+appareat interpretaris ne vix. Vel ea civibus albucius, ex vim quidam
+accusata intellegebat, noluisse instructior sea id. Nec te nonumes
+habemus appellantur, quis dignissim vituperata eu nam.
+
+At vix apeirian patrioque vituperatoribus, an usu agam assum. Debet
+iisque an mea. Per eu dicant ponderum accommodare. Pri alienum
+placerat senserit an, ne eum ferri abhorreant vituperatoribus. Ut mea
+eligendi disputationi. Ius no tation everti impedit, ei magna quidam
+mediocritatem pri.
+
+Legendos perpetua iracundia ne usu, no ius ullum epicurei intellegam,
+ad modus epicuri lucilius eam. In unum quaerendum usu. Ne diam paulo
+has, ea veri virtute sed. Alia honestatis conclusionemque mea eu, ut
+iudico albucius his.
+
+Usu essent probatus eu, sed omnis dolor delicatissimi ex. No qui augue
+dissentias dissentiet. Laudem recteque no usu, vel an velit noluisse,
+an sed utinam eirmod appetere. Ne mea fuisset inimicus ocurreret. At
+vis dicant abhorreant, utinam forensibus nec ne, mei te docendi
+consequat. Brute inermis persecuti cum id. Ut ipsum munere propriae
+usu, dicit graeco disputando id has.
+
+Eros dolore quaerendum nam ei. Timeam ornatus inciderint pro id. Nec
+torquatos sadipscing ei, ancillae molestie per in. Malis principes duo
+ea, usu liber postulant ei.
+
+Graece timeam voluptatibus eu eam. Alia probatus quo no, ea scripta
+feugiat duo. Congue option meliore ex qui, noster invenire appellantur
+ea vel. Eu exerci legendos vel. Consetetur repudiandae vim ut. Vix an
+probo minimum, et nam illud falli tempor.
+
+Cum dico signiferumque eu. Sed ut regione maiorum, id veritus insolens
+tacimates vix. Eu mel sint tamquam lucilius, duo no oporteat
+tacimates. Atqui augue concludaturque vix ei, id mel utroque menandri.
+
+Ad oratio blandit aliquando pro. Vis et dolorum rationibus
+philosophia, ad cum nulla molestie. Hinc fuisset adversarium eum et,
+ne qui nisl verear saperet, vel te quaestio forensibus. Per odio
+option delenit an. Alii placerat has no, in pri nihil platonem
+cotidieque. Est ut elit copiosae scaevola, debet tollit maluisset sea
+an.
+
+Te sea hinc debet pericula, liber ridens fabulas cu sed, quem mutat
+accusam mea et. Elitr labitur albucius et pri, an labore feugait mel.
+Velit zril melius usu ea. Ad stet putent interpretaris qui. Mel no
+error volumus scripserit. In pro paulo iudico, quo ei dolorem
+verterem, affert fabellas dissentiet ea vix.
+
+Vis quot deserunt te. Error aliquid detraxit eu usu, vis alia eruditi
+salutatus cu. Est nostrud bonorum an, ei usu alii salutatus. Vel at
+nisl primis, eum ex aperiri noluisse reformidans. Ad veri velit
+utroque vis, ex equidem detraxit temporibus has.
+
+Inermis appareat usu ne. Eros placerat periculis mea ad, in dictas
+pericula pro. Errem postulant at usu, ea nec amet ornatus mentitum. Ad
+mazim graeco eum, vel ex percipit volutpat iudicabit, sit ne delicata
+interesset. Mel sapientem prodesset abhorreant et, oblique suscipit
+eam id.
+
+An maluisset disputando mea, vidit mnesarchum pri et. Malis insolens
+inciderint no sea. Ea persius maluisset vix, ne vim appellantur
+instructior, consul quidam definiebas pri id. Cum integre feugiat
+pericula in, ex sed persius similique, mel ne natum dicit percipitur.
+
+Primis discere ne pri, errem putent definitionem at vis. Ei mel dolore
+neglegentur, mei tincidunt percipitur ei. Pro ad simul integre
+rationibus. Eu vel alii honestatis definitiones, mea no nonumy
+reprehendunt.
+
+Dicta appareat legendos est cu. Eu vel congue dicunt omittam, no vix
+adhuc minimum constituam, quot noluisse id mel. Eu quot sale mutat
+duo, ex nisl munere invenire duo. Ne nec ullum utamur. Pro alterum
+debitis nostrum no, ut vel aliquid vivendo.
+
+Aliquip fierent praesent quo ne, id sit audiam recusabo delicatissimi.
+Usu postulant incorrupte cu. At pro dicit tibique intellegam, cibo
+dolore impedit id eam, et aeque feugait assentior has. Quando sensibus
+nec ex. Possit sensibus pri ad, unum mutat periculis cu vix.
+
+Mundi tibique vix te, duo simul partiendo qualisque id, est at vidit
+sonet tempor. No per solet aeterno deseruisse. Petentium salutandi
+definiebas pri cu. Munere vivendum est in. Ei justo congue eligendi
+vis, modus offendit omittantur te mel.
+
+Integre voluptaria in qui, sit habemus tractatos constituam no. Utinam
+melius conceptam est ne, quo in minimum apeirian delicata, ut ius
+porro recusabo. Dicant expetenda vix no, ludus scripserit sed ex, eu
+his modo nostro. Ut etiam sonet his, quodsi inciderint philosophia te
+per. Nullam lobortis eu cum, vix an sonet efficiendi repudiandae. Vis
+ad idque fabellas intellegebat.
+
+Eum commodo senserit conclusionemque ex. Sed forensibus sadipscing ut,
+mei in facer delicata periculis, sea ne hinc putent cetero. Nec ne
+alia corpora invenire, alia prima soleat te cum. Eleifend posidonium
+nam at.
+
+Dolorum indoctum cu quo, ex dolor legendos recteque eam, cu pri zril
+discere. Nec civibus officiis dissentiunt ex, est te liber ludus
+elaboraret. Cum ea fabellas invenire. Ex vim nostrud eripuit
+comprehensam, nam te inermis delectus, saepe inermis senserit.
+`

To view, visit this change. To unsubscribe, visit settings.

Gerrit-Project: net
Gerrit-Branch: master
Gerrit-MessageType: newchange
Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
Gerrit-Change-Number: 35237
Gerrit-PatchSet: 1
Gerrit-Owner: Ian Gudger <igu...@google.com>

Ian Gudger (Gerrit)

unread,
Jan 15, 2017, 7:59:09 PM1/15/17
to golang-co...@googlegroups.com

Ian Gudger uploaded patch set #2 to this change.

To view, visit this change. To unsubscribe, visit settings.

Gerrit-Project: net
Gerrit-Branch: master
Gerrit-MessageType: newpatchset
Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
Gerrit-Change-Number: 35237
Gerrit-PatchSet: 2
Gerrit-Owner: Ian Gudger <igu...@google.com>

Ian Gudger (Gerrit)

unread,
Jan 15, 2017, 8:01:36 PM1/15/17
to golang-co...@googlegroups.com

Ian Gudger uploaded patch set #3 to this change.

To view, visit this change. To unsubscribe, visit settings.

Gerrit-Project: net
Gerrit-Branch: master
Gerrit-MessageType: newpatchset
Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
Gerrit-Change-Number: 35237
Gerrit-PatchSet: 3
Gerrit-Owner: Ian Gudger <igu...@google.com>

Ian Gudger (Gerrit)

unread,
Jan 16, 2017, 2:11:50 AM1/16/17
to golang-co...@googlegroups.com

Ian Gudger uploaded patch set #4 to this change.

View Change

x/net/dns: add support for parsing and packing of DNS packets

The Go standard library contains support for parsing and packing DNS
packets, but it is not exported, doesn't follow Go style and is not
very well optimized. Low level DNS functionality is clearly useful
to the Go community as evidenced by the success of
github.com/miekg/dns. This implementation endeavors to avoid the
limitations of both the standard library and github.com/miekg/dns
implementations and is an almost complete rewrite of the code
currently found in on net/dnsmsg.go and net/dnsmsg_test.go.

Goals:
* Minimize heap allocations.
* Allow parsing only what is needed. Avoid unnecessary parsing and
  heap allocations for parts of the message that you don't care about.
  Parsing should be allowed on as small of a granularity as is useful,
  but no smaller as to avoid complicating the interface.
* Parse and pack each byte of the message at most one time.
* Support parsing and packing as many valid DNS packets as possible.

Updates golang/go#16218

Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
---
A dns/dns.go
A dns/message.go
A dns/message_test.go
3 files changed, 1,646 insertions(+), 0 deletions(-)

To view, visit this change. To unsubscribe, visit settings.

Gerrit-Project: net
Gerrit-Branch: master
Gerrit-MessageType: newpatchset
Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
Gerrit-Change-Number: 35237
Gerrit-PatchSet: 4
Gerrit-Owner: Ian Gudger <igu...@google.com>

Ian Gudger (Gerrit)

unread,
Jan 16, 2017, 3:43:35 AM1/16/17
to golang-co...@googlegroups.com

Ian Gudger uploaded patch set #5 to this change.

View Change

x/net/dns: add support for parsing and packing of DNS packets

The Go standard library contains support for parsing and packing DNS
packets, but it is not exported, doesn't follow Go style and is not
very well optimized. Low level DNS functionality is clearly useful
to the Go community as evidenced by the success of
github.com/miekg/dns. This implementation endeavors to avoid the
limitations of both the standard library and github.com/miekg/dns
implementations and is an almost complete rewrite of the code
currently found in on net/dnsmsg.go and net/dnsmsg_test.go.

Goals:
* Minimize heap allocations.
* Allow parsing only what is needed. Avoid unnecessary parsing and
  heap allocations for parts of the message that you don't care about.
  Parsing should be allowed on as small of a granularity as is useful,
  but no smaller as to avoid complicating the interface.
* Parse and pack each byte of the message at most one time.

Updates golang/go#16218

Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
---
A dns/dns.go
A dns/message.go
A dns/message_test.go
3 files changed, 1,646 insertions(+), 0 deletions(-)

To view, visit this change. To unsubscribe, visit settings.

Gerrit-Project: net
Gerrit-Branch: master
Gerrit-MessageType: newpatchset
Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
Gerrit-Change-Number: 35237
Gerrit-PatchSet: 5
Gerrit-Owner: Ian Gudger <igu...@google.com>

Mikio Hara (Gerrit)

unread,
Jan 16, 2017, 4:43:54 AM1/16/17
to Ian Gudger, golang-co...@googlegroups.com

Mikio Hara posted comments on this change.

View Change

Patch set 5:

looks nice, thanks.

(6 comments)

To view, visit this change. To unsubscribe, visit settings.

Gerrit-Project: net
Gerrit-Branch: master
Gerrit-MessageType: comment
Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
Gerrit-Change-Number: 35237
Gerrit-PatchSet: 5
Gerrit-Owner: Ian Gudger <igu...@google.com>
Gerrit-Comment-Date: Mon, 16 Jan 2017 09:43:50 +0000
Gerrit-HasComments: Yes

Mikio Hara (Gerrit)

unread,
Jan 16, 2017, 5:05:06 AM1/16/17
to Ian Gudger, golang-co...@googlegroups.com

Mikio Hara posted comments on this change.

View Change

Patch set 5:

(1 comment)

To view, visit this change. To unsubscribe, visit settings.

Gerrit-Project: net
Gerrit-Branch: master
Gerrit-MessageType: comment
Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
Gerrit-Change-Number: 35237
Gerrit-PatchSet: 5
Gerrit-Owner: Ian Gudger <igu...@google.com>
Gerrit-Comment-Date: Mon, 16 Jan 2017 10:05:02 +0000
Gerrit-HasComments: Yes

Ian Gudger (Gerrit)

unread,
Jan 16, 2017, 2:34:14 PM1/16/17
to golang-co...@googlegroups.com

Ian Gudger uploaded patch set #6 to this change.

View Change

dns/message: add support for parsing and packing of DNS packets

The Go standard library contains support for parsing and packing DNS
packets, but it is not exported, doesn't follow Go style and is not
very well optimized. Low level DNS functionality is clearly useful
to the Go community as evidenced by the success of
github.com/miekg/dns. This implementation endeavors to avoid the
limitations of both the standard library and github.com/miekg/dns
implementations and is an almost complete rewrite of the code
currently found in on net/dnsmsg.go and net/dnsmsg_test.go.

Goals:
* Minimize heap allocations.
* Allow parsing only what is needed. Avoid unnecessary parsing and
  heap allocations for parts of the message that you don't care about.
  Parsing should be allowed on as small of a granularity as is useful,
  but no smaller as to avoid complicating the interface.
* Parse and pack each byte of the message at most one time.

Updates golang/go#16218

Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
---
A dns/message/message.go
A dns/message/message_test.go
2 files changed, 1,659 insertions(+), 0 deletions(-)

To view, visit this change. To unsubscribe, visit settings.

Gerrit-Project: net
Gerrit-Branch: master
Gerrit-MessageType: newpatchset
Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
Gerrit-Change-Number: 35237
Gerrit-PatchSet: 6
Gerrit-Owner: Ian Gudger <igu...@google.com>

Ian Gudger (Gerrit)

unread,
Jan 16, 2017, 2:34:38 PM1/16/17
to golang-co...@googlegroups.com

Ian Gudger posted comments on this change.

View Change

Patch set 6:

(7 comments)

    • drop the repository name prefix x/net, just dns is fine.

    • I moved this code to a new message subpackage. Should the commit message be dns/message or just message?

  • File dns/dns.go:

    • Package dns provides ... or something something not experimental ;)

      Done

    • i'd prefer to drop the use of fmt package for vendoring; see go/src/go/buil

    • Done

    • Done

      I renamed this function to Unpack. Do you think it is ok to leave the Parser, or should I rename that too?

    • A [4]byte ?

      Done

To view, visit this change. To unsubscribe, visit settings.

Gerrit-Project: net
Gerrit-Branch: master
Gerrit-MessageType: comment
Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
Gerrit-Change-Number: 35237
Gerrit-PatchSet: 6
Gerrit-Owner: Ian Gudger <igu...@google.com>
Gerrit-Reviewer: Ian Gudger <igu...@google.com>
Gerrit-Comment-Date: Mon, 16 Jan 2017 19:34:36 +0000
Gerrit-HasComments: Yes

Ian Gudger (Gerrit)

unread,
Jan 16, 2017, 2:41:51 PM1/16/17
to golang-co...@googlegroups.com

Ian Gudger uploaded patch set #7 to this change.

View Change

dns/message: add support for parsing and packing of DNS packets

The Go standard library contains support for parsing and packing DNS
packets, but it is not exported, doesn't follow Go style and is not
very well optimized. Low level DNS functionality is clearly useful
to the Go community as evidenced by the success of
github.com/miekg/dns. This implementation endeavors to avoid the
limitations of both the standard library and github.com/miekg/dns
implementations and is an almost complete rewrite of the code
currently found in on net/dnsmsg.go and net/dnsmsg_test.go.

Goals:
* Minimize heap allocations.
* Allow parsing only what is needed. Avoid unnecessary parsing and
  heap allocations for parts of the message that you don't care about.
  Parsing should be allowed on as small of a granularity as is useful,
  but no smaller as to avoid complicating the interface.
* Parse and pack each byte of the message at most one time.

Updates golang/go#16218

Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
---
A dns/message/message.go
A dns/message/message_test.go
2 files changed, 1,658 insertions(+), 0 deletions(-)

To view, visit this change. To unsubscribe, visit settings.

Gerrit-Project: net
Gerrit-Branch: master
Gerrit-MessageType: newpatchset
Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
Gerrit-Change-Number: 35237
Gerrit-PatchSet: 7

Ian Gudger (Gerrit)

unread,
Jan 16, 2017, 4:48:51 PM1/16/17
to golang-co...@googlegroups.com

Ian Gudger uploaded patch set #8 to this change.

View Change

dns/message: add support for parsing and packing of DNS packets

The Go standard library contains support for parsing and packing DNS
packets, but it is not exported, doesn't follow Go style and is not
very well optimized. Low level DNS functionality is clearly useful
to the Go community as evidenced by the success of
github.com/miekg/dns. This implementation endeavors to avoid the
limitations of both the standard library and github.com/miekg/dns
implementations and is an almost complete rewrite of the code
currently found in on net/dnsmsg.go and net/dnsmsg_test.go.

Goals:
* Minimize heap allocations.
* Allow parsing only what is needed. Avoid unnecessary parsing and
  heap allocations for parts of the message that you don't care about.
  Parsing should be allowed on as small of a granularity as is useful,
  but no smaller as to avoid complicating the interface.
* Parse and pack each byte of the message at most one time.

Updates golang/go#16218
Updates golang/go#10622

Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
---
A dns/message/message.go
A dns/message/message_test.go
2 files changed, 1,659 insertions(+), 0 deletions(-)

To view, visit this change. To unsubscribe, visit settings.

Gerrit-Project: net
Gerrit-Branch: master
Gerrit-MessageType: newpatchset
Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
Gerrit-Change-Number: 35237
Gerrit-PatchSet: 8

Ian Gudger (Gerrit)

unread,
Jan 16, 2017, 4:53:00 PM1/16/17
to golang-co...@googlegroups.com

Ian Gudger uploaded patch set #9 to this change.

View Change

dns/message: add support for parsing and packing of DNS packets

The Go standard library contains support for parsing and packing DNS
packets, but it is not exported, doesn't follow Go style and is not
very well optimized. Low level DNS functionality is clearly useful
to the Go community as evidenced by the success of
github.com/miekg/dns. This implementation endeavors to avoid the
limitations of both the standard library and github.com/miekg/dns
implementations and is an almost complete rewrite of the code
currently found in on net/dnsmsg.go and net/dnsmsg_test.go.

Goals:
* Minimize heap allocations.
* Allow parsing only what is needed. Avoid unnecessary parsing and
  heap allocations for parts of the message that you don't care about.
  Parsing should be allowed on as small of a granularity as is useful,
  but no smaller as to avoid complicating the interface.
* Parse and pack each byte of the message at most one time.

Updates golang/go#16218
Updates golang/go#10622

Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
---
A dns/message/message.go
A dns/message/message_test.go
2 files changed, 1,659 insertions(+), 0 deletions(-)

To view, visit this change. To unsubscribe, visit settings.

Gerrit-Project: net
Gerrit-Branch: master
Gerrit-MessageType: newpatchset
Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
Gerrit-Change-Number: 35237
Gerrit-PatchSet: 9

Matt Layher (Gerrit)

unread,
Jan 17, 2017, 12:25:30 PM1/17/17
to Ian Gudger, golang-co...@googlegroups.com

Matt Layher posted comments on this change.

View Change

Patch set 9:

I am in favor of adding this package, but it's worth noting that this issue is not yet resolved: https://github.com/golang/go/issues/17244.

If mikioh is okay with it though (being that he "owns" x/net), I don't see any problem with including this.

    To view, visit this change. To unsubscribe, visit settings.

    Gerrit-Project: net
    Gerrit-Branch: master
    Gerrit-MessageType: comment
    Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
    Gerrit-Change-Number: 35237
    Gerrit-PatchSet: 9
    Gerrit-Owner: Ian Gudger <igu...@google.com>
    Gerrit-Reviewer: Ian Gudger <igu...@google.com>
    Gerrit-Comment-Date: Tue, 17 Jan 2017 17:25:27 +0000
    Gerrit-HasComments: No

    Ian Gudger (Gerrit)

    unread,
    Jan 17, 2017, 11:02:56 PM1/17/17
    to golang-co...@googlegroups.com

    Ian Gudger posted comments on this change.

    View Change

    Patch set 9:

    I am in favor of adding this package, but it's worth noting that this issue is not yet resolved: https://github.com/golang/go/issues/17244.

    If mikioh is okay with it though (being that he "owns" x/net), I don't see any problem with including this.

    I already got tentative approval from @bradfitz in the associated issue.

      To view, visit this change. To unsubscribe, visit settings.

      Gerrit-Project: net
      Gerrit-Branch: master
      Gerrit-MessageType: comment
      Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
      Gerrit-Change-Number: 35237
      Gerrit-PatchSet: 9
      Gerrit-Owner: Ian Gudger <igu...@google.com>
      Gerrit-Reviewer: Ian Gudger <igu...@google.com>
      Gerrit-Comment-Date: Wed, 18 Jan 2017 04:02:54 +0000
      Gerrit-HasComments: No

      Ian Gudger (Gerrit)

      unread,
      Jan 20, 2017, 6:33:40 PM1/20/17
      to Brad Fitzpatrick, Matthew Dempsky, golang-co...@googlegroups.com

      Ian Gudger posted comments on this change.

      View Change

      Patch set 9:

      Friendly ping.

        To view, visit this change. To unsubscribe, visit settings.

        Gerrit-Project: net
        Gerrit-Branch: master
        Gerrit-MessageType: comment
        Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
        Gerrit-Change-Number: 35237
        Gerrit-PatchSet: 9
        Gerrit-Owner: Ian Gudger <igu...@google.com>
        Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
        Gerrit-Reviewer: Ian Gudger <igu...@google.com>
        Gerrit-Reviewer: Matthew Dempsky <mdem...@google.com>
        Gerrit-Reviewer: Mikio Hara <mikioh...@gmail.com>
        Gerrit-Comment-Date: Fri, 20 Jan 2017 23:33:05 +0000
        Gerrit-HasComments: No

        Mikio Hara (Gerrit)

        unread,
        Jan 20, 2017, 9:59:12 PM1/20/17
        to Ian Gudger, Brad Fitzpatrick, Matthew Dempsky, golang-co...@googlegroups.com

        Mikio Hara posted comments on this change.

        View Change

        Patch set 9:

        looks like no test cases for the Skip series methods of Parser. do you want to add such test cases in a followup cl?

        (6 comments)

          • Do you have any suggestions? I think RFC 1035 is the main RFC that applies

        To view, visit this change. To unsubscribe, visit settings.

        Gerrit-Project: net
        Gerrit-Branch: master
        Gerrit-MessageType: comment
        Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
        Gerrit-Change-Number: 35237
        Gerrit-PatchSet: 9
        Gerrit-Owner: Ian Gudger <igu...@google.com>
        Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
        Gerrit-Reviewer: Ian Gudger <igu...@google.com>
        Gerrit-Reviewer: Matthew Dempsky <mdem...@google.com>
        Gerrit-Reviewer: Mikio Hara <mikioh...@gmail.com>
        Gerrit-Comment-Date: Sat, 21 Jan 2017 02:59:07 +0000
        Gerrit-HasComments: Yes

        Mikio Hara (Gerrit)

        unread,
        Jan 20, 2017, 10:03:18 PM1/20/17
        to Ian Gudger, Brad Fitzpatrick, Matthew Dempsky, golang-co...@googlegroups.com

        Mikio Hara posted comments on this change.

        View Change

        Patch set 9:Run-TryBot +1Code-Review +1

        LGTM, but i'd like to wait for someone who want to review API surfaces.

          To view, visit this change. To unsubscribe, visit settings.

          Gerrit-Project: net
          Gerrit-Branch: master
          Gerrit-MessageType: comment
          Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
          Gerrit-Change-Number: 35237
          Gerrit-PatchSet: 9
          Gerrit-Owner: Ian Gudger <igu...@google.com>
          Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
          Gerrit-Reviewer: Ian Gudger <igu...@google.com>
          Gerrit-Reviewer: Matthew Dempsky <mdem...@google.com>
          Gerrit-Reviewer: Mikio Hara <mikioh...@gmail.com>
          Gerrit-Comment-Date: Sat, 21 Jan 2017 03:03:15 +0000
          Gerrit-HasComments: No

          Gobot Gobot (Gerrit)

          unread,
          Jan 20, 2017, 10:04:04 PM1/20/17
          to Ian Gudger, Brad Fitzpatrick, Matthew Dempsky, golang-co...@googlegroups.com

          Gobot Gobot posted comments on this change.

          View Change

          Patch set 9:

          TryBots beginning. Status page: http://farmer.golang.org/try?commit=f7c9a498

            To view, visit this change. To unsubscribe, visit settings.

            Gerrit-Project: net
            Gerrit-Branch: master
            Gerrit-MessageType: comment
            Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
            Gerrit-Change-Number: 35237
            Gerrit-PatchSet: 9
            Gerrit-Owner: Ian Gudger <igu...@google.com>
            Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
            Gerrit-Reviewer: Ian Gudger <igu...@google.com>
            Gerrit-Reviewer: Matthew Dempsky <mdem...@google.com>
            Gerrit-Reviewer: Mikio Hara <mikioh...@gmail.com>
            Gerrit-CC: Gobot Gobot <go...@golang.org>
            Gerrit-Comment-Date: Sat, 21 Jan 2017 03:04:01 +0000
            Gerrit-HasComments: No

            Gobot Gobot (Gerrit)

            unread,
            Jan 20, 2017, 10:05:18 PM1/20/17
            to Ian Gudger, Brad Fitzpatrick, Matthew Dempsky, golang-co...@googlegroups.com

            Gobot Gobot posted comments on this change.

            View Change

            Patch set 9:TryBot-Result +1

            TryBots are happy.

              To view, visit this change. To unsubscribe, visit settings.

              Gerrit-Project: net
              Gerrit-Branch: master
              Gerrit-MessageType: comment
              Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
              Gerrit-Change-Number: 35237
              Gerrit-PatchSet: 9
              Gerrit-Owner: Ian Gudger <igu...@google.com>
              Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
              Gerrit-Reviewer: Gobot Gobot <go...@golang.org>
              Gerrit-Reviewer: Ian Gudger <igu...@google.com>
              Gerrit-Reviewer: Matthew Dempsky <mdem...@google.com>
              Gerrit-Reviewer: Mikio Hara <mikioh...@gmail.com>
              Gerrit-Comment-Date: Sat, 21 Jan 2017 03:05:16 +0000
              Gerrit-HasComments: No

              Mikio Hara (Gerrit)

              unread,
              Jan 20, 2017, 11:00:40 PM1/20/17
              to Ian Gudger, Gobot Gobot, Brad Fitzpatrick, Matthew Dempsky, golang-co...@googlegroups.com

              Mikio Hara posted comments on this change.

              View Change

              Patch set 9:

              a few nits.

              (4 comments)

              To view, visit this change. To unsubscribe, visit settings.

              Gerrit-Project: net
              Gerrit-Branch: master
              Gerrit-MessageType: comment
              Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
              Gerrit-Change-Number: 35237
              Gerrit-PatchSet: 9
              Gerrit-Owner: Ian Gudger <igu...@google.com>
              Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
              Gerrit-Reviewer: Gobot Gobot <go...@golang.org>
              Gerrit-Reviewer: Ian Gudger <igu...@google.com>
              Gerrit-Reviewer: Matthew Dempsky <mdem...@google.com>
              Gerrit-Reviewer: Mikio Hara <mikioh...@gmail.com>
              Gerrit-Comment-Date: Sat, 21 Jan 2017 04:00:35 +0000
              Gerrit-HasComments: Yes

              Ian Gudger (Gerrit)

              unread,
              Jan 20, 2017, 11:34:30 PM1/20/17
              to Matthew Dempsky, Gobot Gobot, Brad Fitzpatrick, golang-co...@googlegroups.com

              Ian Gudger uploaded patch set #10 to this change.

              View Change

              dns/message: add support for parsing and packing of DNS packets
              
              The Go standard library contains support for packing and unpacking of
              DNS messages, but it is not exported, doesn't follow Go style, and is
              not very well optimized. Low level DNS functionality is clearly useful
              to the Go community as evidenced by the success of
              
              github.com/miekg/dns. This implementation endeavors to avoid the
              limitations of both the standard library and github.com/miekg/dns
              implementations and is an almost complete rewrite of the code
              currently found in on net/dnsmsg.go and net/dnsmsg_test.go.
              
              Goals:
              * Minimize heap allocations.
              * Allow parsing only what is needed. Avoid unnecessary parsing and
                heap allocations for parts of the message that you don't care about.
                Parsing should be allowed on as small of a granularity as is useful,
                but no smaller as to avoid complicating the interface.
              * Parse and pack each byte of the message at most one time.
              
              Updates golang/go#16218
              Updates golang/go#10622
              
              Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
              ---
              A dns/message/message.go
              A dns/message/message_test.go
              2 files changed, 1,694 insertions(+), 0 deletions(-)
              
              

              To view, visit this change. To unsubscribe, visit settings.

              Gerrit-Project: net
              Gerrit-Branch: master
              Gerrit-MessageType: newpatchset
              Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
              Gerrit-Change-Number: 35237
              Gerrit-PatchSet: 10

              Ian Gudger (Gerrit)

              unread,
              Jan 20, 2017, 11:37:40 PM1/20/17
              to Gobot Gobot, Brad Fitzpatrick, Matthew Dempsky, golang-co...@googlegroups.com

              Ian Gudger posted comments on this change.

              View Change

              Patch set 10:

              (6 comments)

              looks like no test cases for the Skip series methods of Parser. do you want to add such test cases in a followup cl?

              Yes, I am planning to add them in a followup CL.

              (9 comments)

                • isn't it better to align s/packet/message/g ?

                • what do you think about having Type, Class, OpCode and RCode types (and a f

                • Done

                • i feel like it's better to drop obsoleted and experimental constants.

                  Done

                • s/packet/message/

                  Done

              To view, visit this change. To unsubscribe, visit settings.

              Gerrit-Project: net
              Gerrit-Branch: master
              Gerrit-MessageType: comment
              Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
              Gerrit-Change-Number: 35237
              Gerrit-PatchSet: 10
              Gerrit-Owner: Ian Gudger <igu...@google.com>
              Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
              Gerrit-Reviewer: Gobot Gobot <go...@golang.org>
              Gerrit-Reviewer: Ian Gudger <igu...@google.com>
              Gerrit-Reviewer: Matthew Dempsky <mdem...@google.com>
              Gerrit-Reviewer: Mikio Hara <mikioh...@gmail.com>
              Gerrit-Comment-Date: Sat, 21 Jan 2017 04:37:38 +0000
              Gerrit-HasComments: Yes

              Ian Gudger (Gerrit)

              unread,
              Jan 21, 2017, 9:23:52 PM1/21/17
              to Matthew Dempsky, Gobot Gobot, Brad Fitzpatrick, golang-co...@googlegroups.com

              Ian Gudger uploaded patch set #11 to this change.

              View Change

              dns/message: add support for parsing and packing of DNS packets
              
              The Go standard library contains support for packing and unpacking of
              DNS messages, but it is not exported, doesn't follow Go style, and is
              not very well optimized. Low level DNS functionality is clearly useful
              to the Go community as evidenced by the success of
              
              github.com/miekg/dns. This implementation endeavors to avoid the
              limitations of both the standard library and github.com/miekg/dns
              implementations and is an almost complete rewrite of the code
              currently found in on net/dnsmsg.go and net/dnsmsg_test.go.
              
              Goals:
              * Minimize heap allocations.
              * Allow parsing only what is needed. Avoid unnecessary parsing and
                heap allocations for parts of the message that you don't care about.
                Parsing should be allowed on as small of a granularity as is useful,
                but no smaller as to avoid complicating the interface.
              * Parse and pack each byte of the message at most one time.
              
              Updates golang/go#16218
              Updates golang/go#10622
              
              Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
              ---
              A dns/message/message.go
              A dns/message/message_test.go
              2 files changed, 1,736 insertions(+), 0 deletions(-)
              
              

              To view, visit this change. To unsubscribe, visit settings.

              Gerrit-Project: net
              Gerrit-Branch: master
              Gerrit-MessageType: newpatchset
              Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
              Gerrit-Change-Number: 35237
              Gerrit-PatchSet: 11

              Masa Sekimura (Gerrit)

              unread,
              Jan 22, 2017, 11:55:31 AM1/22/17
              to Ian Gudger, Gobot Gobot, Brad Fitzpatrick, Matthew Dempsky, golang-co...@googlegroups.com

              Masa Sekimura posted comments on this change.

              View Change

              Patch set 11:

              (1 comment)

              To view, visit this change. To unsubscribe, visit settings.

              Gerrit-Project: net
              Gerrit-Branch: master
              Gerrit-MessageType: comment
              Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
              Gerrit-Change-Number: 35237
              Gerrit-PatchSet: 11
              Gerrit-Owner: Ian Gudger <igu...@google.com>
              Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
              Gerrit-Reviewer: Gobot Gobot <go...@golang.org>
              Gerrit-Reviewer: Ian Gudger <igu...@google.com>
              Gerrit-Reviewer: Matthew Dempsky <mdem...@google.com>
              Gerrit-Reviewer: Mikio Hara <mikioh...@gmail.com>
              Gerrit-Comment-Date: Sun, 22 Jan 2017 16:55:28 +0000
              Gerrit-HasComments: Yes

              Ian Gudger (Gerrit)

              unread,
              Jan 22, 2017, 2:05:25 PM1/22/17
              to Matthew Dempsky, Gobot Gobot, Brad Fitzpatrick, golang-co...@googlegroups.com

              Ian Gudger uploaded patch set #12 to this change.

              View Change

              dns/message: add support for parsing and packing of DNS packets
              
              The Go standard library contains support for packing and unpacking of
              DNS messages, but it is not exported, doesn't follow Go style, and is
              not very well optimized. Low level DNS functionality is clearly useful
              to the Go community as evidenced by the success of
              
              github.com/miekg/dns. This implementation endeavors to avoid the
              limitations of both the standard library and github.com/miekg/dns
              implementations and is an almost complete rewrite of the code
              currently found in on net/dnsmsg.go and net/dnsmsg_test.go.
              
              Goals:
              * Minimize heap allocations.
              * Allow parsing only what is needed. Avoid unnecessary parsing and
                heap allocations for parts of the message that you don't care about.
                Parsing should be allowed on as small of a granularity as is useful,
                but no smaller as to avoid complicating the interface.
              * Parse and pack each byte of the message at most one time.
              
              Updates golang/go#16218
              Updates golang/go#10622
              
              Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
              ---
              A dns/message/message.go
              A dns/message/message_test.go
              2 files changed, 1,737 insertions(+), 0 deletions(-)
              
              

              To view, visit this change. To unsubscribe, visit settings.

              Gerrit-Project: net
              Gerrit-Branch: master
              Gerrit-MessageType: newpatchset
              Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
              Gerrit-Change-Number: 35237
              Gerrit-PatchSet: 12

              Ian Gudger (Gerrit)

              unread,
              Jan 22, 2017, 3:25:17 PM1/22/17
              to Matthew Dempsky, Gobot Gobot, Brad Fitzpatrick, golang-co...@googlegroups.com

              Ian Gudger uploaded patch set #13 to this change.

              View Change

              dns/message: add support for parsing and packing of DNS packets
              
              The Go standard library contains support for packing and unpacking of
              DNS messages, but it is not exported, doesn't follow Go style, and is
              not very well optimized. Low level DNS functionality is clearly useful
              to the Go community as evidenced by the success of
              
              github.com/miekg/dns. This implementation endeavors to avoid the
              limitations of both the standard library and github.com/miekg/dns
              implementations and is an almost complete rewrite of the code
              currently found in on net/dnsmsg.go and net/dnsmsg_test.go.
              
              Goals:
              * Minimize heap allocations.
              * Allow parsing only what is needed. Avoid unnecessary parsing and
                heap allocations for parts of the message that you don't care about.
                Parsing should be allowed on as small of a granularity as is useful,
                but no smaller as to avoid complicating the interface.
              * Parse and pack each byte of the message at most one time.
              
              Updates golang/go#16218
              Updates golang/go#10622
              
              Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
              ---
              A dns/message/message.go
              A dns/message/message_test.go
              2 files changed, 1,800 insertions(+), 0 deletions(-)
              
              

              To view, visit this change. To unsubscribe, visit settings.

              Gerrit-Project: net
              Gerrit-Branch: master
              Gerrit-MessageType: newpatchset
              Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
              Gerrit-Change-Number: 35237
              Gerrit-PatchSet: 13

              Ian Gudger (Gerrit)

              unread,
              Jan 22, 2017, 3:25:35 PM1/22/17
              to Gobot Gobot, Brad Fitzpatrick, Matthew Dempsky, golang-co...@googlegroups.com

              Ian Gudger posted comments on this change.

              View Change

              Patch set 12:

              (9 comments)

              (6 comments) > looks like no test cases for the Skip series methods of Parser. do you want to add such test cases in a followup cl?

              Yes, I am planning to add them in a followup CL.

              I have added some tests.

              (1 comment)

                • Any reason not to use ResourceRecord or RR? Technically resource records co

                • Done

                  Good point.

              To view, visit this change. To unsubscribe, visit settings.

              Gerrit-Project: net
              Gerrit-Branch: master
              Gerrit-MessageType: comment
              Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
              Gerrit-Change-Number: 35237
              Gerrit-PatchSet: 12
              Gerrit-Owner: Ian Gudger <igu...@google.com>
              Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
              Gerrit-Reviewer: Gobot Gobot <go...@golang.org>
              Gerrit-Reviewer: Ian Gudger <igu...@google.com>
              Gerrit-Reviewer: Matthew Dempsky <mdem...@google.com>
              Gerrit-Reviewer: Mikio Hara <mikioh...@gmail.com>
              Gerrit-Comment-Date: Sun, 22 Jan 2017 20:25:33 +0000
              Gerrit-HasComments: Yes

              Matthew Dempsky (Gerrit)

              unread,
              Jan 23, 2017, 12:44:06 PM1/23/17
              to Ian Gudger, Gobot Gobot, Brad Fitzpatrick, golang-co...@googlegroups.com

              Matthew Dempsky posted comments on this change.

              View Change

              Patch set 12:

              (4 comments)

              To view, visit this change. To unsubscribe, visit settings.

              Gerrit-Project: net
              Gerrit-Branch: master
              Gerrit-MessageType: comment
              Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
              Gerrit-Change-Number: 35237
              Gerrit-PatchSet: 12
              Gerrit-Owner: Ian Gudger <igu...@google.com>
              Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
              Gerrit-Reviewer: Gobot Gobot <go...@golang.org>
              Gerrit-Reviewer: Ian Gudger <igu...@google.com>
              Gerrit-Reviewer: Matthew Dempsky <mdem...@google.com>
              Gerrit-Reviewer: Mikio Hara <mikioh...@gmail.com>
              Gerrit-Comment-Date: Mon, 23 Jan 2017 17:44:03 +0000
              Gerrit-HasComments: Yes

              Ian Gudger (Gerrit)

              unread,
              Jan 24, 2017, 1:20:42 AM1/24/17
              to Matthew Dempsky, Gobot Gobot, Brad Fitzpatrick, golang-co...@googlegroups.com

              Ian Gudger uploaded patch set #14 to this change.

              View Change

              dns/message: add support for parsing and packing of DNS packets
              
              The Go standard library contains support for packing and unpacking of
              DNS messages, but it is not exported, doesn't follow Go style, and is
              not very well optimized. Low level DNS functionality is clearly useful
              to the Go community as evidenced by the success of
              github.com/miekg/dns. This implementation endeavors to avoid the
              limitations of both the standard library and github.com/miekg/dns
              implementations and is an almost complete rewrite of the code
              currently found in on net/dnsmsg.go and net/dnsmsg_test.go.
              
              Goals:
              * Minimize heap allocations.
              * Allow parsing only what is needed. Avoid unnecessary parsing and
                heap allocations for parts of the message that you don't care about.
                Parsing should be allowed on as small of a granularity as is useful,
                but no smaller as to avoid complicating the interface.
              * Parse and pack each byte of the message at most one time.
              
              Updates golang/go#16218
              Updates golang/go#10622
              
              Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
              ---
              A dns/message/message.go
              A dns/message/message_test.go
              2 files changed, 1,829 insertions(+), 0 deletions(-)
              
              

              To view, visit this change. To unsubscribe, visit settings.

              Gerrit-Project: net
              Gerrit-Branch: master
              Gerrit-MessageType: newpatchset
              Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
              Gerrit-Change-Number: 35237
              Gerrit-PatchSet: 14

              Ian Gudger (Gerrit)

              unread,
              Jan 24, 2017, 1:28:34 AM1/24/17
              to Matthew Dempsky, Gobot Gobot, Brad Fitzpatrick, golang-co...@googlegroups.com

              Ian Gudger uploaded patch set #15 to this change.

              View Change

              dns/message: add support for parsing and packing of DNS packets
              
              The Go standard library contains support for packing and unpacking of
              DNS messages, but it is not exported, doesn't follow Go style, and is
              not very well optimized. Low level DNS functionality is clearly useful
              to the Go community as evidenced by the success of
              github.com/miekg/dns. This implementation endeavors to avoid the
              limitations of both the standard library and github.com/miekg/dns
              implementations and is an almost complete rewrite of the code
              currently found in on net/dnsmsg.go and net/dnsmsg_test.go.
              
              Goals:
              * Minimize heap allocations.
              * Allow parsing only what is needed. Avoid unnecessary parsing and
                heap allocations for parts of the message that you don't care about.
                Parsing should be allowed on as small of a granularity as is useful,
                but no smaller as to avoid complicating the interface.
              * Parse and pack each byte of the message at most one time.
              
              Updates golang/go#16218
              Updates golang/go#10622
              
              Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
              ---
              A dns/message/message.go
              A dns/message/message_test.go
              2 files changed, 1,826 insertions(+), 0 deletions(-)
              
              

              To view, visit this change. To unsubscribe, visit settings.

              Gerrit-Project: net
              Gerrit-Branch: master
              Gerrit-MessageType: newpatchset
              Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
              Gerrit-Change-Number: 35237
              Gerrit-PatchSet: 15

              Ian Gudger (Gerrit)

              unread,
              Jan 24, 2017, 1:30:49 AM1/24/17
              to Matthew Dempsky, Gobot Gobot, Brad Fitzpatrick, golang-co...@googlegroups.com

              Ian Gudger posted comments on this change.

              View Change

              Patch set 12:

              (4 comments)

                • For example, the WKS Resource type isn't supported.

                • Done

                • Done

                • Done

              To view, visit this change. To unsubscribe, visit settings.

              Gerrit-Project: net
              Gerrit-Branch: master
              Gerrit-MessageType: comment
              Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
              Gerrit-Change-Number: 35237
              Gerrit-PatchSet: 12
              Gerrit-Owner: Ian Gudger <igu...@google.com>
              Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
              Gerrit-Reviewer: Gobot Gobot <go...@golang.org>
              Gerrit-Reviewer: Ian Gudger <igu...@google.com>
              Gerrit-Reviewer: Matthew Dempsky <mdem...@google.com>
              Gerrit-Reviewer: Mikio Hara <mikioh...@gmail.com>
              Gerrit-Comment-Date: Tue, 24 Jan 2017 06:30:46 +0000
              Gerrit-HasComments: Yes

              Ian Gudger (Gerrit)

              unread,
              Jan 24, 2017, 1:35:08 AM1/24/17
              to Matthew Dempsky, Gobot Gobot, Brad Fitzpatrick, golang-co...@googlegroups.com

              Ian Gudger uploaded patch set #16 to this change.

              View Change

              dns/message: add support for parsing and packing of DNS packets
              
              The Go standard library contains support for packing and unpacking of
              DNS messages, but it is not exported, doesn't follow Go style, and is
              not very well optimized. Low level DNS functionality is clearly useful
              to the Go community as evidenced by the success of
              github.com/miekg/dns. This implementation endeavors to avoid the
              limitations of both the standard library and github.com/miekg/dns
              implementations and is an almost complete rewrite of the code
              currently found in on net/dnsmsg.go and net/dnsmsg_test.go.
              
              Goals:
              * Minimize heap allocations.
              * Allow parsing only what is needed. Avoid unnecessary parsing and
                heap allocations for parts of the message that you don't care about.
                Parsing should be allowed on as small of a granularity as is useful,
                but no smaller as to avoid complicating the interface.
              * Parse and pack each byte of the message at most one time.
              
              Updates golang/go#16218
              Updates golang/go#10622
              
              Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
              ---
              A dns/message/message.go
              A dns/message/message_test.go
              2 files changed, 1,827 insertions(+), 0 deletions(-)
              
              

              To view, visit this change. To unsubscribe, visit settings.

              Gerrit-Project: net
              Gerrit-Branch: master
              Gerrit-MessageType: newpatchset
              Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
              Gerrit-Change-Number: 35237
              Gerrit-PatchSet: 16

              Ian Gudger (Gerrit)

              unread,
              Jan 30, 2017, 3:08:04 AM1/30/17
              to Matthew Dempsky, Gobot Gobot, Brad Fitzpatrick, golang-co...@googlegroups.com

              Ian Gudger posted comments on this change.

              View Change

              Patch set 16:

              Friendly ping...

                To view, visit change 35237. To unsubscribe, visit settings.

                Gerrit-Project: net
                Gerrit-Branch: master
                Gerrit-MessageType: comment
                Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
                Gerrit-Change-Number: 35237
                Gerrit-PatchSet: 16
                Gerrit-Owner: Ian Gudger <igu...@google.com>
                Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
                Gerrit-Reviewer: Gobot Gobot <go...@golang.org>
                Gerrit-Reviewer: Ian Gudger <igu...@google.com>
                Gerrit-Reviewer: Matthew Dempsky <mdem...@google.com>
                Gerrit-Reviewer: Mikio Hara <mikioh...@gmail.com>
                Gerrit-Comment-Date: Mon, 30 Jan 2017 08:08:01 +0000
                Gerrit-HasComments: No

                Brad Fitzpatrick (Gerrit)

                unread,
                Jan 30, 2017, 11:34:55 AM1/30/17
                to Ian Gudger, Brad Fitzpatrick, Matthew Dempsky, Gobot Gobot, golang-co...@googlegroups.com

                Brad Fitzpatrick posted comments on this change.

                View Change

                Patch set 16:

                Sorry, we've been focusing on getting the Go 1.8 release out.

                I also think we need to have a plan for who the co-owner of this is. I can review API at least, but I can't do a thorough review. Matthew, what's you time look like?

                  To view, visit change 35237. To unsubscribe, visit settings.

                  Gerrit-Project: net
                  Gerrit-Branch: master
                  Gerrit-MessageType: comment
                  Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
                  Gerrit-Change-Number: 35237
                  Gerrit-PatchSet: 16
                  Gerrit-Owner: Ian Gudger <igu...@google.com>
                  Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
                  Gerrit-Reviewer: Gobot Gobot <go...@golang.org>
                  Gerrit-Reviewer: Ian Gudger <igu...@google.com>
                  Gerrit-Reviewer: Matthew Dempsky <mdem...@google.com>
                  Gerrit-Reviewer: Mikio Hara <mikioh...@gmail.com>
                  Gerrit-Comment-Date: Mon, 30 Jan 2017 16:34:52 +0000
                  Gerrit-HasComments: No

                  Matthew Dempsky (Gerrit)

                  unread,
                  Jan 30, 2017, 2:41:50 PM1/30/17
                  to Ian Gudger, Brad Fitzpatrick, Gobot Gobot, golang-co...@googlegroups.com

                  Matthew Dempsky posted comments on this change.

                  View Change

                  Patch set 16:

                  I can review API at least, but I can't do a thorough review. Matthew, what's you time look like?

                  I should have more time to review now.

                  FWIW, I was imagining an even simpler / lower-level interface than Parser (which could probably be used within Parser). E.g., djbdns uses just these three functions for decoding DNS packets: https://cr.yp.to/djbdns/dns_packet.html

                  Translating to Go, I'd expect something like:

                      type Packet []byte
                      func (p Packet) Copy(pos int, out []byte) int
                      func (p Packet) GetName(pos int) (Name, int)
                      func (p Packet) SkipName(pos int) int
                      // A Name is a domain name in wire format.
                      type Name []byte

                  djbdns returns 0 to indicate an error, but I'd probably use -1 in Go or an error return parameter.

                  (3 comments)

                    • I would just say RFC 1035 compliant in that case. WKS, HINFO, MINFO, etc. have been deprecated by later RFCs.

                  • File dns/message/message.go:

                    • Patch Set #16, Line 422: func (p *Parser) Answer() (Resource, error) {

                      Having separate Answer/Authority/Additional methods seems like a code smell to me. If Parser is already internally going to track which resource section it's in, why make the caller also responsible for that?

                      I'd suggest either Parser continues internally tracking the section, and just includes the section in the return values like so:

                          func (p *Parser) Resource() (Resource, Section, error)

                      Or Parser just blindly parses Resources, and the caller is solely responsible for tracking which section they're in:

                          func (p *Parser) Resource() (Resource, error)
                    • Patch Set #16, Line 604: ns

                      Maybe just rename "a", "ns", and "e" to "r" for resource?

                  To view, visit change 35237. To unsubscribe, visit settings.

                  Gerrit-Project: net
                  Gerrit-Branch: master
                  Gerrit-MessageType: comment
                  Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
                  Gerrit-Change-Number: 35237
                  Gerrit-PatchSet: 16
                  Gerrit-Owner: Ian Gudger <igu...@google.com>
                  Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
                  Gerrit-Reviewer: Gobot Gobot <go...@golang.org>
                  Gerrit-Reviewer: Ian Gudger <igu...@google.com>
                  Gerrit-Reviewer: Matthew Dempsky <mdem...@google.com>
                  Gerrit-Reviewer: Mikio Hara <mikioh...@gmail.com>
                  Gerrit-Comment-Date: Mon, 30 Jan 2017 19:41:47 +0000
                  Gerrit-HasComments: Yes

                  Ian Gudger (Gerrit)

                  unread,
                  Jan 30, 2017, 3:08:51 PM1/30/17
                  to Matthew Dempsky, Brad Fitzpatrick, Gobot Gobot, golang-co...@googlegroups.com

                  Ian Gudger posted comments on this change.

                  View Change

                  Patch set 16:

                  (3 comments)

                  I can review API at least, but I can't do a thorough review. Matthew, what's you time look like?

                  I should have more time to review now.

                  FWIW, I was imagining an even simpler / lower-level interface than Parser (which could probably be used within Parser). E.g., djbdns uses just these three functions for decoding DNS packets: https://cr.yp.to/djbdns/dns_packet.html

                  Translating to Go, I'd expect something like:

                  type Packet []byte

                  func (p Packet) Copy(pos int, out []byte) int func (p Packet) GetName(pos int) (Name, int) func (p Packet) SkipName(pos int) int

                  // A Name is a domain name in wire format. type Name []byte

                  djbdns returns 0 to indicate an error, but I'd probably use -1 in Go or an error return parameter.

                  I reviewed the djbdns code and tried to take its approach. I started off with just the packType, unpackType and skipType functions which are roughly how djbdns works and are what I think you are suggesting.

                  I then thought about how one might actually incrementally parse a DNS message and why they might want to do it. I decided that whole headers and resource records was likely the smallest unit that would be useful to choose between parsing and skipping and left the lower level functions unexported.

                  I am happy to export the low level functionality, but I want to provide a high level interface for ease of use.

                  (1 comment)

                    • I want to give the option to easily only parse one type. I am fine with exposing low-level functionality, but I want to include a high level abstraction as well.

                  To view, visit change 35237. To unsubscribe, visit settings.

                  Gerrit-Project: net
                  Gerrit-Branch: master
                  Gerrit-MessageType: comment
                  Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
                  Gerrit-Change-Number: 35237
                  Gerrit-PatchSet: 16
                  Gerrit-Owner: Ian Gudger <igu...@google.com>
                  Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
                  Gerrit-Reviewer: Gobot Gobot <go...@golang.org>
                  Gerrit-Reviewer: Ian Gudger <igu...@google.com>
                  Gerrit-Reviewer: Matthew Dempsky <mdem...@google.com>
                  Gerrit-Reviewer: Mikio Hara <mikioh...@gmail.com>
                  Gerrit-Comment-Date: Mon, 30 Jan 2017 20:08:49 +0000
                  Gerrit-HasComments: Yes

                  Matthew Dempsky (Gerrit)

                  unread,
                  Jan 31, 2017, 12:32:17 PM1/31/17
                  to Ian Gudger, Brad Fitzpatrick, Gobot Gobot, golang-co...@googlegroups.com

                  Matthew Dempsky posted comments on this change.

                  View Change

                  Patch set 16:

                  I then thought about how one might actually incrementally parse a DNS message and why they might want to do it. I decided that whole headers and resource records was likely the smallest unit that would be useful to choose between parsing and skipping and left the lower level functions unexported.

                  In DNS client code, usually clients are hard coded to handle just a couple record types. For example, I would expect a typical use within package net to look something like:

                    var addrs []IPAddr
                    scanner := dns.NewScanner(msg)
                    for scanner.Answer() {
                      if scanner.Class() != dns.ClassINET || scanner.Name().Equals(qname) {
                        continue
                      }
                      switch scanner.Type() {
                      case dns.TypeA:
                        var d dns.AData
                        if err := d.Unmarshal(&scanner); err != nil {
                          return nil, err
                        }
                        addrs = append(addrs, IPAddr{IP: IPv4(d.A[0], d.A[1], d.A[2], d.A[3])})
                      case dns.TypeAAAA:
                        var d dns.AAAAData
                        if err := rr.Unmarshal(&scanner); err != nil {
                          return nil, err
                        }
                        addrs = append(addrs, IPAddr{IP: d.A[:]})
                      }
                    }
                    if err := scanner.Err(); err != nil {
                      return nil, err
                    }
                    return addrs, nil

                  Aside from adding elements to addrs, this shouldn't require any heap allocations.

                  For Name() to be alloc-free, it would return a type like

                    // A Name is a domain name in wire format.
                    type Name []byte
                    // Equals compares n and x for domain name equality (i.e., ASCII case-insensitively).
                    func (n Name) Equals(x Name) bool

                  Because domain names are limited to 255 bytes (or 256 bytes for MDNS), the scanner object could store a 256 byte buffer internally to hold the name of the current record.

                  A higher level interface would be to provide a method like:

                    func (s *Scanner) Data() (Data, error) {
                      var d Data
                      switch s.Type() {
                      case dns.TypeA:
                        d = new(dns.AData)
                      case dns.TypeAAAA:
                        d = new(dns.AAAAData)
                      case dns.TypeMX:
                        d = new(dns.MXData)
                      ...
                      default:
                        return nil, errUnknownType // or decode generically
                      }
                      if err := d.Unmarshal(s); err != nil {
                        return nil, err
                      }
                      return d, nil
                    }

                  which would be a bit more user friendly, but necessitate heap allocations.

                  And then stacking on top of that, you can easily decode into a complete Message object.

                  WDYT?

                    To view, visit change 35237. To unsubscribe, visit settings.

                    Gerrit-Project: net
                    Gerrit-Branch: master
                    Gerrit-MessageType: comment
                    Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
                    Gerrit-Change-Number: 35237
                    Gerrit-PatchSet: 16
                    Gerrit-Owner: Ian Gudger <igu...@google.com>
                    Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
                    Gerrit-Reviewer: Gobot Gobot <go...@golang.org>
                    Gerrit-Reviewer: Ian Gudger <igu...@google.com>
                    Gerrit-Reviewer: Matthew Dempsky <mdem...@google.com>
                    Gerrit-Reviewer: Mikio Hara <mikioh...@gmail.com>
                    Gerrit-Comment-Date: Tue, 31 Jan 2017 17:32:15 +0000
                    Gerrit-HasComments: No

                    Mikio Hara (Gerrit)

                    unread,
                    Feb 5, 2017, 11:16:42 PM2/5/17
                    to Ian Gudger, Matthew Dempsky, Brad Fitzpatrick, Gobot Gobot, golang-co...@googlegroups.com

                    Mikio Hara posted comments on this change.

                    View Change

                    Patch set 16:

                      scanner := dns.NewScanner(msg)

                    if we need to implement a type like Scanner, i think it should also handle a stream of messages for connection-oriented byte-stream dns transports. it perhaps might be better to take an io.Reader interface instead of a slice of byte, to be able to skip processing on corrupted messages.

                    not sure for unmarshaling, but for marshaling, having a type like "type WireFormat []byte" and its methods looks fine for avoiding unnecessary allocation on message (bulk) transmission.

                    as long as we leave space for addition of stream processing of dns messages on any dns transport, i'm fine with any conclusions.

                      To view, visit change 35237. To unsubscribe, visit settings.

                      Gerrit-Project: net
                      Gerrit-Branch: master
                      Gerrit-MessageType: comment
                      Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
                      Gerrit-Change-Number: 35237
                      Gerrit-PatchSet: 16
                      Gerrit-Owner: Ian Gudger <igu...@google.com>
                      Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
                      Gerrit-Reviewer: Gobot Gobot <go...@golang.org>
                      Gerrit-Reviewer: Ian Gudger <igu...@google.com>
                      Gerrit-Reviewer: Matthew Dempsky <mdem...@google.com>
                      Gerrit-Reviewer: Mikio Hara <mikioh...@gmail.com>
                      Gerrit-Comment-Date: Mon, 06 Feb 2017 04:16:34 +0000
                      Gerrit-HasComments: No

                      Matthew Dempsky (Gerrit)

                      unread,
                      Feb 6, 2017, 1:30:00 PM2/6/17
                      to Ian Gudger, Brad Fitzpatrick, Gobot Gobot, golang-co...@googlegroups.com

                      Matthew Dempsky posted comments on this change.

                      View Change

                      Patch set 16:

                      if we need to implement a type like Scanner, i think it should also handle a stream of messages for connection-oriented byte-stream dns transports. it perhaps might be better to take an io.Reader interface instead of a slice of byte, to be able to skip processing on corrupted messages.

                      I'm not convinced the extra complexity is warranted. DNS packets are limited to 64kB and require random access to the first 16kB to handle name compression anyway.

                        To view, visit change 35237. To unsubscribe, visit settings.

                        Gerrit-Project: net
                        Gerrit-Branch: master
                        Gerrit-MessageType: comment
                        Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
                        Gerrit-Change-Number: 35237
                        Gerrit-PatchSet: 16
                        Gerrit-Owner: Ian Gudger <igu...@google.com>
                        Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
                        Gerrit-Reviewer: Gobot Gobot <go...@golang.org>
                        Gerrit-Reviewer: Ian Gudger <igu...@google.com>
                        Gerrit-Reviewer: Matthew Dempsky <mdem...@google.com>
                        Gerrit-Reviewer: Mikio Hara <mikioh...@gmail.com>
                        Gerrit-Comment-Date: Mon, 06 Feb 2017 18:29:58 +0000
                        Gerrit-HasComments: No

                        Mikio Hara (Gerrit)

                        unread,
                        Feb 7, 2017, 10:33:55 PM2/7/17
                        to Ian Gudger, Matthew Dempsky, Brad Fitzpatrick, Gobot Gobot, golang-co...@googlegroups.com

                        Mikio Hara posted comments on this change.

                        View Change

                        Patch set 16:

                        I'm not convinced the extra complexity is warranted.

                        perhaps it's useful only for AXFR/zone transfer. each response message for client still requires ancillary information for its transport and it might be better to do such job in another package.

                        well, i think patch set #16 has a good shape (except a few nits for documentation), and am fine either way; doing s/Parser/Scanner/g or not.

                          To view, visit change 35237. To unsubscribe, visit settings.

                          Gerrit-Project: net
                          Gerrit-Branch: master
                          Gerrit-MessageType: comment
                          Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
                          Gerrit-Change-Number: 35237
                          Gerrit-PatchSet: 16
                          Gerrit-Owner: Ian Gudger <igu...@google.com>
                          Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
                          Gerrit-Reviewer: Gobot Gobot <go...@golang.org>
                          Gerrit-Reviewer: Ian Gudger <igu...@google.com>
                          Gerrit-Reviewer: Matthew Dempsky <mdem...@google.com>
                          Gerrit-Reviewer: Mikio Hara <mikioh...@gmail.com>
                          Gerrit-Comment-Date: Wed, 08 Feb 2017 03:33:49 +0000
                          Gerrit-HasComments: No

                          Ian Gudger (Gerrit)

                          unread,
                          Feb 7, 2017, 10:45:46 PM2/7/17
                          to Matthew Dempsky, Brad Fitzpatrick, Gobot Gobot, golang-co...@googlegroups.com

                          Ian Gudger posted comments on this change.

                          View Change

                          Patch set 16:

                          Patch Set 16:

                          I am thinking about replacing the nil RR meaning done with a section done error.

                          I can add additional methods on the parser like this: func (*Parser) AnswerHeader() (ResourceHeader, error) which parse the header if not already parsed. I can store the parsed ResourceHeader in the Parser so it doesn't need to be parsed again if Answer() is called.

                          This won't prevent a string allocation for the name, but in my opinion it is a nicer interface.

                            To view, visit change 35237. To unsubscribe, visit settings.

                            Gerrit-Project: net
                            Gerrit-Branch: master
                            Gerrit-MessageType: comment
                            Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
                            Gerrit-Change-Number: 35237
                            Gerrit-PatchSet: 16
                            Gerrit-Owner: Ian Gudger <igu...@google.com>
                            Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
                            Gerrit-Reviewer: Gobot Gobot <go...@golang.org>
                            Gerrit-Reviewer: Ian Gudger <igu...@google.com>
                            Gerrit-Reviewer: Matthew Dempsky <mdem...@google.com>
                            Gerrit-Reviewer: Mikio Hara <mikioh...@gmail.com>
                            Gerrit-Comment-Date: Wed, 08 Feb 2017 03:45:43 +0000
                            Gerrit-HasComments: No

                            Matthew Dempsky (Gerrit)

                            unread,
                            Feb 7, 2017, 11:50:37 PM2/7/17
                            to Ian Gudger, Brad Fitzpatrick, Gobot Gobot, golang-co...@googlegroups.com

                            Matthew Dempsky posted comments on this change.

                            View Change

                            Patch set 16:

                            perhaps it's useful only for AXFR/zone transfer.

                            Even with AXFR, the response is chunked into DNS messages that are each <=64KiB.

                              To view, visit change 35237. To unsubscribe, visit settings.

                              Gerrit-Project: net
                              Gerrit-Branch: master
                              Gerrit-MessageType: comment
                              Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
                              Gerrit-Change-Number: 35237
                              Gerrit-PatchSet: 16
                              Gerrit-Owner: Ian Gudger <igu...@google.com>
                              Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
                              Gerrit-Reviewer: Gobot Gobot <go...@golang.org>
                              Gerrit-Reviewer: Ian Gudger <igu...@google.com>
                              Gerrit-Reviewer: Matthew Dempsky <mdem...@google.com>
                              Gerrit-Reviewer: Mikio Hara <mikioh...@gmail.com>
                              Gerrit-Comment-Date: Wed, 08 Feb 2017 04:50:35 +0000
                              Gerrit-HasComments: No

                              Mikio Hara (Gerrit)

                              unread,
                              Feb 8, 2017, 11:22:39 PM2/8/17
                              to Ian Gudger, Matthew Dempsky, Brad Fitzpatrick, Gobot Gobot, golang-co...@googlegroups.com

                              Mikio Hara posted comments on this change.

                              View Change

                              Patch set 16:

                              Even with AXFR, the response is chunked into DNS messages that are each <=64KiB.

                              yup, i now think that this package should focus on a single message marshaling and unmarshaling.

                                To view, visit change 35237. To unsubscribe, visit settings.

                                Gerrit-Project: net
                                Gerrit-Branch: master
                                Gerrit-MessageType: comment
                                Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
                                Gerrit-Change-Number: 35237
                                Gerrit-PatchSet: 16
                                Gerrit-Owner: Ian Gudger <igu...@google.com>
                                Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
                                Gerrit-Reviewer: Gobot Gobot <go...@golang.org>
                                Gerrit-Reviewer: Ian Gudger <igu...@google.com>
                                Gerrit-Reviewer: Matthew Dempsky <mdem...@google.com>
                                Gerrit-Reviewer: Mikio Hara <mikioh...@gmail.com>
                                Gerrit-Comment-Date: Thu, 09 Feb 2017 04:22:37 +0000
                                Gerrit-HasComments: No

                                Ian Gudger (Gerrit)

                                unread,
                                Feb 12, 2017, 5:43:59 PM2/12/17
                                to Matthew Dempsky, Gobot Gobot, Brad Fitzpatrick, golang-co...@googlegroups.com

                                Ian Gudger uploaded patch set #17 to this change.

                                View Change

                                dns/message: add support for parsing and packing of DNS packets
                                
                                The Go standard library contains support for packing and unpacking of
                                DNS messages, but it is not exported, doesn't follow Go style, and is
                                not very well optimized. Low level DNS functionality is clearly useful
                                to the Go community as evidenced by the success of
                                github.com/miekg/dns. This implementation endeavors to avoid the
                                limitations of both the standard library and github.com/miekg/dns
                                implementations and is an almost complete rewrite of the code
                                currently found in on net/dnsmsg.go and net/dnsmsg_test.go.
                                
                                Goals:
                                * Minimize heap allocations.
                                * Allow parsing only what is needed. Avoid unnecessary parsing and
                                  heap allocations for parts of the message that you don't care about.
                                  Parsing should be allowed on as small of a granularity as is useful,
                                  but no smaller as to avoid complicating the interface.
                                * Parse and pack each byte of the message at most one time.
                                
                                Updates golang/go#16218
                                Updates golang/go#10622
                                
                                Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
                                ---
                                A dns/message/message.go
                                A dns/message/message_test.go
                                2 files changed, 1,872 insertions(+), 0 deletions(-)
                                
                                

                                To view, visit change 35237. To unsubscribe, visit settings.

                                Gerrit-Project: net
                                Gerrit-Branch: master
                                Gerrit-MessageType: newpatchset
                                Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
                                Gerrit-Change-Number: 35237
                                Gerrit-PatchSet: 17

                                Ian Gudger (Gerrit)

                                unread,
                                Feb 12, 2017, 5:44:51 PM2/12/17
                                to Matthew Dempsky, Brad Fitzpatrick, Gobot Gobot, golang-co...@googlegroups.com

                                Ian Gudger posted comments on this change.

                                View Change

                                Patch set 17:

                                Patch Set 16:

                                PTAL

                                  To view, visit change 35237. To unsubscribe, visit settings.

                                  Gerrit-Project: net
                                  Gerrit-Branch: master
                                  Gerrit-MessageType: comment
                                  Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
                                  Gerrit-Change-Number: 35237
                                  Gerrit-PatchSet: 17
                                  Gerrit-Owner: Ian Gudger <igu...@google.com>
                                  Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
                                  Gerrit-Reviewer: Gobot Gobot <go...@golang.org>
                                  Gerrit-Reviewer: Ian Gudger <igu...@google.com>
                                  Gerrit-Reviewer: Matthew Dempsky <mdem...@google.com>
                                  Gerrit-Reviewer: Mikio Hara <mikioh...@gmail.com>
                                  Gerrit-Comment-Date: Sun, 12 Feb 2017 22:44:49 +0000
                                  Gerrit-HasComments: No

                                  Matthew Dempsky (Gerrit)

                                  unread,
                                  Feb 14, 2017, 1:17:27 PM2/14/17
                                  to Ian Gudger, Brad Fitzpatrick, Gobot Gobot, golang-co...@googlegroups.com

                                  Matthew Dempsky posted comments on this change.

                                  View Change

                                  Patch set 17:

                                  PTAL

                                  LGTM protocol wise.

                                  Can you add an Example (i.e., https://golang.org/pkg/testing/#hdr-Examples) for how to parse the A/AAAA answer records from a DNS message for a given qname (analogous to what I proposed earlier)? As long as bradfitz@ thinks it looks okay, I'm happy.

                                    To view, visit change 35237. To unsubscribe, visit settings.

                                    Gerrit-Project: net
                                    Gerrit-Branch: master
                                    Gerrit-MessageType: comment
                                    Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
                                    Gerrit-Change-Number: 35237
                                    Gerrit-PatchSet: 17
                                    Gerrit-Owner: Ian Gudger <igu...@google.com>
                                    Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
                                    Gerrit-Reviewer: Gobot Gobot <go...@golang.org>
                                    Gerrit-Reviewer: Ian Gudger <igu...@google.com>
                                    Gerrit-Reviewer: Matthew Dempsky <mdem...@google.com>
                                    Gerrit-Reviewer: Mikio Hara <mikioh...@gmail.com>
                                    Gerrit-Comment-Date: Tue, 14 Feb 2017 18:17:24 +0000
                                    Gerrit-HasComments: No

                                    Ian Gudger (Gerrit)

                                    unread,
                                    Feb 14, 2017, 3:47:28 PM2/14/17
                                    to Matthew Dempsky, Gobot Gobot, Brad Fitzpatrick, golang-co...@googlegroups.com

                                    Ian Gudger uploaded patch set #18 to this change.

                                    View Change

                                    dns/message: add support for parsing and packing of DNS packets
                                    
                                    The Go standard library contains support for packing and unpacking of
                                    DNS messages, but it is not exported, doesn't follow Go style, and is
                                    not very well optimized. Low level DNS functionality is clearly useful
                                    to the Go community as evidenced by the success of
                                    github.com/miekg/dns. This implementation endeavors to avoid the
                                    limitations of both the standard library and github.com/miekg/dns
                                    implementations and is an almost complete rewrite of the code
                                    currently found in on net/dnsmsg.go and net/dnsmsg_test.go.
                                    
                                    Goals:
                                    * Minimize heap allocations.
                                    * Allow parsing only what is needed. Avoid unnecessary parsing and
                                      heap allocations for parts of the message that you don't care about.
                                      Parsing should be allowed on as small of a granularity as is useful,
                                      but no smaller as to avoid complicating the interface.
                                    * Parse and pack each byte of the message at most one time.
                                    
                                    Updates golang/go#16218
                                    Updates golang/go#10622
                                    
                                    Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
                                    ---
                                    A dns/message/message.go
                                    A dns/message/message_test.go
                                    2 files changed, 1,980 insertions(+), 0 deletions(-)
                                    
                                    

                                    To view, visit change 35237. To unsubscribe, visit settings.

                                    Gerrit-Project: net
                                    Gerrit-Branch: master
                                    Gerrit-MessageType: newpatchset
                                    Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
                                    Gerrit-Change-Number: 35237
                                    Gerrit-PatchSet: 18

                                    Ian Gudger (Gerrit)

                                    unread,
                                    Feb 14, 2017, 3:48:31 PM2/14/17
                                    to Matthew Dempsky, Brad Fitzpatrick, Gobot Gobot, golang-co...@googlegroups.com

                                    Ian Gudger posted comments on this change.

                                    View Change

                                    Patch set 18:

                                    Patch Set 17:

                                    PTAL

                                    LGTM protocol wise.

                                    Can you add an Example (i.e., https://golang.org/pkg/testing/#hdr-Examples) for how to parse the A/AAAA answer records from a DNS message for a given qname (analogous to what I proposed earlier)? As long as bradfitz@ thinks it looks okay, I'm happy.

                                    I added a test. Is that OK?

                                      To view, visit change 35237. To unsubscribe, visit settings.

                                      Gerrit-Project: net
                                      Gerrit-Branch: master
                                      Gerrit-MessageType: comment
                                      Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
                                      Gerrit-Change-Number: 35237
                                      Gerrit-PatchSet: 18
                                      Gerrit-Owner: Ian Gudger <igu...@google.com>
                                      Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
                                      Gerrit-Reviewer: Gobot Gobot <go...@golang.org>
                                      Gerrit-Reviewer: Ian Gudger <igu...@google.com>
                                      Gerrit-Reviewer: Matthew Dempsky <mdem...@google.com>
                                      Gerrit-Reviewer: Mikio Hara <mikioh...@gmail.com>
                                      Gerrit-Comment-Date: Tue, 14 Feb 2017 20:48:29 +0000
                                      Gerrit-HasComments: No

                                      Matthew Dempsky (Gerrit)

                                      unread,
                                      Feb 14, 2017, 4:37:12 PM2/14/17
                                      to Ian Gudger, Brad Fitzpatrick, Gobot Gobot, golang-co...@googlegroups.com

                                      Matthew Dempsky posted comments on this change.

                                      View Change

                                      Patch set 18:

                                      I added a test. Is that OK?

                                      I think an Example would be better. I think extracting A/AAAA records is a pretty representative example of how people will use Parser, so including an best-practices example would be good for documentation purposes. (Examples can also still behave as tests: https://blog.golang.org/examples)

                                        To view, visit change 35237. To unsubscribe, visit settings.

                                        Gerrit-Project: net
                                        Gerrit-Branch: master
                                        Gerrit-MessageType: comment
                                        Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
                                        Gerrit-Change-Number: 35237
                                        Gerrit-PatchSet: 18
                                        Gerrit-Owner: Ian Gudger <igu...@google.com>
                                        Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
                                        Gerrit-Reviewer: Gobot Gobot <go...@golang.org>
                                        Gerrit-Reviewer: Ian Gudger <igu...@google.com>
                                        Gerrit-Reviewer: Matthew Dempsky <mdem...@google.com>
                                        Gerrit-Reviewer: Mikio Hara <mikioh...@gmail.com>
                                        Gerrit-Comment-Date: Tue, 14 Feb 2017 21:37:08 +0000
                                        Gerrit-HasComments: No

                                        Ian Gudger (Gerrit)

                                        unread,
                                        Feb 14, 2017, 4:48:43 PM2/14/17
                                        to Matthew Dempsky, Gobot Gobot, Brad Fitzpatrick, golang-co...@googlegroups.com

                                        Ian Gudger uploaded patch set #19 to this change.

                                        View Change

                                        dns/message: add support for parsing and packing of DNS packets
                                        
                                        The Go standard library contains support for packing and unpacking of
                                        DNS messages, but it is not exported, doesn't follow Go style, and is
                                        not very well optimized. Low level DNS functionality is clearly useful
                                        to the Go community as evidenced by the success of
                                        github.com/miekg/dns. This implementation endeavors to avoid the
                                        limitations of both the standard library and github.com/miekg/dns
                                        implementations and is an almost complete rewrite of the code
                                        currently found in on net/dnsmsg.go and net/dnsmsg_test.go.
                                        
                                        Goals:
                                        * Minimize heap allocations.
                                        * Allow parsing only what is needed. Avoid unnecessary parsing and
                                          heap allocations for parts of the message that you don't care about.
                                          Parsing should be allowed on as small of a granularity as is useful,
                                          but no smaller as to avoid complicating the interface.
                                        * Parse and pack each byte of the message at most one time.
                                        
                                        Updates golang/go#16218
                                        Updates golang/go#10622
                                        
                                        Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
                                        ---
                                        A dns/message/message.go
                                        A dns/message/message_test.go
                                        2 files changed, 1,993 insertions(+), 0 deletions(-)
                                        
                                        

                                        To view, visit change 35237. To unsubscribe, visit settings.

                                        Gerrit-Project: net
                                        Gerrit-Branch: master
                                        Gerrit-MessageType: newpatchset
                                        Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
                                        Gerrit-Change-Number: 35237
                                        Gerrit-PatchSet: 19

                                        Ian Gudger (Gerrit)

                                        unread,
                                        Feb 14, 2017, 4:49:28 PM2/14/17
                                        to Matthew Dempsky, Brad Fitzpatrick, Gobot Gobot, golang-co...@googlegroups.com

                                        Ian Gudger posted comments on this change.

                                        View Change

                                        Patch set 19:

                                        Patch Set 18:

                                        I added a test. Is that OK?

                                        I think an Example would be better. I think extracting A/AAAA records is a pretty representative example of how people will use Parser, so including an best-practices example would be good for documentation purposes. (Examples can also still behave as tests: https://blog.golang.org/examples)

                                        PTAL

                                          To view, visit change 35237. To unsubscribe, visit settings.

                                          Gerrit-Project: net
                                          Gerrit-Branch: master
                                          Gerrit-MessageType: comment
                                          Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
                                          Gerrit-Change-Number: 35237
                                          Gerrit-PatchSet: 19
                                          Gerrit-Owner: Ian Gudger <igu...@google.com>
                                          Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
                                          Gerrit-Reviewer: Gobot Gobot <go...@golang.org>
                                          Gerrit-Reviewer: Ian Gudger <igu...@google.com>
                                          Gerrit-Reviewer: Matthew Dempsky <mdem...@google.com>
                                          Gerrit-Reviewer: Mikio Hara <mikioh...@gmail.com>
                                          Gerrit-Comment-Date: Tue, 14 Feb 2017 21:49:25 +0000
                                          Gerrit-HasComments: No

                                          Ian Gudger (Gerrit)

                                          unread,
                                          Feb 20, 2017, 1:43:59 AM2/20/17
                                          to Matthew Dempsky, Brad Fitzpatrick, Gobot Gobot, golang-co...@googlegroups.com

                                          Ian Gudger posted comments on this change.

                                          View Change

                                          Patch set 19:

                                          Patch Set 19:

                                          Patch Set 18:

                                          I added a test. Is that OK?

                                          I think an Example would be better. I think extracting A/AAAA records is a pretty representative example of how people will use Parser, so including an best-practices example would be good for documentation purposes. (Examples can also still behave as tests: https://blog.golang.org/examples)

                                          PTAL

                                          Friendly ping. Is my example ok?

                                            To view, visit change 35237. To unsubscribe, visit settings.

                                            Gerrit-Project: net
                                            Gerrit-Branch: master
                                            Gerrit-MessageType: comment
                                            Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
                                            Gerrit-Change-Number: 35237
                                            Gerrit-PatchSet: 19
                                            Gerrit-Owner: Ian Gudger <igu...@google.com>
                                            Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
                                            Gerrit-Reviewer: Gobot Gobot <go...@golang.org>
                                            Gerrit-Reviewer: Ian Gudger <igu...@google.com>
                                            Gerrit-Reviewer: Matthew Dempsky <mdem...@google.com>
                                            Gerrit-Reviewer: Mikio Hara <mikioh...@gmail.com>
                                            Gerrit-Comment-Date: Mon, 20 Feb 2017 06:43:58 +0000
                                            Gerrit-HasComments: No

                                            Mikio Hara (Gerrit)

                                            unread,
                                            Feb 20, 2017, 8:12:20 PM2/20/17
                                            to Ian Gudger, Matthew Dempsky, Brad Fitzpatrick, Gobot Gobot, golang-co...@googlegroups.com

                                            Mikio Hara posted comments on this change.

                                            View Change

                                            Patch set 19:

                                            (1 comment)

                                            To view, visit change 35237. To unsubscribe, visit settings.

                                            Gerrit-Project: net
                                            Gerrit-Branch: master
                                            Gerrit-MessageType: comment
                                            Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
                                            Gerrit-Change-Number: 35237
                                            Gerrit-PatchSet: 19
                                            Gerrit-Owner: Ian Gudger <igu...@google.com>
                                            Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
                                            Gerrit-Reviewer: Gobot Gobot <go...@golang.org>
                                            Gerrit-Reviewer: Ian Gudger <igu...@google.com>
                                            Gerrit-Reviewer: Matthew Dempsky <mdem...@google.com>
                                            Gerrit-Reviewer: Mikio Hara <mikioh...@gmail.com>
                                            Gerrit-Comment-Date: Tue, 21 Feb 2017 01:12:14 +0000
                                            Gerrit-HasComments: Yes

                                            Ian Gudger (Gerrit)

                                            unread,
                                            Feb 20, 2017, 8:33:03 PM2/20/17
                                            to Matthew Dempsky, Gobot Gobot, Brad Fitzpatrick, golang-co...@googlegroups.com

                                            Ian Gudger uploaded patch set #20 to this change.

                                            View Change

                                            dns/message: add support for parsing and packing of DNS messages
                                            
                                            The Go standard library contains support for packing and unpacking of
                                            DNS messages, but it is not exported, doesn't follow Go style, and is
                                            not very well optimized. Low level DNS functionality is clearly useful
                                            to the Go community as evidenced by the success of
                                            
                                            github.com/miekg/dns. This implementation endeavors to avoid the
                                            limitations of both the standard library and github.com/miekg/dns
                                            implementations and is an almost complete rewrite of the code
                                            currently found in on net/dnsmsg.go and net/dnsmsg_test.go.
                                            
                                            Goals:
                                            * Minimize heap allocations.
                                            * Allow parsing only what is needed. Avoid unnecessary parsing and
                                              heap allocations for parts of the message that you don't care about.
                                              Parsing should be allowed on as small of a granularity as is useful,
                                              but no smaller as to avoid complicating the interface.
                                            * Parse and pack each byte of the message at most one time.
                                            
                                            Updates golang/go#16218
                                            Updates golang/go#10622
                                            
                                            Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
                                            ---
                                            A dns/message/message.go
                                            A dns/message/message_test.go
                                            2 files changed, 1,993 insertions(+), 0 deletions(-)
                                            
                                            

                                            To view, visit change 35237. To unsubscribe, visit settings.

                                            Gerrit-Project: net
                                            Gerrit-Branch: master
                                            Gerrit-MessageType: newpatchset
                                            Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
                                            Gerrit-Change-Number: 35237
                                            Gerrit-PatchSet: 20

                                            Ian Gudger (Gerrit)

                                            unread,
                                            Feb 20, 2017, 8:33:47 PM2/20/17
                                            to Matthew Dempsky, Brad Fitzpatrick, Gobot Gobot, golang-co...@googlegroups.com

                                            Ian Gudger posted comments on this change.

                                            View Change

                                            Patch set 20:

                                            (1 comment)

                                            To view, visit change 35237. To unsubscribe, visit settings.

                                            Gerrit-Project: net
                                            Gerrit-Branch: master
                                            Gerrit-MessageType: comment
                                            Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
                                            Gerrit-Change-Number: 35237
                                            Gerrit-PatchSet: 20
                                            Gerrit-Owner: Ian Gudger <igu...@google.com>
                                            Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
                                            Gerrit-Reviewer: Gobot Gobot <go...@golang.org>
                                            Gerrit-Reviewer: Ian Gudger <igu...@google.com>
                                            Gerrit-Reviewer: Matthew Dempsky <mdem...@google.com>
                                            Gerrit-Comment-Date: Tue, 21 Feb 2017 01:33:45 +0000
                                            Gerrit-HasComments: Yes

                                            Mikio Hara (Gerrit)

                                            unread,
                                            Feb 20, 2017, 8:52:03 PM2/20/17
                                            to Ian Gudger, Matthew Dempsky, Brad Fitzpatrick, Gobot Gobot, golang-co...@googlegroups.com

                                            Mikio Hara posted comments on this change.

                                            View Change

                                            Patch set 20:Code-Review +1

                                            lgtm; leave to someone in the go team or inner circle.

                                              To view, visit change 35237. To unsubscribe, visit settings.

                                              Gerrit-Project: net
                                              Gerrit-Branch: master
                                              Gerrit-MessageType: comment
                                              Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
                                              Gerrit-Change-Number: 35237
                                              Gerrit-PatchSet: 20
                                              Gerrit-Owner: Ian Gudger <igu...@google.com>
                                              Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
                                              Gerrit-Reviewer: Gobot Gobot <go...@golang.org>
                                              Gerrit-Reviewer: Ian Gudger <igu...@google.com>
                                              Gerrit-Reviewer: Matthew Dempsky <mdem...@google.com>
                                              Gerrit-Reviewer: Mikio Hara <mikioh...@gmail.com>
                                              Gerrit-Comment-Date: Tue, 21 Feb 2017 01:52:00 +0000
                                              Gerrit-HasComments: No

                                              Matthew Dempsky (Gerrit)

                                              unread,
                                              Feb 22, 2017, 12:50:18 PM2/22/17
                                              to Ian Gudger, Brad Fitzpatrick, Gobot Gobot, golang-co...@googlegroups.com

                                              Matthew Dempsky posted comments on this change.

                                              View Change

                                              Patch set 20:

                                              Brad: PTAL at the API and example.

                                              (6 comments)

                                              • File dns/message/message_test.go:

                                                • Patch Set #20, Line 318:

                                                  for {
                                                  		q, err := p.Question()
                                                  		if err == ErrSectionDone {
                                                  			fmt.Println("Searched all questions without finding question for", wantName)
                                                  			break
                                                  		}
                                                  		if err != nil {
                                                  			fmt.Println("Searching for question:", err)
                                                  			return
                                                  		}
                                                  		if q.Name != wantName {
                                                  			continue
                                                  		}
                                                  		fmt.Println("Found question for name", wantName)
                                                  		if err := p.SkipAllQuestions(); err != nil {
                                                  			fmt.Println("Skipping remaining questions:", err)
                                                  			return
                                                  		}
                                                  		break
                                                  	}
                                                  

                                                  Can this all just be replaced by p.SkipAllQuestions()?

                                                • Patch Set #20, Line 342: fmt.Println("Searched all answers without finding answer for", wantName)

                                                  IMO, all of these fmt.Printlns clutter up the example without adding any extra value.

                                                  In the error cases, I recommend just using "panic(err)". Cases like this, you can include a comment if necessary, but I think ErrSectionDone is self-descriptive enough.

                                                • Patch Set #20, Line 349: if h.Name != wantName {

                                                  Does this handle case-insensitivity automatically?

                                                  Also, we should be checking the RR class.

                                                  Lastly, can we skip the p.Answer() call if Type is not A/AAAA so we don't unmarshal unnecessary record types?

                                                • Patch Set #20, Line 361: ar, ok := a.(*AResource)

                                                  Please handle both A and AAAA records.

                                                • Patch Set #20, Line 366: if ar.A != wantIP {

                                                  Accumulate the values into an []IPAddr and just print that at the end. There's no need for wantIP, since the Output check below will enforce that we extracted the correct IP anyway.

                                                • Patch Set #20, Line 370: break

                                                  remove the break. There can be multiple A/AAAA records for a single domain name. In general, code should loop and extract them all.

                                              To view, visit change 35237. To unsubscribe, visit settings.

                                              Gerrit-Project: net
                                              Gerrit-Branch: master
                                              Gerrit-MessageType: comment
                                              Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
                                              Gerrit-Change-Number: 35237
                                              Gerrit-PatchSet: 20
                                              Gerrit-Owner: Ian Gudger <igu...@google.com>
                                              Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
                                              Gerrit-Reviewer: Gobot Gobot <go...@golang.org>
                                              Gerrit-Reviewer: Ian Gudger <igu...@google.com>
                                              Gerrit-Reviewer: Matthew Dempsky <mdem...@google.com>
                                              Gerrit-Reviewer: Mikio Hara <mikioh...@gmail.com>
                                              Gerrit-Comment-Date: Wed, 22 Feb 2017 17:50:16 +0000
                                              Gerrit-HasComments: Yes

                                              Matthew Dempsky (Gerrit)

                                              unread,
                                              Feb 22, 2017, 3:55:48 PM2/22/17
                                              to Ian Gudger, Brad Fitzpatrick, Gobot Gobot, golang-co...@googlegroups.com

                                              Matthew Dempsky posted comments on this change.

                                              View Change

                                              Patch set 20:

                                              In DNS client code, usually clients are hard coded to handle just a couple record types. For example, I would expect a typical use within package net to look something like:

                                              FWIW, I sketched out a proof-of-concept for the API I proposed: https://github.com/mdempsky/dns

                                              The BenchmarkScanner loop is alloc-free.

                                                To view, visit change 35237. To unsubscribe, visit settings.

                                                Gerrit-Project: net
                                                Gerrit-Branch: master
                                                Gerrit-MessageType: comment
                                                Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
                                                Gerrit-Change-Number: 35237
                                                Gerrit-PatchSet: 20
                                                Gerrit-Owner: Ian Gudger <igu...@google.com>
                                                Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
                                                Gerrit-Reviewer: Gobot Gobot <go...@golang.org>
                                                Gerrit-Reviewer: Ian Gudger <igu...@google.com>
                                                Gerrit-Reviewer: Matthew Dempsky <mdem...@google.com>
                                                Gerrit-Reviewer: Mikio Hara <mikioh...@gmail.com>
                                                Gerrit-Comment-Date: Wed, 22 Feb 2017 20:55:45 +0000
                                                Gerrit-HasComments: No

                                                Matthew Dempsky (Gerrit)

                                                unread,
                                                Feb 27, 2017, 3:29:41 PM2/27/17
                                                to Ian Gudger, Brad Fitzpatrick, Gobot Gobot, golang-co...@googlegroups.com

                                                Matthew Dempsky posted comments on this change.

                                                View Change

                                                Patch set 20:

                                                Brad: Do you mind reviewing just the API for Go style? Mikio and I already reviewed the implementation for DNS-specific issues.

                                                  To view, visit change 35237. To unsubscribe, visit settings.

                                                  Gerrit-Project: net
                                                  Gerrit-Branch: master
                                                  Gerrit-MessageType: comment
                                                  Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
                                                  Gerrit-Change-Number: 35237
                                                  Gerrit-PatchSet: 20
                                                  Gerrit-Owner: Ian Gudger <igu...@google.com>
                                                  Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
                                                  Gerrit-Reviewer: Gobot Gobot <go...@golang.org>
                                                  Gerrit-Reviewer: Ian Gudger <igu...@google.com>
                                                  Gerrit-Reviewer: Matthew Dempsky <mdem...@google.com>
                                                  Gerrit-Comment-Date: Mon, 27 Feb 2017 20:29:39 +0000
                                                  Gerrit-HasComments: No

                                                  Brad Fitzpatrick (Gerrit)

                                                  unread,
                                                  Feb 27, 2017, 7:30:11 PM2/27/17
                                                  to Ian Gudger, Brad Fitzpatrick, Matthew Dempsky, Gobot Gobot, golang-co...@googlegroups.com

                                                  Brad Fitzpatrick posted comments on this change.

                                                  View Change

                                                  Patch set 20:Code-Review +1

                                                  API looks good.

                                                  Minor comments, but feel free to ignore.

                                                  (4 comments)

                                                  To view, visit change 35237. To unsubscribe, visit settings.

                                                  Gerrit-Project: net
                                                  Gerrit-Branch: master
                                                  Gerrit-MessageType: comment
                                                  Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
                                                  Gerrit-Change-Number: 35237
                                                  Gerrit-PatchSet: 20
                                                  Gerrit-Owner: Ian Gudger <igu...@google.com>
                                                  Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
                                                  Gerrit-Reviewer: Gobot Gobot <go...@golang.org>
                                                  Gerrit-Reviewer: Ian Gudger <igu...@google.com>
                                                  Gerrit-Reviewer: Matthew Dempsky <mdem...@google.com>
                                                  Gerrit-Comment-Date: Tue, 28 Feb 2017 00:30:09 +0000
                                                  Gerrit-HasComments: Yes

                                                  Ian Gudger (Gerrit)

                                                  unread,
                                                  Feb 27, 2017, 10:50:14 PM2/27/17
                                                  to Matthew Dempsky, Gobot Gobot, Brad Fitzpatrick, golang-co...@googlegroups.com

                                                  Ian Gudger uploaded patch set #21 to this change.

                                                  View Change

                                                  dns/dnsmessage: add support for parsing and packing of DNS messages
                                                  
                                                  The Go standard library contains support for packing and unpacking of
                                                  DNS messages, but it is not exported, doesn't follow Go style, and is
                                                  not very well optimized. Low level DNS functionality is clearly useful
                                                  to the Go community as evidenced by the success of
                                                  
                                                  github.com/miekg/dns. This implementation endeavors to avoid the
                                                  limitations of both the standard library and github.com/miekg/dns
                                                  implementations and is an almost complete rewrite of the code
                                                  currently found in on net/dnsmsg.go and net/dnsmsg_test.go.
                                                  
                                                  Goals:
                                                  * Minimize heap allocations.
                                                  * Allow parsing only what is needed. Avoid unnecessary parsing and
                                                    heap allocations for parts of the message that you don't care about.
                                                    Parsing should be allowed on as small of a granularity as is useful,
                                                    but no smaller as to avoid complicating the interface.
                                                  * Parse and pack each byte of the message at most one time.
                                                  
                                                  Updates golang/go#16218
                                                  Updates golang/go#10622
                                                  
                                                  Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
                                                  ---
                                                  A dns/dnsmessage/message.go
                                                  A dns/dnsmessage/message_test.go
                                                  2 files changed, 1,993 insertions(+), 0 deletions(-)
                                                  
                                                  

                                                  To view, visit change 35237. To unsubscribe, visit settings.

                                                  Gerrit-Project: net
                                                  Gerrit-Branch: master
                                                  Gerrit-MessageType: newpatchset
                                                  Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
                                                  Gerrit-Change-Number: 35237
                                                  Gerrit-PatchSet: 21

                                                  Ian Gudger (Gerrit)

                                                  unread,
                                                  Feb 27, 2017, 11:00:05 PM2/27/17
                                                  to Brad Fitzpatrick, Matthew Dempsky, Gobot Gobot, golang-co...@googlegroups.com

                                                  Ian Gudger posted comments on this change.

                                                  View Change

                                                  Patch set 21:

                                                  (10 comments)

                                                    • wrong year. mostly new code, no?

                                                      packName/unpackName include some code from net. I don't know how old it is, but the original file says Copyright 2009.

                                                    • "message" is a little vague.

                                                      Done

                                                    • Is there any precedent for the name "Start"?

                                                      I found a few in the standard library: exec.Cmd.Start textproto.Pipeline.StartRequest textproto.sequencer.Start httptest.Server.Start gc.Timings.Start

                                                    • Can this all just be replaced by p.SkipAllQuestions()?

                                                    • I wanted to check that the question I was looking for is present.

                                                    • IMO, all of these fmt.Printlns clutter up the example without adding any ex

                                                      Done

                                                    • Does this handle case-insensitivity automatically?

                                                      Done

                                                    • Please handle both A and AAAA records.

                                                      Done

                                                    • Accumulate the values into an []IPAddr and just print that at the end. Ther

                                                      Done

                                                    • remove the break. There can be multiple A/AAAA records for a single domain

                                                      Done

                                                  To view, visit change 35237. To unsubscribe, visit settings.

                                                  Gerrit-Project: net
                                                  Gerrit-Branch: master
                                                  Gerrit-MessageType: comment
                                                  Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
                                                  Gerrit-Change-Number: 35237
                                                  Gerrit-PatchSet: 21
                                                  Gerrit-Owner: Ian Gudger <igu...@google.com>
                                                  Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
                                                  Gerrit-Reviewer: Gobot Gobot <go...@golang.org>
                                                  Gerrit-Reviewer: Ian Gudger <igu...@google.com>
                                                  Gerrit-Reviewer: Matthew Dempsky <mdem...@google.com>
                                                  Gerrit-Comment-Date: Tue, 28 Feb 2017 04:00:03 +0000
                                                  Gerrit-HasComments: Yes

                                                  Ian Gudger (Gerrit)

                                                  unread,
                                                  Mar 1, 2017, 2:45:57 PM3/1/17
                                                  to Brad Fitzpatrick, Matthew Dempsky, Gobot Gobot, golang-co...@googlegroups.com

                                                  Ian Gudger posted comments on this change.

                                                  View Change

                                                  Patch set 21:

                                                  Friendly ping, is this ok to merge?

                                                    To view, visit change 35237. To unsubscribe, visit settings.

                                                    Gerrit-Project: net
                                                    Gerrit-Branch: master
                                                    Gerrit-MessageType: comment
                                                    Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
                                                    Gerrit-Change-Number: 35237
                                                    Gerrit-PatchSet: 21
                                                    Gerrit-Owner: Ian Gudger <igu...@google.com>
                                                    Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
                                                    Gerrit-Reviewer: Gobot Gobot <go...@golang.org>
                                                    Gerrit-Reviewer: Ian Gudger <igu...@google.com>
                                                    Gerrit-Reviewer: Matthew Dempsky <mdem...@google.com>
                                                    Gerrit-Comment-Date: Wed, 01 Mar 2017 19:45:55 +0000
                                                    Gerrit-HasComments: No

                                                    Mikio Hara (Gerrit)

                                                    unread,
                                                    Mar 3, 2017, 4:05:40 AM3/3/17
                                                    to Ian Gudger, Brad Fitzpatrick, Matthew Dempsky, Gobot Gobot, golang-co...@googlegroups.com

                                                    Mikio Hara posted comments on this change.

                                                    View Change

                                                    Patch set 21:Run-TryBot +1Code-Review +1

                                                      To view, visit change 35237. To unsubscribe, visit settings.

                                                      Gerrit-Project: net
                                                      Gerrit-Branch: master
                                                      Gerrit-MessageType: comment
                                                      Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
                                                      Gerrit-Change-Number: 35237
                                                      Gerrit-PatchSet: 21
                                                      Gerrit-Owner: Ian Gudger <igu...@google.com>
                                                      Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
                                                      Gerrit-Reviewer: Gobot Gobot <go...@golang.org>
                                                      Gerrit-Reviewer: Ian Gudger <igu...@google.com>
                                                      Gerrit-Reviewer: Matthew Dempsky <mdem...@google.com>
                                                      Gerrit-Reviewer: Mikio Hara <mikioh...@gmail.com>
                                                      Gerrit-Comment-Date: Fri, 03 Mar 2017 09:05:36 +0000
                                                      Gerrit-HasComments: No

                                                      Gobot Gobot (Gerrit)

                                                      unread,
                                                      Mar 3, 2017, 4:06:19 AM3/3/17
                                                      to Ian Gudger, Brad Fitzpatrick, Matthew Dempsky, golang-co...@googlegroups.com

                                                      Gobot Gobot posted comments on this change.

                                                      View Change

                                                      Patch set 21:

                                                      TryBots beginning. Status page: http://farmer.golang.org/try?commit=5b2ef574

                                                      Gobot Gobot (Gerrit)

                                                      unread,
                                                      Mar 3, 2017, 4:07:46 AM3/3/17
                                                      to Ian Gudger, Brad Fitzpatrick, Matthew Dempsky, golang-co...@googlegroups.com

                                                      Gobot Gobot posted comments on this change.

                                                      View Change

                                                      Patch set 21:TryBot-Result +1

                                                      TryBots are happy.

                                                        To view, visit change 35237. To unsubscribe, visit settings.

                                                        Gerrit-Project: net
                                                        Gerrit-Branch: master
                                                        Gerrit-MessageType: comment
                                                        Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
                                                        Gerrit-Change-Number: 35237
                                                        Gerrit-PatchSet: 21
                                                        Gerrit-Owner: Ian Gudger <igu...@google.com>
                                                        Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
                                                        Gerrit-Reviewer: Gobot Gobot <go...@golang.org>
                                                        Gerrit-Reviewer: Ian Gudger <igu...@google.com>
                                                        Gerrit-Reviewer: Matthew Dempsky <mdem...@google.com>
                                                        Gerrit-Reviewer: Mikio Hara <mikioh...@gmail.com>
                                                        Gerrit-Comment-Date: Fri, 03 Mar 2017 09:07:44 +0000
                                                        Gerrit-HasComments: No

                                                        Mikio Hara (Gerrit)

                                                        unread,
                                                        Mar 3, 2017, 4:11:17 AM3/3/17
                                                        to Ian Gudger, Gobot Gobot, Brad Fitzpatrick, Matthew Dempsky, golang-co...@googlegroups.com

                                                        Mikio Hara posted comments on this change.

                                                        View Change

                                                        Patch set 21:Code-Review +2

                                                        lgtm

                                                          To view, visit change 35237. To unsubscribe, visit settings.

                                                          Gerrit-Project: net
                                                          Gerrit-Branch: master
                                                          Gerrit-MessageType: comment
                                                          Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
                                                          Gerrit-Change-Number: 35237
                                                          Gerrit-PatchSet: 21
                                                          Gerrit-Owner: Ian Gudger <igu...@google.com>
                                                          Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
                                                          Gerrit-Reviewer: Gobot Gobot <go...@golang.org>
                                                          Gerrit-Reviewer: Ian Gudger <igu...@google.com>
                                                          Gerrit-Reviewer: Matthew Dempsky <mdem...@google.com>
                                                          Gerrit-Reviewer: Mikio Hara <mikioh...@gmail.com>
                                                          Gerrit-Comment-Date: Fri, 03 Mar 2017 09:11:13 +0000
                                                          Gerrit-HasComments: No

                                                          Ian Gudger (Gerrit)

                                                          unread,
                                                          Mar 3, 2017, 4:27:31 PM3/3/17
                                                          to Gobot Gobot, Brad Fitzpatrick, Matthew Dempsky, golang-co...@googlegroups.com

                                                          Ian Gudger posted comments on this change.

                                                          View Change

                                                          Patch set 21:

                                                          lgtm

                                                          Does someone want to submit this? I don't have permission.

                                                            To view, visit change 35237. To unsubscribe, visit settings.

                                                            Gerrit-Project: net
                                                            Gerrit-Branch: master
                                                            Gerrit-MessageType: comment
                                                            Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
                                                            Gerrit-Change-Number: 35237
                                                            Gerrit-PatchSet: 21
                                                            Gerrit-Owner: Ian Gudger <igu...@google.com>
                                                            Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
                                                            Gerrit-Reviewer: Gobot Gobot <go...@golang.org>
                                                            Gerrit-Reviewer: Ian Gudger <igu...@google.com>
                                                            Gerrit-Reviewer: Matthew Dempsky <mdem...@google.com>
                                                            Gerrit-Reviewer: Mikio Hara <mikioh...@gmail.com>
                                                            Gerrit-Comment-Date: Fri, 03 Mar 2017 21:27:29 +0000
                                                            Gerrit-HasComments: No

                                                            Matthew Dempsky (Gerrit)

                                                            unread,
                                                            Mar 3, 2017, 4:50:38 PM3/3/17
                                                            to Ian Gudger, golang-...@googlegroups.com, Gobot Gobot, Brad Fitzpatrick, golang-co...@googlegroups.com

                                                            Matthew Dempsky merged this change.

                                                            View Change

                                                            Approvals: Mikio Hara: Looks good to me, approved; Run TryBots Gobot Gobot: TryBots succeeded
                                                            dns/dnsmessage: add support for parsing and packing of DNS messages
                                                            
                                                            The Go standard library contains support for packing and unpacking of
                                                            DNS messages, but it is not exported, doesn't follow Go style, and is
                                                            not very well optimized. Low level DNS functionality is clearly useful
                                                            to the Go community as evidenced by the success of
                                                            github.com/miekg/dns. This implementation endeavors to avoid the
                                                            limitations of both the standard library and github.com/miekg/dns
                                                            implementations and is an almost complete rewrite of the code
                                                            currently found in on net/dnsmsg.go and net/dnsmsg_test.go.
                                                            
                                                            Goals:
                                                            * Minimize heap allocations.
                                                            * Allow parsing only what is needed. Avoid unnecessary parsing and
                                                              heap allocations for parts of the message that you don't care about.
                                                              Parsing should be allowed on as small of a granularity as is useful,
                                                              but no smaller as to avoid complicating the interface.
                                                            * Parse and pack each byte of the message at most one time.
                                                            
                                                            Updates golang/go#16218
                                                            Updates golang/go#10622
                                                            
                                                            Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
                                                            Reviewed-on: https://go-review.googlesource.com/35237
                                                            Run-TryBot: Mikio Hara <mikioh...@gmail.com>
                                                            TryBot-Result: Gobot Gobot <go...@golang.org>
                                                            Reviewed-by: Mikio Hara <mikioh...@gmail.com>
                                                            ---
                                                            A dns/dnsmessage/message.go
                                                            A dns/dnsmessage/message_test.go
                                                            2 files changed, 1,993 insertions(+), 0 deletions(-)
                                                            
                                                            
                                                            diff --git a/dns/dnsmessage/message.go b/dns/dnsmessage/message.go
                                                            new file mode 100644
                                                            index 0000000..da43b0b
                                                            --- /dev/null
                                                            +++ b/dns/dnsmessage/message.go
                                                            @@ -0,0 +1,1418 @@
                                                            +// 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.
                                                            +
                                                            +// Package dnsmessage provides a mostly RFC 1035 compliant implementation of
                                                            +// DNS message packing and unpacking.
                                                            +//
                                                            +// This implementation is designed to minimize heap allocations and avoid
                                                            +// unnecessary packing and unpacking as much as possible.
                                                            +package dnsmessage
                                                            +
                                                            +import (
                                                            +	"errors"
                                                            +)
                                                            +
                                                            +// Packet formats
                                                            +
                                                            +// A Type is a type of DNS request and response.
                                                            +type Type uint16
                                                            +
                                                            +// A Class is a type of network.
                                                            +type Class uint16
                                                            +
                                                            +// An OpCode is a DNS operation code.
                                                            +type OpCode uint16
                                                            +
                                                            +// An RCode is a DNS response status code.
                                                            +type RCode uint16
                                                            +
                                                            +// Wire constants.
                                                            +const (
                                                            +	// ResourceHeader.Type and Question.Type
                                                            +	TypeA     Type = 1
                                                            +	TypeNS    Type = 2
                                                            +	TypeCNAME Type = 5
                                                            +	TypeSOA   Type = 6
                                                            +	TypePTR   Type = 12
                                                            +	TypeMX    Type = 15
                                                            +	TypeTXT   Type = 16
                                                            +	TypeAAAA  Type = 28
                                                            +	TypeSRV   Type = 33
                                                            +
                                                            +	// Question.Type
                                                            +	TypeWKS   Type = 11
                                                            +	TypeHINFO Type = 13
                                                            +	TypeMINFO Type = 14
                                                            +	TypeAXFR  Type = 252
                                                            +	TypeALL   Type = 255
                                                            +
                                                            +	// ResourceHeader.Class and Question.Class
                                                            +	ClassINET   Class = 1
                                                            +	ClassCSNET  Class = 2
                                                            +	ClassCHAOS  Class = 3
                                                            +	ClassHESIOD Class = 4
                                                            +
                                                            +	// Question.Class
                                                            +	ClassANY Class = 255
                                                            +
                                                            +	// Message.Rcode
                                                            +	RCodeSuccess        RCode = 0
                                                            +	RCodeFormatError    RCode = 1
                                                            +	RCodeServerFailure  RCode = 2
                                                            +	RCodeNameError      RCode = 3
                                                            +	RCodeNotImplemented RCode = 4
                                                            +	RCodeRefused        RCode = 5
                                                            +)
                                                            +
                                                            +var (
                                                            +	// ErrNotStarted indicates that the prerequisite information isn't
                                                            +	// available yet because the previous records haven't been appropriately
                                                            +	// parsed or skipped.
                                                            +	ErrNotStarted = errors.New("parsing of this type isn't available yet")
                                                            +
                                                            +	// ErrSectionDone indicated that all records in the section have been
                                                            +	// parsed.
                                                            +	ErrSectionDone = errors.New("parsing of this section has completed")
                                                            +
                                                            +	errBaseLen            = errors.New("insufficient data for base length type")
                                                            +	errCalcLen            = errors.New("insufficient data for calculated length type")
                                                            +	errReserved           = errors.New("segment prefix is reserved")
                                                            +	errTooManyPtr         = errors.New("too many pointers (>10)")
                                                            +	errInvalidPtr         = errors.New("invalid pointer")
                                                            +	errResourceLen        = errors.New("insufficient data for resource body length")
                                                            +	errSegTooLong         = errors.New("segment length too long")
                                                            +	errZeroSegLen         = errors.New("zero length segment")
                                                            +	errResTooLong         = errors.New("resource length too long")
                                                            +	errTooManyQuestions   = errors.New("too many Questions to pack (>65535)")
                                                            +	errTooManyAnswers     = errors.New("too many Answers to pack (>65535)")
                                                            +	errTooManyAuthorities = errors.New("too many Authorities to pack (>65535)")
                                                            +	errTooManyAdditionals = errors.New("too many Additionals to pack (>65535)")
                                                            +)
                                                            +
                                                            +type nestedError struct {
                                                            +	// s is the current level's error message.
                                                            +	s string
                                                            +
                                                            +	// err is the nested error.
                                                            +	err error
                                                            +}
                                                            +
                                                            +// nestedError implements error.Error.
                                                            +func (e *nestedError) Error() string {
                                                            +	return e.s + ": " + e.err.Error()
                                                            +}
                                                            +
                                                            +// Header is a representation of a DNS message header.
                                                            +type Header struct {
                                                            +	ID                 uint16
                                                            +	Response           bool
                                                            +	OpCode             OpCode
                                                            +	Authoritative      bool
                                                            +	Truncated          bool
                                                            +	RecursionDesired   bool
                                                            +	RecursionAvailable bool
                                                            +	RCode              RCode
                                                            +}
                                                            +
                                                            +func (m *Header) pack() (id uint16, bits uint16) {
                                                            +	id = m.ID
                                                            +	bits = uint16(m.OpCode)<<11 | uint16(m.RCode)
                                                            +	if m.RecursionAvailable {
                                                            +		bits |= headerBitRA
                                                            +	}
                                                            +	if m.RecursionDesired {
                                                            +		bits |= headerBitRD
                                                            +	}
                                                            +	if m.Truncated {
                                                            +		bits |= headerBitTC
                                                            +	}
                                                            +	if m.Authoritative {
                                                            +		bits |= headerBitAA
                                                            +	}
                                                            +	if m.Response {
                                                            +		bits |= headerBitQR
                                                            +	}
                                                            +	return
                                                            +}
                                                            +
                                                            +// Message is a representation of a DNS message.
                                                            +type Message struct {
                                                            +	Header
                                                            +	Questions   []Question
                                                            +	Answers     []Resource
                                                            +	Authorities []Resource
                                                            +	Additionals []Resource
                                                            +}
                                                            +
                                                            +type section uint8
                                                            +
                                                            +const (
                                                            +	sectionHeader section = iota
                                                            +	sectionQuestions
                                                            +	sectionAnswers
                                                            +	sectionAuthorities
                                                            +	sectionAdditionals
                                                            +	sectionDone
                                                            +
                                                            +	headerBitQR = 1 << 15 // query/response (response=1)
                                                            +	headerBitAA = 1 << 10 // authoritative
                                                            +	headerBitTC = 1 << 9  // truncated
                                                            +	headerBitRD = 1 << 8  // recursion desired
                                                            +	headerBitRA = 1 << 7  // recursion available
                                                            +)
                                                            +
                                                            +var sectionNames = map[section]string{
                                                            +	sectionHeader:      "header",
                                                            +	sectionQuestions:   "Question",
                                                            +	sectionAnswers:     "Answer",
                                                            +	sectionAuthorities: "Authority",
                                                            +	sectionAdditionals: "Additional",
                                                            +}
                                                            +
                                                            +// header is the wire format for a DNS message header.
                                                            +type header struct {
                                                            +	id          uint16
                                                            +	bits        uint16
                                                            +	questions   uint16
                                                            +	answers     uint16
                                                            +	authorities uint16
                                                            +	additionals uint16
                                                            +}
                                                            +
                                                            +func (h *header) count(sec section) uint16 {
                                                            +	switch sec {
                                                            +	case sectionQuestions:
                                                            +		return h.questions
                                                            +	case sectionAnswers:
                                                            +		return h.answers
                                                            +	case sectionAuthorities:
                                                            +		return h.authorities
                                                            +	case sectionAdditionals:
                                                            +		return h.additionals
                                                            +	}
                                                            +	return 0
                                                            +}
                                                            +
                                                            +func (h *header) pack(msg []byte) []byte {
                                                            +	msg = packUint16(msg, h.id)
                                                            +	msg = packUint16(msg, h.bits)
                                                            +	msg = packUint16(msg, h.questions)
                                                            +	msg = packUint16(msg, h.answers)
                                                            +	msg = packUint16(msg, h.authorities)
                                                            +	return packUint16(msg, h.additionals)
                                                            +}
                                                            +
                                                            +func (h *header) unpack(msg []byte, off int) (int, error) {
                                                            +	newOff := off
                                                            +	var err error
                                                            +	if h.id, newOff, err = unpackUint16(msg, newOff); err != nil {
                                                            +		return off, &nestedError{"id", err}
                                                            +	}
                                                            +	if h.bits, newOff, err = unpackUint16(msg, newOff); err != nil {
                                                            +		return off, &nestedError{"bits", err}
                                                            +	}
                                                            +	if h.questions, newOff, err = unpackUint16(msg, newOff); err != nil {
                                                            +		return off, &nestedError{"questions", err}
                                                            +	}
                                                            +	if h.answers, newOff, err = unpackUint16(msg, newOff); err != nil {
                                                            +		return off, &nestedError{"answers", err}
                                                            +	}
                                                            +	if h.authorities, newOff, err = unpackUint16(msg, newOff); err != nil {
                                                            +		return off, &nestedError{"authorities", err}
                                                            +	}
                                                            +	if h.additionals, newOff, err = unpackUint16(msg, newOff); err != nil {
                                                            +		return off, &nestedError{"additionals", err}
                                                            +	}
                                                            +	return newOff, nil
                                                            +}
                                                            +
                                                            +func (h *header) header() Header {
                                                            +	return Header{
                                                            +		ID:                 h.id,
                                                            +		Response:           (h.bits & headerBitQR) != 0,
                                                            +		OpCode:             OpCode(h.bits>>11) & 0xF,
                                                            +		Authoritative:      (h.bits & headerBitAA) != 0,
                                                            +		Truncated:          (h.bits & headerBitTC) != 0,
                                                            +		RecursionDesired:   (h.bits & headerBitRD) != 0,
                                                            +		RecursionAvailable: (h.bits & headerBitRA) != 0,
                                                            +		RCode:              RCode(h.bits & 0xF),
                                                            +	}
                                                            +}
                                                            +
                                                            +// A Resource is a DNS resource record.
                                                            +type Resource interface {
                                                            +	// Header return's the Resource's ResourceHeader.
                                                            +	Header() *ResourceHeader
                                                            +
                                                            +	// pack packs a Resource except for its header.
                                                            +	pack(msg []byte, compression map[string]int) ([]byte, error)
                                                            +
                                                            +	// realType returns the actual type of the Resource. This is used to
                                                            +	// fill in the header Type field.
                                                            +	realType() Type
                                                            +}
                                                            +
                                                            +func packResource(msg []byte, resource Resource, compression map[string]int) ([]byte, error) {
                                                            +	oldMsg := msg
                                                            +	resource.Header().Type = resource.realType()
                                                            +	msg, length, err := resource.Header().pack(msg, compression)
                                                            +	if err != nil {
                                                            +		return msg, &nestedError{"ResourceHeader", err}
                                                            +	}
                                                            +	preLen := len(msg)
                                                            +	msg, err = resource.pack(msg, compression)
                                                            +	if err != nil {
                                                            +		return msg, &nestedError{"content", err}
                                                            +	}
                                                            +	conLen := len(msg) - preLen
                                                            +	if conLen > int(^uint16(0)) {
                                                            +		return oldMsg, errResTooLong
                                                            +	}
                                                            +	// Fill in the length now that we know how long the content is.
                                                            +	packUint16(length[:0], uint16(conLen))
                                                            +	resource.Header().Length = uint16(conLen)
                                                            +	return msg, nil
                                                            +}
                                                            +
                                                            +// A Parser allows incrementally parsing a DNS message.
                                                            +//
                                                            +// When parsing is started, the Header is parsed. Next, each Question can be
                                                            +// either parsed or skipped. Alternatively, all Questions can be skipped at
                                                            +// once. When all Questions have been parsed, attempting to parse Questions
                                                            +// will return (nil, nil) and attempting to skip Questions will return
                                                            +// (true, nil). After all Questions have been either parsed or skipped, all
                                                            +// Answers, Authorities and Additionals can be either parsed or skipped in the
                                                            +// same way, and each type of Resource must be fully parsed or skipped before
                                                            +// proceeding to the next type of Resource.
                                                            +//
                                                            +// Note that there is no requirement to fully skip or parse the message.
                                                            +type Parser struct {
                                                            +	msg    []byte
                                                            +	header header
                                                            +
                                                            +	section        section
                                                            +	off            int
                                                            +	index          int
                                                            +	resHeaderValid bool
                                                            +	resHeader      ResourceHeader
                                                            +}
                                                            +
                                                            +// Start parses the header and enables the parsing of Questions.
                                                            +func (p *Parser) Start(msg []byte) (Header, error) {
                                                            +	if p.msg != nil {
                                                            +		*p = Parser{}
                                                            +	}
                                                            +	p.msg = msg
                                                            +	var err error
                                                            +	if p.off, err = p.header.unpack(msg, 0); err != nil {
                                                            +		return Header{}, &nestedError{"unpacking header", err}
                                                            +	}
                                                            +	p.section = sectionQuestions
                                                            +	return p.header.header(), nil
                                                            +}
                                                            +
                                                            +func (p *Parser) checkAdvance(sec section) error {
                                                            +	if p.section < sec {
                                                            +		return ErrNotStarted
                                                            +	}
                                                            +	if p.section > sec {
                                                            +		return ErrSectionDone
                                                            +	}
                                                            +	p.resHeaderValid = false
                                                            +	if p.index == int(p.header.count(sec)) {
                                                            +		p.index = 0
                                                            +		p.section++
                                                            +		return ErrSectionDone
                                                            +	}
                                                            +	return nil
                                                            +}
                                                            +
                                                            +func (p *Parser) resource(sec section) (Resource, error) {
                                                            +	var r Resource
                                                            +	hdr, err := p.resourceHeader(sec)
                                                            +	if err != nil {
                                                            +		return r, err
                                                            +	}
                                                            +	p.resHeaderValid = false
                                                            +	r, p.off, err = unpackResource(p.msg, p.off, hdr)
                                                            +	if err != nil {
                                                            +		return nil, &nestedError{"unpacking " + sectionNames[sec], err}
                                                            +	}
                                                            +	p.index++
                                                            +	return r, nil
                                                            +}
                                                            +
                                                            +func (p *Parser) resourceHeader(sec section) (ResourceHeader, error) {
                                                            +	if p.resHeaderValid {
                                                            +		return p.resHeader, nil
                                                            +	}
                                                            +	if err := p.checkAdvance(sec); err != nil {
                                                            +		return ResourceHeader{}, err
                                                            +	}
                                                            +	var hdr ResourceHeader
                                                            +	off, err := hdr.unpack(p.msg, p.off)
                                                            +	if err != nil {
                                                            +		return ResourceHeader{}, err
                                                            +	}
                                                            +	p.resHeaderValid = true
                                                            +	p.resHeader = hdr
                                                            +	p.off = off
                                                            +	return hdr, nil
                                                            +}
                                                            +
                                                            +func (p *Parser) skipResource(sec section) error {
                                                            +	if p.resHeaderValid {
                                                            +		newOff := p.off + int(p.resHeader.Length)
                                                            +		if newOff > len(p.msg) {
                                                            +			return errResourceLen
                                                            +		}
                                                            +		p.off = newOff
                                                            +		p.resHeaderValid = false
                                                            +		p.index++
                                                            +		return nil
                                                            +	}
                                                            +	if err := p.checkAdvance(sec); err != nil {
                                                            +		return err
                                                            +	}
                                                            +	var err error
                                                            +	p.off, err = skipResource(p.msg, p.off)
                                                            +	if err != nil {
                                                            +		return &nestedError{"skipping: " + sectionNames[sec], err}
                                                            +	}
                                                            +	p.index++
                                                            +	return nil
                                                            +}
                                                            +
                                                            +// Question parses a single Question.
                                                            +func (p *Parser) Question() (Question, error) {
                                                            +	if err := p.checkAdvance(sectionQuestions); err != nil {
                                                            +		return Question{}, err
                                                            +	}
                                                            +	name, off, err := unpackName(p.msg, p.off)
                                                            +	if err != nil {
                                                            +		return Question{}, &nestedError{"unpacking Question.Name", err}
                                                            +	}
                                                            +	typ, off, err := unpackType(p.msg, off)
                                                            +	if err != nil {
                                                            +		return Question{}, &nestedError{"unpacking Question.Type", err}
                                                            +	}
                                                            +	class, off, err := unpackClass(p.msg, off)
                                                            +	if err != nil {
                                                            +		return Question{}, &nestedError{"unpacking Question.Class", err}
                                                            +	}
                                                            +	p.off = off
                                                            +	p.index++
                                                            +	return Question{name, typ, class}, nil
                                                            +}
                                                            +
                                                            +// AllQuestions parses all Questions.
                                                            +func (p *Parser) AllQuestions() ([]Question, error) {
                                                            +	qs := make([]Question, 0, p.header.questions)
                                                            +	for {
                                                            +		q, err := p.Question()
                                                            +		if err == ErrSectionDone {
                                                            +			return qs, nil
                                                            +		}
                                                            +		if err != nil {
                                                            +			return nil, err
                                                            +		}
                                                            +		qs = append(qs, q)
                                                            +	}
                                                            +}
                                                            +
                                                            +// SkipQuestion skips a single Question.
                                                            +func (p *Parser) SkipQuestion() error {
                                                            +	if err := p.checkAdvance(sectionQuestions); err != nil {
                                                            +		return err
                                                            +	}
                                                            +	off, err := skipName(p.msg, p.off)
                                                            +	if err != nil {
                                                            +		return &nestedError{"skipping Question Name", err}
                                                            +	}
                                                            +	if off, err = skipType(p.msg, off); err != nil {
                                                            +		return &nestedError{"skipping Question Type", err}
                                                            +	}
                                                            +	if off, err = skipClass(p.msg, off); err != nil {
                                                            +		return &nestedError{"skipping Question Class", err}
                                                            +	}
                                                            +	p.off = off
                                                            +	p.index++
                                                            +	return nil
                                                            +}
                                                            +
                                                            +// SkipAllQuestions skips all Questions.
                                                            +func (p *Parser) SkipAllQuestions() error {
                                                            +	for {
                                                            +		if err := p.SkipQuestion(); err == ErrSectionDone {
                                                            +			return nil
                                                            +		} else if err != nil {
                                                            +			return err
                                                            +		}
                                                            +	}
                                                            +}
                                                            +
                                                            +// AnswerHeader parses a single Answer ResourceHeader.
                                                            +func (p *Parser) AnswerHeader() (ResourceHeader, error) {
                                                            +	return p.resourceHeader(sectionAnswers)
                                                            +}
                                                            +
                                                            +// Answer parses a single Answer Resource.
                                                            +func (p *Parser) Answer() (Resource, error) {
                                                            +	return p.resource(sectionAnswers)
                                                            +}
                                                            +
                                                            +// AllAnswers parses all Answer Resources.
                                                            +func (p *Parser) AllAnswers() ([]Resource, error) {
                                                            +	as := make([]Resource, 0, p.header.answers)
                                                            +	for {
                                                            +		a, err := p.Answer()
                                                            +		if err == ErrSectionDone {
                                                            +			return as, nil
                                                            +		}
                                                            +		if err != nil {
                                                            +			return nil, err
                                                            +		}
                                                            +		as = append(as, a)
                                                            +	}
                                                            +}
                                                            +
                                                            +// SkipAnswer skips a single Answer Resource.
                                                            +func (p *Parser) SkipAnswer() error {
                                                            +	return p.skipResource(sectionAnswers)
                                                            +}
                                                            +
                                                            +// SkipAllAnswers skips all Answer Resources.
                                                            +func (p *Parser) SkipAllAnswers() error {
                                                            +	for {
                                                            +		if err := p.SkipAnswer(); err == ErrSectionDone {
                                                            +			return nil
                                                            +		} else if err != nil {
                                                            +			return err
                                                            +		}
                                                            +	}
                                                            +}
                                                            +
                                                            +// AuthorityHeader parses a single Authority ResourceHeader.
                                                            +func (p *Parser) AuthorityHeader() (ResourceHeader, error) {
                                                            +	return p.resourceHeader(sectionAuthorities)
                                                            +}
                                                            +
                                                            +// Authority parses a single Authority Resource.
                                                            +func (p *Parser) Authority() (Resource, error) {
                                                            +	return p.resource(sectionAuthorities)
                                                            +}
                                                            +
                                                            +// AllAuthorities parses all Authority Resources.
                                                            +func (p *Parser) AllAuthorities() ([]Resource, error) {
                                                            +	as := make([]Resource, 0, p.header.authorities)
                                                            +	for {
                                                            +		a, err := p.Authority()
                                                            +		if err == ErrSectionDone {
                                                            +			return as, nil
                                                            +		}
                                                            +		if err != nil {
                                                            +			return nil, err
                                                            +		}
                                                            +		as = append(as, a)
                                                            +	}
                                                            +}
                                                            +
                                                            +// SkipAuthority skips a single Authority Resource.
                                                            +func (p *Parser) SkipAuthority() error {
                                                            +	return p.skipResource(sectionAuthorities)
                                                            +}
                                                            +
                                                            +// SkipAllAuthorities skips all Authority Resources.
                                                            +func (p *Parser) SkipAllAuthorities() error {
                                                            +	for {
                                                            +		if err := p.SkipAuthority(); err == ErrSectionDone {
                                                            +			return nil
                                                            +		} else if err != nil {
                                                            +			return err
                                                            +		}
                                                            +	}
                                                            +}
                                                            +
                                                            +// AdditionalHeader parses a single Additional ResourceHeader.
                                                            +func (p *Parser) AdditionalHeader() (ResourceHeader, error) {
                                                            +	return p.resourceHeader(sectionAdditionals)
                                                            +}
                                                            +
                                                            +// Additional parses a single Additional Resource.
                                                            +func (p *Parser) Additional() (Resource, error) {
                                                            +	return p.resource(sectionAdditionals)
                                                            +}
                                                            +
                                                            +// AllAdditionals parses all Additional Resources.
                                                            +func (p *Parser) AllAdditionals() ([]Resource, error) {
                                                            +	as := make([]Resource, 0, p.header.additionals)
                                                            +	for {
                                                            +		a, err := p.Additional()
                                                            +		if err == ErrSectionDone {
                                                            +			return as, nil
                                                            +		}
                                                            +		if err != nil {
                                                            +			return nil, err
                                                            +		}
                                                            +		as = append(as, a)
                                                            +	}
                                                            +}
                                                            +
                                                            +// SkipAdditional skips a single Additional Resource.
                                                            +func (p *Parser) SkipAdditional() error {
                                                            +	return p.skipResource(sectionAdditionals)
                                                            +}
                                                            +
                                                            +// SkipAllAdditionals skips all Additional Resources.
                                                            +func (p *Parser) SkipAllAdditionals() error {
                                                            +	for {
                                                            +		if err := p.SkipAdditional(); err == ErrSectionDone {
                                                            +			return nil
                                                            +		} else if err != nil {
                                                            +			return err
                                                            +		}
                                                            +	}
                                                            +}
                                                            +
                                                            +// Unpack parses a full Message.
                                                            +func (m *Message) Unpack(msg []byte) error {
                                                            +	var p Parser
                                                            +	var err error
                                                            +	if m.Header, err = p.Start(msg); err != nil {
                                                            +		return err
                                                            +	}
                                                            +	if m.Questions, err = p.AllQuestions(); err != nil {
                                                            +		return err
                                                            +	}
                                                            +	if m.Answers, err = p.AllAnswers(); err != nil {
                                                            +		return err
                                                            +	}
                                                            +	if m.Authorities, err = p.AllAuthorities(); err != nil {
                                                            +		return err
                                                            +	}
                                                            +	if m.Additionals, err = p.AllAdditionals(); err != nil {
                                                            +		return err
                                                            +	}
                                                            +	return nil
                                                            +}
                                                            +
                                                            +// Pack packs a full Message.
                                                            +func (m *Message) Pack() ([]byte, error) {
                                                            +	// Validate the lengths. It is very unlikely that anyone will try to
                                                            +	// pack more than 65535 of any particular type, but it is possible and
                                                            +	// we should fail gracefully.
                                                            +	if len(m.Questions) > int(^uint16(0)) {
                                                            +		return nil, errTooManyQuestions
                                                            +	}
                                                            +	if len(m.Answers) > int(^uint16(0)) {
                                                            +		return nil, errTooManyAnswers
                                                            +	}
                                                            +	if len(m.Authorities) > int(^uint16(0)) {
                                                            +		return nil, errTooManyAuthorities
                                                            +	}
                                                            +	if len(m.Additionals) > int(^uint16(0)) {
                                                            +		return nil, errTooManyAdditionals
                                                            +	}
                                                            +
                                                            +	var h header
                                                            +	h.id, h.bits = m.Header.pack()
                                                            +
                                                            +	h.questions = uint16(len(m.Questions))
                                                            +	h.answers = uint16(len(m.Answers))
                                                            +	h.authorities = uint16(len(m.Authorities))
                                                            +	h.additionals = uint16(len(m.Additionals))
                                                            +
                                                            +	// The starting capacity doesn't matter too much, but most DNS responses
                                                            +	// Will be <= 512 bytes as it is the limit for DNS over UDP.
                                                            +	msg := make([]byte, 0, 512)
                                                            +
                                                            +	msg = h.pack(msg)
                                                            +
                                                            +	// RFC 1035 allows (but does not require) compression for packing. RFC
                                                            +	// 1035 requires unpacking implementations to support compression, so
                                                            +	// unconditionally enabling it is fine.
                                                            +	//
                                                            +	// DNS lookups are typically done over UDP, and RFC 1035 states that UDP
                                                            +	// DNS packets can be a maximum of 512 bytes long. Without compression,
                                                            +	// many DNS response packets are over this limit, so enabling
                                                            +	// compression will help ensure compliance.
                                                            +	compression := map[string]int{}
                                                            +
                                                            +	for _, q := range m.Questions {
                                                            +		var err error
                                                            +		msg, err = q.pack(msg, compression)
                                                            +		if err != nil {
                                                            +			return nil, &nestedError{"packing Question", err}
                                                            +		}
                                                            +	}
                                                            +	for _, a := range m.Answers {
                                                            +		var err error
                                                            +		msg, err = packResource(msg, a, compression)
                                                            +		if err != nil {
                                                            +			return nil, &nestedError{"packing Answer", err}
                                                            +		}
                                                            +	}
                                                            +	for _, a := range m.Authorities {
                                                            +		var err error
                                                            +		msg, err = packResource(msg, a, compression)
                                                            +		if err != nil {
                                                            +			return nil, &nestedError{"packing Authority", err}
                                                            +		}
                                                            +	}
                                                            +	for _, a := range m.Additionals {
                                                            +		var err error
                                                            +		msg, err = packResource(msg, a, compression)
                                                            +		if err != nil {
                                                            +			return nil, &nestedError{"packing Additional", err}
                                                            +		}
                                                            +	}
                                                            +
                                                            +	return msg, nil
                                                            +}
                                                            +
                                                            +// An ResourceHeader is the header of a DNS resource record. There are
                                                            +// many types of DNS resource records, but they all share the same header.
                                                            +type ResourceHeader struct {
                                                            +	// Name is the domain name for which this resource record pertains.
                                                            +	Name string
                                                            +
                                                            +	// Type is the type of DNS resource record.
                                                            +	//
                                                            +	// This field will be set automatically during packing.
                                                            +	Type Type
                                                            +
                                                            +	// Class is the class of network to which this DNS resource record
                                                            +	// pertains.
                                                            +	Class Class
                                                            +
                                                            +	// TTL is the length of time (measured in seconds) which this resource
                                                            +	// record is valid for (time to live). All Resources in a set should
                                                            +	// have the same TTL (RFC 2181 Section 5.2).
                                                            +	TTL uint32
                                                            +
                                                            +	// Length is the length of data in the resource record after the header.
                                                            +	//
                                                            +	// This field will be set automatically during packing.
                                                            +	Length uint16
                                                            +}
                                                            +
                                                            +// Header implements Resource.Header.
                                                            +func (h *ResourceHeader) Header() *ResourceHeader {
                                                            +	return h
                                                            +}
                                                            +
                                                            +// pack packs all of the fields in a ResourceHeader except for the length. The
                                                            +// length bytes are returned as a slice so they can be filled in after the rest
                                                            +// of the Resource has been packed.
                                                            +func (h *ResourceHeader) pack(oldMsg []byte, compression map[string]int) (msg []byte, length []byte, err error) {
                                                            +	msg = oldMsg
                                                            +	if msg, err = packName(msg, h.Name, compression); err != nil {
                                                            +		return oldMsg, nil, &nestedError{"Name", err}
                                                            +	}
                                                            +	msg = packType(msg, h.Type)
                                                            +	msg = packClass(msg, h.Class)
                                                            +	msg = packUint32(msg, h.TTL)
                                                            +	lenBegin := len(msg)
                                                            +	msg = packUint16(msg, h.Length)
                                                            +	return msg, msg[lenBegin:], nil
                                                            +}
                                                            +
                                                            +func (h *ResourceHeader) unpack(msg []byte, off int) (int, error) {
                                                            +	newOff := off
                                                            +	var err error
                                                            +	if h.Name, newOff, err = unpackName(msg, newOff); err != nil {
                                                            +		return off, &nestedError{"Name", err}
                                                            +	}
                                                            +	if h.Type, newOff, err = unpackType(msg, newOff); err != nil {
                                                            +		return off, &nestedError{"Type", err}
                                                            +	}
                                                            +	if h.Class, newOff, err = unpackClass(msg, newOff); err != nil {
                                                            +		return off, &nestedError{"Class", err}
                                                            +	}
                                                            +	if h.TTL, newOff, err = unpackUint32(msg, newOff); err != nil {
                                                            +		return off, &nestedError{"TTL", err}
                                                            +	}
                                                            +	if h.Length, newOff, err = unpackUint16(msg, newOff); err != nil {
                                                            +		return off, &nestedError{"Length", err}
                                                            +	}
                                                            +	return newOff, nil
                                                            +}
                                                            +
                                                            +func skipResource(msg []byte, off int) (int, error) {
                                                            +	newOff, err := skipName(msg, off)
                                                            +	if err != nil {
                                                            +		return off, &nestedError{"Name", err}
                                                            +	}
                                                            +	if newOff, err = skipType(msg, newOff); err != nil {
                                                            +		return off, &nestedError{"Type", err}
                                                            +	}
                                                            +	if newOff, err = skipClass(msg, newOff); err != nil {
                                                            +		return off, &nestedError{"Class", err}
                                                            +	}
                                                            +	if newOff, err = skipUint32(msg, newOff); err != nil {
                                                            +		return off, &nestedError{"TTL", err}
                                                            +	}
                                                            +	length, newOff, err := unpackUint16(msg, newOff)
                                                            +	if err != nil {
                                                            +		return off, &nestedError{"Length", err}
                                                            +	}
                                                            +	if newOff += int(length); newOff > len(msg) {
                                                            +		return off, errResourceLen
                                                            +	}
                                                            +	return newOff, nil
                                                            +}
                                                            +
                                                            +func packUint16(msg []byte, field uint16) []byte {
                                                            +	return append(msg, byte(field>>8), byte(field))
                                                            +}
                                                            +
                                                            +func unpackUint16(msg []byte, off int) (uint16, int, error) {
                                                            +	if off+2 > len(msg) {
                                                            +		return 0, off, errBaseLen
                                                            +	}
                                                            +	return uint16(msg[off])<<8 | uint16(msg[off+1]), off + 2, nil
                                                            +}
                                                            +
                                                            +func skipUint16(msg []byte, off int) (int, error) {
                                                            +	if off+2 > len(msg) {
                                                            +		return off, errBaseLen
                                                            +	}
                                                            +	return off + 2, nil
                                                            +}
                                                            +
                                                            +func packType(msg []byte, field Type) []byte {
                                                            +	return packUint16(msg, uint16(field))
                                                            +}
                                                            +
                                                            +func unpackType(msg []byte, off int) (Type, int, error) {
                                                            +	t, o, err := unpackUint16(msg, off)
                                                            +	return Type(t), o, err
                                                            +}
                                                            +
                                                            +func skipType(msg []byte, off int) (int, error) {
                                                            +	return skipUint16(msg, off)
                                                            +}
                                                            +
                                                            +func packClass(msg []byte, field Class) []byte {
                                                            +	return packUint16(msg, uint16(field))
                                                            +}
                                                            +
                                                            +func unpackClass(msg []byte, off int) (Class, int, error) {
                                                            +	c, o, err := unpackUint16(msg, off)
                                                            +	return Class(c), o, err
                                                            +}
                                                            +
                                                            +func skipClass(msg []byte, off int) (int, error) {
                                                            +	return skipUint16(msg, off)
                                                            +}
                                                            +
                                                            +func packUint32(msg []byte, field uint32) []byte {
                                                            +	return append(
                                                            +		msg,
                                                            +		byte(field>>24),
                                                            +		byte(field>>16),
                                                            +		byte(field>>8),
                                                            +		byte(field),
                                                            +	)
                                                            +}
                                                            +
                                                            +func unpackUint32(msg []byte, off int) (uint32, int, error) {
                                                            +	if off+4 > len(msg) {
                                                            +		return 0, off, errBaseLen
                                                            +	}
                                                            +	v := uint32(msg[off])<<24 | uint32(msg[off+1])<<16 | uint32(msg[off+2])<<8 | uint32(msg[off+3])
                                                            +	return v, off + 4, nil
                                                            +}
                                                            +
                                                            +func skipUint32(msg []byte, off int) (int, error) {
                                                            +	if off+4 > len(msg) {
                                                            +		return off, errBaseLen
                                                            +	}
                                                            +	return off + 4, nil
                                                            +}
                                                            +
                                                            +func packText(msg []byte, field string) []byte {
                                                            +	for len(field) > 0 {
                                                            +		l := len(field)
                                                            +		if l > 255 {
                                                            +			l = 255
                                                            +		}
                                                            +		msg = append(msg, byte(l))
                                                            +		msg = append(msg, field[:l]...)
                                                            +		field = field[l:]
                                                            +	}
                                                            +	return msg
                                                            +}
                                                            +
                                                            +func unpackText(msg []byte, off int) (string, int, error) {
                                                            +	if off >= len(msg) {
                                                            +		return "", off, errBaseLen
                                                            +	}
                                                            +	beginOff := off + 1
                                                            +	endOff := beginOff + int(msg[off])
                                                            +	if endOff > len(msg) {
                                                            +		return "", off, errCalcLen
                                                            +	}
                                                            +	return string(msg[beginOff:endOff]), endOff, nil
                                                            +}
                                                            +
                                                            +func skipText(msg []byte, off int) (int, error) {
                                                            +	if off >= len(msg) {
                                                            +		return off, errBaseLen
                                                            +	}
                                                            +	endOff := off + 1 + int(msg[off])
                                                            +	if endOff > len(msg) {
                                                            +		return off, errCalcLen
                                                            +	}
                                                            +	return endOff, nil
                                                            +}
                                                            +
                                                            +func packBytes(msg []byte, field []byte) []byte {
                                                            +	return append(msg, field...)
                                                            +}
                                                            +
                                                            +func unpackBytes(msg []byte, off int, field []byte) (int, error) {
                                                            +	newOff := off + len(field)
                                                            +	if newOff > len(msg) {
                                                            +		return off, errBaseLen
                                                            +	}
                                                            +	copy(field, msg[off:newOff])
                                                            +	return newOff, nil
                                                            +}
                                                            +
                                                            +func skipBytes(msg []byte, off int, field []byte) (int, error) {
                                                            +	newOff := off + len(field)
                                                            +	if newOff > len(msg) {
                                                            +		return off, errBaseLen
                                                            +	}
                                                            +	return newOff, nil
                                                            +}
                                                            +
                                                            +// packName packs a domain name.
                                                            +//
                                                            +// Domain names are a sequence of counted strings split at the dots. They end
                                                            +// with a zero-length string. Compression can be used to reuse domain suffixes.
                                                            +//
                                                            +// The compression map will be updated with new domain suffixes. If compression
                                                            +// is nil, compression will not be used.
                                                            +func packName(msg []byte, name string, compression map[string]int) ([]byte, error) {
                                                            +	oldMsg := msg
                                                            +
                                                            +	// Add a trailing dot to canonicalize name.
                                                            +	if n := len(name); n == 0 || name[n-1] != '.' {
                                                            +		name += "."
                                                            +	}
                                                            +
                                                            +	// Allow root domain.
                                                            +	if name == "." {
                                                            +		return append(msg, 0), nil
                                                            +	}
                                                            +
                                                            +	// Emit sequence of counted strings, chopping at dots.
                                                            +	for i, begin := 0, 0; i < len(name); i++ {
                                                            +		// Check for the end of the segment.
                                                            +		if name[i] == '.' {
                                                            +			// The two most significant bits have special meaning.
                                                            +			// It isn't allowed for segments to be long enough to
                                                            +			// need them.
                                                            +			if i-begin >= 1<<6 {
                                                            +				return oldMsg, errSegTooLong
                                                            +			}
                                                            +
                                                            +			// Segments must have a non-zero length.
                                                            +			if i-begin == 0 {
                                                            +				return oldMsg, errZeroSegLen
                                                            +			}
                                                            +
                                                            +			msg = append(msg, byte(i-begin))
                                                            +
                                                            +			for j := begin; j < i; j++ {
                                                            +				msg = append(msg, name[j])
                                                            +			}
                                                            +
                                                            +			begin = i + 1
                                                            +			continue
                                                            +		}
                                                            +
                                                            +		// We can only compress domain suffixes starting with a new
                                                            +		// segment. A pointer is two bytes with the two most significant
                                                            +		// bits set to 1 to indicate that it is a pointer.
                                                            +		if (i == 0 || name[i-1] == '.') && compression != nil {
                                                            +			if ptr, ok := compression[name[i:]]; ok {
                                                            +				// Hit. Emit a pointer instead of the rest of
                                                            +				// the domain.
                                                            +				return append(msg, byte(ptr>>8|0xC0), byte(ptr)), nil
                                                            +			}
                                                            +
                                                            +			// Miss. Add the suffix to the compression table if the
                                                            +			// offset can be stored in the available 14 bytes.
                                                            +			if len(msg) <= int(^uint16(0)>>2) {
                                                            +				compression[name[i:]] = len(msg)
                                                            +			}
                                                            +		}
                                                            +	}
                                                            +	return append(msg, 0), nil
                                                            +}
                                                            +
                                                            +// unpackName unpacks a domain name.
                                                            +func unpackName(msg []byte, off int) (string, int, error) {
                                                            +	// currOff is the current working offset.
                                                            +	currOff := off
                                                            +
                                                            +	// newOff is the offset where the next record will start. Pointers lead
                                                            +	// to data that belongs to other names and thus doesn't count towards to
                                                            +	// the usage of this name.
                                                            +	newOff := off
                                                            +
                                                            +	// name is the domain name being unpacked.
                                                            +	name := make([]byte, 0, 255)
                                                            +
                                                            +	// ptr is the number of pointers followed.
                                                            +	var ptr int
                                                            +Loop:
                                                            +	for {
                                                            +		if currOff >= len(msg) {
                                                            +			return "", off, errBaseLen
                                                            +		}
                                                            +		c := int(msg[currOff])
                                                            +		currOff++
                                                            +		switch c & 0xC0 {
                                                            +		case 0x00: // String segment
                                                            +			if c == 0x00 {
                                                            +				// A zero length signals the end of the name.
                                                            +				break Loop
                                                            +			}
                                                            +			endOff := currOff + c
                                                            +			if endOff > len(msg) {
                                                            +				return "", off, errCalcLen
                                                            +			}
                                                            +			name = append(name, msg[currOff:endOff]...)
                                                            +			name = append(name, '.')
                                                            +			currOff = endOff
                                                            +		case 0xC0: // Pointer
                                                            +			if currOff >= len(msg) {
                                                            +				return "", off, errInvalidPtr
                                                            +			}
                                                            +			c1 := msg[currOff]
                                                            +			currOff++
                                                            +			if ptr == 0 {
                                                            +				newOff = currOff
                                                            +			}
                                                            +			// Don't follow too many pointers, maybe there's a loop.
                                                            +			if ptr++; ptr > 10 {
                                                            +				return "", off, errTooManyPtr
                                                            +			}
                                                            +			currOff = (c^0xC0)<<8 | int(c1)
                                                            +		default:
                                                            +			// Prefixes 0x80 and 0x40 are reserved.
                                                            +			return "", off, errReserved
                                                            +		}
                                                            +	}
                                                            +	if len(name) == 0 {
                                                            +		name = append(name, '.')
                                                            +	}
                                                            +	if ptr == 0 {
                                                            +		newOff = currOff
                                                            +	}
                                                            +	return string(name), newOff, nil
                                                            +}
                                                            +
                                                            +func skipName(msg []byte, off int) (int, error) {
                                                            +	// newOff is the offset where the next record will start. Pointers lead
                                                            +	// to data that belongs to other names and thus doesn't count towards to
                                                            +	// the usage of this name.
                                                            +	newOff := off
                                                            +
                                                            +Loop:
                                                            +	for {
                                                            +		if newOff >= len(msg) {
                                                            +			return off, errBaseLen
                                                            +		}
                                                            +		c := int(msg[newOff])
                                                            +		newOff++
                                                            +		switch c & 0xC0 {
                                                            +		case 0x00:
                                                            +			if c == 0x00 {
                                                            +				// A zero length signals the end of the name.
                                                            +				break Loop
                                                            +			}
                                                            +			// literal string
                                                            +			newOff += c
                                                            +			if newOff > len(msg) {
                                                            +				return off, errCalcLen
                                                            +			}
                                                            +		case 0xC0:
                                                            +			// Pointer to somewhere else in msg.
                                                            +
                                                            +			// Pointers are two bytes.
                                                            +			newOff++
                                                            +
                                                            +			// Don't follow the pointer as the data here has ended.
                                                            +			break Loop
                                                            +		default:
                                                            +			// Prefixes 0x80 and 0x40 are reserved.
                                                            +			return off, errReserved
                                                            +		}
                                                            +	}
                                                            +
                                                            +	return newOff, nil
                                                            +}
                                                            +
                                                            +// A Question is a DNS query.
                                                            +type Question struct {
                                                            +	Name  string
                                                            +	Type  Type
                                                            +	Class Class
                                                            +}
                                                            +
                                                            +func (q *Question) pack(msg []byte, compression map[string]int) ([]byte, error) {
                                                            +	msg, err := packName(msg, q.Name, compression)
                                                            +	if err != nil {
                                                            +		return msg, &nestedError{"Name", err}
                                                            +	}
                                                            +	msg = packType(msg, q.Type)
                                                            +	return packClass(msg, q.Class), nil
                                                            +}
                                                            +
                                                            +func unpackResource(msg []byte, off int, hdr ResourceHeader) (Resource, int, error) {
                                                            +	var (
                                                            +		r    Resource
                                                            +		err  error
                                                            +		name string
                                                            +	)
                                                            +	switch hdr.Type {
                                                            +	case TypeA:
                                                            +		r, err = unpackAResource(hdr, msg, off)
                                                            +		name = "A"
                                                            +	case TypeNS:
                                                            +		r, err = unpackNSResource(hdr, msg, off)
                                                            +		name = "NS"
                                                            +	case TypeCNAME:
                                                            +		r, err = unpackCNAMEResource(hdr, msg, off)
                                                            +		name = "CNAME"
                                                            +	case TypeSOA:
                                                            +		r, err = unpackSOAResource(hdr, msg, off)
                                                            +		name = "SOA"
                                                            +	case TypePTR:
                                                            +		r, err = unpackPTRResource(hdr, msg, off)
                                                            +		name = "PTR"
                                                            +	case TypeMX:
                                                            +		r, err = unpackMXResource(hdr, msg, off)
                                                            +		name = "MX"
                                                            +	case TypeTXT:
                                                            +		r, err = unpackTXTResource(hdr, msg, off)
                                                            +		name = "TXT"
                                                            +	case TypeAAAA:
                                                            +		r, err = unpackAAAAResource(hdr, msg, off)
                                                            +		name = "AAAA"
                                                            +	case TypeSRV:
                                                            +		r, err = unpackSRVResource(hdr, msg, off)
                                                            +		name = "SRV"
                                                            +	}
                                                            +	if err != nil {
                                                            +		return nil, off, &nestedError{name + " record", err}
                                                            +	}
                                                            +	if r != nil {
                                                            +		return r, off + int(hdr.Length), nil
                                                            +	}
                                                            +	return nil, off, errors.New("invalid resource type: " + string(hdr.Type+'0'))
                                                            +}
                                                            +
                                                            +// A CNAMEResource is a CNAME Resource record.
                                                            +type CNAMEResource struct {
                                                            +	ResourceHeader
                                                            +
                                                            +	CNAME string
                                                            +}
                                                            +
                                                            +func (r *CNAMEResource) realType() Type {
                                                            +	return TypeCNAME
                                                            +}
                                                            +
                                                            +func (r *CNAMEResource) pack(msg []byte, compression map[string]int) ([]byte, error) {
                                                            +	return packName(msg, r.CNAME, compression)
                                                            +}
                                                            +
                                                            +func unpackCNAMEResource(hdr ResourceHeader, msg []byte, off int) (*CNAMEResource, error) {
                                                            +	cname, _, err := unpackName(msg, off)
                                                            +	if err != nil {
                                                            +		return nil, err
                                                            +	}
                                                            +	return &CNAMEResource{hdr, cname}, nil
                                                            +}
                                                            +
                                                            +// An MXResource is an MX Resource record.
                                                            +type MXResource struct {
                                                            +	ResourceHeader
                                                            +
                                                            +	Pref uint16
                                                            +	MX   string
                                                            +}
                                                            +
                                                            +func (r *MXResource) realType() Type {
                                                            +	return TypeMX
                                                            +}
                                                            +
                                                            +func (r *MXResource) pack(msg []byte, compression map[string]int) ([]byte, error) {
                                                            +	oldMsg := msg
                                                            +	msg = packUint16(msg, r.Pref)
                                                            +	msg, err := packName(msg, r.MX, compression)
                                                            +	if err != nil {
                                                            +		return oldMsg, &nestedError{"MXResource.MX", err}
                                                            +	}
                                                            +	return msg, nil
                                                            +}
                                                            +
                                                            +func unpackMXResource(hdr ResourceHeader, msg []byte, off int) (*MXResource, error) {
                                                            +	pref, off, err := unpackUint16(msg, off)
                                                            +	if err != nil {
                                                            +		return nil, &nestedError{"Pref", err}
                                                            +	}
                                                            +	mx, _, err := unpackName(msg, off)
                                                            +	if err != nil {
                                                            +		return nil, &nestedError{"MX", err}
                                                            +	}
                                                            +	return &MXResource{hdr, pref, mx}, nil
                                                            +}
                                                            +
                                                            +// An NSResource is an NS Resource record.
                                                            +type NSResource struct {
                                                            +	ResourceHeader
                                                            +
                                                            +	NS string
                                                            +}
                                                            +
                                                            +func (r *NSResource) realType() Type {
                                                            +	return TypeNS
                                                            +}
                                                            +
                                                            +func (r *NSResource) pack(msg []byte, compression map[string]int) ([]byte, error) {
                                                            +	return packName(msg, r.NS, compression)
                                                            +}
                                                            +
                                                            +func unpackNSResource(hdr ResourceHeader, msg []byte, off int) (*NSResource, error) {
                                                            +	ns, _, err := unpackName(msg, off)
                                                            +	if err != nil {
                                                            +		return nil, err
                                                            +	}
                                                            +	return &NSResource{hdr, ns}, nil
                                                            +}
                                                            +
                                                            +// A PTRResource is a PTR Resource record.
                                                            +type PTRResource struct {
                                                            +	ResourceHeader
                                                            +
                                                            +	PTR string
                                                            +}
                                                            +
                                                            +func (r *PTRResource) realType() Type {
                                                            +	return TypePTR
                                                            +}
                                                            +
                                                            +func (r *PTRResource) pack(msg []byte, compression map[string]int) ([]byte, error) {
                                                            +	return packName(msg, r.PTR, compression)
                                                            +}
                                                            +
                                                            +func unpackPTRResource(hdr ResourceHeader, msg []byte, off int) (*PTRResource, error) {
                                                            +	ptr, _, err := unpackName(msg, off)
                                                            +	if err != nil {
                                                            +		return nil, err
                                                            +	}
                                                            +	return &PTRResource{hdr, ptr}, nil
                                                            +}
                                                            +
                                                            +// An SOAResource is an SOA Resource record.
                                                            +type SOAResource struct {
                                                            +	ResourceHeader
                                                            +
                                                            +	NS      string
                                                            +	MBox    string
                                                            +	Serial  uint32
                                                            +	Refresh uint32
                                                            +	Retry   uint32
                                                            +	Expire  uint32
                                                            +
                                                            +	// MinTTL the is the default TTL of Resources records which did not
                                                            +	// contain a TTL value and the TTL of negative responses. (RFC 2308
                                                            +	// Section 4)
                                                            +	MinTTL uint32
                                                            +}
                                                            +
                                                            +func (r *SOAResource) realType() Type {
                                                            +	return TypeSOA
                                                            +}
                                                            +
                                                            +func (r *SOAResource) pack(msg []byte, compression map[string]int) ([]byte, error) {
                                                            +	oldMsg := msg
                                                            +	msg, err := packName(msg, r.NS, compression)
                                                            +	if err != nil {
                                                            +		return oldMsg, &nestedError{"SOAResource.NS", err}
                                                            +	}
                                                            +	msg, err = packName(msg, r.MBox, compression)
                                                            +	if err != nil {
                                                            +		return oldMsg, &nestedError{"SOAResource.MBox", err}
                                                            +	}
                                                            +	msg = packUint32(msg, r.Serial)
                                                            +	msg = packUint32(msg, r.Refresh)
                                                            +	msg = packUint32(msg, r.Retry)
                                                            +	msg = packUint32(msg, r.Expire)
                                                            +	return packUint32(msg, r.MinTTL), nil
                                                            +}
                                                            +
                                                            +func unpackSOAResource(hdr ResourceHeader, msg []byte, off int) (*SOAResource, error) {
                                                            +	ns, off, err := unpackName(msg, off)
                                                            +	if err != nil {
                                                            +		return nil, &nestedError{"NS", err}
                                                            +	}
                                                            +	mbox, off, err := unpackName(msg, off)
                                                            +	if err != nil {
                                                            +		return nil, &nestedError{"MBox", err}
                                                            +	}
                                                            +	serial, off, err := unpackUint32(msg, off)
                                                            +	if err != nil {
                                                            +		return nil, &nestedError{"Serial", err}
                                                            +	}
                                                            +	refresh, off, err := unpackUint32(msg, off)
                                                            +	if err != nil {
                                                            +		return nil, &nestedError{"Refresh", err}
                                                            +	}
                                                            +	retry, off, err := unpackUint32(msg, off)
                                                            +	if err != nil {
                                                            +		return nil, &nestedError{"Retry", err}
                                                            +	}
                                                            +	expire, off, err := unpackUint32(msg, off)
                                                            +	if err != nil {
                                                            +		return nil, &nestedError{"Expire", err}
                                                            +	}
                                                            +	minTTL, _, err := unpackUint32(msg, off)
                                                            +	if err != nil {
                                                            +		return nil, &nestedError{"MinTTL", err}
                                                            +	}
                                                            +	return &SOAResource{hdr, ns, mbox, serial, refresh, retry, expire, minTTL}, nil
                                                            +}
                                                            +
                                                            +// A TXTResource is a TXT Resource record.
                                                            +type TXTResource struct {
                                                            +	ResourceHeader
                                                            +
                                                            +	Txt string // Not a domain name.
                                                            +}
                                                            +
                                                            +func (r *TXTResource) realType() Type {
                                                            +	return TypeTXT
                                                            +}
                                                            +
                                                            +func (r *TXTResource) pack(msg []byte, compression map[string]int) ([]byte, error) {
                                                            +	return packText(msg, r.Txt), nil
                                                            +}
                                                            +
                                                            +func unpackTXTResource(hdr ResourceHeader, msg []byte, off int) (*TXTResource, error) {
                                                            +	var txt string
                                                            +	for n := uint16(0); n < hdr.Length; {
                                                            +		var t string
                                                            +		var err error
                                                            +		if t, off, err = unpackText(msg, off); err != nil {
                                                            +			return nil, &nestedError{"text", err}
                                                            +		}
                                                            +		// Check if we got too many bytes.
                                                            +		if hdr.Length-n < uint16(len(t))+1 {
                                                            +			return nil, errCalcLen
                                                            +		}
                                                            +		n += uint16(len(t)) + 1
                                                            +		txt += t
                                                            +	}
                                                            +	return &TXTResource{hdr, txt}, nil
                                                            +}
                                                            +
                                                            +// An SRVResource is an SRV Resource record.
                                                            +type SRVResource struct {
                                                            +	ResourceHeader
                                                            +
                                                            +	Priority uint16
                                                            +	Weight   uint16
                                                            +	Port     uint16
                                                            +	Target   string // Not compressed as per RFC 2782.
                                                            +}
                                                            +
                                                            +func (r *SRVResource) realType() Type {
                                                            +	return TypeSRV
                                                            +}
                                                            +
                                                            +func (r *SRVResource) pack(msg []byte, compression map[string]int) ([]byte, error) {
                                                            +	oldMsg := msg
                                                            +	msg = packUint16(msg, r.Priority)
                                                            +	msg = packUint16(msg, r.Weight)
                                                            +	msg = packUint16(msg, r.Port)
                                                            +	msg, err := packName(msg, r.Target, nil)
                                                            +	if err != nil {
                                                            +		return oldMsg, &nestedError{"SRVResource.Target", err}
                                                            +	}
                                                            +	return msg, nil
                                                            +}
                                                            +
                                                            +func unpackSRVResource(hdr ResourceHeader, msg []byte, off int) (*SRVResource, error) {
                                                            +	priority, off, err := unpackUint16(msg, off)
                                                            +	if err != nil {
                                                            +		return nil, &nestedError{"Priority", err}
                                                            +	}
                                                            +	weight, off, err := unpackUint16(msg, off)
                                                            +	if err != nil {
                                                            +		return nil, &nestedError{"Weight", err}
                                                            +	}
                                                            +	port, off, err := unpackUint16(msg, off)
                                                            +	if err != nil {
                                                            +		return nil, &nestedError{"Port", err}
                                                            +	}
                                                            +	target, _, err := unpackName(msg, off)
                                                            +	if err != nil {
                                                            +		return nil, &nestedError{"Target", err}
                                                            +	}
                                                            +	return &SRVResource{hdr, priority, weight, port, target}, nil
                                                            +}
                                                            +
                                                            +// An AResource is an A Resource record.
                                                            +type AResource struct {
                                                            +	ResourceHeader
                                                            +
                                                            +	A [4]byte
                                                            +}
                                                            +
                                                            +func (r *AResource) realType() Type {
                                                            +	return TypeA
                                                            +}
                                                            +
                                                            +func (r *AResource) pack(msg []byte, compression map[string]int) ([]byte, error) {
                                                            +	return packBytes(msg, r.A[:]), nil
                                                            +}
                                                            +
                                                            +func unpackAResource(hdr ResourceHeader, msg []byte, off int) (*AResource, error) {
                                                            +	var a [4]byte
                                                            +	if _, err := unpackBytes(msg, off, a[:]); err != nil {
                                                            +		return nil, err
                                                            +	}
                                                            +	return &AResource{hdr, a}, nil
                                                            +}
                                                            +
                                                            +// An AAAAResource is an AAAA Resource record.
                                                            +type AAAAResource struct {
                                                            +	ResourceHeader
                                                            +
                                                            +	AAAA [16]byte
                                                            +}
                                                            +
                                                            +func (r *AAAAResource) realType() Type {
                                                            +	return TypeAAAA
                                                            +}
                                                            +
                                                            +func (r *AAAAResource) pack(msg []byte, compression map[string]int) ([]byte, error) {
                                                            +	return packBytes(msg, r.AAAA[:]), nil
                                                            +}
                                                            +
                                                            +func unpackAAAAResource(hdr ResourceHeader, msg []byte, off int) (*AAAAResource, error) {
                                                            +	var aaaa [16]byte
                                                            +	if _, err := unpackBytes(msg, off, aaaa[:]); err != nil {
                                                            +		return nil, err
                                                            +	}
                                                            +	return &AAAAResource{hdr, aaaa}, nil
                                                            +}
                                                            diff --git a/dns/dnsmessage/message_test.go b/dns/dnsmessage/message_test.go
                                                            new file mode 100644
                                                            index 0000000..46edd72
                                                            --- /dev/null
                                                            +++ b/dns/dnsmessage/message_test.go
                                                            @@ -0,0 +1,575 @@
                                                            +// 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.
                                                            +
                                                            +package dnsmessage
                                                            +
                                                            +import (
                                                            +	"fmt"
                                                            +	"net"
                                                            +	"reflect"
                                                            +	"strings"
                                                            +	"testing"
                                                            +)
                                                            +
                                                            +func (m *Message) String() string {
                                                            +	s := fmt.Sprintf("Message: %#v\n", &m.Header)
                                                            +	if len(m.Questions) > 0 {
                                                            +		s += "-- Questions\n"
                                                            +		for _, q := range m.Questions {
                                                            +			s += fmt.Sprintf("%#v\n", q)
                                                            +		}
                                                            +	}
                                                            +	if len(m.Answers) > 0 {
                                                            +		s += "-- Answers\n"
                                                            +		for _, a := range m.Answers {
                                                            +			s += fmt.Sprintf("%#v\n", a)
                                                            +		}
                                                            +	}
                                                            +	if len(m.Authorities) > 0 {
                                                            +		s += "-- Authorities\n"
                                                            +		for _, ns := range m.Authorities {
                                                            +			s += fmt.Sprintf("%#v\n", ns)
                                                            +		}
                                                            +	}
                                                            +	if len(m.Additionals) > 0 {
                                                            +		s += "-- Additionals\n"
                                                            +		for _, e := range m.Additionals {
                                                            +			s += fmt.Sprintf("%#v\n", e)
                                                            +		}
                                                            +	}
                                                            +	return s
                                                            +}
                                                            +
                                                            +func TestQuestionPackUnpack(t *testing.T) {
                                                            +	want := Question{
                                                            +		Name:  ".",
                                                            +		Type:  TypeA,
                                                            +		Class: ClassINET,
                                                            +	}
                                                            +	buf, err := want.pack(make([]byte, 1, 50), map[string]int{})
                                                            +	if err != nil {
                                                            +		t.Fatal("Packing failed:", err)
                                                            +	}
                                                            +	var p Parser
                                                            +	p.msg = buf
                                                            +	p.header.questions = 1
                                                            +	p.section = sectionQuestions
                                                            +	p.off = 1
                                                            +	got, err := p.Question()
                                                            +	if err != nil {
                                                            +		t.Fatalf("Unpacking failed: %v\n%s", err, string(buf[1:]))
                                                            +	}
                                                            +	if p.off != len(buf) {
                                                            +		t.Errorf("Unpacked different amount than packed: got n = %d, want = %d", p.off, len(buf))
                                                            +	}
                                                            +	if !reflect.DeepEqual(got, want) {
                                                            +		t.Errorf("Got = %+v, want = %+v", got, want)
                                                            +	}
                                                            +}
                                                            +
                                                            +func TestNamePackUnpack(t *testing.T) {
                                                            +	tests := []struct {
                                                            +		in   string
                                                            +		want string
                                                            +		err  error
                                                            +	}{
                                                            +		{"", ".", nil},
                                                            +		{".", ".", nil},
                                                            +		{"google..com", "", errZeroSegLen},
                                                            +		{"google.com", "google.com.", nil},
                                                            +		{"google..com.", "", errZeroSegLen},
                                                            +		{"google.com.", "google.com.", nil},
                                                            +		{".google.com.", "", errZeroSegLen},
                                                            +		{"www..google.com.", "", errZeroSegLen},
                                                            +		{"www.google.com.", "www.google.com.", nil},
                                                            +	}
                                                            +
                                                            +	for _, test := range tests {
                                                            +		buf, err := packName(make([]byte, 0, 30), test.in, map[string]int{})
                                                            +		if err != test.err {
                                                            +			t.Errorf("Packing of %s: got err = %v, want err = %v", test.in, err, test.err)
                                                            +			continue
                                                            +		}
                                                            +		if test.err != nil {
                                                            +			continue
                                                            +		}
                                                            +		got, n, err := unpackName(buf, 0)
                                                            +		if err != nil {
                                                            +			t.Errorf("Unpacking for %s failed: %v", test.in, err)
                                                            +			continue
                                                            +		}
                                                            +		if n != len(buf) {
                                                            +			t.Errorf(
                                                            +				"Unpacked different amount than packed for %s: got n = %d, want = %d",
                                                            +				test.in,
                                                            +				n,
                                                            +				len(buf),
                                                            +			)
                                                            +		}
                                                            +		if got != test.want {
                                                            +			t.Errorf("Unpacking packing of %s: got = %s, want = %s", test.in, got, test.want)
                                                            +		}
                                                            +	}
                                                            +}
                                                            +
                                                            +func TestDNSPackUnpack(t *testing.T) {
                                                            +	wants := []Message{
                                                            +		{
                                                            +			Questions: []Question{
                                                            +				{
                                                            +					Name:  ".",
                                                            +					Type:  TypeAAAA,
                                                            +					Class: ClassINET,
                                                            +				},
                                                            +			},
                                                            +			Answers:     []Resource{},
                                                            +			Authorities: []Resource{},
                                                            +			Additionals: []Resource{},
                                                            +		},
                                                            +		largeTestMsg(),
                                                            +	}
                                                            +	for i, want := range wants {
                                                            +		b, err := want.Pack()
                                                            +		if err != nil {
                                                            +			t.Fatalf("%d: packing failed: %v", i, err)
                                                            +		}
                                                            +		var got Message
                                                            +		err = got.Unpack(b)
                                                            +		if err != nil {
                                                            +			t.Fatalf("%d: unpacking failed: %v", i, err)
                                                            +		}
                                                            +		if !reflect.DeepEqual(got, want) {
                                                            +			t.Errorf("%d: got = %+v, want = %+v", i, &got, &want)
                                                            +		}
                                                            +	}
                                                            +}
                                                            +
                                                            +func TestSkipAll(t *testing.T) {
                                                            +	msg := largeTestMsg()
                                                            +	buf, err := msg.Pack()
                                                            +	if err != nil {
                                                            +		t.Fatal("Packing large test message:", err)
                                                            +	}
                                                            +	var p Parser
                                                            +	if _, err := p.Start(buf); err != nil {
                                                            +		t.Fatal(err)
                                                            +	}
                                                            +
                                                            +	tests := []struct {
                                                            +		name string
                                                            +		f    func() error
                                                            +	}{
                                                            +		{"SkipAllQuestions", p.SkipAllQuestions},
                                                            +		{"SkipAllAnswers", p.SkipAllAnswers},
                                                            +		{"SkipAllAuthorities", p.SkipAllAuthorities},
                                                            +		{"SkipAllAdditionals", p.SkipAllAdditionals},
                                                            +	}
                                                            +	for _, test := range tests {
                                                            +		for i := 1; i <= 3; i++ {
                                                            +			if err := test.f(); err != nil {
                                                            +				t.Errorf("Call #%d to %s(): %v", i, test.name, err)
                                                            +			}
                                                            +		}
                                                            +	}
                                                            +}
                                                            +
                                                            +func TestSkipNotStarted(t *testing.T) {
                                                            +	var p Parser
                                                            +
                                                            +	tests := []struct {
                                                            +		name string
                                                            +		f    func() error
                                                            +	}{
                                                            +		{"SkipAllQuestions", p.SkipAllQuestions},
                                                            +		{"SkipAllAnswers", p.SkipAllAnswers},
                                                            +		{"SkipAllAuthorities", p.SkipAllAuthorities},
                                                            +		{"SkipAllAdditionals", p.SkipAllAdditionals},
                                                            +	}
                                                            +	for _, test := range tests {
                                                            +		if err := test.f(); err != ErrNotStarted {
                                                            +			t.Errorf("Got %s() = %v, want = %v", test.name, err, ErrNotStarted)
                                                            +		}
                                                            +	}
                                                            +}
                                                            +
                                                            +func TestTooManyRecords(t *testing.T) {
                                                            +	const recs = int(^uint16(0)) + 1
                                                            +	tests := []struct {
                                                            +		name string
                                                            +		msg  Message
                                                            +		want error
                                                            +	}{
                                                            +		{
                                                            +			"Questions",
                                                            +			Message{
                                                            +				Questions: make([]Question, recs),
                                                            +			},
                                                            +			errTooManyQuestions,
                                                            +		},
                                                            +		{
                                                            +			"Answers",
                                                            +			Message{
                                                            +				Answers: make([]Resource, recs),
                                                            +			},
                                                            +			errTooManyAnswers,
                                                            +		},
                                                            +		{
                                                            +			"Authorities",
                                                            +			Message{
                                                            +				Authorities: make([]Resource, recs),
                                                            +			},
                                                            +			errTooManyAuthorities,
                                                            +		},
                                                            +		{
                                                            +			"Additionals",
                                                            +			Message{
                                                            +				Additionals: make([]Resource, recs),
                                                            +			},
                                                            +			errTooManyAdditionals,
                                                            +		},
                                                            +	}
                                                            +
                                                            +	for _, test := range tests {
                                                            +		if _, got := test.msg.Pack(); got != test.want {
                                                            +			t.Errorf("Packing %d %s: got = %v, want = %v", recs, test.name, got, test.want)
                                                            +		}
                                                            +	}
                                                            +}
                                                            +
                                                            +func TestVeryLongTxt(t *testing.T) {
                                                            +	want := &TXTResource{
                                                            +		ResourceHeader: ResourceHeader{
                                                            +			Name:  "foo.bar.example.com.",
                                                            +			Type:  TypeTXT,
                                                            +			Class: ClassINET,
                                                            +		},
                                                            +		Txt: loremIpsum,
                                                            +	}
                                                            +	buf, err := packResource(make([]byte, 0, 8000), want, map[string]int{})
                                                            +	if err != nil {
                                                            +		t.Fatal("Packing failed:", err)
                                                            +	}
                                                            +	var hdr ResourceHeader
                                                            +	off, err := hdr.unpack(buf, 0)
                                                            +	if err != nil {
                                                            +		t.Fatal("Unpacking ResourceHeader failed:", err)
                                                            +	}
                                                            +	got, n, err := unpackResource(buf, off, hdr)
                                                            +	if err != nil {
                                                            +		t.Fatal("Unpacking failed:", err)
                                                            +	}
                                                            +	if n != len(buf) {
                                                            +		t.Errorf("Unpacked different amount than packed: got n = %d, want = %d", n, len(buf))
                                                            +	}
                                                            +	if !reflect.DeepEqual(got, want) {
                                                            +		t.Errorf("Got = %+v, want = %+v", got, want)
                                                            +	}
                                                            +}
                                                            +
                                                            +func ExampleHeaderSearch() {
                                                            +	msg := Message{
                                                            +		Header: Header{Response: true, Authoritative: true},
                                                            +		Questions: []Question{
                                                            +			{
                                                            +				Name:  "foo.bar.example.com.",
                                                            +				Type:  TypeA,
                                                            +				Class: ClassINET,
                                                            +			},
                                                            +			{
                                                            +				Name:  "bar.example.com.",
                                                            +				Type:  TypeA,
                                                            +				Class: ClassINET,
                                                            +			},
                                                            +		},
                                                            +		Answers: []Resource{
                                                            +			&AResource{
                                                            +				ResourceHeader: ResourceHeader{
                                                            +					Name:  "foo.bar.example.com.",
                                                            +					Type:  TypeA,
                                                            +					Class: ClassINET,
                                                            +				},
                                                            +				A: [4]byte{127, 0, 0, 1},
                                                            +			},
                                                            +			&AResource{
                                                            +				ResourceHeader: ResourceHeader{
                                                            +					Name:  "bar.example.com.",
                                                            +					Type:  TypeA,
                                                            +					Class: ClassINET,
                                                            +				},
                                                            +				A: [4]byte{127, 0, 0, 2},
                                                            +			},
                                                            +		},
                                                            +	}
                                                            +
                                                            +	buf, err := msg.Pack()
                                                            +	if err != nil {
                                                            +		panic(err)
                                                            +	}
                                                            +
                                                            +	wantName := "bar.example.com."
                                                            +
                                                            +	var p Parser
                                                            +	if _, err := p.Start(buf); err != nil {
                                                            +		panic(err)
                                                            +	}
                                                            +
                                                            +	for {
                                                            +		q, err := p.Question()
                                                            +		if err == ErrSectionDone {
                                                            +			break
                                                            +		}
                                                            +		if err != nil {
                                                            +			panic(err)
                                                            +		}
                                                            +
                                                            +		if q.Name != wantName {
                                                            +			continue
                                                            +		}
                                                            +
                                                            +		fmt.Println("Found question for name", wantName)
                                                            +		if err := p.SkipAllQuestions(); err != nil {
                                                            +			panic(err)
                                                            +		}
                                                            +		break
                                                            +	}
                                                            +
                                                            +	var gotIPs []net.IP
                                                            +	for {
                                                            +		h, err := p.AnswerHeader()
                                                            +		if err == ErrSectionDone {
                                                            +			break
                                                            +		}
                                                            +		if err != nil {
                                                            +			panic(err)
                                                            +		}
                                                            +
                                                            +		if (h.Type != TypeA && h.Type != TypeAAAA) || h.Class != ClassINET {
                                                            +			continue
                                                            +		}
                                                            +
                                                            +		if !strings.EqualFold(h.Name, wantName) {
                                                            +			if err := p.SkipAnswer(); err != nil {
                                                            +				panic(err)
                                                            +			}
                                                            +			continue
                                                            +		}
                                                            +		a, err := p.Answer()
                                                            +		if err != nil {
                                                            +			panic(err)
                                                            +		}
                                                            +
                                                            +		switch r := a.(type) {
                                                            +		default:
                                                            +			panic(fmt.Sprintf("unknown type: %T", r))
                                                            +		case *AResource:
                                                            +			gotIPs = append(gotIPs, r.A[:])
                                                            +		case *AAAAResource:
                                                            +			gotIPs = append(gotIPs, r.AAAA[:])
                                                            +		}
                                                            +	}
                                                            +
                                                            +	fmt.Printf("Found A/AAAA records for name %s: %v\n", wantName, gotIPs)
                                                            +
                                                            +	// Output:
                                                            +	// Found question for name bar.example.com.
                                                            +	// Found A/AAAA records for name bar.example.com.: [127.0.0.2]
                                                            +}
                                                            +
                                                            +func largeTestMsg() Message {
                                                            +	return Message{
                                                            +		Header: Header{Response: true, Authoritative: true},
                                                            +		Questions: []Question{
                                                            +			{
                                                            +				Name:  "foo.bar.example.com.",
                                                            +				Type:  TypeA,
                                                            +				Class: ClassINET,
                                                            +			},
                                                            +		},
                                                            +		Answers: []Resource{
                                                            +			&AResource{
                                                            +				ResourceHeader: ResourceHeader{
                                                            +					Name:  "foo.bar.example.com.",
                                                            +					Type:  TypeA,
                                                            +					Class: ClassINET,
                                                            +				},
                                                            +				A: [4]byte{127, 0, 0, 1},
                                                            +			},
                                                            +			&AResource{
                                                            +				ResourceHeader: ResourceHeader{
                                                            +					Name:  "foo.bar.example.com.",
                                                            +					Type:  TypeA,
                                                            +					Class: ClassINET,
                                                            +				},
                                                            +				A: [4]byte{127, 0, 0, 2},
                                                            +			},
                                                            +		},
                                                            +		Authorities: []Resource{
                                                            +			&NSResource{
                                                            +				ResourceHeader: ResourceHeader{
                                                            +					Name:  "foo.bar.example.com.",
                                                            +					Type:  TypeNS,
                                                            +					Class: ClassINET,
                                                            +				},
                                                            +				NS: "ns1.example.com.",
                                                            +			},
                                                            +			&NSResource{
                                                            +				ResourceHeader: ResourceHeader{
                                                            +					Name:  "foo.bar.example.com.",
                                                            +					Type:  TypeNS,
                                                            +					Class: ClassINET,
                                                            +				},
                                                            +				NS: "ns2.example.com.",
                                                            +			},
                                                            +		},
                                                            +		Additionals: []Resource{
                                                            +			&TXTResource{
                                                            +				ResourceHeader: ResourceHeader{
                                                            +					Name:  "foo.bar.example.com.",
                                                            +					Type:  TypeTXT,
                                                            +					Class: ClassINET,
                                                            +				},
                                                            +				Txt: "So Long, and Thanks for All the Fish",
                                                            +			},
                                                            +			&TXTResource{
                                                            +				ResourceHeader: ResourceHeader{
                                                            +					Name:  "foo.bar.example.com.",
                                                            +					Type:  TypeTXT,
                                                            +					Class: ClassINET,
                                                            +				},
                                                            +				Txt: "Hamster Huey and the Gooey Kablooie",
                                                            +			},
                                                            +		},
                                                            +	}
                                                            +}
                                                            +
                                                            +const loremIpsum = `
                                                            +Lorem ipsum dolor sit amet, nec enim antiopam id, an ullum choro
                                                            +nonumes qui, pro eu debet honestatis mediocritatem. No alia enim eos,
                                                            +magna signiferumque ex vis. Mei no aperiri dissentias, cu vel quas
                                                            +regione. Malorum quaeque vim ut, eum cu semper aliquid invidunt, ei
                                                            +nam ipsum assentior.
                                                            +
                                                            +Nostrum appellantur usu no, vis ex probatus adipiscing. Cu usu illum
                                                            +facilis eleifend. Iusto conceptam complectitur vim id. Tale omnesque
                                                            +no usu, ei oblique sadipscing vim. At nullam voluptua usu, mei laudem
                                                            +reformidans et. Qui ei eros porro reformidans, ius suas veritus
                                                            +torquatos ex. Mea te facer alterum consequat.
                                                            +
                                                            +Soleat torquatos democritum sed et, no mea congue appareat, facer
                                                            +aliquam nec in. Has te ipsum tritani. At justo dicta option nec, movet
                                                            +phaedrum ad nam. Ea detracto verterem liberavisse has, delectus
                                                            +suscipiantur in mei. Ex nam meliore complectitur. Ut nam omnis
                                                            +honestatis quaerendum, ea mea nihil affert detracto, ad vix rebum
                                                            +mollis.
                                                            +
                                                            +Ut epicurei praesent neglegentur pri, prima fuisset intellegebat ad
                                                            +vim. An habemus comprehensam usu, at enim dignissim pro. Eam reque
                                                            +vivendum adipisci ea. Vel ne odio choro minimum. Sea admodum
                                                            +dissentiet ex. Mundi tamquam evertitur ius cu. Homero postea iisque ut
                                                            +pro, vel ne saepe senserit consetetur.
                                                            +
                                                            +Nulla utamur facilisis ius ea, in viderer diceret pertinax eum. Mei no
                                                            +enim quodsi facilisi, ex sed aeterno appareat mediocritatem, eum
                                                            +sententiae deterruisset ut. At suas timeam euismod cum, offendit
                                                            +appareat interpretaris ne vix. Vel ea civibus albucius, ex vim quidam
                                                            +accusata intellegebat, noluisse instructior sea id. Nec te nonumes
                                                            +habemus appellantur, quis dignissim vituperata eu nam.
                                                            +
                                                            +At vix apeirian patrioque vituperatoribus, an usu agam assum. Debet
                                                            +iisque an mea. Per eu dicant ponderum accommodare. Pri alienum
                                                            +placerat senserit an, ne eum ferri abhorreant vituperatoribus. Ut mea
                                                            +eligendi disputationi. Ius no tation everti impedit, ei magna quidam
                                                            +mediocritatem pri.
                                                            +
                                                            +Legendos perpetua iracundia ne usu, no ius ullum epicurei intellegam,
                                                            +ad modus epicuri lucilius eam. In unum quaerendum usu. Ne diam paulo
                                                            +has, ea veri virtute sed. Alia honestatis conclusionemque mea eu, ut
                                                            +iudico albucius his.
                                                            +
                                                            +Usu essent probatus eu, sed omnis dolor delicatissimi ex. No qui augue
                                                            +dissentias dissentiet. Laudem recteque no usu, vel an velit noluisse,
                                                            +an sed utinam eirmod appetere. Ne mea fuisset inimicus ocurreret. At
                                                            +vis dicant abhorreant, utinam forensibus nec ne, mei te docendi
                                                            +consequat. Brute inermis persecuti cum id. Ut ipsum munere propriae
                                                            +usu, dicit graeco disputando id has.
                                                            +
                                                            +Eros dolore quaerendum nam ei. Timeam ornatus inciderint pro id. Nec
                                                            +torquatos sadipscing ei, ancillae molestie per in. Malis principes duo
                                                            +ea, usu liber postulant ei.
                                                            +
                                                            +Graece timeam voluptatibus eu eam. Alia probatus quo no, ea scripta
                                                            +feugiat duo. Congue option meliore ex qui, noster invenire appellantur
                                                            +ea vel. Eu exerci legendos vel. Consetetur repudiandae vim ut. Vix an
                                                            +probo minimum, et nam illud falli tempor.
                                                            +
                                                            +Cum dico signiferumque eu. Sed ut regione maiorum, id veritus insolens
                                                            +tacimates vix. Eu mel sint tamquam lucilius, duo no oporteat
                                                            +tacimates. Atqui augue concludaturque vix ei, id mel utroque menandri.
                                                            +
                                                            +Ad oratio blandit aliquando pro. Vis et dolorum rationibus
                                                            +philosophia, ad cum nulla molestie. Hinc fuisset adversarium eum et,
                                                            +ne qui nisl verear saperet, vel te quaestio forensibus. Per odio
                                                            +option delenit an. Alii placerat has no, in pri nihil platonem
                                                            +cotidieque. Est ut elit copiosae scaevola, debet tollit maluisset sea
                                                            +an.
                                                            +
                                                            +Te sea hinc debet pericula, liber ridens fabulas cu sed, quem mutat
                                                            +accusam mea et. Elitr labitur albucius et pri, an labore feugait mel.
                                                            +Velit zril melius usu ea. Ad stet putent interpretaris qui. Mel no
                                                            +error volumus scripserit. In pro paulo iudico, quo ei dolorem
                                                            +verterem, affert fabellas dissentiet ea vix.
                                                            +
                                                            +Vis quot deserunt te. Error aliquid detraxit eu usu, vis alia eruditi
                                                            +salutatus cu. Est nostrud bonorum an, ei usu alii salutatus. Vel at
                                                            +nisl primis, eum ex aperiri noluisse reformidans. Ad veri velit
                                                            +utroque vis, ex equidem detraxit temporibus has.
                                                            +
                                                            +Inermis appareat usu ne. Eros placerat periculis mea ad, in dictas
                                                            +pericula pro. Errem postulant at usu, ea nec amet ornatus mentitum. Ad
                                                            +mazim graeco eum, vel ex percipit volutpat iudicabit, sit ne delicata
                                                            +interesset. Mel sapientem prodesset abhorreant et, oblique suscipit
                                                            +eam id.
                                                            +
                                                            +An maluisset disputando mea, vidit mnesarchum pri et. Malis insolens
                                                            +inciderint no sea. Ea persius maluisset vix, ne vim appellantur
                                                            +instructior, consul quidam definiebas pri id. Cum integre feugiat
                                                            +pericula in, ex sed persius similique, mel ne natum dicit percipitur.
                                                            +
                                                            +Primis discere ne pri, errem putent definitionem at vis. Ei mel dolore
                                                            +neglegentur, mei tincidunt percipitur ei. Pro ad simul integre
                                                            +rationibus. Eu vel alii honestatis definitiones, mea no nonumy
                                                            +reprehendunt.
                                                            +
                                                            +Dicta appareat legendos est cu. Eu vel congue dicunt omittam, no vix
                                                            +adhuc minimum constituam, quot noluisse id mel. Eu quot sale mutat
                                                            +duo, ex nisl munere invenire duo. Ne nec ullum utamur. Pro alterum
                                                            +debitis nostrum no, ut vel aliquid vivendo.
                                                            +
                                                            +Aliquip fierent praesent quo ne, id sit audiam recusabo delicatissimi.
                                                            +Usu postulant incorrupte cu. At pro dicit tibique intellegam, cibo
                                                            +dolore impedit id eam, et aeque feugait assentior has. Quando sensibus
                                                            +nec ex. Possit sensibus pri ad, unum mutat periculis cu vix.
                                                            +
                                                            +Mundi tibique vix te, duo simul partiendo qualisque id, est at vidit
                                                            +sonet tempor. No per solet aeterno deseruisse. Petentium salutandi
                                                            +definiebas pri cu. Munere vivendum est in. Ei justo congue eligendi
                                                            +vis, modus offendit omittantur te mel.
                                                            +
                                                            +Integre voluptaria in qui, sit habemus tractatos constituam no. Utinam
                                                            +melius conceptam est ne, quo in minimum apeirian delicata, ut ius
                                                            +porro recusabo. Dicant expetenda vix no, ludus scripserit sed ex, eu
                                                            +his modo nostro. Ut etiam sonet his, quodsi inciderint philosophia te
                                                            +per. Nullam lobortis eu cum, vix an sonet efficiendi repudiandae. Vis
                                                            +ad idque fabellas intellegebat.
                                                            +
                                                            +Eum commodo senserit conclusionemque ex. Sed forensibus sadipscing ut,
                                                            +mei in facer delicata periculis, sea ne hinc putent cetero. Nec ne
                                                            +alia corpora invenire, alia prima soleat te cum. Eleifend posidonium
                                                            +nam at.
                                                            +
                                                            +Dolorum indoctum cu quo, ex dolor legendos recteque eam, cu pri zril
                                                            +discere. Nec civibus officiis dissentiunt ex, est te liber ludus
                                                            +elaboraret. Cum ea fabellas invenire. Ex vim nostrud eripuit
                                                            +comprehensam, nam te inermis delectus, saepe inermis senserit.
                                                            +`
                                                            

                                                            To view, visit change 35237. To unsubscribe, visit settings.

                                                            Gerrit-Project: net
                                                            Gerrit-Branch: master
                                                            Gerrit-MessageType: merged
                                                            Gerrit-Change-Id: Ib754d0007609a617d88be867f21c2feb15b6fcd7
                                                            Gerrit-Change-Number: 35237
                                                            Gerrit-PatchSet: 22
                                                            Reply all
                                                            Reply to author
                                                            Forward
                                                            0 new messages