Documentation
¶
Overview ¶
Package wire implements DNS message encoding and decoding from RFC 1035 sections 3 and 4, including name compression in both directions.
Decoding never trusts the header counts. Each count is an upper bound on a loop, and every read is bounded by the octets actually present, so a message claiming 65535 answers in 40 octets fails with a typed error rather than allocating.
Index ¶
Constants ¶
const ( RcodeSuccess uint8 = 0 RcodeFormErr uint8 = 1 RcodeServFail uint8 = 2 RcodeNXDomain uint8 = 3 RcodeNotImp uint8 = 4 RcodeRefused uint8 = 5 )
Response codes from RFC 1035 section 4.1.1.
const DefaultUDPSize uint16 = 1232
DefaultUDPSize is the requestor's UDP payload size this implementation advertises, in octets.
1232 is the current operational recommendation: it keeps a response inside the smallest MTU in common use once IPv6 and UDP headers are accounted for, so a large answer arrives whole rather than as IP fragments that middleboxes and firewalls routinely drop. Measured on this network, 1232 carries the full 828-octet com. NS response intact, while the same query without EDNS0 comes back capped at 509 octets with TC set.
Variables ¶
var ( ErrTruncated = errors.New("wire: message truncated") ErrLabelType = errors.New("wire: reserved label type") ErrLabelTooLong = errors.New("wire: label exceeds 63 octets") ErrNameTooLong = errors.New("wire: name exceeds 255 octets") ErrNameSyntax = errors.New("wire: malformed name") ErrBadPointer = errors.New("wire: compression pointer does not point strictly backwards") ErrTooManyRecords = errors.New("wire: section exceeds 65535 records") ErrRData = errors.New("wire: malformed rdata") )
Decode and encode failures. Every malformed message returns one of these, wrapped with the offset or field that failed, so callers and tests can match on the class of failure with errors.Is.
Functions ¶
func RcodeName ¶
RcodeName renders a response code the way dig prints it.
Here rather than beside the presentation code because there are now two consumers, the CLI output and the control socket, and two switches over the same six constants is one switch too many: the day a seventh is added, one of them gets it.
Types ¶
type A ¶
A is an IPv4 host address, RFC 1035 section 3.4.1.
type CNAME ¶
type CNAME struct{ Target Name }
CNAME is the canonical name of an alias, RFC 1035 section 3.3.1.
type Class ¶
type Class uint16
Class is a DNS class code.
const ClassIN Class = 1
ClassIN is the internet class, the only one this implementation serves.
type EDNS ¶
type EDNS struct {
// UDPSize is the largest response the sender will accept over UDP. Zero
// packs as DefaultUDPSize, since advertising no capacity at all is never
// what a caller means.
UDPSize uint16
// ExtRcode is the high 8 bits of the 12-bit extended RCODE. The low 4
// bits stay in the message header, so the full code is
// uint16(ExtRcode)<<4 | Rcode.
ExtRcode uint8
// Version is the EDNS version, 0 for RFC 6891.
Version uint8
// DO is the DNSSEC OK bit, RFC 3225.
DO bool
Options []Option
}
EDNS is the whole OPT pseudo-record, gathering the fields RFC 6891 scatters across the record's CLASS and TTL.
type Header ¶
type Header struct {
ID uint16
Response bool
Opcode uint8
Authoritative bool
Truncated bool
RecursionDesired bool
RecursionAvailable bool
AuthenticData bool
CheckingDisabled bool
Rcode uint8
}
Header is the 12-octet message header. The section counts are not stored, because they are a function of the sections themselves and a Header that can disagree with its own message is a bug waiting to happen.
type MX ¶
MX is a mail exchange, RFC 1035 section 3.3.9.
type Message ¶
type Message struct {
Header Header
Questions []Question
Answers []RR
Authority []RR
Additional []RR
}
Message is a decoded DNS message.
func (*Message) EDNS ¶
EDNS returns the OPT pseudo-record from the additional section, reporting whether one was present.
RFC 6891 section 6.1.1 permits at most one, and a message carrying more is a format error rather than something to resolve by picking a winner.
type NS ¶
type NS struct{ Host Name }
NS is an authoritative nameserver for a zone, RFC 1035 section 3.3.11.
type Name ¶
type Name string
Name is a domain name in presentation format, fully qualified with a trailing dot. The root is ".".
Case is preserved exactly as it arrived. Names match case-insensitively, which is what EqualFold is for, but decoding never folds case: the 0x20 defence depends on a response echoing the exact case that was sent.
Octets outside the printable ASCII range, along with '.' and '\\', are escaped in the RFC 1035 section 5.1 style, so the presentation form is unambiguous and two Names are byte-equal only when the wire names were byte-equal.
const Root Name = "."
Root is the zero-length name, encoded as a single zero octet.
func ParseName ¶
ParseName converts a presentation-format name to a Name, accepting both "example.com" and "example.com." and canonicalising to the latter.
func (Name) EqualFold ¶
EqualFold reports whether two names are equal under DNS case-insensitive matching.
func (Name) Fold ¶
Fold returns n lowercased, for use as a map key where DNS case-insensitive matching is wanted but a comparison per candidate is too slow.
The result is a key and nothing else. It must never be stored in a record or written to the wire, because the 0x20 defence depends on the case that arrived surviving intact.
Lowercasing the presentation string is sound because that string is always printable ASCII: appendEscaped escapes every octet outside '!' to '~', so there is no multi-byte sequence for strings.ToLower to fold differently from the ASCII rule RFC 4343 specifies. Escaped octets are unaffected for the same reason, since a letter is printable and therefore never escaped in the first place.
func (Name) Suffixes ¶
Suffixes returns n followed by each of its parent names, from the name itself down to its final label. The root is not included, and neither the root nor a name that fails to parse yields anything.
For "a.b.example.com." the result is "a.b.example.com.", "b.example.com.", "example.com.", "com.". Each is a slice of n's own storage, so the walk allocates once for the slice and never for the names.
This exists because a suffix match over the raw string is wrong in the way that matters. Dots inside a label are escaped, so "evil\.com." is a single label whose bytes end in "com." and which must not be treated as living under com. Splitting on unescaped dots only, it yields itself and nothing else.
func (Name) Within ¶
Within reports whether n lies inside zone, either equal to it or below it. Every name is within the root.
This compares labels, not bytes, and the difference is a security one. A label may contain a dot, escaped as "\." in presentation form, so a suffix test over the string would read the single label "evil\.com" as ending in the label "com" and place it inside the com zone. That is exactly the input a bailiwick check exists to reject, so the slower comparison is the only correct one. A name that does not parse is within nothing, which keeps the failure closed rather than open.
type OPT ¶
type OPT struct{ Options []Option }
OPT is the rdata of the EDNS0 pseudo-record, a bare sequence of options.
The rest of what EDNS0 carries lives outside the rdata, in fields the pseudo-record repurposes: CLASS holds the requestor's payload size and TTL holds the extended RCODE, the version and the DO bit. EDNS presents all of it as one value, and is what callers should use.
type Option ¶
Option is one EDNS0 option, RFC 6891 section 6.1.2.
Data aliases the message buffer it was decoded from; see Unknown.
type PTR ¶
type PTR struct{ Target Name }
PTR is a pointer to another name, RFC 1035 section 3.3.12. Unlike CNAME it triggers no further processing at the resolver.
type RData ¶
type RData interface {
// Type reports the record type whose RDATA this is.
Type() Type
// contains filtered or unexported methods
}
RData is the parsed contents of a resource record's RDATA field. Every RR carries one. A type this package does not model decodes as Unknown, which keeps the octets verbatim so that an unfamiliar record still round-trips.
pack is unexported, so the set of implementations is closed to this package. Encoding a record means knowing whether the names inside it may be replaced by a compression pointer, and that is a property of the record type rather than something a caller should be able to supply.
type RR ¶
RR is a resource record. The rdata is parsed into a per-type value from rdata.go, and a type this package does not model keeps its octets as Unknown, so an unfamiliar record still round-trips.
Type is kept alongside Data rather than derived from it, so that reading the type of a record never depends on the rdata being present. Pack rejects a record whose two disagree.
type SOA ¶
type SOA struct {
Primary Name // MNAME, the authoritative server for the zone
Mailbox Name // RNAME, the maintainer's mailbox with '.' standing in for '@'
Serial uint32
Refresh uint32
Retry uint32
Expire uint32
Minimum uint32 // also the negative caching TTL, RFC 2308 section 4
}
SOA marks the start of a zone of authority, RFC 1035 section 3.3.13.
type Span ¶
type Span struct {
Offset int
Length int
Section string // header, question, answer, authority, additional, or trailing
Field string // ID, flags, QNAME, TTL, RDATA, and so on
Detail string // the decoded value, in the terms of the field
}
Span is one field of a message, in the order the decoder read it.
Spans are contiguous and cover the whole buffer: the offsets of any two neighbours meet exactly, and the last one ends at the final octet. That is the property that makes an annotated dump worth anything, because a region nobody can name is a region the decoder was not really reading.
func Annotate ¶
Annotate walks a raw message and returns a span per field.
It reads with the same decoder the resolver reads with, field by field and in the same order, so an annotation is a record of what the parser did rather than a second opinion about what the bytes mean. A message that fails to decode returns the spans read so far along with the error, because the field the parser stopped on is usually the whole answer to why.
type TXT ¶
type TXT struct{ Strings []string }
TXT is one or more character-strings, RFC 1035 section 3.3.14.
The strings stay separate. One TXT record can carry several, each capped at 255 octets, and joining them destroys a boundary that SPF and DKIM both depend on.
type Type ¶
type Type uint16
Type is a DNS resource record type code.
const ( 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 TypeOPT Type = 41 TypeANY Type = 255 )
Record types carried by this implementation. Types outside this set decode successfully with opaque RDATA, because a resolver that rejects an unfamiliar type is broken.
type Unknown ¶
Unknown is the rdata of a type this package does not model, held verbatim because a resolver that rejects an unfamiliar record is broken.
Data aliases the message buffer it was decoded from, so a caller that keeps the record beyond the life of that buffer must copy it first. Every other RData implementation owns its contents, apart from Option.Data.