2023-09-21 05:10:10 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"context"
|
|
|
|
"flag"
|
|
|
|
"fmt"
|
|
|
|
"math/rand"
|
|
|
|
"net"
|
|
|
|
"os"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
var dnsServers []string
|
2023-09-21 16:58:03 +00:00
|
|
|
var failureCounts = make(map[string]int)
|
2023-09-21 19:16:54 +00:00
|
|
|
var showErrors bool
|
2023-09-21 16:58:03 +00:00
|
|
|
|
|
|
|
func loadDNSServersFromFile(filePath string) ([]string, error) {
|
|
|
|
var servers []string
|
2023-09-21 05:10:10 +00:00
|
|
|
|
|
|
|
file, err := os.Open(filePath)
|
|
|
|
if err != nil {
|
2023-09-21 16:58:03 +00:00
|
|
|
return nil, err
|
2023-09-21 05:10:10 +00:00
|
|
|
}
|
|
|
|
defer file.Close()
|
|
|
|
|
|
|
|
scanner := bufio.NewScanner(file)
|
|
|
|
for scanner.Scan() {
|
|
|
|
server := scanner.Text()
|
|
|
|
|
|
|
|
if strings.Contains(server, ":") {
|
|
|
|
host, port, err := net.SplitHostPort(server)
|
|
|
|
if err != nil {
|
2023-09-21 16:58:03 +00:00
|
|
|
return nil, fmt.Errorf("invalid IP:port format for %s", server)
|
2023-09-21 05:10:10 +00:00
|
|
|
}
|
|
|
|
if net.ParseIP(host) == nil {
|
2023-09-21 16:58:03 +00:00
|
|
|
return nil, fmt.Errorf("invalid IP address in %s", server)
|
2023-09-21 05:10:10 +00:00
|
|
|
}
|
|
|
|
if _, err := strconv.Atoi(port); err != nil {
|
2023-09-21 16:58:03 +00:00
|
|
|
return nil, fmt.Errorf("invalid port in %s", server)
|
2023-09-21 05:10:10 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if net.ParseIP(server) == nil {
|
2023-09-21 16:58:03 +00:00
|
|
|
return nil, fmt.Errorf("invalid IP address %s", server)
|
2023-09-21 05:10:10 +00:00
|
|
|
}
|
2023-09-21 16:58:03 +00:00
|
|
|
server += ":53"
|
2023-09-21 05:10:10 +00:00
|
|
|
}
|
|
|
|
|
2023-09-21 16:58:03 +00:00
|
|
|
servers = append(servers, server)
|
2023-09-21 05:10:10 +00:00
|
|
|
}
|
2023-09-21 16:58:03 +00:00
|
|
|
return servers, scanner.Err()
|
2023-09-21 05:10:10 +00:00
|
|
|
}
|
|
|
|
|
2023-09-21 16:58:03 +00:00
|
|
|
func reverseDNSLookup(ip string, server string) (string, error) {
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
|
|
defer cancel()
|
2023-09-21 05:10:10 +00:00
|
|
|
|
|
|
|
resolver := &net.Resolver{
|
|
|
|
PreferGo: true,
|
|
|
|
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
|
|
|
|
d := net.Dialer{}
|
2023-09-21 19:16:54 +00:00
|
|
|
return d.DialContext(ctx, "udp", server)
|
2023-09-21 05:10:10 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
names, err := resolver.LookupAddr(ctx, ip)
|
|
|
|
if err != nil {
|
2023-09-21 16:58:03 +00:00
|
|
|
if isNetworkError(err) {
|
|
|
|
return "", err
|
|
|
|
}
|
2023-09-21 19:16:54 +00:00
|
|
|
return "", err
|
2023-09-21 05:10:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if len(names) == 0 {
|
2023-09-21 19:16:54 +00:00
|
|
|
return fmt.Sprintf("%s | %-18s | No PTR records", time.Now().Format("03:04:05 PM"), server), nil
|
2023-09-21 05:10:10 +00:00
|
|
|
}
|
2023-09-21 19:24:12 +00:00
|
|
|
return fmt.Sprintf("%s | %-18s | %s", time.Now().Format("03:04:05 PM"), server, names[0]), nil
|
2023-09-21 05:10:10 +00:00
|
|
|
}
|
|
|
|
|
2023-09-21 16:58:03 +00:00
|
|
|
func isNetworkError(err error) bool {
|
|
|
|
errorString := err.Error()
|
|
|
|
return strings.Contains(errorString, "timeout") || strings.Contains(errorString, "connection refused")
|
|
|
|
}
|
|
|
|
|
|
|
|
func pickRandomServer(servers []string, triedServers map[string]bool) string {
|
|
|
|
for _, i := range rand.Perm(len(servers)) {
|
|
|
|
if !triedServers[servers[i]] {
|
|
|
|
return servers[i]
|
2023-09-21 05:10:10 +00:00
|
|
|
}
|
|
|
|
}
|
2023-09-21 16:58:03 +00:00
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
func removeFromList(servers []string, server string) []string {
|
|
|
|
var newList []string
|
|
|
|
for _, s := range servers {
|
|
|
|
if s != server {
|
|
|
|
newList = append(newList, s)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return newList
|
2023-09-21 05:10:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func splitCIDR(cidr string, parts int) ([]*net.IPNet, error) {
|
|
|
|
ip, ipNet, err := net.ParseCIDR(cidr)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2023-09-21 19:16:54 +00:00
|
|
|
startIP := make(net.IP, len(ip))
|
|
|
|
copy(startIP, ip)
|
2023-09-21 05:10:10 +00:00
|
|
|
|
|
|
|
maskSize, _ := ipNet.Mask.Size()
|
2023-09-21 16:58:03 +00:00
|
|
|
|
|
|
|
maxParts := 1 << uint(32-maskSize)
|
|
|
|
if parts > maxParts {
|
|
|
|
parts = maxParts
|
|
|
|
}
|
|
|
|
|
2023-09-21 05:10:10 +00:00
|
|
|
newMaskSize := maskSize
|
|
|
|
for ; (1 << uint(newMaskSize-maskSize)) < parts; newMaskSize++ {
|
|
|
|
if newMaskSize > 32 {
|
|
|
|
return nil, fmt.Errorf("too many parts; cannot split further")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var subnets []*net.IPNet
|
|
|
|
for i := 0; i < parts; i++ {
|
|
|
|
subnets = append(subnets, &net.IPNet{
|
2023-09-21 19:16:54 +00:00
|
|
|
IP: make(net.IP, len(startIP)),
|
2023-09-21 05:10:10 +00:00
|
|
|
Mask: net.CIDRMask(newMaskSize, 32),
|
|
|
|
})
|
2023-09-21 19:16:54 +00:00
|
|
|
copy(subnets[i].IP, startIP)
|
|
|
|
incrementIPBy(startIP, 1<<uint(32-newMaskSize))
|
2023-09-21 05:10:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return subnets, nil
|
|
|
|
}
|
|
|
|
|
2023-09-21 16:58:03 +00:00
|
|
|
func worker(cidr *net.IPNet, resultsChan chan string) {
|
|
|
|
for ip := make(net.IP, len(cidr.IP)); copy(ip, cidr.IP) != 0; incrementIP(ip) {
|
|
|
|
if !cidr.Contains(ip) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
triedServers := make(map[string]bool)
|
|
|
|
retries := 10
|
|
|
|
success := false
|
|
|
|
|
|
|
|
for retries > 0 {
|
|
|
|
randomServer := pickRandomServer(dnsServers, triedServers)
|
|
|
|
if randomServer == "" {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
result, err := reverseDNSLookup(ip.String(), randomServer)
|
|
|
|
|
2023-09-21 19:16:54 +00:00
|
|
|
if err != nil {
|
|
|
|
if showErrors {
|
|
|
|
resultsChan <- fmt.Sprintf("%s | %-18s | Error: %s", time.Now().Format("03:04:05 PM"), randomServer, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if isNetworkError(err) {
|
|
|
|
failureCounts[randomServer]++
|
|
|
|
if failureCounts[randomServer] > 10 {
|
|
|
|
dnsServers = removeFromList(dnsServers, randomServer)
|
|
|
|
delete(failureCounts, randomServer)
|
|
|
|
}
|
2023-09-21 16:58:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
triedServers[randomServer] = true
|
|
|
|
retries--
|
|
|
|
continue
|
2023-09-21 19:16:54 +00:00
|
|
|
} else {
|
2023-09-21 16:58:03 +00:00
|
|
|
resultsChan <- result
|
|
|
|
success = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-21 19:16:54 +00:00
|
|
|
if !success && showErrors {
|
|
|
|
resultsChan <- fmt.Sprintf("%s | %-18s | Max retries reached", time.Now().Format("03:04:05 PM"), ip)
|
2023-09-21 16:58:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-21 05:10:10 +00:00
|
|
|
func main() {
|
|
|
|
var cidr string
|
|
|
|
var concurrency int
|
|
|
|
var dnsFile string
|
|
|
|
|
|
|
|
flag.StringVar(&cidr, "cidr", "", "IP address CIDR to perform reverse DNS lookup")
|
|
|
|
flag.IntVar(&concurrency, "concurrency", 10, "Number of concurrent workers for reverse DNS lookup")
|
|
|
|
flag.StringVar(&dnsFile, "dnsfile", "", "Path to the file containing DNS servers (one per line)")
|
2023-09-21 19:16:54 +00:00
|
|
|
flag.BoolVar(&showErrors, "errors", false, "Display errors in the output") // New flag
|
2023-09-21 05:10:10 +00:00
|
|
|
flag.Parse()
|
|
|
|
|
|
|
|
if cidr == "" || dnsFile == "" {
|
|
|
|
fmt.Println("Please provide a CIDR using the -cidr flag and a DNS servers file with the -dnsfile flag.")
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
|
2023-09-21 16:58:03 +00:00
|
|
|
var err error
|
|
|
|
dnsServers, err = loadDNSServersFromFile(dnsFile)
|
|
|
|
if err != nil {
|
2023-09-21 05:10:10 +00:00
|
|
|
fmt.Printf("Error reading DNS servers from file %s: %s\n", dnsFile, err)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(dnsServers) == 0 {
|
|
|
|
fmt.Println("No DNS servers found in the provided file.")
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
|
|
|
|
rand.Seed(time.Now().UnixNano())
|
|
|
|
|
|
|
|
subnets, err := splitCIDR(cidr, concurrency*10) // Create more subnets than workers
|
|
|
|
if err != nil {
|
|
|
|
fmt.Printf("Error splitting CIDR: %s\n", err)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
|
2023-09-21 16:58:03 +00:00
|
|
|
if len(subnets) < concurrency {
|
|
|
|
concurrency = len(subnets) // Limit concurrency to number of subnets
|
|
|
|
}
|
|
|
|
|
2023-09-21 05:10:10 +00:00
|
|
|
cidrChan := make(chan *net.IPNet, len(subnets))
|
|
|
|
for _, subnet := range subnets {
|
|
|
|
cidrChan <- subnet
|
|
|
|
}
|
2023-09-21 16:58:03 +00:00
|
|
|
close(cidrChan)
|
2023-09-21 05:10:10 +00:00
|
|
|
|
2023-09-21 16:58:03 +00:00
|
|
|
resultsChan := make(chan string, concurrency*2)
|
2023-09-21 05:10:10 +00:00
|
|
|
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
for i := 0; i < concurrency; i++ {
|
|
|
|
wg.Add(1)
|
|
|
|
go func() {
|
|
|
|
defer wg.Done()
|
2023-09-21 16:58:03 +00:00
|
|
|
for subnet := range cidrChan {
|
2023-09-21 05:10:10 +00:00
|
|
|
worker(subnet, resultsChan)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
wg.Wait()
|
|
|
|
close(resultsChan)
|
|
|
|
}()
|
|
|
|
|
|
|
|
for result := range resultsChan {
|
|
|
|
fmt.Println(result)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func incrementIP(ip net.IP) {
|
|
|
|
for j := len(ip) - 1; j >= 0; j-- {
|
|
|
|
ip[j]++
|
|
|
|
if ip[j] > 0 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func incrementIPBy(ip net.IP, count int) {
|
|
|
|
for count > 0 {
|
|
|
|
incrementIP(ip)
|
|
|
|
count--
|
|
|
|
}
|
|
|
|
}
|