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 }