66
77import asyncio
88import socket
9+ import aiohttp
910import argparse
10- from typing import List , Optional
11- from dataclasses import dataclass
11+ import json
12+ from typing import List , Optional , Dict , Any
13+ from dataclasses import dataclass , asdict
1214
1315@dataclass
1416class NetworkService :
@@ -49,15 +51,59 @@ async def check_port(self, ip: str, port: int) -> bool:
4951 writer .close ()
5052 await writer .wait_closed ()
5153 return True
52- except (asyncio .TimeoutError , ConnectionRefusedError , OSError ):
54+ except (asyncio .TimeoutError , ConnectionRefusedError , OSError , ConnectionResetError ):
5355 return False
5456
55- def identify_service (self , port : int ) -> str :
56- """Identify service based on port number."""
57+ async def identify_service (self , ip : str , port : int ) -> Dict [str , Any ]:
58+ """Identify service running on the port."""
59+ # First check common ports
5760 for service , ports in self .COMMON_PORTS .items ():
5861 if port in ports :
62+ return {
63+ 'service' : service ,
64+ 'protocol' : 'tcp' ,
65+ 'secure' : port in [443 , 8443 , 8883 ],
66+ 'banner' : ''
67+ }
68+
69+ # Try to identify web services
70+ if port in [80 , 443 , 8080 , 8000 , 8001 , 8888 , 8443 ]:
71+ is_https = port in [443 , 8443 ]
72+ service = await self .check_web_service (ip , port , is_https )
73+ if service :
5974 return service
60- return "unknown"
75+
76+ return {
77+ 'service' : 'unknown' ,
78+ 'protocol' : 'tcp' ,
79+ 'secure' : False ,
80+ 'banner' : ''
81+ }
82+
83+ async def check_web_service (self , ip : str , port : int , is_https : bool = False ) -> Optional [Dict [str , Any ]]:
84+ """Check if a web service is running on the port."""
85+ protocol = 'https' if is_https else 'http'
86+ url = f"{ protocol } ://{ ip } :{ port } "
87+
88+ try :
89+ async with aiohttp .ClientSession () as session :
90+ try :
91+ async with session .get (url , timeout = aiohttp .ClientTimeout (total = self .timeout ),
92+ ssl = False , allow_redirects = True ) as response :
93+ if response .status < 500 : # Any success or client error
94+ return {
95+ 'service' : 'http' if not is_https else 'https' ,
96+ 'protocol' : 'tcp' ,
97+ 'secure' : is_https ,
98+ 'banner' : f"HTTP { response .status } { response .reason } " ,
99+ 'headers' : dict (response .headers )
100+ }
101+ except (aiohttp .ClientError , asyncio .TimeoutError ):
102+ pass
103+ except Exception as e :
104+ pass
105+
106+ return None
61107
62108 async def scan_network (
63109 self ,
@@ -92,12 +138,18 @@ async def scan_network(
92138 async def scan_port (self , ip : str , port : int ) -> NetworkService :
93139 """Scan a single port and return service info."""
94140 is_open = await self .check_port (ip , port )
95- service = self .identify_service (port )
141+ if not is_open :
142+ return NetworkService (ip = ip , port = port , is_up = False )
143+
144+ service_info = await self .identify_service (ip , port )
96145 return NetworkService (
97146 ip = ip ,
98147 port = port ,
99- service = service ,
100- is_up = is_open
148+ service = service_info ['service' ],
149+ protocol = service_info ['protocol' ],
150+ banner = service_info .get ('banner' , '' ),
151+ is_secure = service_info .get ('secure' , False ),
152+ is_up = True
101153 )
102154
103155async def main ():
@@ -109,8 +161,8 @@ async def main():
109161 help = 'Service types to scan (rtsp, http, https, ssh, etc.)' )
110162 parser .add_argument ('--port' , '-p' , default = None ,
111163 help = 'Comma-separated list of ports or port ranges to scan (e.g., 80,443,8000-9000)' )
112- parser .add_argument ('--timeout' , '-t' , type = float , default = 1 .0 ,
113- help = 'Connection timeout in seconds (default: 1 .0)' )
164+ parser .add_argument ('--timeout' , '-t' , type = float , default = 3 .0 ,
165+ help = 'Connection timeout in seconds (default: 3 .0)' )
114166 parser .add_argument ('--verbose' , '-v' , action = 'store_true' ,
115167 help = 'Show detailed output' )
116168
0 commit comments