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.mikioh@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Mikio Hara <mikioh.mikioh@gmail.com>
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.
+`