EDNS0 (Extension Mechanisms for DNS) provides additional features and larger message sizes beyond original DNS limits (RFC 6891).
Pseudo-RR that carries EDNS0 options in the additional section.
type OPT struct {
Hdr RR_Header
Option []EDNS0 // EDNS0 options
}
// Version returns EDNS version (usually 0)
func (rr *OPT) Version() uint8
// SetVersion sets EDNS version
func (rr *OPT) SetVersion(v uint8)
// ExtendedRcode returns extended RCODE (upper 8 bits)
func (rr *OPT) ExtendedRcode() int
// SetExtendedRcode sets extended RCODE
func (rr *OPT) SetExtendedRcode(v uint16)
// UDPSize returns UDP payload size
func (rr *OPT) UDPSize() uint16
// SetUDPSize sets UDP payload size
func (rr *OPT) SetUDPSize(size uint16)
// Do returns DNSSEC OK bit
func (rr *OPT) Do() bool
// SetDo sets DNSSEC OK bit
func (rr *OPT) SetDo(do ...bool)
// Co returns Compact Answers OK bit
func (rr *OPT) Co() bool
// SetCo sets Compact Answers OK bit
func (rr *OPT) SetCo(co ...bool)
// Z returns Z field (14 least significant bits)
func (rr *OPT) Z() uint16
// SetZ sets Z field
func (rr *OPT) SetZ(z uint16)Base interface for all EDNS0 options.
type EDNS0 interface {
Option() uint16 // Option code
String() string // String representation
pack() ([]byte, error) // Pack to wire format
unpack([]byte) error // Unpack from wire format
copy() EDNS0 // Deep copy
}const (
EDNS0LLQ = 0x1 // Long Lived Queries
EDNS0UL = 0x2 // Update Lease
EDNS0NSID = 0x3 // Name Server Identifier (RFC 5001)
EDNS0ESU = 0x4 // ENUM Source-URI
EDNS0DAU = 0x5 // DNSSEC Algorithm Understood
EDNS0DHU = 0x6 // DS Hash Understood
EDNS0N3U = 0x7 // NSEC3 Hash Understood
EDNS0SUBNET = 0x8 // Client Subnet (RFC 7871)
EDNS0EXPIRE = 0x9 // EDNS Expire
EDNS0COOKIE = 0xa // DNS Cookie (RFC 7873)
EDNS0TCPKEEPALIVE = 0xb // TCP Keepalive (RFC 7828)
EDNS0PADDING = 0xc // Padding (RFC 7830)
EDNS0EDE = 0xf // Extended DNS Errors (RFC 8914)
EDNS0LOCALSTART = 0xFDE9 // Local/experimental range start
EDNS0LOCALEND = 0xFFFE // Local/experimental range end
}type EDNS0_NSID struct {
Code uint16 // EDNS0NSID
Nsid string // Hex encoded server identifier
}
func (e *EDNS0_NSID) Option() uint16
func (e *EDNS0_NSID) String() stringtype EDNS0_SUBNET struct {
Code uint16 // EDNS0SUBNET
Family uint16 // 1 for IPv4, 2 for IPv6
SourceNetmask uint8 // Client netmask
SourceScope uint8 // Scope netmask
Address net.IP // Client address
}
func (e *EDNS0_SUBNET) Option() uint16
func (e *EDNS0_SUBNET) String() stringtype EDNS0_COOKIE struct {
Code uint16 // EDNS0COOKIE
Cookie string // Hex encoded cookie
}
func (e *EDNS0_COOKIE) Option() uint16
func (e *EDNS0_COOKIE) String() stringtype EDNS0_EXPIRE struct {
Code uint16 // EDNS0EXPIRE
Expire uint32 // Expire time in seconds
}
func (e *EDNS0_EXPIRE) Option() uint16
func (e *EDNS0_EXPIRE) String() stringtype EDNS0_TCP_KEEPALIVE struct {
Code uint16 // EDNS0TCPKEEPALIVE
Length uint16 // Option length
Timeout uint16 // Keepalive timeout (100ms units)
}
func (e *EDNS0_TCP_KEEPALIVE) Option() uint16
func (e *EDNS0_TCP_KEEPALIVE) String() stringtype EDNS0_PADDING struct {
Code uint16 // EDNS0PADDING
Padding []byte // Padding bytes
}
func (e *EDNS0_PADDING) Option() uint16
func (e *EDNS0_PADDING) String() stringtype EDNS0_EDE struct {
Code uint16 // EDNS0EDE
InfoCode uint16 // Extended error code
ExtraText string // Human-readable error text
}
func (e *EDNS0_EDE) Option() uint16
func (e *EDNS0_EDE) String() stringtype EDNS0_DAU struct {
Code uint16 // EDNS0DAU
AlgCode []uint8 // Supported DNSSEC algorithms
}
func (e *EDNS0_DAU) Option() uint16
func (e *EDNS0_DAU) String() stringtype EDNS0_DHU struct {
Code uint16 // EDNS0DHU
AlgCode []uint8 // Supported DS hash algorithms
}
func (e *EDNS0_DHU) Option() uint16
func (e *EDNS0_DHU) String() stringtype EDNS0_N3U struct {
Code uint16 // EDNS0N3U
AlgCode []uint8 // Supported NSEC3 hash algorithms
}
func (e *EDNS0_N3U) Option() uint16
func (e *EDNS0_N3U) String() stringtype EDNS0_LLQ struct {
Code uint16 // EDNS0LLQ
Version uint16 // LLQ version
Opcode uint16 // LLQ opcode
Error uint16 // LLQ error code
Id uint64 // LLQ identifier
LeaseLife uint32 // Lease lifetime
}
func (e *EDNS0_LLQ) Option() uint16
func (e *EDNS0_LLQ) String() stringtype EDNS0_UL struct {
Code uint16 // EDNS0UL
Lease uint32 // Lease time in seconds
}
func (e *EDNS0_UL) Option() uint16
func (e *EDNS0_UL) String() stringtype EDNS0_ESU struct {
Code uint16 // EDNS0ESU
Uri string // Source URI
}
func (e *EDNS0_ESU) Option() uint16
func (e *EDNS0_ESU) String() stringtype EDNS0_LOCAL struct {
Code uint16 // Option code (EDNS0LOCALSTART-EDNS0LOCALEND)
Data []byte // Option data
}
func (e *EDNS0_LOCAL) Option() uint16
func (e *EDNS0_LOCAL) String() stringm := new(dns.Msg)
m.SetQuestion(dns.Fqdn("example.com"), dns.TypeA)
// Add EDNS0 with 4096 byte UDP size
m.SetEdns0(4096, false)
// Send query
c := new(dns.Client)
r, _, err := c.Exchange(m, "8.8.8.8:53")m := new(dns.Msg)
m.SetQuestion(dns.Fqdn("example.com"), dns.TypeA)
// Enable DNSSEC (DO bit)
m.SetEdns0(4096, true)
c := new(dns.Client)
r, _, err := c.Exchange(m, "8.8.8.8:53")
if err != nil {
log.Fatal(err)
}
// Check for DNSSEC records
for _, rr := range r.Answer {
if rrsig, ok := rr.(*dns.RRSIG); ok {
fmt.Printf("Found RRSIG: %v\n", rrsig)
}
}m := new(dns.Msg)
m.SetQuestion(dns.Fqdn("example.com"), dns.TypeA)
// Add EDNS0 OPT record
opt := new(dns.OPT)
opt.Hdr.Name = "."
opt.Hdr.Rrtype = dns.TypeOPT
opt.SetUDPSize(4096)
// Add client subnet option
ecs := new(dns.EDNS0_SUBNET)
ecs.Code = dns.EDNS0SUBNET
ecs.Family = 1 // IPv4
ecs.SourceNetmask = 24
ecs.SourceScope = 0
ecs.Address = net.ParseIP("203.0.113.0").To4()
opt.Option = append(opt.Option, ecs)
m.Extra = append(m.Extra, opt)
c := new(dns.Client)
r, _, err := c.Exchange(m, "8.8.8.8:53")m := new(dns.Msg)
m.SetQuestion(dns.Fqdn("example.com"), dns.TypeA)
// Create OPT record
opt := new(dns.OPT)
opt.Hdr.Name = "."
opt.Hdr.Rrtype = dns.TypeOPT
opt.SetUDPSize(4096)
// Add NSID option (empty to request server's NSID)
nsid := new(dns.EDNS0_NSID)
nsid.Code = dns.EDNS0NSID
nsid.Nsid = "" // Empty = request
opt.Option = append(opt.Option, nsid)
m.Extra = append(m.Extra, opt)
c := new(dns.Client)
r, _, err := c.Exchange(m, "8.8.8.8:53")
if err != nil {
log.Fatal(err)
}
// Extract NSID from response
if opt := r.IsEdns0(); opt != nil {
for _, o := range opt.Option {
if nsid, ok := o.(*dns.EDNS0_NSID); ok {
fmt.Printf("Server NSID: %s\n", nsid.Nsid)
}
}
}m := new(dns.Msg)
m.SetQuestion(dns.Fqdn("example.com"), dns.TypeA)
opt := new(dns.OPT)
opt.Hdr.Name = "."
opt.Hdr.Rrtype = dns.TypeOPT
opt.SetUDPSize(4096)
// Add cookie option
cookie := new(dns.EDNS0_COOKIE)
cookie.Code = dns.EDNS0COOKIE
cookie.Cookie = generateClientCookie() // 8-byte client cookie
opt.Option = append(opt.Option, cookie)
m.Extra = append(m.Extra, opt)
c := new(dns.Client)
r, _, err := c.Exchange(m, "8.8.8.8:53")
if err != nil {
log.Fatal(err)
}
// Extract server cookie for subsequent requests
if opt := r.IsEdns0(); opt != nil {
for _, o := range opt.Option {
if cookie, ok := o.(*dns.EDNS0_COOKIE); ok {
// Save cookie for next request
fmt.Printf("Server cookie: %s\n", cookie.Cookie)
}
}
}m := new(dns.Msg)
m.SetQuestion(dns.Fqdn("example.com"), dns.TypeA)
opt := new(dns.OPT)
opt.Hdr.Name = "."
opt.Hdr.Rrtype = dns.TypeOPT
opt.SetUDPSize(4096)
// Add TCP keepalive option
keepalive := new(dns.EDNS0_TCP_KEEPALIVE)
keepalive.Code = dns.EDNS0TCPKEEPALIVE
keepalive.Timeout = 300 // 30 seconds (in 100ms units)
opt.Option = append(opt.Option, keepalive)
m.Extra = append(m.Extra, opt)
c := &dns.Client{Net: "tcp"}
r, _, err := c.Exchange(m, "8.8.8.8:53")m := new(dns.Msg)
m.SetQuestion(dns.Fqdn("example.com"), dns.TypeA)
opt := new(dns.OPT)
opt.Hdr.Name = "."
opt.Hdr.Rrtype = dns.TypeOPT
opt.SetUDPSize(4096)
// Add padding to reach specific message size
padding := new(dns.EDNS0_PADDING)
padding.Code = dns.EDNS0PADDING
padding.Padding = make([]byte, 128) // 128 bytes of padding
opt.Option = append(opt.Option, padding)
m.Extra = append(m.Extra, opt)
c := new(dns.Client)
r, _, err := c.Exchange(m, "1.1.1.1:53")// Server returning extended error
dns.HandleFunc(".", func(w dns.ResponseWriter, r *dns.Msg) {
m := new(dns.Msg)
m.SetRcode(r, dns.RcodeServerFailure)
// Add EDE option to explain error
opt := new(dns.OPT)
opt.Hdr.Name = "."
opt.Hdr.Rrtype = dns.TypeOPT
opt.SetUDPSize(4096)
ede := new(dns.EDNS0_EDE)
ede.Code = dns.EDNS0EDE
ede.InfoCode = 1 // Unsupported DNSKEY Algorithm
ede.ExtraText = "Algorithm 255 not supported"
opt.Option = append(opt.Option, ede)
m.Extra = append(m.Extra, opt)
w.WriteMsg(m)
})
// Client receiving extended error
if opt := response.IsEdns0(); opt != nil {
for _, o := range opt.Option {
if ede, ok := o.(*dns.EDNS0_EDE); ok {
fmt.Printf("Extended Error %d: %s\n", ede.InfoCode, ede.ExtraText)
}
}
}m := new(dns.Msg)
m.SetQuestion(dns.Fqdn("example.com"), dns.TypeDNSKEY)
opt := new(dns.OPT)
opt.Hdr.Name = "."
opt.Hdr.Rrtype = dns.TypeOPT
opt.SetUDPSize(4096)
opt.SetDo(true)
// Signal supported DNSSEC algorithms
dau := new(dns.EDNS0_DAU)
dau.Code = dns.EDNS0DAU
dau.AlgCode = []uint8{
dns.RSASHA256,
dns.RSASHA512,
dns.ECDSAP256SHA256,
dns.ECDSAP384SHA384,
dns.ED25519,
}
// Signal supported DS hash algorithms
dhu := new(dns.EDNS0_DHU)
dhu.Code = dns.EDNS0DHU
dhu.AlgCode = []uint8{
dns.SHA256,
dns.SHA384,
}
// Signal supported NSEC3 hash algorithms
n3u := new(dns.EDNS0_N3U)
n3u.Code = dns.EDNS0N3U
n3u.AlgCode = []uint8{dns.SHA1}
opt.Option = append(opt.Option, dau, dhu, n3u)
m.Extra = append(m.Extra, opt)// Use local/experimental option code
m := new(dns.Msg)
m.SetQuestion(dns.Fqdn("example.com"), dns.TypeA)
opt := new(dns.OPT)
opt.Hdr.Name = "."
opt.Hdr.Rrtype = dns.TypeOPT
opt.SetUDPSize(4096)
// Custom option in local range
local := new(dns.EDNS0_LOCAL)
local.Code = 0xFFF0 // In EDNS0LOCALSTART-EDNS0LOCALEND range
local.Data = []byte("custom data")
opt.Option = append(opt.Option, local)
m.Extra = append(m.Extra, opt)m := new(dns.Msg)
m.SetQuestion(dns.Fqdn("example.com"), dns.TypeA)
m.SetEdns0(4096, false)
c := new(dns.Client)
r, _, err := c.Exchange(m, "8.8.8.8:53")
if err != nil {
log.Fatal(err)
}
// Check if server supports EDNS0
if opt := r.IsEdns0(); opt != nil {
fmt.Printf("Server supports EDNS0\n")
fmt.Printf("UDP size: %d\n", opt.UDPSize())
fmt.Printf("Version: %d\n", opt.Version())
fmt.Printf("DO bit: %v\n", opt.Do())
} else {
fmt.Println("Server does not support EDNS0")
}m := new(dns.Msg)
m.SetQuestion(dns.Fqdn("example.com"), dns.TypeA)
opt := new(dns.OPT)
opt.Hdr.Name = "."
opt.Hdr.Rrtype = dns.TypeOPT
opt.SetUDPSize(4096)
opt.SetDo(true)
// Add multiple options
nsid := &dns.EDNS0_NSID{Code: dns.EDNS0NSID}
ecs := &dns.EDNS0_SUBNET{
Code: dns.EDNS0SUBNET,
Family: 1,
SourceNetmask: 24,
Address: net.ParseIP("203.0.113.0").To4(),
}
cookie := &dns.EDNS0_COOKIE{
Code: dns.EDNS0COOKIE,
Cookie: "0123456789abcdef",
}
opt.Option = append(opt.Option, nsid, ecs, cookie)
m.Extra = append(m.Extra, opt)Common InfoCode values for EDNS0_EDE:
const (
ExtendedErrorCodeOther = 0
ExtendedErrorCodeUnsupportedDNSKEYAlg = 1
ExtendedErrorCodeUnsupportedDSDigest = 2
ExtendedErrorCodeStaleAnswer = 3
ExtendedErrorCodeForgedAnswer = 4
ExtendedErrorCodeDNSSECIndeterminate = 5
ExtendedErrorCodeDNSSECBogus = 6
ExtendedErrorCodeSignatureExpired = 7
ExtendedErrorCodeSignatureNotYetValid = 8
ExtendedErrorCodeDNSKEYMissing = 9
ExtendedErrorCodeRRSIGsMissing = 10
ExtendedErrorCodeNoZoneKeyBitSet = 11
ExtendedErrorCodeNSECMissing = 12
ExtendedErrorCodeCachedError = 13
ExtendedErrorCodeNotReady = 14
ExtendedErrorCodeBlocked = 15
ExtendedErrorCodeCensored = 16
ExtendedErrorCodeFiltered = 17
ExtendedErrorCodeProhibited = 18
ExtendedErrorCodeStaleNXDOMAINAnswer = 19
ExtendedErrorCodeNotAuthoritative = 20
ExtendedErrorCodeNotSupported = 21
ExtendedErrorCodeNoReachableAuthority = 22
ExtendedErrorCodeNetworkError = 23
ExtendedErrorCodeInvalidData = 24
)