From a9a254e62d3da609d925f825abd4f9bbb8e16637 Mon Sep 17 00:00:00 2001 From: Jeff Carr Date: Sat, 4 May 2019 14:25:23 -0700 Subject: [PATCH] Add a DNS lookup of the servername. Only listen on that IPv6 address. Signed-off-by: Jeff Carr --- server/Makefile | 5 +- server/dnstrace.go | 146 +++++++++++++++++++++++++++++++++++++++++++++ server/server.go | 25 +++++++- 3 files changed, 172 insertions(+), 4 deletions(-) create mode 100644 server/dnstrace.go diff --git a/server/Makefile b/server/Makefile index 196fd8f..4ee4658 100644 --- a/server/Makefile +++ b/server/Makefile @@ -2,7 +2,10 @@ DNSNAME = $(shell hostname -f) # check if DNSNAME is a FQDN -all: +run: + go run *.go + +gaper: # 'gaper' is a simple and smart golang tool that just rebuilds every time you change a file # go get -u github.com/maxcnunes/gaper gaper diff --git a/server/dnstrace.go b/server/dnstrace.go new file mode 100644 index 0000000..d4a42c5 --- /dev/null +++ b/server/dnstrace.go @@ -0,0 +1,146 @@ +// inspired from github.com/rs/dnstrace/main.go + +package main + +import "flag" +import "fmt" +import "log" +import "net" +import "os" +import "strings" +import "time" + +import "github.com/miekg/dns" +import "github.com/rs/dnstrace/client" + +const ( + cReset = 0 + cBold = 1 + cRed = 31 + cGreen = 32 + cYellow = 33 + cBlue = 34 + cMagenta = 35 + cCyan = 36 + cGray = 37 + cDarkGray = 90 +) + +func colorize(s interface{}, color int, enabled bool) string { + if !enabled { + return fmt.Sprintf("%v", s) + } + return fmt.Sprintf("\x1b[%dm%v\x1b[0m", color, s) +} + +func dnstrace(hostname string, qtypestr string) dns.RR { + color := flag.Bool("color", true, "Enable/disable colors") + + qname := dns.Fqdn(hostname) + // qtype := dns.TypeA + qtype := dns.StringToType[qtypestr] + + col := func(s interface{}, c int) string { + return colorize(s, c, *color) + } + + m := &dns.Msg{} + m.SetQuestion(qname, qtype) + // Set DNSSEC opt to better emulate the default queries from a nameserver. + o := &dns.OPT{ + Hdr: dns.RR_Header{ + Name: ".", + Rrtype: dns.TypeOPT, + }, + } + o.SetDo() + o.SetUDPSize(dns.DefaultMsgSize) + m.Extra = append(m.Extra, o) + + c := client.New() + c.Client.Timeout = 500 * time.Millisecond + t := client.Tracer{ + GotIntermediaryResponse: func(i int, m *dns.Msg, rs client.Responses, rtype client.ResponseType) { + fr := rs.Fastest() + var r *dns.Msg + if fr != nil { + r = fr.Msg + } + qname := m.Question[0].Name + qtype := dns.TypeToString[m.Question[0].Qtype] + if i > 1 { + fmt.Println() + } + fmt.Printf("%d - query %s %s", i, qtype, qname) + if r != nil { + fmt.Printf(": %s", strings.Replace(strings.Replace(r.MsgHdr.String(), ";; ", "", -1), "\n", ", ", -1)) + } + fmt.Println() + for _, pr := range rs { + ln := 0 + if pr.Msg != nil { + ln = pr.Msg.Len() + } + rtt := float64(pr.RTT) / float64(time.Millisecond) + lrtt := "0ms (from cache)" + if pr.Server.HasGlue { + lrtt = "0ms (from glue)" + } else if pr.Server.LookupRTT > 0 { + lrtt = fmt.Sprintf("%.2fms", float64(pr.Server.LookupRTT)/float64(time.Millisecond)) + } + fmt.Printf(col(" - %d bytes in %.2fms + %s lookup on %s(%s)", cDarkGray), ln, rtt, lrtt, pr.Server.Name, pr.Addr) + if pr.Err != nil { + err := pr.Err + if oerr, ok := err.(*net.OpError); ok { + err = oerr.Err + } + fmt.Printf(": %v", col(err, cRed)) + } + fmt.Print("\n") + } + + switch rtype { + case client.ResponseTypeDelegation: + var label string + for _, rr := range r.Ns { + if ns, ok := rr.(*dns.NS); ok { + label = ns.Header().Name + break + } + } + _, ns := c.DCache.Get(label) + for _, s := range ns { + var glue string + if s.HasGlue { + glue = col("glue: "+strings.Join(s.Addrs, ","), cDarkGray) + } else { + glue = col("no glue", cYellow) + } + fmt.Printf("%s %d NS %s (%s)\n", label, s.TTL, s.Name, glue) + } + case client.ResponseTypeCNAME: + for _, rr := range r.Answer { + fmt.Println(rr) + } + } + }, + FollowingCNAME: func(domain, target string) { + fmt.Printf(col("\n~ following CNAME %s -> %s\n", cBlue), domain, target) + }, + } + r, rtt, err := c.RecursiveQuery(m, t) + if err != nil { + fmt.Printf(col("*** error: %v\n", cRed), err) + os.Exit(1) + } + + fmt.Println() + fmt.Printf(col(";; Cold best path time: %s\n\n", cGray), rtt) + for _, rr := range r.Answer { + log.Println(rr) + } + for _, rr := range r.Answer { + return rr + } + return nil +} diff --git a/server/server.go b/server/server.go index 2ad4a59..8dbfc3c 100644 --- a/server/server.go +++ b/server/server.go @@ -15,6 +15,13 @@ import "strings" import "time" import "log" +// will try to get this hosts FQDN +import "github.com/Showmax/go-fqdn" + +import "github.com/miekg/dns" + +import "github.com/davecgh/go-spew/spew" + const MIN = 1 const MAX = 100 @@ -25,8 +32,20 @@ const ( ) func main() { - // // Listen for incoming connections. - l, err := net.Listen(CONN_TYPE, CONN_HOST+":"+CONN_PORT) + hostname := fqdn.Get() + log.Println("FQDN hostname is", hostname) + + // lookup the IP address from DNS + dnsRR := dnstrace(hostname, "AAAA") + spew.Dump(dnsRR) + ipaddr := dns.Field(dnsRR, 1) + log.Println("ipaddr", ipaddr) + + listenstr := "[" + ipaddr + "]:" + CONN_PORT + log.Println("listenstr", listenstr) + + // // Listen for incoming connections on the IPv6 address only + l, err := net.Listen(CONN_TYPE, listenstr) if err != nil { log.Println("Error listening:", err.Error()) return @@ -35,7 +54,7 @@ func main() { // Close the listener when the application closes. defer l.Close() - log.Println("Listening on " + CONN_HOST + ":" + CONN_PORT) + log.Println("Listening on " + listenstr) rand.Seed(time.Now().Unix())