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