11import os
2- import requests
3- import threading
4- from rich import print
52from concurrent .futures import ThreadPoolExecutor , as_completed
3+
64from bugscanx .utils .common import get_input
75from .logger import SubFinderConsole
8- from .sources import get_all_sources , get_bulk_sources
6+ from .sources import get_sources
97from .utils import is_valid_domain , filter_valid_subdomains
108
119class SubFinder :
1210 def __init__ (self ):
1311 self .console = SubFinderConsole ()
14-
15- def process_domain (self , domain , output_file , sources , total , completed_counter ):
12+ self .completed = 0
13+
14+ def _fetch_from_source (self , source , domain ):
15+ try :
16+ found = source .fetch (domain )
17+ return filter_valid_subdomains (found , domain )
18+ except Exception :
19+ return set ()
20+
21+ def _save_subdomains (self , subdomains , output_file ):
22+ if subdomains :
23+ with open (output_file , "a" , encoding = "utf-8" ) as f :
24+ f .write ("\n " .join (sorted (subdomains )) + "\n " )
25+
26+ def process_domain (self , domain , output_file , sources , total ):
1627 if not is_valid_domain (domain ):
17- with completed_counter .get_lock ():
18- completed_counter .value += 1
28+ self .completed += 1
1929 return set ()
2030
21- self .console .start_domain_scan (domain )
22- self .console .show_progress ( completed_counter . value , total )
31+ self .console .print_domain_start (domain )
32+ self .console .print_progress ( self . completed , total )
2333
24- with requests .Session () as session :
25- results = []
26- with ThreadPoolExecutor (max_workers = 6 ) as executor :
27- future_to_source = {
28- executor .submit (source .fetch , domain , session ): source .name
29- for source in sources
30- }
31-
32- for future in as_completed (future_to_source ):
33- try :
34- found = future .result ()
35- filtered = filter_valid_subdomains (found , domain )
36- results .append (filtered )
37- except Exception :
38- results .append (set ())
39-
40- subdomains = set ().union (* results ) if results else set ()
34+ with ThreadPoolExecutor (max_workers = 6 ) as executor :
35+ futures = [
36+ executor .submit (self ._fetch_from_source , source , domain )
37+ for source in sources
38+ ]
39+ results = [f .result () for f in as_completed (futures )]
40+
41+ subdomains = set ().union (* results ) if results else set ()
4142
4243 self .console .update_domain_stats (domain , len (subdomains ))
4344 self .console .print_domain_complete (domain , len (subdomains ))
45+ self ._save_subdomains (subdomains , output_file )
4446
45- if subdomains :
46- with open (output_file , "a" , encoding = "utf-8" ) as f :
47- f .write ("\n " .join (sorted (subdomains )) + "\n " )
48-
49- with completed_counter .get_lock ():
50- completed_counter .value += 1
51- self .console .show_progress (completed_counter .value , total )
52-
47+ self .completed += 1
48+ self .console .print_progress (self .completed , total )
5349 return subdomains
5450
5551 def run (self , domains , output_file , sources ):
@@ -58,55 +54,42 @@ def run(self, domains, output_file, sources):
5854 return
5955
6056 os .makedirs (os .path .dirname (output_file ) or '.' , exist_ok = True )
61- completed_counter = threading . Value ( 'i' , 0 )
57+ self . completed = 0
6258 all_subdomains = set ()
63-
59+ total = len (domains )
60+
6461 with ThreadPoolExecutor (max_workers = 3 ) as executor :
65- future_to_domain = {
66- executor .submit (
67- self .process_domain ,
68- domain ,
69- output_file ,
70- sources ,
71- len (domains ),
72- completed_counter
73- ): domain for domain in domains
74- }
75-
76- for future in as_completed (future_to_domain ):
77- domain = future_to_domain [future ]
62+ futures = [
63+ executor .submit (self .process_domain , domain , output_file , sources , total )
64+ for domain in domains
65+ ]
66+ for future in as_completed (futures ):
7867 try :
7968 result = future .result ()
80- if result :
81- all_subdomains .update (result )
69+ all_subdomains .update (result )
8270 except Exception as e :
83- self .console .print (f"Error processing { domain } : { str (e )} " )
71+ self .console .print (f"Error processing domain: { str (e )} " )
8472
8573 self .console .print_final_summary (output_file )
8674 return all_subdomains
8775
76+
8877def main ():
8978 domains = []
90- input_type = get_input ("Select input mode" , "choice" ,
91- choices = ["Manual" , "File" ])
92-
79+ sources = get_sources ()
80+ input_type = get_input ("Select input mode" , "choice" ,
81+ choices = ["Manual" , "File" ])
82+
9383 if input_type == "Manual" :
9484 domain_input = get_input ("Enter domain(s)" )
9585 domains = [d .strip () for d in domain_input .split (',' ) if is_valid_domain (d .strip ())]
96- sources = get_all_sources ()
9786 default_output = f"{ domains [0 ]} _subdomains.txt"
98-
9987 else :
10088 file_path = get_input ("Enter filename" , "file" )
10189 with open (file_path , 'r' ) as f :
10290 domains = [d .strip () for d in f if is_valid_domain (d .strip ())]
103- sources = get_bulk_sources ()
10491 default_output = f"{ file_path .rsplit ('.' , 1 )[0 ]} _subdomains.txt"
10592
106- if not domains :
107- print ("[bold red] No valid domains provided" )
108- return
109-
11093 output_file = get_input ("Enter output filename" , default = default_output )
11194 subfinder = SubFinder ()
11295 subfinder .run (domains , output_file , sources )
0 commit comments