DEV Community

林子篆
林子篆

Posted on • Originally published at dannypsnl.github.io on

Practical issue about DNS – EDNS0

I have to create a checker for our DNS server.

Our DNS server will return its config via TXT.

Code would like:

package main

import "github.com/miekg/dns"

// func main
m := dns.Msg{}
// `google.com.` is correct format, `.` is required!
m.SetQuestion("google.com.", dns.TypeTXT)
c := dns.Client{}
// write out port 53(:53) is required, dns package we used at here won't automatically fix port to 53.
// Unlike some cli tool such as Dig
response, _, err := c.Exchange(&m, "8.8.8.8:53")
for _, t := range r.Answer {
    txt := t.(*dns.TXT)
    // dns.TXT.Txt is []string
    for _, t := txt.Txt {
        // t is string, now you can use it
        println(t)
    }
}

Enter fullscreen mode Exit fullscreen mode

But our config stream is too big to over the limit of UDP! And we must get it.

RFC 6891 :

Traditional DNS messages are limited to 512 octets in size when sent over UDP [RFC1035].

After researching, I find we can extend DNS packet size by EDNS0.

RFC 6891 :

EDNS(0) specifies a way to advertise additional features such as larger response size capability, which is intended to help avoid truncated UDP responses, which in turn cause retry over TCP. It therefore provides support for transporting these larger packet sizes without needing to resort to TCP for transport.

Code is:

m.SetEdns0(4096, true)

Enter fullscreen mode Exit fullscreen mode

In SetEdns0:

func (dns *Msg) SetEdns0(udpsize uint16, do bool) *Msg {
    e := new(OPT)
    e.Hdr.Name = "."
    e.Hdr.Rrtype = TypeOPT
    e.SetUDPSize(udpsize)
    if do {
        e.SetDo()
    }
    dns.Extra = append(dns.Extra, e)
    return dns
}

Enter fullscreen mode Exit fullscreen mode

The code that change UDP size is e.SetUDPSize, so let’s take a look:

func (rr *OPT) SetUDPSize(size uint16) {
    rr.Hdr.Class = size
}

Enter fullscreen mode Exit fullscreen mode

Type of OPT.Hdr is RR_Header, then we dig into RFC 6891 , on page 5 you can find:

The fixed part of an OPT RR is structured as follows:

+------------+--------------+------------------------------+
| Field Name | Field Type | Description |
+------------+--------------+------------------------------+
| NAME | domain name | MUST be 0 (root domain) |
| TYPE | u_int16_t | OPT (41) |
| CLASS | u_int16_t | requestor's UDP payload size |
| TTL | u_int32_t | extended RCODE and flags |
| RDLEN | u_int16_t | length of all RDATA |
| RDATA | octet stream | {attribute,value} pairs |
+------------+--------------+------------------------------+

Enter fullscreen mode Exit fullscreen mode

As expected, RR_Header.Class will change UDP payload size. Celebrate it!!!

More info:

Top comments (0)