dns/ex/q/q.go

421 lines
11 KiB
Go
Raw Normal View History

2013-05-13 00:11:13 +10:00
// Copyright 2011 Miek Gieben. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
2012-10-14 20:30:08 +11:00
// Q is a small utility which acts and behaves like 'dig' from BIND.
// It is meant to stay lean and mean, while having a bunch of handy
// features, like -check which checks if a packet is correctly signed (without
// checking the chain of trust).
package main
2011-02-09 06:22:43 +11:00
import (
"flag"
"fmt"
2012-08-24 20:42:41 +10:00
"github.com/miekg/dns"
2012-06-01 05:40:07 +10:00
"net"
"os"
2011-02-25 02:13:23 +11:00
"strconv"
"strings"
"time"
)
2012-09-04 02:53:34 +10:00
// TODO: serial in ixfr
var (
2012-12-10 05:26:32 +11:00
dnskey *dns.DNSKEY
2012-09-04 02:53:34 +10:00
short *bool
)
2012-01-24 06:47:39 +11:00
func main() {
2012-09-04 02:53:34 +10:00
short = flag.Bool("short", false, "abbreviate long DNSSEC records")
2011-09-19 23:16:05 +10:00
dnssec := flag.Bool("dnssec", false, "request DNSSEC records")
query := flag.Bool("question", false, "show question")
2012-01-21 23:41:52 +11:00
check := flag.Bool("check", false, "check internal DNSSEC consistency")
2012-05-21 23:45:26 +10:00
six := flag.Bool("6", false, "use IPv6 only")
four := flag.Bool("4", false, "use IPv4 only")
2012-02-25 06:35:56 +11:00
anchor := flag.String("anchor", "", "use the DNSKEY in this file for interal DNSSEC consistency")
2012-05-06 01:37:33 +10:00
tsig := flag.String("tsig", "", "request tsig with key: [hmac:]name:key")
2011-12-18 06:14:39 +11:00
port := flag.Int("port", 53, "port number to use")
2011-09-19 23:16:05 +10:00
aa := flag.Bool("aa", false, "set AA flag in query")
ad := flag.Bool("ad", false, "set AD flag in query")
cd := flag.Bool("cd", false, "set CD flag in query")
2012-02-22 21:42:51 +11:00
rd := flag.Bool("rd", true, "set RD flag in query")
2012-02-25 09:43:34 +11:00
fallback := flag.Bool("fallback", false, "fallback to 4096 bytes bufsize and after that TCP")
2011-09-19 23:16:05 +10:00
tcp := flag.Bool("tcp", false, "TCP mode")
2012-06-01 05:40:07 +10:00
nsid := flag.Bool("nsid", false, "set edns nsid option")
client := flag.String("client", "", "set edns client-subnet option")
clientdraftcode := flag.Bool("clientdraft", false, "set edns client-subnet option using the draft option code")
2012-09-04 02:53:34 +10:00
//serial := flag.Int("serial", 0, "perform an IXFR with this serial")
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "Usage: %s [options] [@server] [qtype] [qclass] [name ...]\n", os.Args[0])
flag.PrintDefaults()
}
2010-12-31 03:15:59 +11:00
2011-07-05 07:57:26 +10:00
qtype := uint16(0)
2012-09-04 02:53:34 +10:00
qclass := uint16(dns.ClassINET)
var qname []string
flag.Parse()
2012-02-25 06:35:56 +11:00
if *anchor != "" {
f, err := os.Open(*anchor)
if err != nil {
fmt.Fprintf(os.Stderr, "Failure to open %s: %s\n", *anchor, err.Error())
}
r, err := dns.ReadRR(f, *anchor)
if err != nil {
fmt.Fprintf(os.Stderr, "Failure to read an RR from %s: %s\n", *anchor, err.Error())
}
2012-12-10 05:26:32 +11:00
if k, ok := r.(*dns.DNSKEY); !ok {
2012-02-25 06:35:56 +11:00
fmt.Fprintf(os.Stderr, "No DNSKEY read from %s\n", *anchor)
} else {
dnskey = k
}
}
var nameserver string
2011-02-24 01:25:11 +11:00
Flags:
for i := 0; i < flag.NArg(); i++ {
// If it starts with @ it is a nameserver
if flag.Arg(i)[0] == '@' {
nameserver = flag.Arg(i)
2011-02-24 01:25:11 +11:00
continue Flags
}
2011-02-25 02:13:23 +11:00
// First class, then type, to make ANY queries possible
2011-01-19 01:44:30 +11:00
// And if it looks like type, it is a type
2012-12-02 19:34:40 +11:00
if k, ok := dns.StringToType[strings.ToUpper(flag.Arg(i))]; ok {
2011-12-18 06:14:39 +11:00
qtype = k
continue Flags
}
2011-01-19 01:44:30 +11:00
// If it looks like a class, it is a class
2012-12-02 19:34:40 +11:00
if k, ok := dns.StringToClass[strings.ToUpper(flag.Arg(i))]; ok {
2011-12-18 06:14:39 +11:00
qclass = k
continue Flags
}
2011-02-25 02:13:23 +11:00
// If it starts with TYPExxx it is unknown rr
if strings.HasPrefix(flag.Arg(i), "TYPE") {
i, e := strconv.Atoi(string([]byte(flag.Arg(i))[4:]))
if e == nil {
qtype = uint16(i)
continue Flags
}
}
2011-02-22 02:00:30 +11:00
// Anything else is a qname
qname = append(qname, flag.Arg(i))
}
2011-07-25 16:58:34 +10:00
if len(qname) == 0 {
qname = make([]string, 1)
qname[0] = "."
qtype = dns.TypeNS
}
if qtype == 0 {
qtype = dns.TypeA
}
2011-03-29 01:13:34 +11:00
if len(nameserver) == 0 {
conf, err := dns.ClientConfigFromFile("/etc/resolv.conf")
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(2)
}
nameserver = "@" + conf.Servers[0]
}
nameserver = string([]byte(nameserver)[1:]) // chop off @
2012-10-16 05:07:53 +11:00
// if the nameserver is from /etc/resolv.conf the [ and ] are already
// added, thereby breaking net.ParseIP. Check for this and don't
// fully qualify such a name
2012-10-16 04:02:25 +11:00
if nameserver[0] == '[' && nameserver[len(nameserver)-1] == ']' {
2012-10-16 05:07:53 +11:00
nameserver = nameserver[1 : len(nameserver)-1]
2012-10-16 04:02:25 +11:00
}
if i := net.ParseIP(nameserver); i != nil {
2012-12-01 05:33:39 +11:00
nameserver = net.JoinHostPort(nameserver, strconv.Itoa(*port))
} else {
nameserver = dns.Fqdn(nameserver) + ":" + strconv.Itoa(*port)
}
c := new(dns.Client)
2013-10-12 08:34:04 +11:00
t := new(dns.Transfer)
c.Net = "udp"
if *four {
c.Net = "udp4"
}
if *six {
c.Net = "udp6"
}
if *tcp {
2011-07-05 07:57:26 +10:00
c.Net = "tcp"
2012-05-21 23:45:26 +10:00
if *four {
c.Net = "tcp4"
}
if *six {
c.Net = "tcp6"
}
}
2011-03-29 01:51:29 +11:00
2011-07-05 07:57:26 +10:00
m := new(dns.Msg)
m.MsgHdr.Authoritative = *aa
m.MsgHdr.AuthenticatedData = *ad
m.MsgHdr.CheckingDisabled = *cd
m.MsgHdr.RecursionDesired = *rd
m.Question = make([]dns.Question, 1)
2012-06-01 05:40:07 +10:00
if *dnssec || *nsid || *client != "" {
2012-12-10 05:26:32 +11:00
o := new(dns.OPT)
o.Hdr.Name = "."
o.Hdr.Rrtype = dns.TypeOPT
if *dnssec {
o.SetDo()
o.SetUDPSize(dns.DefaultMsgSize)
}
if *nsid {
e := new(dns.EDNS0_NSID)
2012-06-01 03:33:12 +10:00
e.Code = dns.EDNS0NSID
o.Option = append(o.Option, e)
2012-06-01 18:05:27 +10:00
// NSD will not return nsid when the udp message size is too small
2012-06-01 18:21:26 +10:00
o.SetUDPSize(dns.DefaultMsgSize)
}
2012-06-01 05:40:07 +10:00
if *client != "" {
e := new(dns.EDNS0_SUBNET)
e.Code = dns.EDNS0SUBNET
if *clientdraftcode {
e.DraftOption = true
}
2012-06-01 05:40:07 +10:00
e.SourceScope = 0
e.Address = net.ParseIP(*client)
if e.Address == nil {
fmt.Fprintf(os.Stderr, "Failure to parse IP address: %s\n", *client)
return
}
2012-06-01 06:40:52 +10:00
e.Family = 1 // IP4
2012-06-01 19:45:47 +10:00
e.SourceNetmask = net.IPv4len * 8
2012-06-01 06:40:52 +10:00
if e.Address.To4() == nil {
2012-06-01 05:40:07 +10:00
e.Family = 2 // IP6
2012-06-01 19:45:47 +10:00
e.SourceNetmask = net.IPv6len * 8
2012-06-01 05:40:07 +10:00
}
o.Option = append(o.Option, e)
}
m.Extra = append(m.Extra, o)
2011-07-05 07:57:26 +10:00
}
2011-09-20 20:52:23 +10:00
2012-11-20 01:58:02 +11:00
for _, v := range qname {
m.Question[0] = dns.Question{dns.Fqdn(v), qtype, qclass}
2011-07-25 16:58:34 +10:00
m.Id = dns.Id()
// Add tsig
if *tsig != "" {
if algo, name, secret, ok := tsigKeyParse(*tsig); ok {
2012-05-21 22:56:14 +10:00
m.SetTsig(name, algo, 300, time.Now().Unix())
2012-03-03 07:19:37 +11:00
c.TsigSecret = map[string]string{name: secret}
2013-10-12 08:34:04 +11:00
t.TsigSecret = map[string]string{name: secret}
} else {
2012-09-04 02:53:34 +10:00
fmt.Fprintf(os.Stderr, "TSIG key data error\n")
return
}
}
if *query {
fmt.Printf("%s", m.String())
fmt.Printf("\n;; size: %d bytes\n\n", m.Len())
}
2013-10-12 08:34:04 +11:00
if qtype == dns.TypeAXFR || qtype == dns.TypeIXFR {
env, err := t.In(m, nameserver)
if err != nil {
fmt.Printf(";; %s\n", err.Error())
continue
}
for e := range env {
fmt.Printf("%s\n", e.RR)
}
2012-09-04 02:53:34 +10:00
continue
}
2012-12-01 05:33:39 +11:00
r, rtt, e := c.Exchange(m, nameserver)
2013-02-09 02:39:21 +11:00
Redo:
2012-12-01 05:33:39 +11:00
if e != nil {
fmt.Printf(";; %s\n", e.Error())
continue
}
if r.Id != m.Id {
fmt.Fprintf(os.Stderr, "Id mismatch\n")
return
}
if r.MsgHdr.Truncated && *fallback {
if c.Net != "tcp" {
if !*dnssec {
fmt.Printf(";; Truncated, trying %d bytes bufsize\n", dns.DefaultMsgSize)
2012-12-10 05:26:32 +11:00
o := new(dns.OPT)
2012-12-01 05:33:39 +11:00
o.Hdr.Name = "."
o.Hdr.Rrtype = dns.TypeOPT
o.SetUDPSize(dns.DefaultMsgSize)
m.Extra = append(m.Extra, o)
r, rtt, e = c.Exchange(m, nameserver)
*dnssec = true
goto Redo
} else {
// First EDNS, then TCP
fmt.Printf(";; Truncated, trying TCP\n")
c.Net = "tcp"
r, rtt, e = c.Exchange(m, nameserver)
goto Redo
2012-02-25 09:43:34 +11:00
}
2011-02-25 02:13:23 +11:00
}
2012-11-20 00:43:02 +11:00
}
2012-12-01 05:33:39 +11:00
if r.MsgHdr.Truncated && !*fallback {
fmt.Printf(";; Truncated\n")
}
if *check {
sigCheck(r, nameserver, *tcp)
}
if *short {
r = shortMsg(r)
}
fmt.Printf("%v", r)
2012-12-26 22:19:27 +11:00
fmt.Printf("\n;; query time: %.3d µs, server: %s(%s), size: %d bytes\n", rtt/1e3, nameserver, c.Net, r.Len())
2012-09-04 03:08:21 +10:00
}
}
func tsigKeyParse(s string) (algo, name, secret string, ok bool) {
s1 := strings.SplitN(s, ":", 3)
switch len(s1) {
case 2:
return "hmac-md5.sig-alg.reg.int.", s1[0], s1[1], true
case 3:
switch s1[0] {
case "hmac-md5":
2012-12-13 20:47:33 +11:00
return "hmac-md5.sig-alg.reg.int.", s1[1], s1[2], true
case "hmac-sha1":
return "hmac-sha1.", s1[1], s1[2], true
case "hmac-sha256":
return "hmac-sha256.", s1[1], s1[2], true
}
}
return
}
func sectionCheck(set []dns.RR, server string, tcp bool) {
2012-12-10 05:26:32 +11:00
var key *dns.DNSKEY
2012-01-21 23:41:52 +11:00
for _, rr := range set {
if rr.Header().Rrtype == dns.TypeRRSIG {
2012-12-10 05:26:32 +11:00
rrset := getRRset(set, rr.Header().Name, rr.(*dns.RRSIG).TypeCovered)
2012-02-25 06:35:56 +11:00
if dnskey == nil {
2012-12-10 05:26:32 +11:00
key = getKey(rr.(*dns.RRSIG).SignerName, rr.(*dns.RRSIG).KeyTag, server, tcp)
2012-02-25 06:35:56 +11:00
} else {
key = dnskey
}
2012-01-21 23:41:52 +11:00
if key == nil {
2012-12-10 05:26:32 +11:00
fmt.Printf(";? DNSKEY %s/%d not found\n", rr.(*dns.RRSIG).SignerName, rr.(*dns.RRSIG).KeyTag)
2012-02-25 06:35:56 +11:00
continue
}
where := "net"
if dnskey != nil {
where = "disk"
2012-01-21 23:41:52 +11:00
}
2012-12-10 05:26:32 +11:00
if err := rr.(*dns.RRSIG).Verify(key, rrset); err != nil {
2012-05-06 01:37:33 +10:00
fmt.Printf(";- Bogus signature, %s does not validate (DNSKEY %s/%d/%s) [%s]\n",
2012-12-10 05:26:32 +11:00
shortSig(rr.(*dns.RRSIG)), key.Header().Name, key.KeyTag(), where, err.Error())
2012-01-21 23:41:52 +11:00
} else {
2012-12-10 05:26:32 +11:00
fmt.Printf(";+ Secure signature, %s validates (DNSKEY %s/%d/%s)\n", shortSig(rr.(*dns.RRSIG)), key.Header().Name, key.KeyTag(), where)
2012-01-21 23:41:52 +11:00
}
}
}
}
2013-09-21 01:15:27 +10:00
// Check the sigs in the msg, get the signer's key (additional query), get the
2012-01-20 06:45:01 +11:00
// rrset from the message, check the signature(s)
func sigCheck(in *dns.Msg, server string, tcp bool) {
2012-02-25 06:35:56 +11:00
sectionCheck(in.Answer, server, tcp)
sectionCheck(in.Ns, server, tcp)
sectionCheck(in.Extra, server, tcp)
2012-01-21 21:58:26 +11:00
}
2012-01-20 06:45:01 +11:00
2012-01-21 21:58:26 +11:00
// Return the RRset belonging to the signature with name and type t
func getRRset(l []dns.RR, name string, t uint16) []dns.RR {
2012-01-21 23:41:52 +11:00
l1 := make([]dns.RR, 0)
for _, rr := range l {
2012-02-29 05:24:38 +11:00
if strings.ToLower(rr.Header().Name) == strings.ToLower(name) && rr.Header().Rrtype == t {
2012-01-21 23:41:52 +11:00
l1 = append(l1, rr)
}
}
return l1
2012-01-20 06:45:01 +11:00
}
// Get the key from the DNS (uses the local resolver) and return them.
// If nothing is found we return nil
2012-12-10 05:26:32 +11:00
func getKey(name string, keytag uint16, server string, tcp bool) *dns.DNSKEY {
c := new(dns.Client)
2012-02-25 06:35:56 +11:00
if tcp {
c.Net = "tcp"
}
2012-01-21 23:41:52 +11:00
m := new(dns.Msg)
m.SetQuestion(name, dns.TypeDNSKEY)
2012-02-29 05:24:38 +11:00
m.SetEdns0(4096, true)
2012-11-20 01:58:02 +11:00
r, _, err := c.Exchange(m, server)
2012-01-21 23:41:52 +11:00
if err != nil {
return nil
}
for _, k := range r.Answer {
2012-12-10 05:26:32 +11:00
if k1, ok := k.(*dns.DNSKEY); ok {
2012-01-21 23:41:52 +11:00
if k1.KeyTag() == keytag {
return k1
}
}
}
return nil
2012-01-20 06:45:01 +11:00
}
2012-01-21 22:16:35 +11:00
// shorten RRSIG to "miek.nl RRSIG(NS)"
2012-12-10 05:26:32 +11:00
func shortSig(sig *dns.RRSIG) string {
2012-12-02 19:34:40 +11:00
return sig.Header().Name + " RRSIG(" + dns.TypeToString[sig.TypeCovered] + ")"
2012-01-21 22:16:35 +11:00
}
// Walk trough message and short Key data and Sig data
func shortMsg(in *dns.Msg) *dns.Msg {
for i := 0; i < len(in.Answer); i++ {
in.Answer[i] = shortRR(in.Answer[i])
}
for i := 0; i < len(in.Ns); i++ {
in.Ns[i] = shortRR(in.Ns[i])
}
for i := 0; i < len(in.Extra); i++ {
in.Extra[i] = shortRR(in.Extra[i])
}
return in
}
func shortRR(r dns.RR) dns.RR {
switch t := r.(type) {
2012-12-10 05:26:32 +11:00
case *dns.DS:
t.Digest = "..."
2012-12-10 05:26:32 +11:00
case *dns.DNSKEY:
2011-10-06 23:47:49 +11:00
t.PublicKey = "..."
2012-12-10 05:26:32 +11:00
case *dns.RRSIG:
2011-10-06 23:47:49 +11:00
t.Signature = "..."
2012-12-10 05:26:32 +11:00
case *dns.NSEC3:
2012-12-01 05:33:39 +11:00
t.Salt = "." // Nobody cares
if len(t.TypeBitMap) > 5 {
t.TypeBitMap = t.TypeBitMap[1:5]
}
}
return r
}
2012-09-04 02:53:34 +10:00
func doXfr(c *dns.Client, m *dns.Msg, nameserver string) {
/*
2013-10-12 08:34:04 +11:00
if t, e := c.TransferIn(m, nameserver); e == nil {
for r := range t {
if r.Error == nil {
for _, rr := range r.RR {
if *short {
rr = shortRR(rr)
}
fmt.Printf("%v\n", rr)
2012-09-04 02:53:34 +10:00
}
2013-10-12 08:34:04 +11:00
} else {
fmt.Fprintf(os.Stderr, "Failure to read XFR: %s\n", r.Error.Error())
2012-09-04 02:53:34 +10:00
}
}
2013-10-12 08:34:04 +11:00
} else {
fmt.Fprintf(os.Stderr, "Failure to read XFR: %s\n", e.Error())
2012-09-04 02:53:34 +10:00
}
*/
2012-09-04 02:53:34 +10:00
}