Python Asyncio port scanner
Today I needed to scan a few thousands of network devices on some ports and of course I needed some kind of concurrency. And here below is a quick, dirty and mostly crappy implementation of asyncio port scanning. Use at your own risk.
import asyncio
import time
now = time.time()
async def check_port(ip, port, loop):
conn = asyncio.open_connection(ip, port, loop=loop)
try:
reader, writer = await asyncio.wait_for(conn, timeout=3)
print(ip, port, 'ok')
return (ip, port, True)
except:
print(ip, port, 'nok')
return (ip, port, False)
async def run(dests, ports, loop):
tasks = [asyncio.ensure_future(check_port(d, p, loop)) for d in dests for p in ports]
responses = await asyncio.gather(*tasks)
return responses
dests = ['asgdshdsgagşilkı', '1.1.1.2', '1.1.1.3']
ports = [22, 23]
loop = asyncio.get_event_loop()
future = asyncio.ensure_future(run(dests, ports, loop))
loop.run_until_complete(future)
print('#'*50)
print('Results: ', future.result())
print('#'*50)
print('Total time: ', time.time() - now)
And the output:
> python test.py
asgdshdsgagşilkı 22 nok
asgdshdsgagşilkı 23 nok
1.1.1.2 22 nok
1.1.1.3 23 nok
1.1.1.2 23 nok
1.1.1.3 22 nok
##################################################
Results: [('asgdshdsgagşilkı', 22, False), ('asgdshdsgagşilkı', 23, False), ('1.1.1.2', 22, False), ('1.1.1.2', 23, False), ('1.1.1.3', 22, False), ('1.1.1.3', 23, False)]
##################################################
Total time: 21.0092453956604
A quick note: If you run this script on a Windows machine for lots of destination address, you’ll most likely get a ValueError like this. “ValueError: too many file descriptors in select()” Since I have a Linux machine, I didn’t bother searching a solution.
Update: The solution for this problem is using asyncio.Semaphore
. It limits the maximum concurrent connection. Source.
async def check_port(ip, port, loop):
conn = asyncio.open_connection(ip, port, loop=loop)
try:
reader, writer = await asyncio.wait_for(conn, timeout=3)
print(ip, port, 'ok')
return (ip, port, True)
except:
print(ip, port, 'nok')
return (ip, port, False)
async def check_port_sem(sem, ip, port, loop):
async with sem:
return await check_port(ip, port, loop)
async def run(dests, ports, loop):
sem = asyncio.Semaphore(400) #Change this value for limitation
tasks = [asyncio.ensure_future(check_port_sem(sem, d, p, loop)) for d in dests for p in ports]
responses = await asyncio.gather(*tasks)
return responses
Leave a comment