2012-11-04 22:41:24 +11:00
|
|
|
// Go equivalent of the "DNS & BIND" book check-soa program.
|
2013-05-15 19:18:37 +10:00
|
|
|
// Created by Stephane Bortzmeyer.
|
2012-11-04 09:14:11 +11:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"github.com/miekg/dns"
|
|
|
|
"os"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
TIMEOUT time.Duration = 5 // seconds
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
localm *dns.Msg
|
|
|
|
localc *dns.Client
|
|
|
|
conf *dns.ClientConfig
|
|
|
|
)
|
|
|
|
|
2012-11-04 19:32:24 +11:00
|
|
|
func localQuery(qname string, qtype uint16) (*dns.Msg, error) {
|
2012-11-04 19:23:08 +11:00
|
|
|
localm.SetQuestion(qname, qtype)
|
|
|
|
for i := range conf.Servers {
|
|
|
|
server := conf.Servers[i]
|
2012-11-18 23:53:36 +11:00
|
|
|
r, _, err := localc.Exchange(localm, server+":"+conf.Port)
|
2012-11-04 19:23:08 +11:00
|
|
|
if r == nil || r.Rcode == dns.RcodeNameError || r.Rcode == dns.RcodeSuccess {
|
2012-11-04 09:14:11 +11:00
|
|
|
return r, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil, errors.New("No name server to answer the question")
|
|
|
|
}
|
|
|
|
|
|
|
|
func main() {
|
2012-11-04 19:23:08 +11:00
|
|
|
var err error
|
2012-11-04 09:14:11 +11:00
|
|
|
if len(os.Args) != 2 {
|
|
|
|
fmt.Printf("%s ZONE\n", os.Args[0])
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
conf, err = dns.ClientConfigFromFile("/etc/resolv.conf")
|
|
|
|
if conf == nil {
|
|
|
|
fmt.Printf("Cannot initialize the local resolver: %s\n", err)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
localm = new(dns.Msg)
|
2013-05-13 16:26:08 +10:00
|
|
|
localm.RecursionDesired = true
|
2012-11-04 09:14:11 +11:00
|
|
|
localm.Question = make([]dns.Question, 1)
|
|
|
|
localc = new(dns.Client)
|
|
|
|
localc.ReadTimeout = TIMEOUT * 1e9
|
2012-11-04 19:23:08 +11:00
|
|
|
r, err := localQuery(dns.Fqdn(os.Args[1]), dns.TypeNS)
|
2012-11-04 09:14:11 +11:00
|
|
|
if r == nil {
|
2012-11-04 19:23:08 +11:00
|
|
|
fmt.Printf("Cannot retrieve the list of name servers for %s: %s\n", dns.Fqdn(os.Args[1]), err)
|
2012-11-04 09:14:11 +11:00
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
if r.Rcode == dns.RcodeNameError {
|
2012-11-04 19:23:08 +11:00
|
|
|
fmt.Printf("No such domain %s\n", dns.Fqdn(os.Args[1]))
|
2012-11-04 09:14:11 +11:00
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
m := new(dns.Msg)
|
2013-05-13 16:26:08 +10:00
|
|
|
m.RecursionDesired = false
|
2012-11-04 09:14:11 +11:00
|
|
|
m.Question = make([]dns.Question, 1)
|
|
|
|
c := new(dns.Client)
|
|
|
|
c.ReadTimeout = TIMEOUT * 1e9
|
|
|
|
success := true
|
|
|
|
numNS := 0
|
2012-11-04 19:23:08 +11:00
|
|
|
for _, ans := range r.Answer {
|
2012-11-04 09:14:11 +11:00
|
|
|
switch ans.(type) {
|
2012-12-10 05:26:32 +11:00
|
|
|
case *dns.NS:
|
|
|
|
nameserver := ans.(*dns.NS).Ns
|
2012-11-04 09:14:11 +11:00
|
|
|
numNS += 1
|
|
|
|
ips := make([]string, 0)
|
|
|
|
fmt.Printf("%s : ", nameserver)
|
|
|
|
ra, err := localQuery(nameserver, dns.TypeA)
|
|
|
|
if ra == nil {
|
|
|
|
fmt.Printf("Error getting the IPv4 address of %s: %s\n", nameserver, err)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
if ra.Rcode != dns.RcodeSuccess {
|
2012-12-02 19:34:40 +11:00
|
|
|
fmt.Printf("Error getting the IPv4 address of %s: %s\n", nameserver, dns.RcodeToString[ra.Rcode])
|
2012-11-04 09:14:11 +11:00
|
|
|
os.Exit(1)
|
|
|
|
}
|
2012-11-04 19:23:08 +11:00
|
|
|
for _, ansa := range ra.Answer {
|
2012-11-04 09:14:11 +11:00
|
|
|
switch ansa.(type) {
|
2012-12-10 05:26:32 +11:00
|
|
|
case *dns.A:
|
|
|
|
ips = append(ips, ansa.(*dns.A).A.String())
|
2012-11-04 09:14:11 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
raaaa, err := localQuery(nameserver, dns.TypeAAAA)
|
|
|
|
if raaaa == nil {
|
|
|
|
fmt.Printf("Error getting the IPv6 address of %s: %s\n", nameserver, err)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
if raaaa.Rcode != dns.RcodeSuccess {
|
2012-12-02 19:34:40 +11:00
|
|
|
fmt.Printf("Error getting the IPv6 address of %s: %s\n", nameserver, dns.RcodeToString[raaaa.Rcode])
|
2012-11-04 09:14:11 +11:00
|
|
|
os.Exit(1)
|
|
|
|
}
|
2012-11-04 19:23:08 +11:00
|
|
|
for _, ansaaaa := range raaaa.Answer {
|
2012-11-04 09:14:11 +11:00
|
|
|
switch ansaaaa.(type) {
|
2012-12-10 05:26:32 +11:00
|
|
|
case *dns.AAAA:
|
|
|
|
ips = append(ips, ansaaaa.(*dns.AAAA).AAAA.String())
|
2012-11-04 09:14:11 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(ips) == 0 {
|
|
|
|
success = false
|
|
|
|
fmt.Printf("No IP address for this server")
|
|
|
|
}
|
2012-11-04 19:23:08 +11:00
|
|
|
for _, ip := range ips {
|
|
|
|
m.Question[0] = dns.Question{dns.Fqdn(os.Args[1]), dns.TypeSOA, dns.ClassINET}
|
2012-11-04 09:14:11 +11:00
|
|
|
nsAddressPort := ""
|
2012-11-04 19:23:08 +11:00
|
|
|
if strings.ContainsAny(":", ip) {
|
2012-11-04 19:27:23 +11:00
|
|
|
// IPv6 address
|
2012-11-04 19:23:08 +11:00
|
|
|
nsAddressPort = "[" + ip + "]:53"
|
2012-11-04 09:14:11 +11:00
|
|
|
} else {
|
2012-11-04 19:23:08 +11:00
|
|
|
nsAddressPort = ip + ":53"
|
2012-11-04 09:14:11 +11:00
|
|
|
}
|
2012-11-18 23:53:36 +11:00
|
|
|
soa, _, err := c.Exchange(m, nsAddressPort)
|
2012-11-04 19:23:08 +11:00
|
|
|
// TODO: retry if timeout? Otherwise, one lost UDP packet and it is the end
|
2012-11-04 09:14:11 +11:00
|
|
|
if soa == nil {
|
|
|
|
success = false
|
2012-11-04 19:23:08 +11:00
|
|
|
fmt.Printf("%s (%s) ", ip, err)
|
2012-11-04 19:32:24 +11:00
|
|
|
goto Next
|
|
|
|
}
|
|
|
|
if soa.Rcode != dns.RcodeSuccess {
|
|
|
|
success = false
|
2012-12-02 19:34:40 +11:00
|
|
|
fmt.Printf("%s (%s) ", ips, dns.RcodeToString[soa.Rcode])
|
2012-11-04 19:32:24 +11:00
|
|
|
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) {
|
2012-12-10 05:26:32 +11:00
|
|
|
case *dns.SOA:
|
2013-05-13 16:26:08 +10:00
|
|
|
if soa.Authoritative {
|
2012-11-04 19:32:24 +11:00
|
|
|
// TODO: test if all name servers have the same serial ?
|
2012-12-10 05:26:32 +11:00
|
|
|
fmt.Printf("%s (%d) ", ips, rsoa.(*dns.SOA).Serial)
|
2012-11-04 09:14:11 +11:00
|
|
|
} else {
|
2012-11-04 19:32:24 +11:00
|
|
|
success = false
|
|
|
|
fmt.Printf("%s (not authoritative) ", ips)
|
2012-11-04 09:14:11 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-11-04 19:32:24 +11:00
|
|
|
Next:
|
2012-11-04 09:14:11 +11:00
|
|
|
fmt.Printf("\n")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if numNS == 0 {
|
2012-11-04 19:23:08 +11:00
|
|
|
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]))
|
2012-11-04 09:14:11 +11:00
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
if success {
|
|
|
|
os.Exit(0)
|
|
|
|
}
|
2012-11-04 19:23:08 +11:00
|
|
|
os.Exit(1)
|
2012-11-04 09:14:11 +11:00
|
|
|
}
|