|
5 | 5 | // Package dnsmessage provides a mostly RFC 1035 compliant implementation of
|
6 | 6 | // DNS message packing and unpacking.
|
7 | 7 | //
|
| 8 | +// The package also supports messages with Extension Mechanisms for DNS |
| 9 | +// (EDNS(0)) as defined in RFC 6891. |
| 10 | +// |
8 | 11 | // This implementation is designed to minimize heap allocations and avoid
|
9 | 12 | // unnecessary packing and unpacking as much as possible.
|
10 | 13 | package dnsmessage
|
@@ -39,6 +42,7 @@ const (
|
39 | 42 | TypeTXT Type = 16
|
40 | 43 | TypeAAAA Type = 28
|
41 | 44 | TypeSRV Type = 33
|
| 45 | + TypeOPT Type = 41 |
42 | 46 |
|
43 | 47 | // Question.Type
|
44 | 48 | TypeWKS Type = 11
|
@@ -802,6 +806,24 @@ func (p *Parser) AAAAResource() (AAAAResource, error) {
|
802 | 806 | return r, nil
|
803 | 807 | }
|
804 | 808 |
|
| 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 | + |
805 | 827 | // Unpack parses a full Message.
|
806 | 828 | func (m *Message) Unpack(msg []byte) error {
|
807 | 829 | var p Parser
|
@@ -1278,6 +1300,30 @@ func (b *Builder) AAAAResource(h ResourceHeader, r AAAAResource) error {
|
1278 | 1300 | return nil
|
1279 | 1301 | }
|
1280 | 1302 |
|
| 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 | + |
1281 | 1327 | // Finish ends message building and generates a binary message.
|
1282 | 1328 | func (b *Builder) Finish() ([]byte, error) {
|
1283 | 1329 | if b.section < sectionHeader {
|
@@ -1366,6 +1412,44 @@ func (h *ResourceHeader) fixLen(msg []byte, length []byte, preLen int) error {
|
1366 | 1412 | return nil
|
1367 | 1413 | }
|
1368 | 1414 |
|
| 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 | + |
1369 | 1453 | func skipResource(msg []byte, off int) (int, error) {
|
1370 | 1454 | newOff, err := skipName(msg, off)
|
1371 | 1455 | if err != nil {
|
@@ -1794,6 +1878,11 @@ func unpackResourceBody(msg []byte, off int, hdr ResourceHeader) (ResourceBody,
|
1794 | 1878 | rb, err = unpackSRVResource(msg, off)
|
1795 | 1879 | r = &rb
|
1796 | 1880 | name = "SRV"
|
| 1881 | + case TypeOPT: |
| 1882 | + var rb OPTResource |
| 1883 | + rb, err = unpackOPTResource(msg, off, hdr.Length) |
| 1884 | + r = &rb |
| 1885 | + name = "OPT" |
1797 | 1886 | }
|
1798 | 1887 | if err != nil {
|
1799 | 1888 | return nil, off, &nestedError{name + " record", err}
|
@@ -2101,3 +2190,58 @@ func unpackAAAAResource(msg []byte, off int) (AAAAResource, error) {
|
2101 | 2190 | }
|
2102 | 2191 | return AAAAResource{aaaa}, nil
|
2103 | 2192 | }
|
| 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