dns/ex/reflect/reflect.go

178 lines
4.2 KiB
Go

/*
* A name server which sends back the IP address of its client, the
* recursive resolver. When queried for type TXT, it sends back the text
* form of the address. When queried for type A (resp. AAAA), it sends
* back the IPv4 (resp. v6) address.
*
* 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 from:
* Stephane Bortzmeyer <stephane+grong@bortzmeyer.org>
*
* Adapted to Go DNS (i.e. completely rewritten)
* Miek Gieben <miek@miek.nl>
*/
package main
import (
"flag"
"fmt"
"github.com/miekg/dns"
"log"
"net"
"os"
"os/signal"
"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 v4 {
rr = new(dns.RR_A)
rr.(*dns.RR_A).Hdr = dns.RR_Header{Name: dom, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 0}
rr.(*dns.RR_A).A = a.To4()
} else {
rr = new(dns.RR_AAAA)
rr.(*dns.RR_AAAA).Hdr = dns.RR_Header{Name: dom, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: 0}
rr.(*dns.RR_AAAA).AAAA = a
}
t := new(dns.RR_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.TypeAXFR:
c := make(chan *dns.XfrToken)
var e *error
if err := dns.XfrSend(w, r, c, e); err != nil {
close(c)
return
}
soa, _ := dns.NewRR(`whoami.miek.nl. IN SOA elektron.atoom.net. miekg.atoom.net. (
2009032802
21600
7200
604800
3600)`)
c <- &dns.XfrToken{RR: []dns.RR{soa, t, rr, soa}}
close(c)
w.Hijack()
// w.Close() // Client closes
return
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)
}
if r.IsTsig() != nil {
if w.TsigStatus() == nil {
m.SetTsig(r.Extra[len(r.Extra)-1].(*dns.RR_TSIG).Hdr.Name, dns.HmacMD5, 300, time.Now().Unix())
} else {
println("Status", w.TsigStatus().Error())
}
}
if *printf {
fmt.Printf("%v\n", m.String())
}
w.Write(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() {
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 = a[0], a[1]
}
if *cpuprofile != "" {
f, err := os.Create(*cpuprofile)
if err != nil {
log.Fatal(err)
}
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
}
dns.HandleFunc(".", 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
}
}
}