|
1 | | -import concurrent.futures |
| 1 | +import os |
| 2 | +from concurrent.futures import ThreadPoolExecutor, as_completed |
| 3 | + |
2 | 4 | from bugscanx.utils.common import get_input, is_cidr |
3 | 5 | from .sources import get_scrapers |
4 | 6 | from .utils import process_input, process_file |
5 | | -from .result_manager import ResultManager |
6 | | -from .logger import IPLookupConsole, console |
7 | | - |
8 | | -def extract_domains(ip, scrapers, ip_console): |
9 | | - ip_console.start_ip_scan(ip) |
10 | | - domains = [] |
11 | | - for scraper in scrapers: |
12 | | - domain_list = scraper.fetch_domains(ip) |
13 | | - if domain_list: |
14 | | - domains.extend(domain_list) |
15 | | - |
16 | | - domains = sorted(set(domains)) |
17 | | - return (ip, domains) |
18 | | - |
19 | | -def process_ips(ips, output_file): |
20 | | - if not ips: |
21 | | - console.print("[bold red] No valid IPs/CIDRs to process.[/bold red]") |
22 | | - return 0 |
23 | | - |
24 | | - scrapers = get_scrapers() |
25 | | - ip_console = IPLookupConsole() |
26 | | - result_manager = ResultManager(output_file, ip_console) |
27 | | - |
28 | | - def process_ip(ip): |
29 | | - ip, domains = extract_domains(ip, scrapers, ip_console) |
| 7 | +from .logger import IPLookupConsole |
| 8 | + |
| 9 | + |
| 10 | +class IPLookup: |
| 11 | + def __init__(self): |
| 12 | + self.console = IPLookupConsole() |
| 13 | + self.completed = 0 |
| 14 | + |
| 15 | + def _fetch_from_source(self, source, ip): |
| 16 | + try: |
| 17 | + return source.fetch(ip) |
| 18 | + except Exception: |
| 19 | + return set() |
| 20 | + |
| 21 | + def _save_domains(self, domains, output_file): |
30 | 22 | if domains: |
31 | | - result_manager.save_result(ip, domains) |
32 | | - return ip, domains |
33 | | - |
34 | | - with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor: |
35 | | - futures = {executor.submit(process_ip, ip): ip for ip in ips} |
36 | | - for future in concurrent.futures.as_completed(futures): |
37 | | - future.result() |
38 | | - |
39 | | - for scraper in scrapers: |
40 | | - scraper.close() |
41 | | - |
42 | | - ip_console.print_final_summary(output_file) |
43 | | - return ip_console.total_domains |
44 | | - |
45 | | -def get_input_interactively(): |
| 23 | + with open(output_file, "a", encoding="utf-8") as f: |
| 24 | + f.write("\n".join(sorted(domains)) + "\n") |
| 25 | + |
| 26 | + def process_ip(self, ip, output_file, scrapers, total): |
| 27 | + self.console.print_ip_start(ip) |
| 28 | + self.console.print_progress(self.completed, total) |
| 29 | + |
| 30 | + with ThreadPoolExecutor(max_workers=3) as executor: |
| 31 | + futures = [ |
| 32 | + executor.submit(self._fetch_from_source, source, ip) |
| 33 | + for source in scrapers |
| 34 | + ] |
| 35 | + results = [f.result() for f in as_completed(futures)] |
| 36 | + |
| 37 | + domains = set().union(*results) if results else set() |
| 38 | + |
| 39 | + self.console.update_ip_stats(ip, len(domains)) |
| 40 | + self.console.print_ip_complete(ip, len(domains)) |
| 41 | + self._save_domains(domains, output_file) |
| 42 | + |
| 43 | + self.completed += 1 |
| 44 | + self.console.print_progress(self.completed, total) |
| 45 | + return domains |
| 46 | + |
| 47 | + def run(self, ips, output_file, scrapers=None): |
| 48 | + if not ips: |
| 49 | + self.console.print_error("No valid IPs provided") |
| 50 | + return |
| 51 | + |
| 52 | + os.makedirs(os.path.dirname(output_file) or '.', exist_ok=True) |
| 53 | + self.completed = 0 |
| 54 | + all_domains = set() |
| 55 | + total = len(ips) |
| 56 | + scrapers = scrapers or get_scrapers() |
| 57 | + |
| 58 | + with ThreadPoolExecutor(max_workers=5) as executor: |
| 59 | + futures = [ |
| 60 | + executor.submit(self.process_ip, ip, output_file, scrapers, total) |
| 61 | + for ip in ips |
| 62 | + ] |
| 63 | + for future in as_completed(futures): |
| 64 | + try: |
| 65 | + result = future.result() |
| 66 | + all_domains.update(result) |
| 67 | + except Exception as e: |
| 68 | + self.console.print_error(f"Error processing IP: {str(e)}") |
| 69 | + |
| 70 | + for scraper in scrapers: |
| 71 | + scraper.close() |
| 72 | + |
| 73 | + self.console.print_final_summary(output_file) |
| 74 | + return all_domains |
| 75 | + |
| 76 | + |
| 77 | +def main(): |
46 | 78 | ips = [] |
47 | | - |
48 | | - input_choice = get_input("Select input mode", "choice", |
49 | | - choices=["Manual", "File"]) |
50 | | - |
51 | | - if input_choice == "Manual": |
52 | | - cidr = get_input("Enter IP or CIDR", validators=[is_cidr]) |
53 | | - ips.extend(process_input(cidr)) |
| 79 | + input_type = get_input("Select input mode", "choice", |
| 80 | + choices=["Manual", "File"]) |
| 81 | + |
| 82 | + if input_type == "Manual": |
| 83 | + ip_input = get_input("Enter IP or CIDR", validators=[is_cidr]) |
| 84 | + ips.extend(process_input(ip_input)) |
| 85 | + default_output = f"{ip_input}_domains.txt".replace("/", "-") |
54 | 86 | else: |
55 | 87 | file_path = get_input("Enter filename", "file") |
56 | 88 | ips.extend(process_file(file_path)) |
57 | | - |
58 | | - output_file = get_input("Enter output filename") |
59 | | - return ips, output_file |
60 | | - |
61 | | -def main(ips=None, output_file=None): |
62 | | - if ips is None or output_file is None: |
63 | | - ips, output_file = get_input_interactively() |
64 | | - process_ips(ips, output_file) |
| 89 | + default_output = f"{file_path.rsplit('.', 1)[0]}_domains.txt" |
| 90 | + |
| 91 | + output_file = get_input("Enter output filename", default=default_output) |
| 92 | + iplookup = IPLookup() |
| 93 | + iplookup.run(ips, output_file) |
0 commit comments