Home > Net >  How to speed up my sequential port scanner using multi-threading & return successful values only?
How to speed up my sequential port scanner using multi-threading & return successful values only?

Time:12-12

I was trying convert sequential code of port scanner to become fast as it's so slow :(.

Sequential code

import sys ,socket
from datetime import datetime
from threading import Thread

def target():
    t=input(str("Enter  target:"))
    target=socket.gethostbyname(t)
    try:
        for p in range(1,1026):
            s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

            result=s.connect_ex((target,p))
            if result==0:
                service=socket.getservbyport(p)
                print(f"port {p} is open service {service}")
            else:
                 print(f"port {p} is close")

            s.close()
    except KeyboardInterrupt:
        sys.exit()
    except socket.error:
        print("Host not responding")

def main():
    target()

if __name__=="__main__":
    main()

I successfully convert it Faster But I want to get successful output only in ThreadPoolexecutor but I can't here What I do.

Fast Code

import socket
import threading
import concurrent.futures
import re


def scan(ip, port):
    lock = threading.Lock()
    scanner = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    scanner.settimeout(.1)
    ip = re.sub("(https:// | http:// | \/)", '', ip)
    ip = socket.gethostbyname(ip)

    try:
        scanner.connect((ip, port))
        scanner.close()
        with lock:
            result = f"Port {port} is OPEN Running {socket.getservbyport(port)}"
            print(result)
            return result
    except:
        pass


def run(ip_num: str, scan_fn, nums_ports: int) -> list:
    result = []
    with concurrent.futures.ThreadPoolExecutor(max_workers=100) as executor:
        for port in range(nums_ports):
            future = executor.submit(scan_fn, ip_num, port   1)
            result.append(future.result())
    print(result)  # empty
    return result


def main():
    ip = input("target> ")
    run(ip, scan, 1025)
    # with concurrent.futures.ThreadPoolExecutor(max_workers=100) as executor:
    #     for port in range(1025):
    #         executor.submit(scan, ip, port   1)


if __name__ == "__main__":
    main()

output if Target google.com

target> google.com
Port 80 is OPEN Running http
Port 443 is OPEN Running https
[None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, 'Port 80 is OPEN Running http', None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, 'Port 443 is OPEN Running https', None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None]

this above Output comes out after a long time how to make it faster and just take the successful result of that printed in first.

CodePudding user response:

The reason your program is slow is here:

def run(ip_num: str, scan_fn, nums_ports: int) -> list:
    result = []
    with concurrent.futures.ThreadPoolExecutor(max_workers=100) as executor:
        for port in range(nums_ports):
            future = executor.submit(scan_fn, ip_num, port   1)
            result.append(future.result()) # Waits for function to return a value
    print(result)
    return result

The call to future.result() doesn't return until the future is done. Your main thread must wait until one of your secondary threads runs the function scan_fn (which is actually the function named scan). The future isn't done until scan runs, finishes and returns a value. Even though you are running in a Pool, the logic of your program forces the calls to scan to execute one at a time. You get no benefit at all from multithreading.

There are a couple of ways to fix this. You might consider using the capability of pool.map but here is a simple approach that is similar to your existing program, using pool.submit. I made no changes to the function scan. I hard-coded the url because I got tired typing "google.com" at the prompt :-)

import socket
import threading
import concurrent.futures
import re
import time


def scan(ip, port):
    lock = threading.Lock()
    scanner = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    scanner.settimeout(.1)
    ip = re.sub("(https:// | http:// | \/)", '', ip)
    ip = socket.gethostbyname(ip)

    try:
        scanner.connect((ip, port))
        scanner.close()
        with lock:
            result = f"Port {port} is OPEN Running {socket.getservbyport(port)}"
            print(result)
            return result
    except:
        pass


def run(ip_num: str, scan_fn, nums_ports: int) -> list:
    futures = []
    with concurrent.futures.ThreadPoolExecutor(max_workers=100) as executor:
        for port in range(nums_ports):
            futures.append(executor.submit(scan_fn, ip_num, port   1))
        # At this point all the futures are pending, and are running
        # in 100 other threads
    result = []
    for future in futures:
        r = future.result()   # Wait on each result
        if r is not None:     # Save only the non-None results
            result.append(r)
    print(result) 
    return result


def main():
    t = time.time()
    run("google.com", scan, 1025)
    print("Total execution time", time.time() - t)


if __name__ == "__main__":
    main()

The program output is:

Port 80 is OPEN Running http
Port 443 is OPEN Running https
['Port 80 is OPEN Running http', 'Port 443 is OPEN Running https']
Total execution time 1.2854032516479492

The execution time for 1025 scans is only 1.28 seconds.

  • Related