22
33"""
44Project: Port Scanner
5- Description: Installs and runs nmap with colored scan results
5+ Description: Async port scanner with Nmap support
66Author: batubyte
7- Date: 2025-15-06
7+ Date: 2025-06-17
88"""
99
1010import subprocess
1111import platform
12+ import argparse
13+ import asyncio
1214import shutil
15+ import socket
1316import sys
1417import re
1518
19+ DESCRIPTION = "Async port scanner with Nmap support"
20+ VERSION = "0.1.1"
21+
1622RED = "\033 [31m"
1723GREEN = "\033 [32m"
1824YELLOW = "\033 [33m"
2228
2329
2430def install_nmap ():
25- if shutil .which ("nmap" ) is not None :
31+ if shutil .which ("nmap" ):
2632 return
2733
2834 answer = input ("Nmap not found. Install? [Y/n]: " ).strip ().lower ()
@@ -34,16 +40,20 @@ def install_nmap():
3440 subprocess .run (["sudo" , "apt-get" , "update" ], check = True )
3541 subprocess .run (["sudo" , "apt-get" , "install" , "-y" , "nmap" ], check = True )
3642 elif system == "Windows" :
37- print ("Install nmap from https://nmap.org/download.html" )
38- print ("Then restart your system. " )
43+ print ("Install nmap from https://nmap.org/download.html#windows " )
44+ print ("Restart system after installation " )
3945 sys .exit (1 )
4046
4147 if shutil .which ("nmap" ) is None :
4248 print ("Installation failed. Install nmap manually." )
4349 sys .exit (1 )
4450
4551
46- def print_output (output ):
52+ def run_nmap (args ):
53+ cmd = ["nmap" ] + args
54+ result = subprocess .run (cmd , capture_output = True , text = True , check = True )
55+ output = result .stdout
56+
4757 ports_section = False
4858 for line in output .splitlines ():
4959 line = line .strip ()
@@ -73,24 +83,63 @@ def print_output(output):
7383 print (line )
7484
7585
76- def nmap (args ):
77- cmd = ["nmap" ] + args
78- result = subprocess .run (cmd , capture_output = True , text = True , check = True )
79- print_output (result .stdout )
86+ async def scan_port (host , port , sem ):
87+ async with sem :
88+ try :
89+ reader , writer = await asyncio .open_connection (host , port )
90+ print (f"{ port } /tcp open" )
91+ writer .close ()
92+ await writer .wait_closed ()
93+ except :
94+ pass
8095
8196
82- def main ():
97+ async def full_scan (host ):
98+ sem = asyncio .Semaphore (500 )
99+ tasks = [scan_port (host , port , sem ) for port in range (1 , 65536 )]
83100 try :
101+ await asyncio .gather (* tasks )
102+ except asyncio .CancelledError :
103+ pass
104+
105+
106+ def valid_host (host ):
107+ try :
108+ socket .gethostbyname (host )
109+ return True
110+ except socket .error :
111+ return False
112+
113+
114+ def parse_args (parser ):
115+ parser .add_argument ("-v" , "--version" , action = "version" , version = VERSION )
116+ parser .add_argument ("-n" , nargs = argparse .REMAINDER , help = "Run nmap" )
117+ parser .add_argument ("target" , nargs = "?" , help = "IP address, hostname or domain" )
118+ return parser .parse_args ()
119+
120+
121+ def main ():
122+ parser = argparse .ArgumentParser (description = DESCRIPTION )
123+ args = parse_args (parser )
124+
125+ if args .n :
84126 install_nmap ()
85- if len (sys . argv ) < 2 :
127+ if len (args . n ) == 0 :
86128 subprocess .run (["nmap" , "-h" ])
87- sys .exit (0 )
88-
89- nmap (sys .argv [1 :])
90- except Exception as e :
91- print (f"Error: { e } " )
129+ else :
130+ run_nmap (args .n )
131+ elif args .target and valid_host (args .target ):
132+ asyncio .run (full_scan (args .target ))
133+ else :
134+ parser .print_help ()
92135 sys .exit (1 )
93136
94137
95138if __name__ == "__main__" :
96- main ()
139+ try :
140+ main ()
141+ except KeyboardInterrupt :
142+ sys .exit (130 )
143+ except Exception as e :
144+ print (f"Error: { e } " )
145+ sys .exit (1 )
0 commit comments