2019-05-05 02:00:39 -05:00
|
|
|
package dnssecsocket
|
2019-05-04 16:25:23 -05:00
|
|
|
|
2019-05-05 02:00:39 -05:00
|
|
|
// inspired from github.com/rs/dnstrace/main.go
|
2019-05-04 16:25:23 -05:00
|
|
|
|
|
|
|
import "fmt"
|
|
|
|
import "log"
|
|
|
|
import "net"
|
|
|
|
import "os"
|
|
|
|
import "strings"
|
|
|
|
import "time"
|
|
|
|
|
|
|
|
import "github.com/miekg/dns"
|
|
|
|
import "github.com/rs/dnstrace/client"
|
|
|
|
|
2019-05-04 17:07:21 -05:00
|
|
|
// import "github.com/davecgh/go-spew/spew"
|
|
|
|
|
2019-05-04 16:25:23 -05:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2019-05-05 02:00:39 -05:00
|
|
|
func Dnstrace(hostname string, qtypestr string) dns.RR {
|
2019-05-04 17:07:21 -05:00
|
|
|
// color := flag.Bool("color", true, "Enable/disable colors")
|
|
|
|
color := true
|
2019-05-04 16:25:23 -05:00
|
|
|
|
|
|
|
qname := dns.Fqdn(hostname)
|
|
|
|
// qtype := dns.TypeA
|
|
|
|
qtype := dns.StringToType[qtypestr]
|
|
|
|
|
|
|
|
col := func(s interface{}, c int) string {
|
2019-05-04 17:07:21 -05:00
|
|
|
return colorize(s, c, color)
|
2019-05-04 16:25:23 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
2019-05-09 19:57:51 -05:00
|
|
|
|
|
|
|
func ResolveIPv6hostname(hostname string) *net.TCPAddr {
|
|
|
|
dnsRR := Dnstrace(hostname, "AAAA")
|
|
|
|
aaaa := dns.Field(dnsRR, 1)
|
|
|
|
localTCPAddr, _ := net.ResolveTCPAddr("tcp", aaaa)
|
|
|
|
return localTCPAddr
|
|
|
|
}
|