diff --git a/dns.go b/dns.go index 1a5703ff..e2d75934 100644 --- a/dns.go +++ b/dns.go @@ -61,7 +61,8 @@ // c := new(Client) // in, err := c.Exchange(m1, "127.0.0.1:53") // -// An asynchronous query is also possible, see client.Do and client.DoRtt. +// An asynchronous query is also possible, see client.Do or client.DoRtt, when +// you are interested in the round trip time of the exchange. // // From a birds eye view a dns message consists out of four sections. // The question section: in.Question, the answer section: in.Answer, diff --git a/ex/check-soa/check-soa.go b/ex/check-soa/check-soa.go index 9abd2e97..9c29082c 100644 --- a/ex/check-soa/check-soa.go +++ b/ex/check-soa/check-soa.go @@ -1,3 +1,4 @@ +// Go equivalent of the "DNS & BIND" book check-soa program. package main import ( @@ -19,18 +20,12 @@ var ( conf *dns.ClientConfig ) -func localQuery(qname string, qtype uint16) (r *dns.Msg, err error) { - localm.Question[0] = dns.Question{qname, qtype, dns.ClassINET} - for serverIndex := range conf.Servers { - server := conf.Servers[serverIndex] +func localQuery(qname string, qtype uint16) (*dns.Msg, error) { + localm.SetQuestion(qname, qtype) + for i := range conf.Servers { + server := conf.Servers[i] r, err := localc.Exchange(localm, server+":"+conf.Port) - if r == nil { - return r, err - } - if r.Rcode == dns.RcodeNameError { - return r, err - } - if r.Rcode == dns.RcodeSuccess { + if r == nil || r.Rcode == dns.RcodeNameError || r.Rcode == dns.RcodeSuccess { return r, err } } @@ -38,17 +33,11 @@ func localQuery(qname string, qtype uint16) (r *dns.Msg, err error) { } func main() { - var ( - err error - ) + var err error if len(os.Args) != 2 { fmt.Printf("%s ZONE\n", os.Args[0]) os.Exit(1) } - zone := os.Args[1] - if zone[len(zone)-1] != '.' { - zone += "." - } conf, err = dns.ClientConfigFromFile("/etc/resolv.conf") if conf == nil { fmt.Printf("Cannot initialize the local resolver: %s\n", err) @@ -59,13 +48,13 @@ func main() { localm.Question = make([]dns.Question, 1) localc = new(dns.Client) localc.ReadTimeout = TIMEOUT * 1e9 - r, err := localQuery(zone, dns.TypeNS) + r, err := localQuery(dns.Fqdn(os.Args[1]), dns.TypeNS) if r == nil { - fmt.Printf("Cannot retrieve the list of name servers for %s: %s\n", zone, err) + fmt.Printf("Cannot retrieve the list of name servers for %s: %s\n", dns.Fqdn(os.Args[1]), err) os.Exit(1) } if r.Rcode == dns.RcodeNameError { - fmt.Printf("No such domain %s\n", zone) + fmt.Printf("No such domain %s\n", dns.Fqdn(os.Args[1])) os.Exit(1) } m := new(dns.Msg) @@ -75,8 +64,7 @@ func main() { c.ReadTimeout = TIMEOUT * 1e9 success := true numNS := 0 - for i := range r.Answer { - ans := r.Answer[i] + for _, ans := range r.Answer { switch ans.(type) { case *dns.RR_NS: nameserver := ans.(*dns.RR_NS).Ns @@ -92,8 +80,7 @@ func main() { fmt.Printf("Error getting the IPv4 address of %s: %s\n", nameserver, dns.Rcode_str[ra.Rcode]) os.Exit(1) } - for j := range ra.Answer { - ansa := ra.Answer[j] + for _, ansa := range ra.Answer { switch ansa.(type) { case *dns.RR_A: ips = append(ips, ansa.(*dns.RR_A).A.String()) @@ -108,8 +95,7 @@ func main() { fmt.Printf("Error getting the IPv6 address of %s: %s\n", nameserver, dns.Rcode_str[raaaa.Rcode]) os.Exit(1) } - for j := range raaaa.Answer { - ansaaaa := raaaa.Answer[j] + for _, ansaaaa := range raaaa.Answer { switch ansaaaa.(type) { case *dns.RR_AAAA: ips = append(ips, ansaaaa.(*dns.RR_AAAA).AAAA.String()) @@ -119,54 +105,54 @@ func main() { success = false fmt.Printf("No IP address for this server") } - for j := range ips { - m.Question[0] = dns.Question{zone, dns.TypeSOA, dns.ClassINET} + for _, ip := range ips { + m.Question[0] = dns.Question{dns.Fqdn(os.Args[1]), dns.TypeSOA, dns.ClassINET} nsAddressPort := "" - if strings.ContainsAny(":", ips[j]) { - /* IPv6 address */ - nsAddressPort = "[" + ips[j] + "]:53" + if strings.ContainsAny(":", ip) { + // IPv6 address + nsAddressPort = "[" + ip + "]:53" } else { - nsAddressPort = ips[j] + ":53" + nsAddressPort = ip + ":53" } soa, err := c.Exchange(m, nsAddressPort) - /* TODO: retry if timeout? Otherwise, one lost UDP packet and it is the end ... */ + // TODO: retry if timeout? Otherwise, one lost UDP packet and it is the end if soa == nil { success = false - fmt.Printf("%s (%s) ", ips[j], err) - } else { - if soa.Rcode != dns.RcodeSuccess { - success = false - fmt.Printf("%s (%s) ", ips[j], dns.Rcode_str[soa.Rcode]) + fmt.Printf("%s (%s) ", ip, err) + goto Next + } + if soa.Rcode != dns.RcodeSuccess { + success = false + fmt.Printf("%s (%s) ", ips, dns.Rcode_str[soa.Rcode]) + goto Next + } + if len(soa.Answer) == 0 { // May happen if the server is a recursor, not authoritative, since we query with RD=0 + success = false + fmt.Printf("%s (0 answer) ", ip) + goto Next + } + rsoa := soa.Answer[0] + switch rsoa.(type) { + case *dns.RR_SOA: + if soa.MsgHdr.Authoritative { + // TODO: test if all name servers have the same serial ? + fmt.Printf("%s (%d) ", ips, rsoa.(*dns.RR_SOA).Serial) } else { - if len(soa.Answer) == 0 { /* May happen if the server is a recursor, not authoritative, since we query with RD=0 */ - success = false - fmt.Printf("%s (0 answer) ", ips[j]) - } else { - rsoa := soa.Answer[0] - switch rsoa.(type) { - case *dns.RR_SOA: - if soa.MsgHdr.Authoritative { - /* TODO: test if all name servers have the same serial ? */ - fmt.Printf("%s (%d) ", ips[j], rsoa.(*dns.RR_SOA).Serial) - } else { - success = false - fmt.Printf("%s (not authoritative) ", ips[j]) - } - } - } + success = false + fmt.Printf("%s (not authoritative) ", ips) } } } + Next: fmt.Printf("\n") } } if numNS == 0 { - fmt.Printf("No NS records for \"%s\". It is probably a CNAME to a domain but not a zone\n", zone) + fmt.Printf("No NS records for \"%s\". It is probably a CNAME to a domain but not a zone\n", dns.Fqdn(os.Args[1])) os.Exit(1) } if success { os.Exit(0) - } else { - os.Exit(1) } + os.Exit(1) }