package dnssecsocket // inspired from github.com/rs/dnstrace/main.go import "fmt" import "net" // import "os" import "strings" import "time" import "github.com/miekg/dns" import "github.com/rs/dnstrace/client" // import log "github.com/sirupsen/logrus" // this is cool, but breaks the Windows build // import "github.com/wercker/journalhook" // import "github.com/davecgh/go-spew/spew" 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") color := true 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(1) 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 { log(args.VerboseDnssec) } log(args.VerboseDnssec, "%d - query %s %s", i, qtype, qname) if r != nil { log(args.VerboseDnssec, ": %s", strings.Replace(strings.Replace(r.MsgHdr.String(), ";; ", "", -1), "\n", ", ", -1)) } log(args.VerboseDnssec) 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)) } log(args.VerboseDnssec, 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 } log(args.VerboseDnssec, ": %v", col(err, cRed)) } log(args.VerboseDnssec, "\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) } log(args.VerboseDnssec, "%s %d NS %s (%s)\n", label, s.TTL, s.Name, glue) } case client.ResponseTypeCNAME: for _, rr := range r.Answer { log(args.VerboseDnssec, rr) } } }, FollowingCNAME: func(domain, target string) { log(args.VerboseDnssec, col("\n~ following CNAME %s -> %s\n", cBlue), domain, target) }, } r, rtt, err := c.RecursiveQuery(m, t) if err != nil { log(args.VerboseDnssec, col("*** error: %v\n", cRed), err) return nil } log(args.VerboseDnssec) log(args.VerboseDnssec, col(";; Cold best path time: %s\n\n", cGray), rtt) for i, rr := range r.Answer { log(args.VerboseDnssec, "r.Answer =", i, rr, args.VerboseDnssec) } return r.Answer // for _, rr := range r.Answer { // return rr // } // return nil } func ResolveIPv6hostname(hostname string) *net.TCPAddr { dnsRR := Dnstrace(hostname, "AAAA") if (dnsRR == nil) { return nil } aaaa := dns.Field(dnsRR[1], 1) localTCPAddr, _ := net.ResolveTCPAddr("tcp", aaaa) return localTCPAddr } func UseJournalctl() { log(args.VerboseDnssec, "journalhook is disabled because it breaks the Windows build right now") // journalhook.Enable() }