patdown/common/dns.go

175 lines
3.9 KiB
Go

package common
import (
"fmt"
"time"
"github.com/miekg/dns"
)
type Pair struct {
Nameserver string
Domain string
}
var (
Nameservers, Valid, Recursive []string
Found bool
)
func message(domain string, reqtype uint16, ra bool) *dns.Msg {
msg := new(dns.Msg)
msg.Id = dns.Id()
msg.RecursionDesired = ra
msg.Question = make([]dns.Question, 1)
msg.Question[0] = dns.Question{dns.Fqdn(domain), reqtype, dns.ClassINET}
return msg
}
func ParseNS(nservers []string) ([]string, []string) {
var valid, recursive []string
msg := message("supernets.org", dns.TypeA, false)
for _, ns := range nservers {
in, err := dns.Exchange(msg, ns+":53")
if err != nil {
Error("nameserver " + ns + " is not responding")
continue
}
if in.Rcode == dns.RcodeRefused {
Warning("nameserver " + ns + " refused the test query, non-recursive snooping may not be viable")
}
if in.RecursionAvailable {
Success("nameserver " + ns + " is recursive")
recursive = append(recursive, ns)
}
valid = append(valid, ns)
}
return valid, recursive
}
func TestReq() bool {
msg := message("cloudflare.com", dns.TypeA, false)
in, err := dns.Exchange(msg, "1.1.1.1:53")
if err != nil {
return false
}
if len(in.Answer) > 0 {
return true
}
return false
}
func PullNS(d string) {
nsmsg := message(d, dns.TypeNS, true)
in, err := dns.Exchange(nsmsg, "1.1.1.1:53")
if err != nil {
Fatal("unable to retrieve nameservers for " + d)
}
for _, ans := range in.Answer {
ns, ok := ans.(*dns.NS)
if ok {
Nameservers = append(Nameservers, ns.Ns)
}
}
}
func Verify() {
if !TestReq() {
Error("neutral non-recursive query was refused, are you on a vpn or dirty box?")
}
Success("neutral non-recursive test query succeeded")
Valid, Recursive = ParseNS(Nameservers)
Info(fmt.Sprintf("%d/%d nameservers are recursive", len(Recursive), len(Valid)))
if len(Valid) == 0 {
Fatal("no valid nameservers available")
}
}
func Query(q <-chan Pair, tracker chan<- interface{}, delay int) {
for pair := range q {
msg := message(pair.Domain, dns.TypeA, false)
in, err := dns.Exchange(msg, pair.Nameserver+":53")
if err != nil {
Error(err.Error())
continue
}
if len(in.Answer) > 0 {
Found = true
fmt.Printf("[%s] associated domain %s found on %s\n", Vendors[Domains[pair.Domain].Vendor], pair.Domain, pair.Nameserver)
}
time.Sleep(time.Duration(delay) * time.Millisecond)
}
tracker <- 1337
}
func QueryRA(q <-chan Pair, tracker chan<- interface{}, delay int) {
for pair := range q {
msg := message(pair.Domain, dns.TypeA, true)
for x := 0; x < 3; x++ {
in, err := dns.Exchange(msg, pair.Nameserver+":53")
if err != nil {
Error("hiccup on " + pair.Nameserver + " retrying...")
time.Sleep(1 * time.Second)
continue
}
if len(in.Answer) > 0 {
Found = true
if in.Answer[0].Header().Ttl != Domains[pair.Domain].TTL {
fmt.Printf("[%s] associated domain %s found on %s with mismatched TTL of %d\n", Vendors[Domains[pair.Domain].Vendor], pair.Domain, pair.Nameserver, in.Answer[0].Header().Ttl)
}
break
}
}
time.Sleep(time.Duration(delay) * time.Millisecond)
}
tracker <- 1337
}
func Run(ra bool, threads, delay int) {
pairs := make(chan Pair)
tracker := make(chan interface{})
if !ra {
// non-recursive snoop
Info(fmt.Sprintf("non-recursive snooping on %d resolvers...\n", len(Valid)))
go func() {
for i := 0; i < threads; i++ {
Query(pairs, tracker, delay)
}
}()
for _, ns := range Valid {
for k, _ := range Domains {
pairs <- Pair{Nameserver: ns, Domain: k}
}
}
close(pairs)
} else {
Info(fmt.Sprintf("recursively snooping on %d resolvers...\n", len(Recursive)))
go func() {
for i := 0; i < threads; i++ {
QueryRA(pairs, tracker, delay)
}
}()
for _, ns := range Recursive {
for k, _ := range Domains {
pairs <- Pair{Nameserver: ns, Domain: k}
}
}
close(pairs)
}
for x := 0; x < threads; x++ {
<-tracker
}
}