aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/golang.org/x/net/dns/dnsmessage/message.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/golang.org/x/net/dns/dnsmessage/message.go')
-rw-r--r--vendor/golang.org/x/net/dns/dnsmessage/message.go818
1 files changed, 709 insertions, 109 deletions
diff --git a/vendor/golang.org/x/net/dns/dnsmessage/message.go b/vendor/golang.org/x/net/dns/dnsmessage/message.go
index c7244b7..dc5b143 100644
--- a/vendor/golang.org/x/net/dns/dnsmessage/message.go
+++ b/vendor/golang.org/x/net/dns/dnsmessage/message.go
@@ -5,6 +5,9 @@
// Package dnsmessage provides a mostly RFC 1035 compliant implementation of
// DNS message packing and unpacking.
//
+// The package also supports messages with Extension Mechanisms for DNS
+// (EDNS(0)) as defined in RFC 6891.
+//
// This implementation is designed to minimize heap allocations and avoid
// unnecessary packing and unpacking as much as possible.
package dnsmessage
@@ -13,21 +16,11 @@ import (
"errors"
)
-// Packet formats
+// Message 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
@@ -39,6 +32,7 @@ const (
TypeTXT Type = 16
TypeAAAA Type = 28
TypeSRV Type = 33
+ TypeOPT Type = 41
// Question.Type
TypeWKS Type = 11
@@ -46,7 +40,46 @@ const (
TypeMINFO Type = 14
TypeAXFR Type = 252
TypeALL Type = 255
+)
+var typeNames = map[Type]string{
+ TypeA: "TypeA",
+ TypeNS: "TypeNS",
+ TypeCNAME: "TypeCNAME",
+ TypeSOA: "TypeSOA",
+ TypePTR: "TypePTR",
+ TypeMX: "TypeMX",
+ TypeTXT: "TypeTXT",
+ TypeAAAA: "TypeAAAA",
+ TypeSRV: "TypeSRV",
+ TypeOPT: "TypeOPT",
+ TypeWKS: "TypeWKS",
+ TypeHINFO: "TypeHINFO",
+ TypeMINFO: "TypeMINFO",
+ TypeAXFR: "TypeAXFR",
+ TypeALL: "TypeALL",
+}
+
+// String implements fmt.Stringer.String.
+func (t Type) String() string {
+ if n, ok := typeNames[t]; ok {
+ return n
+ }
+ return printUint16(uint16(t))
+}
+
+// GoString implements fmt.GoStringer.GoString.
+func (t Type) GoString() string {
+ if n, ok := typeNames[t]; ok {
+ return "dnsmessage." + n
+ }
+ return printUint16(uint16(t))
+}
+
+// A Class is a type of network.
+type Class uint16
+
+const (
// ResourceHeader.Class and Question.Class
ClassINET Class = 1
ClassCSNET Class = 2
@@ -55,7 +88,44 @@ const (
// Question.Class
ClassANY Class = 255
+)
+
+var classNames = map[Class]string{
+ ClassINET: "ClassINET",
+ ClassCSNET: "ClassCSNET",
+ ClassCHAOS: "ClassCHAOS",
+ ClassHESIOD: "ClassHESIOD",
+ ClassANY: "ClassANY",
+}
+
+// String implements fmt.Stringer.String.
+func (c Class) String() string {
+ if n, ok := classNames[c]; ok {
+ return n
+ }
+ return printUint16(uint16(c))
+}
+// GoString implements fmt.GoStringer.GoString.
+func (c Class) GoString() string {
+ if n, ok := classNames[c]; ok {
+ return "dnsmessage." + n
+ }
+ return printUint16(uint16(c))
+}
+
+// An OpCode is a DNS operation code.
+type OpCode uint16
+
+// GoString implements fmt.GoStringer.GoString.
+func (o OpCode) GoString() string {
+ return printUint16(uint16(o))
+}
+
+// An RCode is a DNS response status code.
+type RCode uint16
+
+const (
// Message.Rcode
RCodeSuccess RCode = 0
RCodeFormatError RCode = 1
@@ -65,6 +135,116 @@ const (
RCodeRefused RCode = 5
)
+var rCodeNames = map[RCode]string{
+ RCodeSuccess: "RCodeSuccess",
+ RCodeFormatError: "RCodeFormatError",
+ RCodeServerFailure: "RCodeServerFailure",
+ RCodeNameError: "RCodeNameError",
+ RCodeNotImplemented: "RCodeNotImplemented",
+ RCodeRefused: "RCodeRefused",
+}
+
+// String implements fmt.Stringer.String.
+func (r RCode) String() string {
+ if n, ok := rCodeNames[r]; ok {
+ return n
+ }
+ return printUint16(uint16(r))
+}
+
+// GoString implements fmt.GoStringer.GoString.
+func (r RCode) GoString() string {
+ if n, ok := rCodeNames[r]; ok {
+ return "dnsmessage." + n
+ }
+ return printUint16(uint16(r))
+}
+
+func printPaddedUint8(i uint8) string {
+ b := byte(i)
+ return string([]byte{
+ b/100 + '0',
+ b/10%10 + '0',
+ b%10 + '0',
+ })
+}
+
+func printUint8Bytes(buf []byte, i uint8) []byte {
+ b := byte(i)
+ if i >= 100 {
+ buf = append(buf, b/100+'0')
+ }
+ if i >= 10 {
+ buf = append(buf, b/10%10+'0')
+ }
+ return append(buf, b%10+'0')
+}
+
+func printByteSlice(b []byte) string {
+ if len(b) == 0 {
+ return ""
+ }
+ buf := make([]byte, 0, 5*len(b))
+ buf = printUint8Bytes(buf, uint8(b[0]))
+ for _, n := range b[1:] {
+ buf = append(buf, ',', ' ')
+ buf = printUint8Bytes(buf, uint8(n))
+ }
+ return string(buf)
+}
+
+const hexDigits = "0123456789abcdef"
+
+func printString(str []byte) string {
+ buf := make([]byte, 0, len(str))
+ for i := 0; i < len(str); i++ {
+ c := str[i]
+ if c == '.' || c == '-' || c == ' ' ||
+ 'A' <= c && c <= 'Z' ||
+ 'a' <= c && c <= 'z' ||
+ '0' <= c && c <= '9' {
+ buf = append(buf, c)
+ continue
+ }
+
+ upper := c >> 4
+ lower := (c << 4) >> 4
+ buf = append(
+ buf,
+ '\\',
+ 'x',
+ hexDigits[upper],
+ hexDigits[lower],
+ )
+ }
+ return string(buf)
+}
+
+func printUint16(i uint16) string {
+ return printUint32(uint32(i))
+}
+
+func printUint32(i uint32) string {
+ // Max value is 4294967295.
+ buf := make([]byte, 10)
+ for b, d := buf, uint32(1000000000); d > 0; d /= 10 {
+ b[0] = byte(i/d%10 + '0')
+ if b[0] == '0' && len(b) == len(buf) && len(buf) > 1 {
+ buf = buf[1:]
+ }
+ b = b[1:]
+ i %= d
+ }
+ return string(buf)
+}
+
+func printBool(b bool) string {
+ if b {
+ return "true"
+ }
+ return "false"
+}
+
var (
// ErrNotStarted indicates that the prerequisite information isn't
// available yet because the previous records haven't been appropriately
@@ -90,6 +270,8 @@ var (
errTooManyAuthorities = errors.New("too many Authorities to pack (>65535)")
errTooManyAdditionals = errors.New("too many Additionals to pack (>65535)")
errNonCanonicalName = errors.New("name is not in canonical format (it must end with a .)")
+ errStringTooLong = errors.New("character string exceeds maximum length (255)")
+ errCompressedSRV = errors.New("compressed name in SRV resource data")
)
// Internal constants.
@@ -159,6 +341,19 @@ func (m *Header) pack() (id uint16, bits uint16) {
return
}
+// GoString implements fmt.GoStringer.GoString.
+func (m *Header) GoString() string {
+ return "dnsmessage.Header{" +
+ "ID: " + printUint16(m.ID) + ", " +
+ "Response: " + printBool(m.Response) + ", " +
+ "OpCode: " + m.OpCode.GoString() + ", " +
+ "Authoritative: " + printBool(m.Authoritative) + ", " +
+ "Truncated: " + printBool(m.Truncated) + ", " +
+ "RecursionDesired: " + printBool(m.RecursionDesired) + ", " +
+ "RecursionAvailable: " + printBool(m.RecursionAvailable) + ", " +
+ "RCode: " + m.RCode.GoString() + "}"
+}
+
// Message is a representation of a DNS message.
type Message struct {
Header
@@ -218,6 +413,7 @@ func (h *header) count(sec section) uint16 {
return 0
}
+// pack appends the wire format of the header to msg.
func (h *header) pack(msg []byte) []byte {
msg = packUint16(msg, h.id)
msg = packUint16(msg, h.bits)
@@ -270,28 +466,39 @@ type Resource struct {
Body ResourceBody
}
+func (r *Resource) GoString() string {
+ return "dnsmessage.Resource{" +
+ "Header: " + r.Header.GoString() +
+ ", Body: &" + r.Body.GoString() +
+ "}"
+}
+
// A ResourceBody is a DNS resource record minus the header.
type ResourceBody interface {
// pack packs a Resource except for its header.
- pack(msg []byte, compression map[string]int) ([]byte, error)
+ pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error)
// realType returns the actual type of the Resource. This is used to
// fill in the header Type field.
realType() Type
+
+ // GoString implements fmt.GoStringer.GoString.
+ GoString() string
}
-func (r *Resource) pack(msg []byte, compression map[string]int) ([]byte, error) {
+// pack appends the wire format of the Resource to msg.
+func (r *Resource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) {
if r.Body == nil {
return msg, errNilResouceBody
}
oldMsg := msg
r.Header.Type = r.Body.realType()
- msg, length, err := r.Header.pack(msg, compression)
+ msg, length, err := r.Header.pack(msg, compression, compressionOff)
if err != nil {
return msg, &nestedError{"ResourceHeader", err}
}
preLen := len(msg)
- msg, err = r.Body.pack(msg, compression)
+ msg, err = r.Body.pack(msg, compression, compressionOff)
if err != nil {
return msg, &nestedError{"content", err}
}
@@ -436,7 +643,13 @@ func (p *Parser) Question() (Question, error) {
// AllQuestions parses all Questions.
func (p *Parser) AllQuestions() ([]Question, error) {
- qs := make([]Question, 0, p.header.questions)
+ // Multiple questions are valid according to the spec,
+ // but servers don't actually support them. There will
+ // be at most one question here.
+ //
+ // Do not pre-allocate based on info in p.header, since
+ // the data is untrusted.
+ qs := []Question{}
for {
q, err := p.Question()
if err == ErrSectionDone {
@@ -492,7 +705,16 @@ func (p *Parser) Answer() (Resource, error) {
// AllAnswers parses all Answer Resources.
func (p *Parser) AllAnswers() ([]Resource, error) {
- as := make([]Resource, 0, p.header.answers)
+ // The most common query is for A/AAAA, which usually returns
+ // a handful of IPs.
+ //
+ // Pre-allocate up to a certain limit, since p.header is
+ // untrusted data.
+ n := int(p.header.answers)
+ if n > 20 {
+ n = 20
+ }
+ as := make([]Resource, 0, n)
for {
a, err := p.Answer()
if err == ErrSectionDone {
@@ -533,7 +755,16 @@ func (p *Parser) Authority() (Resource, error) {
// AllAuthorities parses all Authority Resources.
func (p *Parser) AllAuthorities() ([]Resource, error) {
- as := make([]Resource, 0, p.header.authorities)
+ // Authorities contains SOA in case of NXDOMAIN and friends,
+ // otherwise it is empty.
+ //
+ // Pre-allocate up to a certain limit, since p.header is
+ // untrusted data.
+ n := int(p.header.authorities)
+ if n > 10 {
+ n = 10
+ }
+ as := make([]Resource, 0, n)
for {
a, err := p.Authority()
if err == ErrSectionDone {
@@ -574,7 +805,16 @@ func (p *Parser) Additional() (Resource, error) {
// AllAdditionals parses all Additional Resources.
func (p *Parser) AllAdditionals() ([]Resource, error) {
- as := make([]Resource, 0, p.header.additionals)
+ // Additionals usually contain OPT, and sometimes A/AAAA
+ // glue records.
+ //
+ // Pre-allocate up to a certain limit, since p.header is
+ // untrusted data.
+ n := int(p.header.additionals)
+ if n > 10 {
+ n = 10
+ }
+ as := make([]Resource, 0, n)
for {
a, err := p.Additional()
if err == ErrSectionDone {
@@ -765,6 +1005,24 @@ func (p *Parser) AAAAResource() (AAAAResource, error) {
return r, nil
}
+// OPTResource parses a single OPTResource.
+//
+// One of the XXXHeader methods must have been called before calling this
+// method.
+func (p *Parser) OPTResource() (OPTResource, error) {
+ if !p.resHeaderValid || p.resHeader.Type != TypeOPT {
+ return OPTResource{}, ErrNotStarted
+ }
+ r, err := unpackOPTResource(p.msg, p.off, p.resHeader.Length)
+ if err != nil {
+ return OPTResource{}, err
+ }
+ p.off += int(p.resHeader.Length)
+ p.resHeaderValid = false
+ p.index++
+ return r, nil
+}
+
// Unpack parses a full Message.
func (m *Message) Unpack(msg []byte) error {
var p Parser
@@ -819,6 +1077,7 @@ func (m *Message) AppendPack(b []byte) ([]byte, error) {
h.authorities = uint16(len(m.Authorities))
h.additionals = uint16(len(m.Additionals))
+ compressionOff := len(b)
msg := h.pack(b)
// RFC 1035 allows (but does not require) compression for packing. RFC
@@ -826,32 +1085,32 @@ func (m *Message) AppendPack(b []byte) ([]byte, error) {
// 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
+ // DNS messages can be a maximum of 512 bytes long. Without compression,
+ // many DNS response messages are over this limit, so enabling
// compression will help ensure compliance.
compression := map[string]int{}
for i := range m.Questions {
var err error
- if msg, err = m.Questions[i].pack(msg, compression); err != nil {
+ if msg, err = m.Questions[i].pack(msg, compression, compressionOff); err != nil {
return nil, &nestedError{"packing Question", err}
}
}
for i := range m.Answers {
var err error
- if msg, err = m.Answers[i].pack(msg, compression); err != nil {
+ if msg, err = m.Answers[i].pack(msg, compression, compressionOff); err != nil {
return nil, &nestedError{"packing Answer", err}
}
}
for i := range m.Authorities {
var err error
- if msg, err = m.Authorities[i].pack(msg, compression); err != nil {
+ if msg, err = m.Authorities[i].pack(msg, compression, compressionOff); err != nil {
return nil, &nestedError{"packing Authority", err}
}
}
for i := range m.Additionals {
var err error
- if msg, err = m.Additionals[i].pack(msg, compression); err != nil {
+ if msg, err = m.Additionals[i].pack(msg, compression, compressionOff); err != nil {
return nil, &nestedError{"packing Additional", err}
}
}
@@ -859,37 +1118,104 @@ func (m *Message) AppendPack(b []byte) ([]byte, error) {
return msg, nil
}
+// GoString implements fmt.GoStringer.GoString.
+func (m *Message) GoString() string {
+ s := "dnsmessage.Message{Header: " + m.Header.GoString() + ", " +
+ "Questions: []dnsmessage.Question{"
+ if len(m.Questions) > 0 {
+ s += m.Questions[0].GoString()
+ for _, q := range m.Questions[1:] {
+ s += ", " + q.GoString()
+ }
+ }
+ s += "}, Answers: []dnsmessage.Resource{"
+ if len(m.Answers) > 0 {
+ s += m.Answers[0].GoString()
+ for _, a := range m.Answers[1:] {
+ s += ", " + a.GoString()
+ }
+ }
+ s += "}, Authorities: []dnsmessage.Resource{"
+ if len(m.Authorities) > 0 {
+ s += m.Authorities[0].GoString()
+ for _, a := range m.Authorities[1:] {
+ s += ", " + a.GoString()
+ }
+ }
+ s += "}, Additionals: []dnsmessage.Resource{"
+ if len(m.Additionals) > 0 {
+ s += m.Additionals[0].GoString()
+ for _, a := range m.Additionals[1:] {
+ s += ", " + a.GoString()
+ }
+ }
+ return s + "}}"
+}
+
// A Builder allows incrementally packing a DNS message.
+//
+// Example usage:
+// buf := make([]byte, 2, 514)
+// b := NewBuilder(buf, Header{...})
+// b.EnableCompression()
+// // Optionally start a section and add things to that section.
+// // Repeat adding sections as necessary.
+// buf, err := b.Finish()
+// // If err is nil, buf[2:] will contain the built bytes.
type Builder struct {
- msg []byte
- header header
- section section
+ // msg is the storage for the message being built.
+ msg []byte
+
+ // section keeps track of the current section being built.
+ section section
+
+ // header keeps track of what should go in the header when Finish is
+ // called.
+ header header
+
+ // start is the starting index of the bytes allocated in msg for header.
+ start int
+
+ // compression is a mapping from name suffixes to their starting index
+ // in msg.
compression map[string]int
}
-// Start initializes the builder.
+// NewBuilder creates a new builder with compression disabled.
//
-// buf is optional (nil is fine), but if provided, Start takes ownership of buf.
-func (b *Builder) Start(buf []byte, h Header) {
- b.StartWithoutCompression(buf, h)
- b.compression = map[string]int{}
+// Note: Most users will want to immediately enable compression with the
+// EnableCompression method. See that method's comment for why you may or may
+// not want to enable compression.
+//
+// The DNS message is appended to the provided initial buffer buf (which may be
+// nil) as it is built. The final message is returned by the (*Builder).Finish
+// method, which may return the same underlying array if there was sufficient
+// capacity in the slice.
+func NewBuilder(buf []byte, h Header) Builder {
+ if buf == nil {
+ buf = make([]byte, 0, packStartingCap)
+ }
+ b := Builder{msg: buf, start: len(buf)}
+ b.header.id, b.header.bits = h.pack()
+ var hb [headerLen]byte
+ b.msg = append(b.msg, hb[:]...)
+ b.section = sectionHeader
+ return b
}
-// StartWithoutCompression initializes the builder with compression disabled.
+// EnableCompression enables compression in the Builder.
//
-// This avoids compression related allocations, but can result in larger message
-// sizes. Be careful with this mode as it can cause messages to exceed the UDP
-// size limit.
+// Leaving compression disabled avoids compression related allocations, but can
+// result in larger message sizes. Be careful with this mode as it can cause
+// messages to exceed the UDP size limit.
//
-// buf is optional (nil is fine), but if provided, Start takes ownership of buf.
-func (b *Builder) StartWithoutCompression(buf []byte, h Header) {
- *b = Builder{msg: buf}
- b.header.id, b.header.bits = h.pack()
- if cap(b.msg) < headerLen {
- b.msg = make([]byte, 0, packStartingCap)
- }
- b.msg = b.msg[:headerLen]
- b.section = sectionHeader
+// According to RFC 1035, section 4.1.4, the use of compression is optional, but
+// all implementations must accept both compressed and uncompressed DNS
+// messages.
+//
+// Compression should be enabled before any sections are added for best results.
+func (b *Builder) EnableCompression() {
+ b.compression = map[string]int{}
}
func (b *Builder) startCheck(s section) error {
@@ -970,7 +1296,7 @@ func (b *Builder) Question(q Question) error {
if b.section > sectionQuestions {
return ErrSectionDone
}
- msg, err := q.pack(b.msg, b.compression)
+ msg, err := q.pack(b.msg, b.compression, b.start)
if err != nil {
return err
}
@@ -997,12 +1323,12 @@ func (b *Builder) CNAMEResource(h ResourceHeader, r CNAMEResource) error {
return err
}
h.Type = r.realType()
- msg, length, err := h.pack(b.msg, b.compression)
+ msg, length, err := h.pack(b.msg, b.compression, b.start)
if err != nil {
return &nestedError{"ResourceHeader", err}
}
preLen := len(msg)
- if msg, err = r.pack(msg, b.compression); err != nil {
+ if msg, err = r.pack(msg, b.compression, b.start); err != nil {
return &nestedError{"CNAMEResource body", err}
}
if err := h.fixLen(msg, length, preLen); err != nil {
@@ -1021,12 +1347,12 @@ func (b *Builder) MXResource(h ResourceHeader, r MXResource) error {
return err
}
h.Type = r.realType()
- msg, length, err := h.pack(b.msg, b.compression)
+ msg, length, err := h.pack(b.msg, b.compression, b.start)
if err != nil {
return &nestedError{"ResourceHeader", err}
}
preLen := len(msg)
- if msg, err = r.pack(msg, b.compression); err != nil {
+ if msg, err = r.pack(msg, b.compression, b.start); err != nil {
return &nestedError{"MXResource body", err}
}
if err := h.fixLen(msg, length, preLen); err != nil {
@@ -1045,12 +1371,12 @@ func (b *Builder) NSResource(h ResourceHeader, r NSResource) error {
return err
}
h.Type = r.realType()
- msg, length, err := h.pack(b.msg, b.compression)
+ msg, length, err := h.pack(b.msg, b.compression, b.start)
if err != nil {
return &nestedError{"ResourceHeader", err}
}
preLen := len(msg)
- if msg, err = r.pack(msg, b.compression); err != nil {
+ if msg, err = r.pack(msg, b.compression, b.start); err != nil {
return &nestedError{"NSResource body", err}
}
if err := h.fixLen(msg, length, preLen); err != nil {
@@ -1069,12 +1395,12 @@ func (b *Builder) PTRResource(h ResourceHeader, r PTRResource) error {
return err
}
h.Type = r.realType()
- msg, length, err := h.pack(b.msg, b.compression)
+ msg, length, err := h.pack(b.msg, b.compression, b.start)
if err != nil {
return &nestedError{"ResourceHeader", err}
}
preLen := len(msg)
- if msg, err = r.pack(msg, b.compression); err != nil {
+ if msg, err = r.pack(msg, b.compression, b.start); err != nil {
return &nestedError{"PTRResource body", err}
}
if err := h.fixLen(msg, length, preLen); err != nil {
@@ -1093,12 +1419,12 @@ func (b *Builder) SOAResource(h ResourceHeader, r SOAResource) error {
return err
}
h.Type = r.realType()
- msg, length, err := h.pack(b.msg, b.compression)
+ msg, length, err := h.pack(b.msg, b.compression, b.start)
if err != nil {
return &nestedError{"ResourceHeader", err}
}
preLen := len(msg)
- if msg, err = r.pack(msg, b.compression); err != nil {
+ if msg, err = r.pack(msg, b.compression, b.start); err != nil {
return &nestedError{"SOAResource body", err}
}
if err := h.fixLen(msg, length, preLen); err != nil {
@@ -1117,12 +1443,12 @@ func (b *Builder) TXTResource(h ResourceHeader, r TXTResource) error {
return err
}
h.Type = r.realType()
- msg, length, err := h.pack(b.msg, b.compression)
+ msg, length, err := h.pack(b.msg, b.compression, b.start)
if err != nil {
return &nestedError{"ResourceHeader", err}
}
preLen := len(msg)
- if msg, err = r.pack(msg, b.compression); err != nil {
+ if msg, err = r.pack(msg, b.compression, b.start); err != nil {
return &nestedError{"TXTResource body", err}
}
if err := h.fixLen(msg, length, preLen); err != nil {
@@ -1141,12 +1467,12 @@ func (b *Builder) SRVResource(h ResourceHeader, r SRVResource) error {
return err
}
h.Type = r.realType()
- msg, length, err := h.pack(b.msg, b.compression)
+ msg, length, err := h.pack(b.msg, b.compression, b.start)
if err != nil {
return &nestedError{"ResourceHeader", err}
}
preLen := len(msg)
- if msg, err = r.pack(msg, b.compression); err != nil {
+ if msg, err = r.pack(msg, b.compression, b.start); err != nil {
return &nestedError{"SRVResource body", err}
}
if err := h.fixLen(msg, length, preLen); err != nil {
@@ -1165,12 +1491,12 @@ func (b *Builder) AResource(h ResourceHeader, r AResource) error {
return err
}
h.Type = r.realType()
- msg, length, err := h.pack(b.msg, b.compression)
+ msg, length, err := h.pack(b.msg, b.compression, b.start)
if err != nil {
return &nestedError{"ResourceHeader", err}
}
preLen := len(msg)
- if msg, err = r.pack(msg, b.compression); err != nil {
+ if msg, err = r.pack(msg, b.compression, b.start); err != nil {
return &nestedError{"AResource body", err}
}
if err := h.fixLen(msg, length, preLen); err != nil {
@@ -1189,12 +1515,12 @@ func (b *Builder) AAAAResource(h ResourceHeader, r AAAAResource) error {
return err
}
h.Type = r.realType()
- msg, length, err := h.pack(b.msg, b.compression)
+ msg, length, err := h.pack(b.msg, b.compression, b.start)
if err != nil {
return &nestedError{"ResourceHeader", err}
}
preLen := len(msg)
- if msg, err = r.pack(msg, b.compression); err != nil {
+ if msg, err = r.pack(msg, b.compression, b.start); err != nil {
return &nestedError{"AAAAResource body", err}
}
if err := h.fixLen(msg, length, preLen); err != nil {
@@ -1207,13 +1533,38 @@ func (b *Builder) AAAAResource(h ResourceHeader, r AAAAResource) error {
return nil
}
-// Finish ends message building and generates a binary packet.
+// OPTResource adds a single OPTResource.
+func (b *Builder) OPTResource(h ResourceHeader, r OPTResource) error {
+ if err := b.checkResourceSection(); err != nil {
+ return err
+ }
+ h.Type = r.realType()
+ msg, length, err := h.pack(b.msg, b.compression, b.start)
+ if err != nil {
+ return &nestedError{"ResourceHeader", err}
+ }
+ preLen := len(msg)
+ if msg, err = r.pack(msg, b.compression, b.start); err != nil {
+ return &nestedError{"OPTResource body", err}
+ }
+ if err := h.fixLen(msg, length, preLen); err != nil {
+ return err
+ }
+ if err := b.incrementSectionCount(); err != nil {
+ return err
+ }
+ b.msg = msg
+ return nil
+}
+
+// Finish ends message building and generates a binary message.
func (b *Builder) Finish() ([]byte, error) {
if b.section < sectionHeader {
return nil, ErrNotStarted
}
b.section = sectionDone
- b.header.pack(b.msg[:0])
+ // Space for the header was allocated in NewBuilder.
+ b.header.pack(b.msg[b.start:b.start])
return b.msg, nil
}
@@ -1243,12 +1594,23 @@ type ResourceHeader struct {
Length uint16
}
-// 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) {
+// GoString implements fmt.GoStringer.GoString.
+func (h *ResourceHeader) GoString() string {
+ return "dnsmessage.ResourceHeader{" +
+ "Name: " + h.Name.GoString() + ", " +
+ "Type: " + h.Type.GoString() + ", " +
+ "Class: " + h.Class.GoString() + ", " +
+ "TTL: " + printUint32(h.TTL) + ", " +
+ "Length: " + printUint16(h.Length) + "}"
+}
+
+// pack appends the wire format of the ResourceHeader to oldMsg.
+//
+// The bytes where length was packed are returned as a slice so they can be
+// updated after the rest of the Resource has been packed.
+func (h *ResourceHeader) pack(oldMsg []byte, compression map[string]int, compressionOff int) (msg []byte, length []byte, err error) {
msg = oldMsg
- if msg, err = h.Name.pack(msg, compression); err != nil {
+ if msg, err = h.Name.pack(msg, compression, compressionOff); err != nil {
return oldMsg, nil, &nestedError{"Name", err}
}
msg = packType(msg, h.Type)
@@ -1293,6 +1655,44 @@ func (h *ResourceHeader) fixLen(msg []byte, length []byte, preLen int) error {
return nil
}
+// EDNS(0) wire costants.
+const (
+ edns0Version = 0
+
+ edns0DNSSECOK = 0x00008000
+ ednsVersionMask = 0x00ff0000
+ edns0DNSSECOKMask = 0x00ff8000
+)
+
+// SetEDNS0 configures h for EDNS(0).
+//
+// The provided extRCode must be an extedned RCode.
+func (h *ResourceHeader) SetEDNS0(udpPayloadLen int, extRCode RCode, dnssecOK bool) error {
+ h.Name = Name{Data: [nameLen]byte{'.'}, Length: 1} // RFC 6891 section 6.1.2
+ h.Type = TypeOPT
+ h.Class = Class(udpPayloadLen)
+ h.TTL = uint32(extRCode) >> 4 << 24
+ if dnssecOK {
+ h.TTL |= edns0DNSSECOK
+ }
+ return nil
+}
+
+// DNSSECAllowed reports whether the DNSSEC OK bit is set.
+func (h *ResourceHeader) DNSSECAllowed() bool {
+ return h.TTL&edns0DNSSECOKMask == edns0DNSSECOK // RFC 6891 section 6.1.3
+}
+
+// ExtendedRCode returns an extended RCode.
+//
+// The provided rcode must be the RCode in DNS message header.
+func (h *ResourceHeader) ExtendedRCode(rcode RCode) RCode {
+ if h.TTL&ednsVersionMask == edns0Version { // RFC 6891 section 6.1.3
+ return RCode(h.TTL>>24<<4) | rcode
+ }
+ return rcode
+}
+
func skipResource(msg []byte, off int) (int, error) {
newOff, err := skipName(msg, off)
if err != nil {
@@ -1317,6 +1717,7 @@ func skipResource(msg []byte, off int) (int, error) {
return newOff, nil
}
+// packUint16 appends the wire format of field to msg.
func packUint16(msg []byte, field uint16) []byte {
return append(msg, byte(field>>8), byte(field))
}
@@ -1335,6 +1736,7 @@ func skipUint16(msg []byte, off int) (int, error) {
return off + uint16Len, nil
}
+// packType appends the wire format of field to msg.
func packType(msg []byte, field Type) []byte {
return packUint16(msg, uint16(field))
}
@@ -1348,6 +1750,7 @@ func skipType(msg []byte, off int) (int, error) {
return skipUint16(msg, off)
}
+// packClass appends the wire format of field to msg.
func packClass(msg []byte, field Class) []byte {
return packUint16(msg, uint16(field))
}
@@ -1361,6 +1764,7 @@ func skipClass(msg []byte, off int) (int, error) {
return skipUint16(msg, off)
}
+// packUint32 appends the wire format of field to msg.
func packUint32(msg []byte, field uint32) []byte {
return append(
msg,
@@ -1386,17 +1790,16 @@ func skipUint32(msg []byte, off int) (int, error) {
return off + uint32Len, 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:]
+// packText appends the wire format of field to msg.
+func packText(msg []byte, field string) ([]byte, error) {
+ l := len(field)
+ if l > 255 {
+ return nil, errStringTooLong
}
- return msg
+ msg = append(msg, byte(l))
+ msg = append(msg, field...)
+
+ return msg, nil
}
func unpackText(msg []byte, off int) (string, int, error) {
@@ -1422,6 +1825,7 @@ func skipText(msg []byte, off int) (int, error) {
return endOff, nil
}
+// packBytes appends the wire format of field to msg.
func packBytes(msg []byte, field []byte) []byte {
return append(msg, field...)
}
@@ -1462,18 +1866,33 @@ func NewName(name string) (Name, error) {
return n, nil
}
+// MustNewName creates a new Name from a string and panics on error.
+func MustNewName(name string) Name {
+ n, err := NewName(name)
+ if err != nil {
+ panic("creating name: " + err.Error())
+ }
+ return n
+}
+
+// String implements fmt.Stringer.String.
func (n Name) String() string {
return string(n.Data[:n.Length])
}
-// pack packs a domain name.
+// GoString implements fmt.GoStringer.GoString.
+func (n *Name) GoString() string {
+ return `dnsmessage.MustNewName("` + printString(n.Data[:n.Length]) + `")`
+}
+
+// pack appends the wire format of the Name to msg.
//
// 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 (n *Name) pack(msg []byte, compression map[string]int) ([]byte, error) {
+func (n *Name) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) {
oldMsg := msg
// Add a trailing dot to canonicalize name.
@@ -1525,7 +1944,7 @@ func (n *Name) pack(msg []byte, compression map[string]int) ([]byte, error) {
// 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[string(n.Data[i:])] = len(msg)
+ compression[string(n.Data[i:])] = len(msg) - compressionOff
}
}
}
@@ -1534,6 +1953,10 @@ func (n *Name) pack(msg []byte, compression map[string]int) ([]byte, error) {
// unpack unpacks a domain name.
func (n *Name) unpack(msg []byte, off int) (int, error) {
+ return n.unpackCompressed(msg, off, true /* allowCompression */)
+}
+
+func (n *Name) unpackCompressed(msg []byte, off int, allowCompression bool) (int, error) {
// currOff is the current working offset.
currOff := off
@@ -1569,6 +1992,9 @@ Loop:
name = append(name, '.')
currOff = endOff
case 0xC0: // Pointer
+ if !allowCompression {
+ return off, errCompressedSRV
+ }
if currOff >= len(msg) {
return off, errInvalidPtr
}
@@ -1648,8 +2074,9 @@ type Question struct {
Class Class
}
-func (q *Question) pack(msg []byte, compression map[string]int) ([]byte, error) {
- msg, err := q.Name.pack(msg, compression)
+// pack appends the wire format of the Question to msg.
+func (q *Question) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) {
+ msg, err := q.Name.pack(msg, compression, compressionOff)
if err != nil {
return msg, &nestedError{"Name", err}
}
@@ -1657,6 +2084,14 @@ func (q *Question) pack(msg []byte, compression map[string]int) ([]byte, error)
return packClass(msg, q.Class), nil
}
+// GoString implements fmt.GoStringer.GoString.
+func (q *Question) GoString() string {
+ return "dnsmessage.Question{" +
+ "Name: " + q.Name.GoString() + ", " +
+ "Type: " + q.Type.GoString() + ", " +
+ "Class: " + q.Class.GoString() + "}"
+}
+
func unpackResourceBody(msg []byte, off int, hdr ResourceHeader) (ResourceBody, int, error) {
var (
r ResourceBody
@@ -1709,6 +2144,11 @@ func unpackResourceBody(msg []byte, off int, hdr ResourceHeader) (ResourceBody,
rb, err = unpackSRVResource(msg, off)
r = &rb
name = "SRV"
+ case TypeOPT:
+ var rb OPTResource
+ rb, err = unpackOPTResource(msg, off, hdr.Length)
+ r = &rb
+ name = "OPT"
}
if err != nil {
return nil, off, &nestedError{name + " record", err}
@@ -1728,8 +2168,14 @@ func (r *CNAMEResource) realType() Type {
return TypeCNAME
}
-func (r *CNAMEResource) pack(msg []byte, compression map[string]int) ([]byte, error) {
- return r.CNAME.pack(msg, compression)
+// pack appends the wire format of the CNAMEResource to msg.
+func (r *CNAMEResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) {
+ return r.CNAME.pack(msg, compression, compressionOff)
+}
+
+// GoString implements fmt.GoStringer.GoString.
+func (r *CNAMEResource) GoString() string {
+ return "dnsmessage.CNAMEResource{CNAME: " + r.CNAME.GoString() + "}"
}
func unpackCNAMEResource(msg []byte, off int) (CNAMEResource, error) {
@@ -1750,16 +2196,24 @@ func (r *MXResource) realType() Type {
return TypeMX
}
-func (r *MXResource) pack(msg []byte, compression map[string]int) ([]byte, error) {
+// pack appends the wire format of the MXResource to msg.
+func (r *MXResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) {
oldMsg := msg
msg = packUint16(msg, r.Pref)
- msg, err := r.MX.pack(msg, compression)
+ msg, err := r.MX.pack(msg, compression, compressionOff)
if err != nil {
return oldMsg, &nestedError{"MXResource.MX", err}
}
return msg, nil
}
+// GoString implements fmt.GoStringer.GoString.
+func (r *MXResource) GoString() string {
+ return "dnsmessage.MXResource{" +
+ "Pref: " + printUint16(r.Pref) + ", " +
+ "MX: " + r.MX.GoString() + "}"
+}
+
func unpackMXResource(msg []byte, off int) (MXResource, error) {
pref, off, err := unpackUint16(msg, off)
if err != nil {
@@ -1781,8 +2235,14 @@ func (r *NSResource) realType() Type {
return TypeNS
}
-func (r *NSResource) pack(msg []byte, compression map[string]int) ([]byte, error) {
- return r.NS.pack(msg, compression)
+// pack appends the wire format of the NSResource to msg.
+func (r *NSResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) {
+ return r.NS.pack(msg, compression, compressionOff)
+}
+
+// GoString implements fmt.GoStringer.GoString.
+func (r *NSResource) GoString() string {
+ return "dnsmessage.NSResource{NS: " + r.NS.GoString() + "}"
}
func unpackNSResource(msg []byte, off int) (NSResource, error) {
@@ -1802,8 +2262,14 @@ func (r *PTRResource) realType() Type {
return TypePTR
}
-func (r *PTRResource) pack(msg []byte, compression map[string]int) ([]byte, error) {
- return r.PTR.pack(msg, compression)
+// pack appends the wire format of the PTRResource to msg.
+func (r *PTRResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) {
+ return r.PTR.pack(msg, compression, compressionOff)
+}
+
+// GoString implements fmt.GoStringer.GoString.
+func (r *PTRResource) GoString() string {
+ return "dnsmessage.PTRResource{PTR: " + r.PTR.GoString() + "}"
}
func unpackPTRResource(msg []byte, off int) (PTRResource, error) {
@@ -1833,13 +2299,14 @@ func (r *SOAResource) realType() Type {
return TypeSOA
}
-func (r *SOAResource) pack(msg []byte, compression map[string]int) ([]byte, error) {
+// pack appends the wire format of the SOAResource to msg.
+func (r *SOAResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) {
oldMsg := msg
- msg, err := r.NS.pack(msg, compression)
+ msg, err := r.NS.pack(msg, compression, compressionOff)
if err != nil {
return oldMsg, &nestedError{"SOAResource.NS", err}
}
- msg, err = r.MBox.pack(msg, compression)
+ msg, err = r.MBox.pack(msg, compression, compressionOff)
if err != nil {
return oldMsg, &nestedError{"SOAResource.MBox", err}
}
@@ -1850,6 +2317,18 @@ func (r *SOAResource) pack(msg []byte, compression map[string]int) ([]byte, erro
return packUint32(msg, r.MinTTL), nil
}
+// GoString implements fmt.GoStringer.GoString.
+func (r *SOAResource) GoString() string {
+ return "dnsmessage.SOAResource{" +
+ "NS: " + r.NS.GoString() + ", " +
+ "MBox: " + r.MBox.GoString() + ", " +
+ "Serial: " + printUint32(r.Serial) + ", " +
+ "Refresh: " + printUint32(r.Refresh) + ", " +
+ "Retry: " + printUint32(r.Retry) + ", " +
+ "Expire: " + printUint32(r.Expire) + ", " +
+ "MinTTL: " + printUint32(r.MinTTL) + "}"
+}
+
func unpackSOAResource(msg []byte, off int) (SOAResource, error) {
var ns Name
off, err := ns.unpack(msg, off)
@@ -1885,19 +2364,41 @@ func unpackSOAResource(msg []byte, off int) (SOAResource, error) {
// A TXTResource is a TXT Resource record.
type TXTResource struct {
- Txt string // Not a domain name.
+ TXT []string
}
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
+// pack appends the wire format of the TXTResource to msg.
+func (r *TXTResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) {
+ oldMsg := msg
+ for _, s := range r.TXT {
+ var err error
+ msg, err = packText(msg, s)
+ if err != nil {
+ return oldMsg, err
+ }
+ }
+ return msg, nil
+}
+
+// GoString implements fmt.GoStringer.GoString.
+func (r *TXTResource) GoString() string {
+ s := "dnsmessage.TXTResource{TXT: []string{"
+ if len(r.TXT) == 0 {
+ return s + "}}"
+ }
+ s += `"` + printString([]byte(r.TXT[0]))
+ for _, t := range r.TXT[1:] {
+ s += `", "` + printString([]byte(t))
+ }
+ return s + `"}}`
}
func unpackTXTResource(msg []byte, off int, length uint16) (TXTResource, error) {
- var txt string
+ txts := make([]string, 0, 1)
for n := uint16(0); n < length; {
var t string
var err error
@@ -1909,9 +2410,9 @@ func unpackTXTResource(msg []byte, off int, length uint16) (TXTResource, error)
return TXTResource{}, errCalcLen
}
n += uint16(len(t)) + 1
- txt += t
+ txts = append(txts, t)
}
- return TXTResource{txt}, nil
+ return TXTResource{txts}, nil
}
// An SRVResource is an SRV Resource record.
@@ -1926,18 +2427,28 @@ func (r *SRVResource) realType() Type {
return TypeSRV
}
-func (r *SRVResource) pack(msg []byte, compression map[string]int) ([]byte, error) {
+// pack appends the wire format of the SRVResource to msg.
+func (r *SRVResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) {
oldMsg := msg
msg = packUint16(msg, r.Priority)
msg = packUint16(msg, r.Weight)
msg = packUint16(msg, r.Port)
- msg, err := r.Target.pack(msg, nil)
+ msg, err := r.Target.pack(msg, nil, compressionOff)
if err != nil {
return oldMsg, &nestedError{"SRVResource.Target", err}
}
return msg, nil
}
+// GoString implements fmt.GoStringer.GoString.
+func (r *SRVResource) GoString() string {
+ return "dnsmessage.SRVResource{" +
+ "Priority: " + printUint16(r.Priority) + ", " +
+ "Weight: " + printUint16(r.Weight) + ", " +
+ "Port: " + printUint16(r.Port) + ", " +
+ "Target: " + r.Target.GoString() + "}"
+}
+
func unpackSRVResource(msg []byte, off int) (SRVResource, error) {
priority, off, err := unpackUint16(msg, off)
if err != nil {
@@ -1952,7 +2463,7 @@ func unpackSRVResource(msg []byte, off int) (SRVResource, error) {
return SRVResource{}, &nestedError{"Port", err}
}
var target Name
- if _, err := target.unpack(msg, off); err != nil {
+ if _, err := target.unpackCompressed(msg, off, false /* allowCompression */); err != nil {
return SRVResource{}, &nestedError{"Target", err}
}
return SRVResource{priority, weight, port, target}, nil
@@ -1967,10 +2478,17 @@ func (r *AResource) realType() Type {
return TypeA
}
-func (r *AResource) pack(msg []byte, compression map[string]int) ([]byte, error) {
+// pack appends the wire format of the AResource to msg.
+func (r *AResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) {
return packBytes(msg, r.A[:]), nil
}
+// GoString implements fmt.GoStringer.GoString.
+func (r *AResource) GoString() string {
+ return "dnsmessage.AResource{" +
+ "A: [4]byte{" + printByteSlice(r.A[:]) + "}}"
+}
+
func unpackAResource(msg []byte, off int) (AResource, error) {
var a [4]byte
if _, err := unpackBytes(msg, off, a[:]); err != nil {
@@ -1988,7 +2506,14 @@ func (r *AAAAResource) realType() Type {
return TypeAAAA
}
-func (r *AAAAResource) pack(msg []byte, compression map[string]int) ([]byte, error) {
+// GoString implements fmt.GoStringer.GoString.
+func (r *AAAAResource) GoString() string {
+ return "dnsmessage.AAAAResource{" +
+ "AAAA: [16]byte{" + printByteSlice(r.AAAA[:]) + "}}"
+}
+
+// pack appends the wire format of the AAAAResource to msg.
+func (r *AAAAResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) {
return packBytes(msg, r.AAAA[:]), nil
}
@@ -1999,3 +2524,78 @@ func unpackAAAAResource(msg []byte, off int) (AAAAResource, error) {
}
return AAAAResource{aaaa}, nil
}
+
+// An OPTResource is an OPT pseudo Resource record.
+//
+// The pseudo resource record is part of the extension mechanisms for DNS
+// as defined in RFC 6891.
+type OPTResource struct {
+ Options []Option
+}
+
+// An Option represents a DNS message option within OPTResource.
+//
+// The message option is part of the extension mechanisms for DNS as
+// defined in RFC 6891.
+type Option struct {
+ Code uint16 // option code
+ Data []byte
+}
+
+// GoString implements fmt.GoStringer.GoString.
+func (o *Option) GoString() string {
+ return "dnsmessage.Option{" +
+ "Code: " + printUint16(o.Code) + ", " +
+ "Data: []byte{" + printByteSlice(o.Data) + "}}"
+}
+
+func (r *OPTResource) realType() Type {
+ return TypeOPT
+}
+
+func (r *OPTResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) {
+ for _, opt := range r.Options {
+ msg = packUint16(msg, opt.Code)
+ l := uint16(len(opt.Data))
+ msg = packUint16(msg, l)
+ msg = packBytes(msg, opt.Data)
+ }
+ return msg, nil
+}
+
+// GoString implements fmt.GoStringer.GoString.
+func (r *OPTResource) GoString() string {
+ s := "dnsmessage.OPTResource{Options: []dnsmessage.Option{"
+ if len(r.Options) == 0 {
+ return s + "}}"
+ }
+ s += r.Options[0].GoString()
+ for _, o := range r.Options[1:] {
+ s += ", " + o.GoString()
+ }
+ return s + "}}"
+}
+
+func unpackOPTResource(msg []byte, off int, length uint16) (OPTResource, error) {
+ var opts []Option
+ for oldOff := off; off < oldOff+int(length); {
+ var err error
+ var o Option
+ o.Code, off, err = unpackUint16(msg, off)
+ if err != nil {
+ return OPTResource{}, &nestedError{"Code", err}
+ }
+ var l uint16
+ l, off, err = unpackUint16(msg, off)
+ if err != nil {
+ return OPTResource{}, &nestedError{"Data", err}
+ }
+ o.Data = make([]byte, l)
+ if copy(o.Data, msg[off:]) != int(l) {
+ return OPTResource{}, &nestedError{"Data", errCalcLen}
+ }
+ off += int(l)
+ opts = append(opts, o)
+ }
+ return OPTResource{opts}, nil
+}