massrdns

- dynamic reverse dns lookup with rotating servers
git clone git://git.acid.vegas/massrdns.git
Log | Files | Refs | Archive | README | LICENSE

massrdns.go (6223B)

      1 package main
      2 
      3 import (
      4 	"bufio"
      5 	"context"
      6 	"flag"
      7 	"fmt"
      8 	"math/rand"
      9 	"net"
     10 	"os"
     11 	"strconv"
     12 	"strings"
     13 	"sync"
     14 	"time"
     15 )
     16 
     17 var dnsServers []string
     18 var failureCounts = make(map[string]int)
     19 var showErrors bool
     20 var inProgressIPs sync.Map
     21 
     22 func loadDNSServersFromFile(filePath string) ([]string, error) {
     23 	var servers []string
     24 
     25 	file, err := os.Open(filePath)
     26 	if err != nil {
     27 		return nil, err
     28 	}
     29 	defer file.Close()
     30 
     31 	scanner := bufio.NewScanner(file)
     32 	for scanner.Scan() {
     33 		server := scanner.Text()
     34 
     35 		if strings.Contains(server, ":") {
     36 			host, port, err := net.SplitHostPort(server)
     37 			if err != nil {
     38 				return nil, fmt.Errorf("invalid IP:port format for %s", server)
     39 			}
     40 			if net.ParseIP(host) == nil {
     41 				return nil, fmt.Errorf("invalid IP address in %s", server)
     42 			}
     43 			if _, err := strconv.Atoi(port); err != nil {
     44 				return nil, fmt.Errorf("invalid port in %s", server)
     45 			}
     46 		} else {
     47 			if net.ParseIP(server) == nil {
     48 				return nil, fmt.Errorf("invalid IP address %s", server)
     49 			}
     50 			server += ":53"
     51 		}
     52 
     53 		servers = append(servers, server)
     54 	}
     55 	return servers, scanner.Err()
     56 }
     57 
     58 func reverseDNSLookup(ip string, server string) (string, error) {
     59 	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
     60 	defer cancel()
     61 
     62 	resolver := &net.Resolver{
     63 		PreferGo: true,
     64 		Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
     65 			d := net.Dialer{}
     66 			return d.DialContext(ctx, "udp", server)
     67 		},
     68 	}
     69 
     70 	names, err := resolver.LookupAddr(ctx, ip)
     71 	if err != nil {
     72 		if isNetworkError(err) {
     73 			return "", err
     74 		}
     75 		return "", err
     76 	}
     77 
     78 	if len(names) == 0 {
     79 		return fmt.Sprintf("%s | %-18s | No PTR records", time.Now().Format("03:04:05 PM"), ip), nil
     80 	}
     81 	return fmt.Sprintf("%s | %-18s | %s", time.Now().Format("03:04:05 PM"), ip, names[0]), nil
     82 }
     83 
     84 func isNetworkError(err error) bool {
     85 	errorString := err.Error()
     86 	return strings.Contains(errorString, "timeout") || strings.Contains(errorString, "connection refused")
     87 }
     88 
     89 func pickRandomServer(servers []string, triedServers map[string]bool) string {
     90 	for _, i := range rand.Perm(len(servers)) {
     91 		if !triedServers[servers[i]] {
     92 			return servers[i]
     93 		}
     94 	}
     95 	return ""
     96 }
     97 
     98 func removeFromList(servers []string, server string) []string {
     99 	var newList []string
    100 	for _, s := range servers {
    101 		if s != server {
    102 			newList = append(newList, s)
    103 		}
    104 	}
    105 	return newList
    106 }
    107 
    108 func splitCIDR(cidr string, parts int) ([]*net.IPNet, error) {
    109 	ip, ipNet, err := net.ParseCIDR(cidr)
    110 	if err != nil {
    111 		return nil, err
    112 	}
    113 	startIP := make(net.IP, len(ip))
    114 	copy(startIP, ip)
    115 
    116 	maskSize, _ := ipNet.Mask.Size()
    117 
    118 	maxParts := 1 << uint(32-maskSize)
    119 	if parts > maxParts {
    120 		parts = maxParts
    121 	}
    122 
    123 	newMaskSize := maskSize
    124 	for ; (1 << uint(newMaskSize-maskSize)) < parts; newMaskSize++ {
    125 		if newMaskSize > 32 {
    126 			return nil, fmt.Errorf("too many parts; cannot split further")
    127 		}
    128 	}
    129 
    130 	var subnets []*net.IPNet
    131 	for i := 0; i < parts; i++ {
    132 		subnets = append(subnets, &net.IPNet{
    133 			IP:   make(net.IP, len(startIP)),
    134 			Mask: net.CIDRMask(newMaskSize, 32),
    135 		})
    136 		copy(subnets[i].IP, startIP)
    137 		incrementIPBy(startIP, 1<<uint(32-newMaskSize))
    138 	}
    139 
    140 	return subnets, nil
    141 }
    142 
    143 func worker(cidr *net.IPNet, resultsChan chan string) {
    144 	for ip := make(net.IP, len(cidr.IP)); copy(ip, cidr.IP) != 0; incrementIP(ip) {
    145 		if !cidr.Contains(ip) {
    146 			break
    147 		}
    148 
    149 		_, alreadyProcessing := inProgressIPs.LoadOrStore(ip.String(), true)
    150 		if alreadyProcessing {
    151 			continue
    152 		}
    153 		defer inProgressIPs.Delete(ip.String())
    154 
    155 		triedServers := make(map[string]bool)
    156 		retries := 10
    157 		success := false
    158 
    159 		for retries > 0 {
    160 			randomServer := pickRandomServer(dnsServers, triedServers)
    161 			if randomServer == "" {
    162 				break
    163 			}
    164 
    165 			result, err := reverseDNSLookup(ip.String(), randomServer)
    166 
    167 			if err != nil {
    168 				if showErrors {
    169 					resultsChan <- fmt.Sprintf("%s | %-18s | Error: %s", time.Now().Format("03:04:05 PM"), randomServer, err)
    170 				}
    171 
    172 				if isNetworkError(err) {
    173 					failureCounts[randomServer]++
    174 					if failureCounts[randomServer] > 10 {
    175 						dnsServers = removeFromList(dnsServers, randomServer)
    176 						delete(failureCounts, randomServer)
    177 					}
    178 				}
    179 
    180 				triedServers[randomServer] = true
    181 				retries--
    182 				continue
    183 			} else {
    184 				resultsChan <- result
    185 				success = true
    186 				break
    187 			}
    188 		}
    189 
    190 		if !success && showErrors {
    191 			resultsChan <- fmt.Sprintf("%s | %-18s | Max retries reached", time.Now().Format("03:04:05 PM"), ip)
    192 		}
    193 	}
    194 }
    195 
    196 func main() {
    197 	var cidr string
    198 	var concurrency int
    199 	var dnsFile string
    200 
    201 	flag.StringVar(&cidr, "cidr", "", "IP address CIDR to perform reverse DNS lookup")
    202 	flag.IntVar(&concurrency, "concurrency", 10, "Number of concurrent workers for reverse DNS lookup")
    203 	flag.StringVar(&dnsFile, "dnsfile", "", "Path to the file containing DNS servers (one per line)")
    204 	flag.BoolVar(&showErrors, "errors", false, "Display errors in the output") // New flag
    205 	flag.Parse()
    206 
    207 	if cidr == "" || dnsFile == "" {
    208 		fmt.Println("Please provide a CIDR using the -cidr flag and a DNS servers file with the -dnsfile flag.")
    209 		os.Exit(1)
    210 	}
    211 
    212 	var err error
    213 	dnsServers, err = loadDNSServersFromFile(dnsFile)
    214 	if err != nil {
    215 		fmt.Printf("Error reading DNS servers from file %s: %s\n", dnsFile, err)
    216 		os.Exit(1)
    217 	}
    218 
    219 	if len(dnsServers) == 0 {
    220 		fmt.Println("No DNS servers found in the provided file.")
    221 		os.Exit(1)
    222 	}
    223 
    224 	rand.Seed(time.Now().UnixNano())
    225 
    226 	subnets, err := splitCIDR(cidr, concurrency*10)
    227 	if err != nil {
    228 		fmt.Printf("Error splitting CIDR: %s\n", err)
    229 		os.Exit(1)
    230 	}
    231 
    232 	if len(subnets) < concurrency {
    233 		concurrency = len(subnets)
    234 	}
    235 
    236 	cidrChan := make(chan *net.IPNet, len(subnets))
    237 	for _, subnet := range subnets {
    238 		cidrChan <- subnet
    239 	}
    240 	close(cidrChan)
    241 
    242 	resultsChan := make(chan string, concurrency*2)
    243 
    244 	var wg sync.WaitGroup
    245 	for i := 0; i < concurrency; i++ {
    246 		wg.Add(1)
    247 		go func() {
    248 			defer wg.Done()
    249 			for subnet := range cidrChan {
    250 				worker(subnet, resultsChan)
    251 			}
    252 		}()
    253 	}
    254 
    255 	go func() {
    256 		wg.Wait()
    257 		close(resultsChan)
    258 	}()
    259 
    260 	for result := range resultsChan {
    261 		fmt.Println(result)
    262 	}
    263 }
    264 
    265 func incrementIP(ip net.IP) {
    266 	for j := len(ip) - 1; j >= 0; j-- {
    267 		ip[j]++
    268 		if ip[j] > 0 {
    269 			break
    270 		}
    271 	}
    272 }
    273 
    274 func incrementIPBy(ip net.IP, count int) {
    275 	for count > 0 {
    276 		incrementIP(ip)
    277 		count--
    278 	}
    279 }