1package dns 2 3import ( 4 "encoding/binary" 5 "encoding/hex" 6 "errors" 7 "fmt" 8 "net" 9 "strconv" 10) 11 12// EDNS0 Option codes. 13const ( 14 EDNS0LLQ = 0x1 // long lived queries: http://tools.ietf.org/html/draft-sekar-dns-llq-01 15 EDNS0UL = 0x2 // update lease draft: http://files.dns-sd.org/draft-sekar-dns-ul.txt 16 EDNS0NSID = 0x3 // nsid (See RFC 5001) 17 EDNS0DAU = 0x5 // DNSSEC Algorithm Understood 18 EDNS0DHU = 0x6 // DS Hash Understood 19 EDNS0N3U = 0x7 // NSEC3 Hash Understood 20 EDNS0SUBNET = 0x8 // client-subnet (See RFC 7871) 21 EDNS0EXPIRE = 0x9 // EDNS0 expire 22 EDNS0COOKIE = 0xa // EDNS0 Cookie 23 EDNS0TCPKEEPALIVE = 0xb // EDNS0 tcp keep alive (See RFC 7828) 24 EDNS0PADDING = 0xc // EDNS0 padding (See RFC 7830) 25 EDNS0EDE = 0xf // EDNS0 extended DNS errors (See RFC 8914) 26 EDNS0LOCALSTART = 0xFDE9 // Beginning of range reserved for local/experimental use (See RFC 6891) 27 EDNS0LOCALEND = 0xFFFE // End of range reserved for local/experimental use (See RFC 6891) 28 _DO = 1 << 15 // DNSSEC OK 29) 30 31// makeDataOpt is used to unpack the EDNS0 option(s) from a message. 32func makeDataOpt(code uint16) EDNS0 { 33 // All the EDNS0.* constants above need to be in this switch. 34 switch code { 35 case EDNS0LLQ: 36 return new(EDNS0_LLQ) 37 case EDNS0UL: 38 return new(EDNS0_UL) 39 case EDNS0NSID: 40 return new(EDNS0_NSID) 41 case EDNS0DAU: 42 return new(EDNS0_DAU) 43 case EDNS0DHU: 44 return new(EDNS0_DHU) 45 case EDNS0N3U: 46 return new(EDNS0_N3U) 47 case EDNS0SUBNET: 48 return new(EDNS0_SUBNET) 49 case EDNS0EXPIRE: 50 return new(EDNS0_EXPIRE) 51 case EDNS0COOKIE: 52 return new(EDNS0_COOKIE) 53 case EDNS0TCPKEEPALIVE: 54 return new(EDNS0_TCP_KEEPALIVE) 55 case EDNS0PADDING: 56 return new(EDNS0_PADDING) 57 case EDNS0EDE: 58 return new(EDNS0_EDE) 59 default: 60 e := new(EDNS0_LOCAL) 61 e.Code = code 62 return e 63 } 64} 65 66// OPT is the EDNS0 RR appended to messages to convey extra (meta) information. 67// See RFC 6891. 68type OPT struct { 69 Hdr RR_Header 70 Option []EDNS0 `dns:"opt"` 71} 72 73func (rr *OPT) String() string { 74 s := "\n;; OPT PSEUDOSECTION:\n; EDNS: version " + strconv.Itoa(int(rr.Version())) + "; " 75 if rr.Do() { 76 s += "flags: do; " 77 } else { 78 s += "flags: ; " 79 } 80 s += "udp: " + strconv.Itoa(int(rr.UDPSize())) 81 82 for _, o := range rr.Option { 83 switch o.(type) { 84 case *EDNS0_NSID: 85 s += "\n; NSID: " + o.String() 86 h, e := o.pack() 87 var r string 88 if e == nil { 89 for _, c := range h { 90 r += "(" + string(c) + ")" 91 } 92 s += " " + r 93 } 94 case *EDNS0_SUBNET: 95 s += "\n; SUBNET: " + o.String() 96 case *EDNS0_COOKIE: 97 s += "\n; COOKIE: " + o.String() 98 case *EDNS0_UL: 99 s += "\n; UPDATE LEASE: " + o.String() 100 case *EDNS0_LLQ: 101 s += "\n; LONG LIVED QUERIES: " + o.String() 102 case *EDNS0_DAU: 103 s += "\n; DNSSEC ALGORITHM UNDERSTOOD: " + o.String() 104 case *EDNS0_DHU: 105 s += "\n; DS HASH UNDERSTOOD: " + o.String() 106 case *EDNS0_N3U: 107 s += "\n; NSEC3 HASH UNDERSTOOD: " + o.String() 108 case *EDNS0_LOCAL: 109 s += "\n; LOCAL OPT: " + o.String() 110 case *EDNS0_PADDING: 111 s += "\n; PADDING: " + o.String() 112 case *EDNS0_EDE: 113 s += "\n; EDE: " + o.String() 114 } 115 } 116 return s 117} 118 119func (rr *OPT) len(off int, compression map[string]struct{}) int { 120 l := rr.Hdr.len(off, compression) 121 for _, o := range rr.Option { 122 l += 4 // Account for 2-byte option code and 2-byte option length. 123 lo, _ := o.pack() 124 l += len(lo) 125 } 126 return l 127} 128 129func (*OPT) parse(c *zlexer, origin string) *ParseError { 130 return &ParseError{err: "OPT records do not have a presentation format"} 131} 132 133func (rr *OPT) isDuplicate(r2 RR) bool { return false } 134 135// return the old value -> delete SetVersion? 136 137// Version returns the EDNS version used. Only zero is defined. 138func (rr *OPT) Version() uint8 { 139 return uint8(rr.Hdr.Ttl & 0x00FF0000 >> 16) 140} 141 142// SetVersion sets the version of EDNS. This is usually zero. 143func (rr *OPT) SetVersion(v uint8) { 144 rr.Hdr.Ttl = rr.Hdr.Ttl&0xFF00FFFF | uint32(v)<<16 145} 146 147// ExtendedRcode returns the EDNS extended RCODE field (the upper 8 bits of the TTL). 148func (rr *OPT) ExtendedRcode() int { 149 return int(rr.Hdr.Ttl&0xFF000000>>24) << 4 150} 151 152// SetExtendedRcode sets the EDNS extended RCODE field. 153// 154// If the RCODE is not an extended RCODE, will reset the extended RCODE field to 0. 155func (rr *OPT) SetExtendedRcode(v uint16) { 156 rr.Hdr.Ttl = rr.Hdr.Ttl&0x00FFFFFF | uint32(v>>4)<<24 157} 158 159// UDPSize returns the UDP buffer size. 160func (rr *OPT) UDPSize() uint16 { 161 return rr.Hdr.Class 162} 163 164// SetUDPSize sets the UDP buffer size. 165func (rr *OPT) SetUDPSize(size uint16) { 166 rr.Hdr.Class = size 167} 168 169// Do returns the value of the DO (DNSSEC OK) bit. 170func (rr *OPT) Do() bool { 171 return rr.Hdr.Ttl&_DO == _DO 172} 173 174// SetDo sets the DO (DNSSEC OK) bit. 175// If we pass an argument, set the DO bit to that value. 176// It is possible to pass 2 or more arguments. Any arguments after the 1st is silently ignored. 177func (rr *OPT) SetDo(do ...bool) { 178 if len(do) == 1 { 179 if do[0] { 180 rr.Hdr.Ttl |= _DO 181 } else { 182 rr.Hdr.Ttl &^= _DO 183 } 184 } else { 185 rr.Hdr.Ttl |= _DO 186 } 187} 188 189// Z returns the Z part of the OPT RR as a uint16 with only the 15 least significant bits used. 190func (rr *OPT) Z() uint16 { 191 return uint16(rr.Hdr.Ttl & 0x7FFF) 192} 193 194// SetZ sets the Z part of the OPT RR, note only the 15 least significant bits of z are used. 195func (rr *OPT) SetZ(z uint16) { 196 rr.Hdr.Ttl = rr.Hdr.Ttl&^0x7FFF | uint32(z&0x7FFF) 197} 198 199// EDNS0 defines an EDNS0 Option. An OPT RR can have multiple options appended to it. 200type EDNS0 interface { 201 // Option returns the option code for the option. 202 Option() uint16 203 // pack returns the bytes of the option data. 204 pack() ([]byte, error) 205 // unpack sets the data as found in the buffer. Is also sets 206 // the length of the slice as the length of the option data. 207 unpack([]byte) error 208 // String returns the string representation of the option. 209 String() string 210 // copy returns a deep-copy of the option. 211 copy() EDNS0 212} 213 214// EDNS0_NSID option is used to retrieve a nameserver 215// identifier. When sending a request Nsid must be set to the empty string 216// The identifier is an opaque string encoded as hex. 217// Basic use pattern for creating an nsid option: 218// 219// o := new(dns.OPT) 220// o.Hdr.Name = "." 221// o.Hdr.Rrtype = dns.TypeOPT 222// e := new(dns.EDNS0_NSID) 223// e.Code = dns.EDNS0NSID 224// e.Nsid = "AA" 225// o.Option = append(o.Option, e) 226type EDNS0_NSID struct { 227 Code uint16 // Always EDNS0NSID 228 Nsid string // This string needs to be hex encoded 229} 230 231func (e *EDNS0_NSID) pack() ([]byte, error) { 232 h, err := hex.DecodeString(e.Nsid) 233 if err != nil { 234 return nil, err 235 } 236 return h, nil 237} 238 239// Option implements the EDNS0 interface. 240func (e *EDNS0_NSID) Option() uint16 { return EDNS0NSID } // Option returns the option code. 241func (e *EDNS0_NSID) unpack(b []byte) error { e.Nsid = hex.EncodeToString(b); return nil } 242func (e *EDNS0_NSID) String() string { return e.Nsid } 243func (e *EDNS0_NSID) copy() EDNS0 { return &EDNS0_NSID{e.Code, e.Nsid} } 244 245// EDNS0_SUBNET is the subnet option that is used to give the remote nameserver 246// an idea of where the client lives. See RFC 7871. It can then give back a different 247// answer depending on the location or network topology. 248// Basic use pattern for creating an subnet option: 249// 250// o := new(dns.OPT) 251// o.Hdr.Name = "." 252// o.Hdr.Rrtype = dns.TypeOPT 253// e := new(dns.EDNS0_SUBNET) 254// e.Code = dns.EDNS0SUBNET 255// e.Family = 1 // 1 for IPv4 source address, 2 for IPv6 256// e.SourceNetmask = 32 // 32 for IPV4, 128 for IPv6 257// e.SourceScope = 0 258// e.Address = net.ParseIP("127.0.0.1").To4() // for IPv4 259// // e.Address = net.ParseIP("2001:7b8:32a::2") // for IPV6 260// o.Option = append(o.Option, e) 261// 262// This code will parse all the available bits when unpacking (up to optlen). 263// When packing it will apply SourceNetmask. If you need more advanced logic, 264// patches welcome and good luck. 265type EDNS0_SUBNET struct { 266 Code uint16 // Always EDNS0SUBNET 267 Family uint16 // 1 for IP, 2 for IP6 268 SourceNetmask uint8 269 SourceScope uint8 270 Address net.IP 271} 272 273// Option implements the EDNS0 interface. 274func (e *EDNS0_SUBNET) Option() uint16 { return EDNS0SUBNET } 275 276func (e *EDNS0_SUBNET) pack() ([]byte, error) { 277 b := make([]byte, 4) 278 binary.BigEndian.PutUint16(b[0:], e.Family) 279 b[2] = e.SourceNetmask 280 b[3] = e.SourceScope 281 switch e.Family { 282 case 0: 283 // "dig" sets AddressFamily to 0 if SourceNetmask is also 0 284 // We might don't need to complain either 285 if e.SourceNetmask != 0 { 286 return nil, errors.New("dns: bad address family") 287 } 288 case 1: 289 if e.SourceNetmask > net.IPv4len*8 { 290 return nil, errors.New("dns: bad netmask") 291 } 292 if len(e.Address.To4()) != net.IPv4len { 293 return nil, errors.New("dns: bad address") 294 } 295 ip := e.Address.To4().Mask(net.CIDRMask(int(e.SourceNetmask), net.IPv4len*8)) 296 needLength := (e.SourceNetmask + 8 - 1) / 8 // division rounding up 297 b = append(b, ip[:needLength]...) 298 case 2: 299 if e.SourceNetmask > net.IPv6len*8 { 300 return nil, errors.New("dns: bad netmask") 301 } 302 if len(e.Address) != net.IPv6len { 303 return nil, errors.New("dns: bad address") 304 } 305 ip := e.Address.Mask(net.CIDRMask(int(e.SourceNetmask), net.IPv6len*8)) 306 needLength := (e.SourceNetmask + 8 - 1) / 8 // division rounding up 307 b = append(b, ip[:needLength]...) 308 default: 309 return nil, errors.New("dns: bad address family") 310 } 311 return b, nil 312} 313 314func (e *EDNS0_SUBNET) unpack(b []byte) error { 315 if len(b) < 4 { 316 return ErrBuf 317 } 318 e.Family = binary.BigEndian.Uint16(b) 319 e.SourceNetmask = b[2] 320 e.SourceScope = b[3] 321 switch e.Family { 322 case 0: 323 // "dig" sets AddressFamily to 0 if SourceNetmask is also 0 324 // It's okay to accept such a packet 325 if e.SourceNetmask != 0 { 326 return errors.New("dns: bad address family") 327 } 328 e.Address = net.IPv4(0, 0, 0, 0) 329 case 1: 330 if e.SourceNetmask > net.IPv4len*8 || e.SourceScope > net.IPv4len*8 { 331 return errors.New("dns: bad netmask") 332 } 333 addr := make(net.IP, net.IPv4len) 334 copy(addr, b[4:]) 335 e.Address = addr.To16() 336 case 2: 337 if e.SourceNetmask > net.IPv6len*8 || e.SourceScope > net.IPv6len*8 { 338 return errors.New("dns: bad netmask") 339 } 340 addr := make(net.IP, net.IPv6len) 341 copy(addr, b[4:]) 342 e.Address = addr 343 default: 344 return errors.New("dns: bad address family") 345 } 346 return nil 347} 348 349func (e *EDNS0_SUBNET) String() (s string) { 350 if e.Address == nil { 351 s = "<nil>" 352 } else if e.Address.To4() != nil { 353 s = e.Address.String() 354 } else { 355 s = "[" + e.Address.String() + "]" 356 } 357 s += "/" + strconv.Itoa(int(e.SourceNetmask)) + "/" + strconv.Itoa(int(e.SourceScope)) 358 return 359} 360 361func (e *EDNS0_SUBNET) copy() EDNS0 { 362 return &EDNS0_SUBNET{ 363 e.Code, 364 e.Family, 365 e.SourceNetmask, 366 e.SourceScope, 367 e.Address, 368 } 369} 370 371// The EDNS0_COOKIE option is used to add a DNS Cookie to a message. 372// 373// o := new(dns.OPT) 374// o.Hdr.Name = "." 375// o.Hdr.Rrtype = dns.TypeOPT 376// e := new(dns.EDNS0_COOKIE) 377// e.Code = dns.EDNS0COOKIE 378// e.Cookie = "24a5ac.." 379// o.Option = append(o.Option, e) 380// 381// The Cookie field consists out of a client cookie (RFC 7873 Section 4), that is 382// always 8 bytes. It may then optionally be followed by the server cookie. The server 383// cookie is of variable length, 8 to a maximum of 32 bytes. In other words: 384// 385// cCookie := o.Cookie[:16] 386// sCookie := o.Cookie[16:] 387// 388// There is no guarantee that the Cookie string has a specific length. 389type EDNS0_COOKIE struct { 390 Code uint16 // Always EDNS0COOKIE 391 Cookie string // Hex-encoded cookie data 392} 393 394func (e *EDNS0_COOKIE) pack() ([]byte, error) { 395 h, err := hex.DecodeString(e.Cookie) 396 if err != nil { 397 return nil, err 398 } 399 return h, nil 400} 401 402// Option implements the EDNS0 interface. 403func (e *EDNS0_COOKIE) Option() uint16 { return EDNS0COOKIE } 404func (e *EDNS0_COOKIE) unpack(b []byte) error { e.Cookie = hex.EncodeToString(b); return nil } 405func (e *EDNS0_COOKIE) String() string { return e.Cookie } 406func (e *EDNS0_COOKIE) copy() EDNS0 { return &EDNS0_COOKIE{e.Code, e.Cookie} } 407 408// The EDNS0_UL (Update Lease) (draft RFC) option is used to tell the server to set 409// an expiration on an update RR. This is helpful for clients that cannot clean 410// up after themselves. This is a draft RFC and more information can be found at 411// https://tools.ietf.org/html/draft-sekar-dns-ul-02 412// 413// o := new(dns.OPT) 414// o.Hdr.Name = "." 415// o.Hdr.Rrtype = dns.TypeOPT 416// e := new(dns.EDNS0_UL) 417// e.Code = dns.EDNS0UL 418// e.Lease = 120 // in seconds 419// o.Option = append(o.Option, e) 420type EDNS0_UL struct { 421 Code uint16 // Always EDNS0UL 422 Lease uint32 423 KeyLease uint32 424} 425 426// Option implements the EDNS0 interface. 427func (e *EDNS0_UL) Option() uint16 { return EDNS0UL } 428func (e *EDNS0_UL) String() string { return fmt.Sprintf("%d %d", e.Lease, e.KeyLease) } 429func (e *EDNS0_UL) copy() EDNS0 { return &EDNS0_UL{e.Code, e.Lease, e.KeyLease} } 430 431// Copied: http://golang.org/src/pkg/net/dnsmsg.go 432func (e *EDNS0_UL) pack() ([]byte, error) { 433 var b []byte 434 if e.KeyLease == 0 { 435 b = make([]byte, 4) 436 } else { 437 b = make([]byte, 8) 438 binary.BigEndian.PutUint32(b[4:], e.KeyLease) 439 } 440 binary.BigEndian.PutUint32(b, e.Lease) 441 return b, nil 442} 443 444func (e *EDNS0_UL) unpack(b []byte) error { 445 switch len(b) { 446 case 4: 447 e.KeyLease = 0 448 case 8: 449 e.KeyLease = binary.BigEndian.Uint32(b[4:]) 450 default: 451 return ErrBuf 452 } 453 e.Lease = binary.BigEndian.Uint32(b) 454 return nil 455} 456 457// EDNS0_LLQ stands for Long Lived Queries: http://tools.ietf.org/html/draft-sekar-dns-llq-01 458// Implemented for completeness, as the EDNS0 type code is assigned. 459type EDNS0_LLQ struct { 460 Code uint16 // Always EDNS0LLQ 461 Version uint16 462 Opcode uint16 463 Error uint16 464 Id uint64 465 LeaseLife uint32 466} 467 468// Option implements the EDNS0 interface. 469func (e *EDNS0_LLQ) Option() uint16 { return EDNS0LLQ } 470 471func (e *EDNS0_LLQ) pack() ([]byte, error) { 472 b := make([]byte, 18) 473 binary.BigEndian.PutUint16(b[0:], e.Version) 474 binary.BigEndian.PutUint16(b[2:], e.Opcode) 475 binary.BigEndian.PutUint16(b[4:], e.Error) 476 binary.BigEndian.PutUint64(b[6:], e.Id) 477 binary.BigEndian.PutUint32(b[14:], e.LeaseLife) 478 return b, nil 479} 480 481func (e *EDNS0_LLQ) unpack(b []byte) error { 482 if len(b) < 18 { 483 return ErrBuf 484 } 485 e.Version = binary.BigEndian.Uint16(b[0:]) 486 e.Opcode = binary.BigEndian.Uint16(b[2:]) 487 e.Error = binary.BigEndian.Uint16(b[4:]) 488 e.Id = binary.BigEndian.Uint64(b[6:]) 489 e.LeaseLife = binary.BigEndian.Uint32(b[14:]) 490 return nil 491} 492 493func (e *EDNS0_LLQ) String() string { 494 s := strconv.FormatUint(uint64(e.Version), 10) + " " + strconv.FormatUint(uint64(e.Opcode), 10) + 495 " " + strconv.FormatUint(uint64(e.Error), 10) + " " + strconv.FormatUint(e.Id, 10) + 496 " " + strconv.FormatUint(uint64(e.LeaseLife), 10) 497 return s 498} 499func (e *EDNS0_LLQ) copy() EDNS0 { 500 return &EDNS0_LLQ{e.Code, e.Version, e.Opcode, e.Error, e.Id, e.LeaseLife} 501} 502 503// EDNS0_DAU implements the EDNS0 "DNSSEC Algorithm Understood" option. See RFC 6975. 504type EDNS0_DAU struct { 505 Code uint16 // Always EDNS0DAU 506 AlgCode []uint8 507} 508 509// Option implements the EDNS0 interface. 510func (e *EDNS0_DAU) Option() uint16 { return EDNS0DAU } 511func (e *EDNS0_DAU) pack() ([]byte, error) { return e.AlgCode, nil } 512func (e *EDNS0_DAU) unpack(b []byte) error { e.AlgCode = b; return nil } 513 514func (e *EDNS0_DAU) String() string { 515 s := "" 516 for _, alg := range e.AlgCode { 517 if a, ok := AlgorithmToString[alg]; ok { 518 s += " " + a 519 } else { 520 s += " " + strconv.Itoa(int(alg)) 521 } 522 } 523 return s 524} 525func (e *EDNS0_DAU) copy() EDNS0 { return &EDNS0_DAU{e.Code, e.AlgCode} } 526 527// EDNS0_DHU implements the EDNS0 "DS Hash Understood" option. See RFC 6975. 528type EDNS0_DHU struct { 529 Code uint16 // Always EDNS0DHU 530 AlgCode []uint8 531} 532 533// Option implements the EDNS0 interface. 534func (e *EDNS0_DHU) Option() uint16 { return EDNS0DHU } 535func (e *EDNS0_DHU) pack() ([]byte, error) { return e.AlgCode, nil } 536func (e *EDNS0_DHU) unpack(b []byte) error { e.AlgCode = b; return nil } 537 538func (e *EDNS0_DHU) String() string { 539 s := "" 540 for _, alg := range e.AlgCode { 541 if a, ok := HashToString[alg]; ok { 542 s += " " + a 543 } else { 544 s += " " + strconv.Itoa(int(alg)) 545 } 546 } 547 return s 548} 549func (e *EDNS0_DHU) copy() EDNS0 { return &EDNS0_DHU{e.Code, e.AlgCode} } 550 551// EDNS0_N3U implements the EDNS0 "NSEC3 Hash Understood" option. See RFC 6975. 552type EDNS0_N3U struct { 553 Code uint16 // Always EDNS0N3U 554 AlgCode []uint8 555} 556 557// Option implements the EDNS0 interface. 558func (e *EDNS0_N3U) Option() uint16 { return EDNS0N3U } 559func (e *EDNS0_N3U) pack() ([]byte, error) { return e.AlgCode, nil } 560func (e *EDNS0_N3U) unpack(b []byte) error { e.AlgCode = b; return nil } 561 562func (e *EDNS0_N3U) String() string { 563 // Re-use the hash map 564 s := "" 565 for _, alg := range e.AlgCode { 566 if a, ok := HashToString[alg]; ok { 567 s += " " + a 568 } else { 569 s += " " + strconv.Itoa(int(alg)) 570 } 571 } 572 return s 573} 574func (e *EDNS0_N3U) copy() EDNS0 { return &EDNS0_N3U{e.Code, e.AlgCode} } 575 576// EDNS0_EXPIRE implements the EDNS0 option as described in RFC 7314. 577type EDNS0_EXPIRE struct { 578 Code uint16 // Always EDNS0EXPIRE 579 Expire uint32 580} 581 582// Option implements the EDNS0 interface. 583func (e *EDNS0_EXPIRE) Option() uint16 { return EDNS0EXPIRE } 584func (e *EDNS0_EXPIRE) String() string { return strconv.FormatUint(uint64(e.Expire), 10) } 585func (e *EDNS0_EXPIRE) copy() EDNS0 { return &EDNS0_EXPIRE{e.Code, e.Expire} } 586 587func (e *EDNS0_EXPIRE) pack() ([]byte, error) { 588 b := make([]byte, 4) 589 binary.BigEndian.PutUint32(b, e.Expire) 590 return b, nil 591} 592 593func (e *EDNS0_EXPIRE) unpack(b []byte) error { 594 if len(b) == 0 { 595 // zero-length EXPIRE query, see RFC 7314 Section 2 596 return nil 597 } 598 if len(b) < 4 { 599 return ErrBuf 600 } 601 e.Expire = binary.BigEndian.Uint32(b) 602 return nil 603} 604 605// The EDNS0_LOCAL option is used for local/experimental purposes. The option 606// code is recommended to be within the range [EDNS0LOCALSTART, EDNS0LOCALEND] 607// (RFC6891), although any unassigned code can actually be used. The content of 608// the option is made available in Data, unaltered. 609// Basic use pattern for creating a local option: 610// 611// o := new(dns.OPT) 612// o.Hdr.Name = "." 613// o.Hdr.Rrtype = dns.TypeOPT 614// e := new(dns.EDNS0_LOCAL) 615// e.Code = dns.EDNS0LOCALSTART 616// e.Data = []byte{72, 82, 74} 617// o.Option = append(o.Option, e) 618type EDNS0_LOCAL struct { 619 Code uint16 620 Data []byte 621} 622 623// Option implements the EDNS0 interface. 624func (e *EDNS0_LOCAL) Option() uint16 { return e.Code } 625func (e *EDNS0_LOCAL) String() string { 626 return strconv.FormatInt(int64(e.Code), 10) + ":0x" + hex.EncodeToString(e.Data) 627} 628func (e *EDNS0_LOCAL) copy() EDNS0 { 629 b := make([]byte, len(e.Data)) 630 copy(b, e.Data) 631 return &EDNS0_LOCAL{e.Code, b} 632} 633 634func (e *EDNS0_LOCAL) pack() ([]byte, error) { 635 b := make([]byte, len(e.Data)) 636 copied := copy(b, e.Data) 637 if copied != len(e.Data) { 638 return nil, ErrBuf 639 } 640 return b, nil 641} 642 643func (e *EDNS0_LOCAL) unpack(b []byte) error { 644 e.Data = make([]byte, len(b)) 645 copied := copy(e.Data, b) 646 if copied != len(b) { 647 return ErrBuf 648 } 649 return nil 650} 651 652// EDNS0_TCP_KEEPALIVE is an EDNS0 option that instructs the server to keep 653// the TCP connection alive. See RFC 7828. 654type EDNS0_TCP_KEEPALIVE struct { 655 Code uint16 // Always EDNSTCPKEEPALIVE 656 Length uint16 // the value 0 if the TIMEOUT is omitted, the value 2 if it is present; 657 Timeout uint16 // an idle timeout value for the TCP connection, specified in units of 100 milliseconds, encoded in network byte order. 658} 659 660// Option implements the EDNS0 interface. 661func (e *EDNS0_TCP_KEEPALIVE) Option() uint16 { return EDNS0TCPKEEPALIVE } 662 663func (e *EDNS0_TCP_KEEPALIVE) pack() ([]byte, error) { 664 if e.Timeout != 0 && e.Length != 2 { 665 return nil, errors.New("dns: timeout specified but length is not 2") 666 } 667 if e.Timeout == 0 && e.Length != 0 { 668 return nil, errors.New("dns: timeout not specified but length is not 0") 669 } 670 b := make([]byte, 4+e.Length) 671 binary.BigEndian.PutUint16(b[0:], e.Code) 672 binary.BigEndian.PutUint16(b[2:], e.Length) 673 if e.Length == 2 { 674 binary.BigEndian.PutUint16(b[4:], e.Timeout) 675 } 676 return b, nil 677} 678 679func (e *EDNS0_TCP_KEEPALIVE) unpack(b []byte) error { 680 if len(b) < 4 { 681 return ErrBuf 682 } 683 e.Length = binary.BigEndian.Uint16(b[2:4]) 684 if e.Length != 0 && e.Length != 2 { 685 return errors.New("dns: length mismatch, want 0/2 but got " + strconv.FormatUint(uint64(e.Length), 10)) 686 } 687 if e.Length == 2 { 688 if len(b) < 6 { 689 return ErrBuf 690 } 691 e.Timeout = binary.BigEndian.Uint16(b[4:6]) 692 } 693 return nil 694} 695 696func (e *EDNS0_TCP_KEEPALIVE) String() (s string) { 697 s = "use tcp keep-alive" 698 if e.Length == 0 { 699 s += ", timeout omitted" 700 } else { 701 s += fmt.Sprintf(", timeout %dms", e.Timeout*100) 702 } 703 return 704} 705func (e *EDNS0_TCP_KEEPALIVE) copy() EDNS0 { return &EDNS0_TCP_KEEPALIVE{e.Code, e.Length, e.Timeout} } 706 707// EDNS0_PADDING option is used to add padding to a request/response. The default 708// value of padding SHOULD be 0x0 but other values MAY be used, for instance if 709// compression is applied before encryption which may break signatures. 710type EDNS0_PADDING struct { 711 Padding []byte 712} 713 714// Option implements the EDNS0 interface. 715func (e *EDNS0_PADDING) Option() uint16 { return EDNS0PADDING } 716func (e *EDNS0_PADDING) pack() ([]byte, error) { return e.Padding, nil } 717func (e *EDNS0_PADDING) unpack(b []byte) error { e.Padding = b; return nil } 718func (e *EDNS0_PADDING) String() string { return fmt.Sprintf("%0X", e.Padding) } 719func (e *EDNS0_PADDING) copy() EDNS0 { 720 b := make([]byte, len(e.Padding)) 721 copy(b, e.Padding) 722 return &EDNS0_PADDING{b} 723} 724 725// Extended DNS Error Codes (RFC 8914). 726const ( 727 ExtendedErrorCodeOther uint16 = iota 728 ExtendedErrorCodeUnsupportedDNSKEYAlgorithm 729 ExtendedErrorCodeUnsupportedDSDigestType 730 ExtendedErrorCodeStaleAnswer 731 ExtendedErrorCodeForgedAnswer 732 ExtendedErrorCodeDNSSECIndeterminate 733 ExtendedErrorCodeDNSBogus 734 ExtendedErrorCodeSignatureExpired 735 ExtendedErrorCodeSignatureNotYetValid 736 ExtendedErrorCodeDNSKEYMissing 737 ExtendedErrorCodeRRSIGsMissing 738 ExtendedErrorCodeNoZoneKeyBitSet 739 ExtendedErrorCodeNSECMissing 740 ExtendedErrorCodeCachedError 741 ExtendedErrorCodeNotReady 742 ExtendedErrorCodeBlocked 743 ExtendedErrorCodeCensored 744 ExtendedErrorCodeFiltered 745 ExtendedErrorCodeProhibited 746 ExtendedErrorCodeStaleNXDOMAINAnswer 747 ExtendedErrorCodeNotAuthoritative 748 ExtendedErrorCodeNotSupported 749 ExtendedErrorCodeNoReachableAuthority 750 ExtendedErrorCodeNetworkError 751 ExtendedErrorCodeInvalidData 752) 753 754// ExtendedErrorCodeToString maps extended error info codes to a human readable 755// description. 756var ExtendedErrorCodeToString = map[uint16]string{ 757 ExtendedErrorCodeOther: "Other", 758 ExtendedErrorCodeUnsupportedDNSKEYAlgorithm: "Unsupported DNSKEY Algorithm", 759 ExtendedErrorCodeUnsupportedDSDigestType: "Unsupported DS Digest Type", 760 ExtendedErrorCodeStaleAnswer: "Stale Answer", 761 ExtendedErrorCodeForgedAnswer: "Forged Answer", 762 ExtendedErrorCodeDNSSECIndeterminate: "DNSSEC Indeterminate", 763 ExtendedErrorCodeDNSBogus: "DNSSEC Bogus", 764 ExtendedErrorCodeSignatureExpired: "Signature Expired", 765 ExtendedErrorCodeSignatureNotYetValid: "Signature Not Yet Valid", 766 ExtendedErrorCodeDNSKEYMissing: "DNSKEY Missing", 767 ExtendedErrorCodeRRSIGsMissing: "RRSIGs Missing", 768 ExtendedErrorCodeNoZoneKeyBitSet: "No Zone Key Bit Set", 769 ExtendedErrorCodeNSECMissing: "NSEC Missing", 770 ExtendedErrorCodeCachedError: "Cached Error", 771 ExtendedErrorCodeNotReady: "Not Ready", 772 ExtendedErrorCodeBlocked: "Blocked", 773 ExtendedErrorCodeCensored: "Censored", 774 ExtendedErrorCodeFiltered: "Filtered", 775 ExtendedErrorCodeProhibited: "Prohibited", 776 ExtendedErrorCodeStaleNXDOMAINAnswer: "Stale NXDOMAIN Answer", 777 ExtendedErrorCodeNotAuthoritative: "Not Authoritative", 778 ExtendedErrorCodeNotSupported: "Not Supported", 779 ExtendedErrorCodeNoReachableAuthority: "No Reachable Authority", 780 ExtendedErrorCodeNetworkError: "Network Error", 781 ExtendedErrorCodeInvalidData: "Invalid Data", 782} 783 784// StringToExtendedErrorCode is a map from human readable descriptions to 785// extended error info codes. 786var StringToExtendedErrorCode = reverseInt16(ExtendedErrorCodeToString) 787 788// EDNS0_EDE option is used to return additional information about the cause of 789// DNS errors. 790type EDNS0_EDE struct { 791 InfoCode uint16 792 ExtraText string 793} 794 795// Option implements the EDNS0 interface. 796func (e *EDNS0_EDE) Option() uint16 { return EDNS0EDE } 797func (e *EDNS0_EDE) copy() EDNS0 { return &EDNS0_EDE{e.InfoCode, e.ExtraText} } 798 799func (e *EDNS0_EDE) String() string { 800 info := strconv.FormatUint(uint64(e.InfoCode), 10) 801 if s, ok := ExtendedErrorCodeToString[e.InfoCode]; ok { 802 info += fmt.Sprintf(" (%s)", s) 803 } 804 return fmt.Sprintf("%s: (%s)", info, e.ExtraText) 805} 806 807func (e *EDNS0_EDE) pack() ([]byte, error) { 808 b := make([]byte, 2+len(e.ExtraText)) 809 binary.BigEndian.PutUint16(b[0:], e.InfoCode) 810 copy(b[2:], []byte(e.ExtraText)) 811 return b, nil 812} 813 814func (e *EDNS0_EDE) unpack(b []byte) error { 815 if len(b) < 2 { 816 return ErrBuf 817 } 818 e.InfoCode = binary.BigEndian.Uint16(b[0:]) 819 e.ExtraText = string(b[2:]) 820 return nil 821} 822