-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdns_query.py
More file actions
115 lines (98 loc) · 3.69 KB
/
dns_query.py
File metadata and controls
115 lines (98 loc) · 3.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#!/usr/bin/env python3
import argparse
from pathlib import Path
import re
import logging
import sys
# Set up logger
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
formatter = logging.Formatter(
"%(asctime)s %(levelname)s %(name)s: %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)
stream_handler = logging.StreamHandler(sys.stderr)
stream_handler.setFormatter(formatter)
logger.addHandler(stream_handler)
def query_dns(domain, only_cname=False) -> list:
"""Perform a DNS query for the given domain."""
import subprocess
try:
command = ["dig", "+short", domain]
if only_cname:
command.append("CNAME")
result = subprocess.run(command, capture_output=True, text=True, check=True)
return result.stdout.strip().splitlines()
except subprocess.CalledProcessError as e:
print(f"Error querying DNS for {domain}: {e}")
return []
def extract_domains_from_file(file_path: Path) -> list:
"""Extract domains from a given file."""
with open(file_path, "r") as file:
content = file.readlines()
return extract_domains_from_list(content)
def extract_domains_from_list(domains: list) -> list:
"""Extract domains from a given list."""
valid_domains = []
for domain in domains:
# only allow domain or http(s)://domain
if re.match(r"^(https?://)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}(:\d+)?$", domain):
logger.debug(f"Valid domain found: {domain}")
valid_domain = re.sub(r"^(https?://)?", "", domain).strip()
valid_domain = re.sub(r":\d+$", "", valid_domain)
valid_domains.append(valid_domain)
else:
logger.warning(f"Invalid domain format: {domain}")
return valid_domains
def query_cname_recursively_and_print(domain: str, indent: int = 1):
"""Recursively query CNAME records and print them. (Recursive function)"""
if indent > 10:
logger.warning("Maximum recursion depth reached.")
return
results = query_dns(domain, only_cname=True)
if results:
# print(" " * indent + f"Results for {domain}:")
for result in results:
print(" " * (indent) + f"- {result}")
# Recursively query the CNAME record
query_cname_recursively_and_print(result, indent + 1)
# else:
# print(" " * indent + "- []")
def main():
parser = argparse.ArgumentParser(description="DNS Query Tool")
parser.add_argument(
"-f", "--file", type=Path, help="Path to the file containing domains"
)
parser.add_argument("-d", "--domain", type=str, help="Single domain to query")
parser.add_argument(
"-c", "--only-cname", action="store_true", help="Query only CNAME records"
)
parser.add_argument(
"-r",
"--recursive",
action="store_true",
help="Recursively query gotten subdomains (dedicated for only_cname mode)",
)
args = parser.parse_args()
if args.file:
domains = extract_domains_from_file(args.file)
elif args.domain:
domains = [args.domain]
else:
parser.error("Either --file or --domain must be provided.")
if args.only_cname and args.recursive:
for domain in domains:
print(f"Querying CNAME records for {domain}:")
query_cname_recursively_and_print(domain)
else:
for domain in domains:
print(f"Querying DNS records for {domain}:")
results = query_dns(domain, only_cname=args.only_cname)
if results:
# print("Results:")
for result in results:
print(f" - {result}")
# else:
# print(" - []")
if __name__ == "__main__":
main()