# Go DNS (update)


I'm finally back to coding Go DNS and making it work with
the latest Go releases. Also the API has changed quite significantly
since the last time I blogged about it.

So this I will detail
[key2ds](https://github.com/miekg/godns/blob/master/_examples/key2ds/key2ds.go) which is small utility that queries
a zone and print any DNSKEY records as DS records on the fly, to show the new API and
some sample usage.

    % ./key2ds sidn.nl
    sidn.nl.    0   IN  DS  42033 8 1 343F74674D36C9B5BE2CEB2C401AC4EDEB2A05B2
    sidn.nl.    0   IN  DS  42033 8 2 BF985EC0738FACC89EE0B12FBD9261827C59191D9EA6A9BDFF55F9BDF3DBBFF3
    sidn.nl.    0   IN  DS  39274 8 1 E79E031DFDE8E68EF1E2C6CA0943C2CC0DED1889
    sidn.nl.    0   IN  DS  39274 8 2 8E8A8CFB40FD0C30BFA82E53752E1C257DAFB7B6206D12B9EDA43AF3EAB2157D

This util uses synchronous queries. I will explain the `main`-function:

    func main() {
            conf, err := dns.ClientConfigFromFile("/etc/resolv.conf")
            if len(os.Args) != 2 || err != nil {
                    fmt.Printf("%s DOMAIN\n", os.Args[0])
                    os.Exit(1)
            }

Read the resolver config from `/etc/resolv.conf` and check if
enough parameters have been given.

        m := new(dns.Msg)
        m.SetQuestion(os.Args[1], dns.TypeDNSKEY)

Prepare a new dns message to send to the other side. I'm interested in
the `DNSKEY`s for the name given on the command line.

        // Set EDNS0's Do bit
        e := new(dns.RR_OPT)
        e.Hdr.Name = "."
        e.Hdr.Rrtype = dns.TypeOPT
        e.SetUDPSize(2048)
        e.SetDo()
        m.Extra = append(m.Extra, e)

This is DNSSEC so I must prepare an EDNS0 section, which is
nothing more than adding an OPT RR to the additional section. I'm
pondering making EDNS0 easier and provide a few helper functions (ideas welcome!).
For now the whole OPT RR must be defined from scratch.

        c := dns.NewClient()
        r := c.Exchange(m, conf.Servers[0])
        if r == nil {
                fmt.Printf("*** no answer received for %s\n", os.Args[1])
                os.Exit(1)
        }

Create a new client and use `Exchange()` to perform send the query and 
wait for the reply. Note that we only use the first server defined
in `/etc/resolv.conf`. (There is room for some improvements :-) )

        if r.Rcode != dns.RcodeSuccess {
                fmt.Printf(" *** invalid answer name %s after DNSKEY query for %s\n", os.Args[1], os.Args[1])
                os.Exit(1)
        }

If anything is wrong with the answer I bail out here.

        for _, k := range r.Answer {
                if key, ok := k.(*dns.RR_DNSKEY); ok {

Loop through the answer section and check each RR to see
if it is a DNSKEY record with a type check.

                        ds := key.ToDS(dns.HashSHA1)
                        ds.Hdr.Ttl = 0
                        fmt.Printf("%v\n", ds)

For each DNSKEY convert it to a SHA1 DS records with `ToDS()`

                            ds = key.ToDS(dns.HashSHA256)
                            ds.Hdr.Ttl = 0
                            fmt.Printf("%v\n", ds)
                    }
            }
    }

And do the same for SHA256 and print that too.

