loading...

Practical issue about DNS – EDNS0

dannypsnl profile image 林子篆 Originally published at dannypsnl.github.io on ・2 min read

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

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)

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
}

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
}

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 |
+------------+--------------+------------------------------+

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

More info:

Posted on by:

dannypsnl profile

林子篆

@dannypsnl

I am a programming language theory lover; good at system software like Networking, OS.

Discussion

markdown guide