// 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 . // // Adapted to Go (i.e. completely rewritten) by Miek Gieben . 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 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.TypeAXFR: c := make(chan *dns.Envelope) var e *error if err := dns.TransferOut(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.Envelope{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.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() { 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 } } }