or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

client.mdconstants.mddnssec.mddynamic-updates.mdedns0.mdindex.mdmessaging.mdrr-types.mdserver.mdtsig.mdutilities.mdzone-parsing.mdzone-transfer.md
tile.json

edns0.mddocs/

EDNS0 - Extension Mechanisms for DNS

EDNS0 (Extension Mechanisms for DNS) provides additional features and larger message sizes beyond original DNS limits (RFC 6891).

Core Types

OPT Record

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)

EDNS0 Interface

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
}

Option Code Constants

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
}

EDNS0 Option Types

EDNS0_NSID - Name Server Identifier

type EDNS0_NSID struct {
	Code uint16 // EDNS0NSID
	Nsid string // Hex encoded server identifier
}

func (e *EDNS0_NSID) Option() uint16
func (e *EDNS0_NSID) String() string

EDNS0_SUBNET - Client Subnet

type 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() string

EDNS0_COOKIE - DNS Cookie

type EDNS0_COOKIE struct {
	Code   uint16 // EDNS0COOKIE
	Cookie string // Hex encoded cookie
}

func (e *EDNS0_COOKIE) Option() uint16
func (e *EDNS0_COOKIE) String() string

EDNS0_EXPIRE - EDNS Expire

type EDNS0_EXPIRE struct {
	Code   uint16 // EDNS0EXPIRE
	Expire uint32 // Expire time in seconds
}

func (e *EDNS0_EXPIRE) Option() uint16
func (e *EDNS0_EXPIRE) String() string

EDNS0_TCP_KEEPALIVE - TCP Keepalive

type 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() string

EDNS0_PADDING - Padding

type EDNS0_PADDING struct {
	Code    uint16 // EDNS0PADDING
	Padding []byte // Padding bytes
}

func (e *EDNS0_PADDING) Option() uint16
func (e *EDNS0_PADDING) String() string

EDNS0_EDE - Extended DNS Errors

type 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() string

EDNS0_DAU - DNSSEC Algorithm Understood

type EDNS0_DAU struct {
	Code      uint16  // EDNS0DAU
	AlgCode   []uint8 // Supported DNSSEC algorithms
}

func (e *EDNS0_DAU) Option() uint16
func (e *EDNS0_DAU) String() string

EDNS0_DHU - DS Hash Understood

type EDNS0_DHU struct {
	Code      uint16  // EDNS0DHU
	AlgCode   []uint8 // Supported DS hash algorithms
}

func (e *EDNS0_DHU) Option() uint16
func (e *EDNS0_DHU) String() string

EDNS0_N3U - NSEC3 Hash Understood

type EDNS0_N3U struct {
	Code      uint16  // EDNS0N3U
	AlgCode   []uint8 // Supported NSEC3 hash algorithms
}

func (e *EDNS0_N3U) Option() uint16
func (e *EDNS0_N3U) String() string

EDNS0_LLQ - Long Lived Queries

type 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() string

EDNS0_UL - Update Lease

type EDNS0_UL struct {
	Code  uint16 // EDNS0UL
	Lease uint32 // Lease time in seconds
}

func (e *EDNS0_UL) Option() uint16
func (e *EDNS0_UL) String() string

EDNS0_ESU - ENUM Source URI

type EDNS0_ESU struct {
	Code uint16 // EDNS0ESU
	Uri  string // Source URI
}

func (e *EDNS0_ESU) Option() uint16
func (e *EDNS0_ESU) String() string

EDNS0_LOCAL - Local/Experimental

type EDNS0_LOCAL struct {
	Code uint16 // Option code (EDNS0LOCALSTART-EDNS0LOCALEND)
	Data []byte // Option data
}

func (e *EDNS0_LOCAL) Option() uint16
func (e *EDNS0_LOCAL) String() string

Usage Examples

Basic EDNS0 with Larger UDP Size

m := 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")

EDNS0 with DNSSEC

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)
	}
}

Client Subnet (ECS)

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")

NSID - Name Server Identifier

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)
		}
	}
}

DNS Cookies

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)
		}
	}
}

TCP Keepalive

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")

Padding for Privacy

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")

Extended DNS Errors

// 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)
		}
	}
}

DNSSEC Algorithm Signaling

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)

Custom Local Option

// 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)

Checking for EDNS0 Support

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")
}

Multiple EDNS0 Options

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)

Extended Error Codes (EDE)

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
)

Related Topics

  • DNS Messaging - Adding EDNS0 to messages
  • DNSSEC Operations - Using DO bit
  • Client Operations - Client-side EDNS0
  • Server Operations - Server-side EDNS0
  • Constants - All EDNS0 constants