Skip to content

Commit 36f3e60

Browse files
committed
Update dns_recon.py
1 parent 5c30140 commit 36f3e60

File tree

1 file changed

+111
-59
lines changed

1 file changed

+111
-59
lines changed

dns_recon.py

Lines changed: 111 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -27,25 +27,49 @@
2727
- ipaddress
2828
"""
2929

30+
import sys
31+
import os
3032
import argparse
31-
import concurrent.futures
32-
import dns.name
33-
import dns.query
34-
import dns.resolver
35-
import dns.reversename
36-
import dns.zone
37-
import ipaddress
3833
import json
3934
import socket
40-
import sys
35+
import concurrent.futures
4136
import time
4237
from collections import defaultdict
4338
from datetime import datetime
39+
import ipaddress
4440

45-
# Configure DNS resolver
46-
dns_resolver = dns.resolver.Resolver()
41+
# Check dependencies
42+
def check_dependencies():
43+
"""Check if required dependencies are installed."""
44+
try:
45+
import dns.name
46+
import dns.query
47+
import dns.resolver
48+
import dns.reversename
49+
import dns.zone
50+
except ImportError:
51+
print("Error: Required package 'dnspython' is not installed.")
52+
print("\nTo install the required package, run:")
53+
print(" pip install dnspython")
54+
print("\nOn Windows:")
55+
print(" python -m pip install dnspython")
56+
print("\nOn Linux/MacOS:")
57+
print(" pip3 install dnspython")
58+
sys.exit(1)
4759

48-
# Default DNS servers (can be overridden via command line)
60+
try:
61+
import ipaddress
62+
except ImportError:
63+
print("Error: Required package 'ipaddress' is not installed.")
64+
print("\nTo install the required package, run:")
65+
print(" pip install ipaddress")
66+
print("\nOn Windows:")
67+
print(" python -m pip install ipaddress")
68+
print("\nOn Linux/MacOS:")
69+
print(" pip3 install ipaddress")
70+
sys.exit(1)
71+
72+
# Constants
4973
DEFAULT_DNS_SERVERS = [
5074
'8.8.8.8', # Google
5175
'1.1.1.1', # Cloudflare
@@ -65,6 +89,53 @@
6589
'portal', 'ssh', 'git', 'cdn', 'cloud', 'support', 'web'
6690
]
6791

92+
# Utility functions
93+
def save_results(results, output, domain=None, ip=None, ip_range=None, dns_servers=None):
94+
"""Save results to a file."""
95+
try:
96+
with open(output, 'w') as f:
97+
# Add metadata
98+
result_data = {
99+
'metadata': {
100+
'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
101+
'target_domain': domain,
102+
'target_ip': ip,
103+
'target_range': ip_range,
104+
'dns_servers': dns_servers
105+
},
106+
'results': results
107+
}
108+
109+
json.dump(result_data, f, indent=2)
110+
111+
print(f"Results saved to {output}")
112+
except Exception as e:
113+
print(f"Error saving results: {e}")
114+
115+
# CLI arguments parsing
116+
def parse_arguments():
117+
"""Parse command line arguments."""
118+
parser = argparse.ArgumentParser(description="DNS Reconnaissance Tool")
119+
parser.add_argument("--domain", "-d", help="Target domain")
120+
parser.add_argument("--ip", "-i", help="Target IP address for reverse lookup")
121+
parser.add_argument("--range", "-r", help="IP range in CIDR notation (e.g., 192.168.1.0/24)")
122+
parser.add_argument("--server", "-s", action="append", help="DNS server to use (can be specified multiple times)")
123+
parser.add_argument("--timeout", "-t", type=int, default=5, help="Timeout for DNS queries in seconds")
124+
parser.add_argument("--threads", "-n", type=int, default=10, help="Number of threads for concurrent queries")
125+
parser.add_argument("--verbose", "-v", action="store_true", help="Enable verbose output")
126+
parser.add_argument("--output", "-o", help="Output file path (JSON format)")
127+
parser.add_argument("--subdomains", "-w", help="Path to subdomain wordlist file")
128+
parser.add_argument("--history", action="store_true", help="Attempt to retrieve historical DNS records")
129+
130+
args = parser.parse_args()
131+
132+
# Validate that at least one target is specified
133+
if not (args.domain or args.ip or args.range):
134+
parser.error("At least one target (--domain, --ip, or --range) must be specified")
135+
136+
return args
137+
138+
# Main DNS class
68139
class DNSRecon:
69140
def __init__(self, domain=None, ip=None, ip_range=None, dns_servers=None,
70141
timeout=5, threads=10, verbose=False, output=None,
@@ -84,6 +155,13 @@ def __init__(self, domain=None, ip=None, ip_range=None, dns_servers=None,
84155
subdomains (str): Path to subdomain wordlist file
85156
history (bool): Retrieve historical DNS records if available
86157
"""
158+
# Import DNS modules here to ensure dependencies are checked first
159+
import dns.name
160+
import dns.query
161+
import dns.resolver
162+
import dns.reversename
163+
import dns.zone
164+
87165
self.domain = domain
88166
self.ip = ip
89167
self.ip_range = ip_range
@@ -104,9 +182,10 @@ def __init__(self, domain=None, ip=None, ip_range=None, dns_servers=None,
104182
}
105183

106184
# Configure resolver
107-
dns_resolver.timeout = self.timeout
108-
dns_resolver.lifetime = self.timeout
109-
dns_resolver.nameservers = [self.dns_servers[0]] # Primary DNS server
185+
self.resolver = dns.resolver.Resolver()
186+
self.resolver.timeout = self.timeout
187+
self.resolver.lifetime = self.timeout
188+
self.resolver.nameservers = [self.dns_servers[0]] # Primary DNS server
110189

111190
def run(self):
112191
"""
@@ -159,7 +238,14 @@ def run(self):
159238
print(f"\nReconnaissance completed in {duration:.2f} seconds")
160239

161240
if self.output:
162-
self._save_results()
241+
save_results(
242+
self.results,
243+
self.output,
244+
domain=self.domain,
245+
ip=self.ip,
246+
ip_range=self.ip_range,
247+
dns_servers=self.dns_servers
248+
)
163249

164250
return self.results
165251

@@ -177,14 +263,14 @@ def _query_record(self, domain, record_type):
177263
try:
178264
if record_type == 'DMARC':
179265
# DMARC records are stored as TXT records at _dmarc.domain
180-
answers = dns_resolver.resolve(f'_dmarc.{domain}', 'TXT')
266+
answers = self.resolver.resolve(f'_dmarc.{domain}', 'TXT')
181267
elif record_type == 'SPF':
182268
# SPF records are stored as TXT records
183-
answers = dns_resolver.resolve(domain, 'TXT')
269+
answers = self.resolver.resolve(domain, 'TXT')
184270
# Filter for SPF records
185271
return [str(rdata).strip('"') for rdata in answers if 'spf' in str(rdata).lower()]
186272
else:
187-
answers = dns_resolver.resolve(domain, record_type)
273+
answers = self.resolver.resolve(domain, record_type)
188274

189275
if record_type == 'A' or record_type == 'AAAA':
190276
return [rdata.address for rdata in answers]
@@ -397,7 +483,7 @@ def _resolve_subdomain(self, subdomain):
397483
str: IP address(es) if resolved, None otherwise
398484
"""
399485
try:
400-
answers = dns_resolver.resolve(subdomain, 'A')
486+
answers = self.resolver.resolve(subdomain, 'A')
401487
return ', '.join(rdata.address for rdata in answers)
402488
except Exception:
403489
return None
@@ -414,7 +500,7 @@ def _perform_reverse_lookup(self, ip):
414500
"""
415501
try:
416502
reverse_name = dns.reversename.from_address(ip)
417-
answers = dns_resolver.resolve(reverse_name, 'PTR')
503+
answers = self.resolver.resolve(reverse_name, 'PTR')
418504
hostnames = [str(rdata).rstrip('.') for rdata in answers]
419505

420506
if hostnames:
@@ -487,7 +573,7 @@ def _perform_reverse_lookup_quiet(self, ip):
487573
"""
488574
try:
489575
reverse_name = dns.reversename.from_address(ip)
490-
answers = dns_resolver.resolve(reverse_name, 'PTR')
576+
answers = self.resolver.resolve(reverse_name, 'PTR')
491577
hostnames = [str(rdata).rstrip('.') for rdata in answers]
492578

493579
if hostnames:
@@ -498,47 +584,13 @@ def _perform_reverse_lookup_quiet(self, ip):
498584
pass
499585

500586
return []
501-
502-
def _save_results(self):
503-
"""Save results to a file."""
504-
try:
505-
with open(self.output, 'w') as f:
506-
# Add metadata
507-
result_data = {
508-
'metadata': {
509-
'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
510-
'target_domain': self.domain,
511-
'target_ip': self.ip,
512-
'target_range': self.ip_range,
513-
'dns_servers': self.dns_servers
514-
},
515-
'results': self.results
516-
}
517-
518-
json.dump(result_data, f, indent=2)
519-
520-
print(f"Results saved to {self.output}")
521-
except Exception as e:
522-
print(f"Error saving results: {e}")
523587

524588
def main():
525-
parser = argparse.ArgumentParser(description="DNS Reconnaissance Tool")
526-
parser.add_argument("--domain", "-d", help="Target domain")
527-
parser.add_argument("--ip", "-i", help="Target IP address for reverse lookup")
528-
parser.add_argument("--range", "-r", help="IP range in CIDR notation (e.g., 192.168.1.0/24)")
529-
parser.add_argument("--server", "-s", action="append", help="DNS server to use (can be specified multiple times)")
530-
parser.add_argument("--timeout", "-t", type=int, default=5, help="Timeout for DNS queries in seconds")
531-
parser.add_argument("--threads", "-n", type=int, default=10, help="Number of threads for concurrent queries")
532-
parser.add_argument("--verbose", "-v", action="store_true", help="Enable verbose output")
533-
parser.add_argument("--output", "-o", help="Output file path (JSON format)")
534-
parser.add_argument("--subdomains", "-w", help="Path to subdomain wordlist file")
535-
parser.add_argument("--history", action="store_true", help="Attempt to retrieve historical DNS records")
536-
537-
args = parser.parse_args()
589+
# Check dependencies first
590+
check_dependencies()
538591

539-
# Validate that at least one target is specified
540-
if not (args.domain or args.ip or args.range):
541-
parser.error("At least one target (--domain, --ip, or --range) must be specified")
592+
# Parse command line arguments
593+
args = parse_arguments()
542594

543595
try:
544596
recon = DNSRecon(

0 commit comments

Comments
 (0)