Put examples in their own repository

example repository is github.com:miekg/ex/dns
This commit is contained in:
Miek Gieben 2013-10-13 11:23:14 +01:00
parent 4862dd17b4
commit 5902b88b4a
6 changed files with 5 additions and 972 deletions

View File

@ -59,10 +59,12 @@ correctly, the following should work:
go get github.com/miekg/dns
go build github.com/miekg/dns
## Examples
A short "how to use the API" is at the beginning of dns.go (this also will show
when you call `go doc github.com/miekg/dns`. Sample
programs can be found in the `ex` directory. They can also be build
with: `go build`.
when you call `go doc github.com/miekg/dns`.
Example programs can be found in the `github.com/miekg/exdns` repository.
## Supported RFCs

View File

@ -1,159 +0,0 @@
// Go equivalent of the "DNS & BIND" book check-soa program.
// Created by Stephane Bortzmeyer.
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
)
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 || r.Rcode == dns.RcodeNameError || r.Rcode == dns.RcodeSuccess {
return r, err
}
}
return nil, errors.New("No name server to answer the question")
}
func main() {
var err error
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)
localm.RecursionDesired = true
localm.Question = make([]dns.Question, 1)
localc = new(dns.Client)
localc.ReadTimeout = TIMEOUT * 1e9
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", dns.Fqdn(os.Args[1]), err)
os.Exit(1)
}
if r.Rcode == dns.RcodeNameError {
fmt.Printf("No such domain %s\n", dns.Fqdn(os.Args[1]))
os.Exit(1)
}
m := new(dns.Msg)
m.RecursionDesired = false
m.Question = make([]dns.Question, 1)
c := new(dns.Client)
c.ReadTimeout = TIMEOUT * 1e9
success := true
numNS := 0
for _, ans := range r.Answer {
switch ans.(type) {
case *dns.NS:
nameserver := ans.(*dns.NS).Ns
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 {
fmt.Printf("Error getting the IPv4 address of %s: %s\n", nameserver, dns.RcodeToString[ra.Rcode])
os.Exit(1)
}
for _, ansa := range ra.Answer {
switch ansa.(type) {
case *dns.A:
ips = append(ips, ansa.(*dns.A).A.String())
}
}
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 {
fmt.Printf("Error getting the IPv6 address of %s: %s\n", nameserver, dns.RcodeToString[raaaa.Rcode])
os.Exit(1)
}
for _, ansaaaa := range raaaa.Answer {
switch ansaaaa.(type) {
case *dns.AAAA:
ips = append(ips, ansaaaa.(*dns.AAAA).AAAA.String())
}
}
if len(ips) == 0 {
success = false
fmt.Printf("No IP address for this server")
}
for _, ip := range ips {
m.Question[0] = dns.Question{dns.Fqdn(os.Args[1]), dns.TypeSOA, dns.ClassINET}
nsAddressPort := ""
if strings.ContainsAny(":", ip) {
// IPv6 address
nsAddressPort = "[" + ip + "]:53"
} else {
nsAddressPort = ip + ":53"
}
soa, _, err := c.Exchange(m, nsAddressPort)
// TODO: retry if timeout? Otherwise, one lost UDP packet and it is the end
if soa == nil {
success = false
fmt.Printf("%s (%s) ", ip, err)
goto Next
}
if soa.Rcode != dns.RcodeSuccess {
success = false
fmt.Printf("%s (%s) ", ips, dns.RcodeToString[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.SOA:
if soa.Authoritative {
// TODO: test if all name servers have the same serial ?
fmt.Printf("%s (%d) ", ips, rsoa.(*dns.SOA).Serial)
} else {
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", dns.Fqdn(os.Args[1]))
os.Exit(1)
}
if success {
os.Exit(0)
}
os.Exit(1)
}

View File

@ -1,77 +0,0 @@
// 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.
// An AS112 blackhole DNS server. Similar to the one found in evldns.
// Also see https://www.as112.net/
package main
import (
"github.com/miekg/dns"
"log"
"os"
"os/signal"
"runtime"
"syscall"
)
const SOA string = "@ SOA prisoner.iana.org. hostmaster.root-servers.org. 2002040800 1800 900 0604800 604800"
func NewRR(s string) dns.RR { r, _ := dns.NewRR(s); return r }
var zones = map[string]dns.RR{
"10.in-addr.arpa.": NewRR("$ORIGIN 10.in-addr.arpa.\n" + SOA),
"254.169.in-addr.arpa.": NewRR("$ORIGIN 254.169.in-addr.arpa.\n" + SOA),
"168.192.in-addr.arpa.": NewRR("$ORIGIN 168.192.in-addr.arpa.\n" + SOA),
"16.172.in-addr.arpa.": NewRR("$ORIGIN 16.172.in-addr.arpa.\n" + SOA),
"17.172.in-addr.arpa.": NewRR("$ORIGIN 17.172.in-addr.arpa.\n" + SOA),
"18.172.in-addr.arpa.": NewRR("$ORIGIN 18.172.in-addr.arpa.\n" + SOA),
"19.172.in-addr.arpa.": NewRR("$ORIGIN 19.172.in-addr.arpa.\n" + SOA),
"20.172.in-addr.arpa.": NewRR("$ORIGIN 20.172.in-addr.arpa.\n" + SOA),
"21.172.in-addr.arpa.": NewRR("$ORIGIN 21.172.in-addr.arpa.\n" + SOA),
"22.172.in-addr.arpa.": NewRR("$ORIGIN 22.172.in-addr.arpa.\n" + SOA),
"23.172.in-addr.arpa.": NewRR("$ORIGIN 23.172.in-addr.arpa.\n" + SOA),
"24.172.in-addr.arpa.": NewRR("$ORIGIN 24.172.in-addr.arpa.\n" + SOA),
"25.172.in-addr.arpa.": NewRR("$ORIGIN 25.172.in-addr.arpa.\n" + SOA),
"26.172.in-addr.arpa.": NewRR("$ORIGIN 26.172.in-addr.arpa.\n" + SOA),
"27.172.in-addr.arpa.": NewRR("$ORIGIN 27.172.in-addr.arpa.\n" + SOA),
"28.172.in-addr.arpa.": NewRR("$ORIGIN 28.172.in-addr.arpa.\n" + SOA),
"29.172.in-addr.arpa.": NewRR("$ORIGIN 29.172.in-addr.arpa.\n" + SOA),
"30.172.in-addr.arpa.": NewRR("$ORIGIN 30.172.in-addr.arpa.\n" + SOA),
"31.172.in-addr.arpa.": NewRR("$ORIGIN 31.172.in-addr.arpa.\n" + SOA),
}
func main() {
runtime.GOMAXPROCS(runtime.NumCPU() * 4)
for z, rr := range zones {
rrx := rr.(*dns.SOA) // Needed to create the actual RR, and not an reference.
dns.HandleFunc(z, func(w dns.ResponseWriter, r *dns.Msg) {
m := new(dns.Msg)
m.SetReply(r)
m.Authoritative = true
m.Ns = []dns.RR{rrx}
w.WriteMsg(m)
})
}
go func() {
err := dns.ListenAndServe(":8053", "tcp", nil)
if err != nil {
log.Fatal("Failed to set tcp listener %s\n", err.Error())
}
}()
go func() {
err := dns.ListenAndServe(":8053", "udp", nil)
if err != nil {
log.Fatal("Failed to set udp listener %s\n", err.Error())
}
}()
sig := make(chan os.Signal)
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
for {
select {
case s := <-sig:
log.Fatalf("Signal (%d) received, stopping\n", s)
}
}
}

View File

@ -1,95 +0,0 @@
// 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.
// Chaos is a small program that prints the version.bind and hostname.bind
// for each address of the nameserver given as argument.
package main
import (
"fmt"
"github.com/miekg/dns"
"net"
"os"
)
func main() {
if len(os.Args) != 2 {
fmt.Printf("%s NAMESERVER\n", os.Args[0])
os.Exit(1)
}
conf, _ := dns.ClientConfigFromFile("/etc/resolv.conf")
m := new(dns.Msg)
m.Question = make([]dns.Question, 1)
c := new(dns.Client)
addr := addresses(conf, c, os.Args[1])
if len(addr) == 0 {
fmt.Printf("No address found for %s\n", os.Args[1])
os.Exit(1)
}
for _, a := range addr {
m.Question[0] = dns.Question{"version.bind.", dns.TypeTXT, dns.ClassCHAOS}
in, rtt, _ := c.Exchange(m, a)
if in != nil && len(in.Answer) > 0 {
fmt.Printf("(time %.3d µs) %v\n", rtt/1e3, in.Answer[0])
}
m.Question[0] = dns.Question{"hostname.bind.", dns.TypeTXT, dns.ClassCHAOS}
in, rtt, _ = c.Exchange(m, a)
if in != nil && len(in.Answer) > 0 {
fmt.Printf("(time %.3d µs) %v\n", rtt/1e3, in.Answer[0])
}
}
}
func do(t chan *dns.Msg, c *dns.Client, m *dns.Msg, addr string) {
go func() {
r, _, err := c.Exchange(m, addr)
if err != nil {
//print error stuff
t <- nil
}
t <- r
}()
}
func addresses(conf *dns.ClientConfig, c *dns.Client, name string) (ips []string) {
m4 := new(dns.Msg)
m4.SetQuestion(dns.Fqdn(os.Args[1]), dns.TypeA)
m6 := new(dns.Msg)
m6.SetQuestion(dns.Fqdn(os.Args[1]), dns.TypeAAAA)
t := make(chan *dns.Msg)
defer close(t)
do(t, c, m4, net.JoinHostPort(conf.Servers[0], conf.Port))
do(t, c, m6, net.JoinHostPort(conf.Servers[0], conf.Port))
i := 2 // two outstanding queries
forever:
for {
select {
case d := <-t:
i--
if d == nil {
continue
}
if i == 0 {
break forever
}
if d.Rcode == dns.RcodeSuccess {
for _, a := range d.Answer {
switch a.(type) {
case *dns.A:
ips = append(ips,
net.JoinHostPort(a.(*dns.A).A.String(), "53"))
case *dns.AAAA:
ips = append(ips,
net.JoinHostPort(a.(*dns.AAAA).AAAA.String(), "53"))
}
}
}
}
}
return ips
}

437
ex/q/q.go
View File

@ -1,437 +0,0 @@
// 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.
// 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).
// When using -check a comment is printed:
//
// ;+ Secure signature, miek.nl. RRSIG(SOA) validates (DNSKEY miek.nl./4155/net)
//
// which says the SOA has a valid RRSIG and it validated with the DNSKEY of miek.nl,
// which has key id 4155 and is retrieved from the server. Other values are 'disk'.
package main
import (
"flag"
"fmt"
"github.com/miekg/dns"
"net"
"os"
"strconv"
"strings"
"time"
)
// TODO: serial in ixfr
var (
dnskey *dns.DNSKEY
short *bool
)
func main() {
short = flag.Bool("short", false, "abbreviate long DNSSEC records")
dnssec := flag.Bool("dnssec", false, "request DNSSEC records")
query := flag.Bool("question", false, "show question")
check := flag.Bool("check", false, "check internal DNSSEC consistency")
six := flag.Bool("6", false, "use IPv6 only")
four := flag.Bool("4", false, "use IPv4 only")
anchor := flag.String("anchor", "", "use the DNSKEY in this file for interal DNSSEC consistency")
tsig := flag.String("tsig", "", "request tsig with key: [hmac:]name:key")
port := flag.Int("port", 53, "port number to use")
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")
rd := flag.Bool("rd", true, "set RD flag in query")
fallback := flag.Bool("fallback", false, "fallback to 4096 bytes bufsize and after that TCP")
tcp := flag.Bool("tcp", false, "TCP mode")
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")
//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()
}
qtype := uint16(0)
qclass := uint16(dns.ClassINET)
var qname []string
flag.Parse()
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())
}
if k, ok := r.(*dns.DNSKEY); !ok {
fmt.Fprintf(os.Stderr, "No DNSKEY read from %s\n", *anchor)
} else {
dnskey = k
}
}
var nameserver string
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)
continue Flags
}
// First class, then type, to make ANY queries possible
// And if it looks like type, it is a type
if k, ok := dns.StringToType[strings.ToUpper(flag.Arg(i))]; ok {
qtype = k
continue Flags
}
// If it looks like a class, it is a class
if k, ok := dns.StringToClass[strings.ToUpper(flag.Arg(i))]; ok {
qclass = k
continue Flags
}
// 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
}
}
// Anything else is a qname
qname = append(qname, flag.Arg(i))
}
if len(qname) == 0 {
qname = make([]string, 1)
qname[0] = "."
qtype = dns.TypeNS
}
if qtype == 0 {
qtype = dns.TypeA
}
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 @
// 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
if nameserver[0] == '[' && nameserver[len(nameserver)-1] == ']' {
nameserver = nameserver[1 : len(nameserver)-1]
}
if i := net.ParseIP(nameserver); i != nil {
nameserver = net.JoinHostPort(nameserver, strconv.Itoa(*port))
} else {
nameserver = dns.Fqdn(nameserver) + ":" + strconv.Itoa(*port)
}
c := new(dns.Client)
t := new(dns.Transfer)
c.Net = "udp"
if *four {
c.Net = "udp4"
}
if *six {
c.Net = "udp6"
}
if *tcp {
c.Net = "tcp"
if *four {
c.Net = "tcp4"
}
if *six {
c.Net = "tcp6"
}
}
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)
if *dnssec || *nsid || *client != "" {
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)
e.Code = dns.EDNS0NSID
o.Option = append(o.Option, e)
// NSD will not return nsid when the udp message size is too small
o.SetUDPSize(dns.DefaultMsgSize)
}
if *client != "" {
e := new(dns.EDNS0_SUBNET)
e.Code = dns.EDNS0SUBNET
if *clientdraftcode {
e.DraftOption = true
}
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
}
e.Family = 1 // IP4
e.SourceNetmask = net.IPv4len * 8
if e.Address.To4() == nil {
e.Family = 2 // IP6
e.SourceNetmask = net.IPv6len * 8
}
o.Option = append(o.Option, e)
}
m.Extra = append(m.Extra, o)
}
query:
for _, v := range qname {
m.Question[0] = dns.Question{dns.Fqdn(v), qtype, qclass}
m.Id = dns.Id()
if *tsig != "" {
if algo, name, secret, ok := tsigKeyParse(*tsig); ok {
m.SetTsig(name, algo, 300, time.Now().Unix())
c.TsigSecret = map[string]string{name: secret}
t.TsigSecret = map[string]string{name: secret}
} else {
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())
}
if qtype == dns.TypeAXFR || qtype == dns.TypeIXFR {
env, err := t.In(m, nameserver)
if err != nil {
fmt.Printf(";; %s\n", err.Error())
continue
}
envelope := 0
record := 0
for e := range env {
if e.Error != nil {
fmt.Printf(";; %s\n", e.Error.Error())
continue query
}
for _, r := range e.RR {
fmt.Printf("%s\n", r)
}
record+=len(e.RR)
envelope++
}
fmt.Printf("\n;; xfr size: %d records (envelopes %d)\n", record, envelope)
continue
}
r, rtt, e := c.Exchange(m, nameserver)
Redo:
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)
o := new(dns.OPT)
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
}
}
}
if r.MsgHdr.Truncated && !*fallback {
fmt.Printf(";; Truncated\n")
}
if *check {
sigCheck(r, nameserver, *tcp)
}
if *short {
r = shortMsg(r)
}
fmt.Printf("%v", r)
fmt.Printf("\n;; query time: %.3d µs, server: %s(%s), size: %d bytes\n", rtt/1e3, nameserver, c.Net, r.Len())
}
}
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.", dns.Fqdn(s1[0]), s1[1], true
case 3:
switch s1[0] {
case "hmac-md5":
return "hmac-md5.sig-alg.reg.int.", dns.Fqdn(s1[1]), s1[2], true
case "hmac-sha1":
return "hmac-sha1.", dns.Fqdn(s1[1]), s1[2], true
case "hmac-sha256":
return "hmac-sha256.", dns.Fqdn(s1[1]), s1[2], true
}
}
return
}
func sectionCheck(set []dns.RR, server string, tcp bool) {
var key *dns.DNSKEY
for _, rr := range set {
if rr.Header().Rrtype == dns.TypeRRSIG {
rrset := getRRset(set, rr.Header().Name, rr.(*dns.RRSIG).TypeCovered)
if dnskey == nil {
key = getKey(rr.(*dns.RRSIG).SignerName, rr.(*dns.RRSIG).KeyTag, server, tcp)
} else {
key = dnskey
}
if key == nil {
fmt.Printf(";? DNSKEY %s/%d not found\n", rr.(*dns.RRSIG).SignerName, rr.(*dns.RRSIG).KeyTag)
continue
}
where := "net"
if dnskey != nil {
where = "disk"
}
if err := rr.(*dns.RRSIG).Verify(key, rrset); err != nil {
fmt.Printf(";- Bogus signature, %s does not validate (DNSKEY %s/%d/%s) [%s]\n",
shortSig(rr.(*dns.RRSIG)), key.Header().Name, key.KeyTag(), where, err.Error())
} else {
fmt.Printf(";+ Secure signature, %s validates (DNSKEY %s/%d/%s)\n", shortSig(rr.(*dns.RRSIG)), key.Header().Name, key.KeyTag(), where)
}
}
}
}
// Check the sigs in the msg, get the signer's key (additional query), get the
// rrset from the message, check the signature(s)
func sigCheck(in *dns.Msg, server string, tcp bool) {
sectionCheck(in.Answer, server, tcp)
sectionCheck(in.Ns, server, tcp)
sectionCheck(in.Extra, server, tcp)
}
// Return the RRset belonging to the signature with name and type t
func getRRset(l []dns.RR, name string, t uint16) []dns.RR {
l1 := make([]dns.RR, 0)
for _, rr := range l {
if strings.ToLower(rr.Header().Name) == strings.ToLower(name) && rr.Header().Rrtype == t {
l1 = append(l1, rr)
}
}
return l1
}
// Get the key from the DNS (uses the local resolver) and return them.
// If nothing is found we return nil
func getKey(name string, keytag uint16, server string, tcp bool) *dns.DNSKEY {
c := new(dns.Client)
if tcp {
c.Net = "tcp"
}
m := new(dns.Msg)
m.SetQuestion(name, dns.TypeDNSKEY)
m.SetEdns0(4096, true)
r, _, err := c.Exchange(m, server)
if err != nil {
return nil
}
for _, k := range r.Answer {
if k1, ok := k.(*dns.DNSKEY); ok {
if k1.KeyTag() == keytag {
return k1
}
}
}
return nil
}
// shorten RRSIG to "miek.nl RRSIG(NS)"
func shortSig(sig *dns.RRSIG) string {
return sig.Header().Name + " RRSIG(" + dns.TypeToString[sig.TypeCovered] + ")"
}
// 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) {
case *dns.DS:
t.Digest = "..."
case *dns.DNSKEY:
t.PublicKey = "..."
case *dns.RRSIG:
t.Signature = "..."
case *dns.NSEC3:
t.Salt = "." // Nobody cares
if len(t.TypeBitMap) > 5 {
t.TypeBitMap = t.TypeBitMap[1:5]
}
}
return r
}
func doXfr(c *dns.Client, m *dns.Msg, nameserver string) {
/*
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)
}
} else {
fmt.Fprintf(os.Stderr, "Failure to read XFR: %s\n", r.Error.Error())
}
}
} else {
fmt.Fprintf(os.Stderr, "Failure to read XFR: %s\n", e.Error())
}
*/
}

View File

@ -1,201 +0,0 @@
// 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.
// Reflect is a small name server which sends back the IP address of its client, the
// recursive resolver.
// When queried for type A (resp. AAAA), it sends back the IPv4 (resp. v6) address.
// In the additional section the port number and transport are shown.
//
// Basic use pattern:
//
// dig @localhost -p 8053 whoami.miek.nl A
//
// ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 2157
// ;; flags: qr rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
// ;; QUESTION SECTION:
// ;whoami.miek.nl. IN A
//
// ;; ANSWER SECTION:
// whoami.miek.nl. 0 IN A 127.0.0.1
//
// ;; ADDITIONAL SECTION:
// whoami.miek.nl. 0 IN TXT "Port: 56195 (udp)"
//
// Similar services: whoami.ultradns.net, whoami.akamai.net. Also (but it
// is not their normal goal): rs.dns-oarc.net, porttest.dns-oarc.net,
// amiopen.openresolvers.org.
//
// Original version is from: Stephane Bortzmeyer <stephane+grong@bortzmeyer.org>.
//
// Adapted to Go (i.e. completely rewritten) by Miek Gieben <miek@miek.nl>.
package main
import (
"flag"
"fmt"
"github.com/miekg/dns"
"log"
"net"
"os"
"os/signal"
"runtime"
"runtime/pprof"
"strconv"
"strings"
"syscall"
"time"
)
var (
printf *bool
compress *bool
tsig *string
)
const dom = "whoami.miek.nl."
func handleReflect(w dns.ResponseWriter, r *dns.Msg) {
var (
v4 bool
rr dns.RR
str string
a net.IP
)
// TC must be done here
m := new(dns.Msg)
m.SetReply(r)
m.Compress = *compress
if ip, ok := w.RemoteAddr().(*net.UDPAddr); ok {
str = "Port: " + strconv.Itoa(ip.Port) + " (udp)"
a = ip.IP
v4 = a.To4() != nil
}
if ip, ok := w.RemoteAddr().(*net.TCPAddr); ok {
str = "Port: " + strconv.Itoa(ip.Port) + " (tcp)"
a = ip.IP
v4 = a.To4() != nil
}
/*
if o := r.IsEdns0(); o != nil {
for _, s := range o.Option {
switch e := s.(type) {
case *dns.EDNS0_SUBNET:
log.Printf("Edns0 subnet %s", e.Address)
}
}
}
*/
if v4 {
rr = new(dns.A)
rr.(*dns.A).Hdr = dns.RR_Header{Name: dom, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 0}
rr.(*dns.A).A = a.To4()
} else {
rr = new(dns.AAAA)
rr.(*dns.AAAA).Hdr = dns.RR_Header{Name: dom, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: 0}
rr.(*dns.AAAA).AAAA = a
}
t := new(dns.TXT)
t.Hdr = dns.RR_Header{Name: dom, Rrtype: dns.TypeTXT, Class: dns.ClassINET, Ttl: 0}
t.Txt = []string{str}
switch r.Question[0].Qtype {
case dns.TypeTXT:
m.Answer = append(m.Answer, t)
m.Extra = append(m.Extra, rr)
default:
fallthrough
case dns.TypeAAAA, dns.TypeA:
m.Answer = append(m.Answer, rr)
m.Extra = append(m.Extra, t)
case dns.TypeAXFR, dns.TypeIXFR:
c := make(chan *dns.Envelope)
tr := new(dns.Transfer)
defer close(c)
err := tr.Out(w, r, c)
if err != nil {
return
}
soa, _ := dns.NewRR(`whoami.miek.nl. 0 IN SOA linode.atoom.net. miek.miek.nl. 2009032802 21600 7200 604800 3600`)
c <- &dns.Envelope{RR: []dns.RR{soa, t, rr, soa}}
w.Hijack()
// w.Close() // Client closes connection
return
}
if r.IsTsig() != nil {
if w.TsigStatus() == nil {
m.SetTsig(r.Extra[len(r.Extra)-1].(*dns.TSIG).Hdr.Name, dns.HmacMD5, 300, time.Now().Unix())
} else {
println("Status", w.TsigStatus().Error())
}
}
if *printf {
fmt.Printf("%v\n", m.String())
}
w.WriteMsg(m)
}
func serve(net, name, secret string) {
switch name {
case "":
err := dns.ListenAndServe(":8053", net, nil)
if err != nil {
fmt.Printf("Failed to setup the "+net+" server: %s\n", err.Error())
}
default:
server := &dns.Server{Addr: ":8053", Net: net, TsigSecret: map[string]string{name: secret}}
err := server.ListenAndServe()
if err != nil {
fmt.Printf("Failed to setup the "+net+" server: %s\n", err.Error())
}
}
}
func main() {
runtime.GOMAXPROCS(runtime.NumCPU() * 4)
cpuprofile := flag.String("cpuprofile", "", "write cpu profile to file")
printf = flag.Bool("print", false, "print replies")
compress = flag.Bool("compress", false, "compress replies")
tsig = flag.String("tsig", "", "use MD5 hmac tsig: keyname:base64")
var name, secret string
flag.Usage = func() {
flag.PrintDefaults()
}
flag.Parse()
if *tsig != "" {
a := strings.SplitN(*tsig, ":", 2)
name, secret = dns.Fqdn(a[0]), a[1] // fqdn the name, which everybody forgets...
}
if *cpuprofile != "" {
f, err := os.Create(*cpuprofile)
if err != nil {
log.Fatal(err)
}
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
}
dns.HandleFunc("miek.nl.", handleReflect)
dns.HandleFunc("authors.bind.", dns.HandleAuthors)
dns.HandleFunc("authors.server.", dns.HandleAuthors)
dns.HandleFunc("version.bind.", dns.HandleVersion)
dns.HandleFunc("version.server.", dns.HandleVersion)
go serve("tcp", name, secret)
go serve("udp", name, secret)
sig := make(chan os.Signal)
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
forever:
for {
select {
case s := <-sig:
fmt.Printf("Signal (%d) received, stopping\n", s)
break forever
}
}
}