Skip to content

Commit d9ff770

Browse files
committed
dns/dnsmessage: add basic support for EDNS(0)
This change introduces OPTResourse type to support DNS messages containing various extension options as defined in RFC 6891. Parsing and building OPT pseudo records requires allocations like TXT records. Also adds DNSSECAllowed, ExtendedRCode and SetEDNS0 methods to ResourceHeader for convenience. Updates golang/go#6464. Updates golang/go#16218. Change-Id: Ib72cea277201e4122c6b5effa320084ff351c886 Reviewed-on: https://go-review.googlesource.com/47170 Reviewed-by: Ian Gudger <[email protected]>
1 parent 6078986 commit d9ff770

File tree

2 files changed

+325
-2
lines changed

2 files changed

+325
-2
lines changed

dns/dnsmessage/message.go

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
// Package dnsmessage provides a mostly RFC 1035 compliant implementation of
66
// DNS message packing and unpacking.
77
//
8+
// The package also supports messages with Extension Mechanisms for DNS
9+
// (EDNS(0)) as defined in RFC 6891.
10+
//
811
// This implementation is designed to minimize heap allocations and avoid
912
// unnecessary packing and unpacking as much as possible.
1013
package dnsmessage
@@ -39,6 +42,7 @@ const (
3942
TypeTXT Type = 16
4043
TypeAAAA Type = 28
4144
TypeSRV Type = 33
45+
TypeOPT Type = 41
4246

4347
// Question.Type
4448
TypeWKS Type = 11
@@ -802,6 +806,24 @@ func (p *Parser) AAAAResource() (AAAAResource, error) {
802806
return r, nil
803807
}
804808

809+
// OPTResource parses a single OPTResource.
810+
//
811+
// One of the XXXHeader methods must have been called before calling this
812+
// method.
813+
func (p *Parser) OPTResource() (OPTResource, error) {
814+
if !p.resHeaderValid || p.resHeader.Type != TypeOPT {
815+
return OPTResource{}, ErrNotStarted
816+
}
817+
r, err := unpackOPTResource(p.msg, p.off, p.resHeader.Length)
818+
if err != nil {
819+
return OPTResource{}, err
820+
}
821+
p.off += int(p.resHeader.Length)
822+
p.resHeaderValid = false
823+
p.index++
824+
return r, nil
825+
}
826+
805827
// Unpack parses a full Message.
806828
func (m *Message) Unpack(msg []byte) error {
807829
var p Parser
@@ -1278,6 +1300,30 @@ func (b *Builder) AAAAResource(h ResourceHeader, r AAAAResource) error {
12781300
return nil
12791301
}
12801302

1303+
// OPTResource adds a single OPTResource.
1304+
func (b *Builder) OPTResource(h ResourceHeader, r OPTResource) error {
1305+
if err := b.checkResourceSection(); err != nil {
1306+
return err
1307+
}
1308+
h.Type = r.realType()
1309+
msg, length, err := h.pack(b.msg, b.compression, b.start)
1310+
if err != nil {
1311+
return &nestedError{"ResourceHeader", err}
1312+
}
1313+
preLen := len(msg)
1314+
if msg, err = r.pack(msg, b.compression, b.start); err != nil {
1315+
return &nestedError{"OPTResource body", err}
1316+
}
1317+
if err := h.fixLen(msg, length, preLen); err != nil {
1318+
return err
1319+
}
1320+
if err := b.incrementSectionCount(); err != nil {
1321+
return err
1322+
}
1323+
b.msg = msg
1324+
return nil
1325+
}
1326+
12811327
// Finish ends message building and generates a binary message.
12821328
func (b *Builder) Finish() ([]byte, error) {
12831329
if b.section < sectionHeader {
@@ -1366,6 +1412,44 @@ func (h *ResourceHeader) fixLen(msg []byte, length []byte, preLen int) error {
13661412
return nil
13671413
}
13681414

1415+
// EDNS(0) wire costants.
1416+
const (
1417+
edns0Version = 0
1418+
1419+
edns0DNSSECOK = 0x00008000
1420+
ednsVersionMask = 0x00ff0000
1421+
edns0DNSSECOKMask = 0x00ff8000
1422+
)
1423+
1424+
// SetEDNS0 configures h for EDNS(0).
1425+
//
1426+
// The provided extRCode must be an extedned RCode.
1427+
func (h *ResourceHeader) SetEDNS0(udpPayloadLen int, extRCode RCode, dnssecOK bool) error {
1428+
h.Name = Name{Data: [nameLen]byte{'.'}, Length: 1} // RFC 6891 section 6.1.2
1429+
h.Type = TypeOPT
1430+
h.Class = Class(udpPayloadLen)
1431+
h.TTL = uint32(extRCode) >> 4 << 24
1432+
if dnssecOK {
1433+
h.TTL |= edns0DNSSECOK
1434+
}
1435+
return nil
1436+
}
1437+
1438+
// DNSSECAllowed reports whether the DNSSEC OK bit is set.
1439+
func (h *ResourceHeader) DNSSECAllowed() bool {
1440+
return h.TTL&edns0DNSSECOKMask == edns0DNSSECOK // RFC 6891 section 6.1.3
1441+
}
1442+
1443+
// ExtendedRCode returns an extended RCode.
1444+
//
1445+
// The provided rcode must be the RCode in DNS message header.
1446+
func (h *ResourceHeader) ExtendedRCode(rcode RCode) RCode {
1447+
if h.TTL&ednsVersionMask == edns0Version { // RFC 6891 section 6.1.3
1448+
return RCode(h.TTL>>24<<4) | rcode
1449+
}
1450+
return rcode
1451+
}
1452+
13691453
func skipResource(msg []byte, off int) (int, error) {
13701454
newOff, err := skipName(msg, off)
13711455
if err != nil {
@@ -1794,6 +1878,11 @@ func unpackResourceBody(msg []byte, off int, hdr ResourceHeader) (ResourceBody,
17941878
rb, err = unpackSRVResource(msg, off)
17951879
r = &rb
17961880
name = "SRV"
1881+
case TypeOPT:
1882+
var rb OPTResource
1883+
rb, err = unpackOPTResource(msg, off, hdr.Length)
1884+
r = &rb
1885+
name = "OPT"
17971886
}
17981887
if err != nil {
17991888
return nil, off, &nestedError{name + " record", err}
@@ -2101,3 +2190,58 @@ func unpackAAAAResource(msg []byte, off int) (AAAAResource, error) {
21012190
}
21022191
return AAAAResource{aaaa}, nil
21032192
}
2193+
2194+
// An OPTResource is an OPT pseudo Resource record.
2195+
//
2196+
// The pseudo resource record is part of the extension mechanisms for DNS
2197+
// as defined in RFC 6891.
2198+
type OPTResource struct {
2199+
Options []Option
2200+
}
2201+
2202+
// An Option represents a DNS message option within OPTResource.
2203+
//
2204+
// The message option is part of the extension mechanisms for DNS as
2205+
// defined in RFC 6891.
2206+
type Option struct {
2207+
Code uint16 // option code
2208+
Data []byte
2209+
}
2210+
2211+
func (r *OPTResource) realType() Type {
2212+
return TypeOPT
2213+
}
2214+
2215+
func (r *OPTResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) {
2216+
for _, opt := range r.Options {
2217+
msg = packUint16(msg, opt.Code)
2218+
l := uint16(len(opt.Data))
2219+
msg = packUint16(msg, l)
2220+
msg = packBytes(msg, opt.Data)
2221+
}
2222+
return msg, nil
2223+
}
2224+
2225+
func unpackOPTResource(msg []byte, off int, length uint16) (OPTResource, error) {
2226+
var opts []Option
2227+
for oldOff := off; off < oldOff+int(length); {
2228+
var err error
2229+
var o Option
2230+
o.Code, off, err = unpackUint16(msg, off)
2231+
if err != nil {
2232+
return OPTResource{}, &nestedError{"Code", err}
2233+
}
2234+
var l uint16
2235+
l, off, err = unpackUint16(msg, off)
2236+
if err != nil {
2237+
return OPTResource{}, &nestedError{"Data", err}
2238+
}
2239+
o.Data = make([]byte, l)
2240+
if copy(o.Data, msg[off:]) != int(l) {
2241+
return OPTResource{}, &nestedError{"Data", errCalcLen}
2242+
}
2243+
off += int(l)
2244+
opts = append(opts, o)
2245+
}
2246+
return OPTResource{opts}, nil
2247+
}

0 commit comments

Comments
 (0)