Home > database >  I am trying to make a port scanner however its extremly slow and ineffective due to the my condition
I am trying to make a port scanner however its extremly slow and ineffective due to the my condition

Time:12-26

The details are that I belive there are to many things going on within my for loop and I was wondering if there was a more effective way of approaching this task

I hope to map the port to service using a ton of if/elif statements yet its slow. How can I map ports to service outside my for loop for faster speed times

import socket
import pyfiglet
from colorama import Fore
ascii_banner = pyfiglet.figlet_format("Port Scanner")
print(ascii_banner)
remoteserver = input('please enter your target:')
remoteserverIP = socket.gethostbyname(remoteserver) # name resoultion
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
for port in range(1,22):
    result = sock.connect_ex((remoteserverIP,port))
    if result == 0:
        result = port
        print(f"{result} is open")
        if result == 80:
            print(' Service:http')
        elif result == 21:
            print('Service:ftp')
        elif result == 22:
            print('Service:ssh')
        elif result == 23:
            print('Service:telnet')
        elif result == 554:
            print('Service:RTSP')
        elif result == 5432:
            print('Service:PostgreSQL')
        elif result == 3306:
            print('Service:MySQL')

CodePudding user response:

To reduce the number of ifs and elses in your loop, you can use a dictionary. One possibility is:

outputs = {
    "input1": output1,
    "input2": output2,
    "input3": output3,
    # ...
}


input_from_user = get_input_from_user()

if input_from_user in outputs:
    output = outputs[input_from_user]
else:
    output = handle_invalid_input(input_from_user)

do_something_with_output(output)

However, an important point when you are developing a port scanner, you need to keep in mind that the services can be configured on different ports. Thus, the port may not be an effective reference to the service being run, making your code and results/outputs less consistent.

You can try to return the banner of the service, like:

s.recv(1024).decode()

The socket will return the banner in 1024 bytes of buffer size and then we decode it to a string. You would need to add something like:

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
banner = s.recv(1024).decode()

Ed1: Adding a reference to portscanner and an exemplo of using the banner: reference

CodePudding user response:

I'd just map them to a dict in the format { port: service, }. You can get create a list of the keys in the dict using a built-in method and then just base your output off of whether or not the port mapping exists in your dictionary.

import socket

common_services = {
  80: "Service:http",
  21: "Service:ftp",
  22: "Service:ssh",
  12: "Service:telnet",
  554: "Service:RTSP",
  5432: "Service:PostgreSQL",
  3306: "Service:MySQL",
}

known_ports = list(common_services.keys())
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

for port in range(1,22):
    result = sock.connect_ex((ip,port))
    if result == 0:
        msg = f"{port} is open, {common_services[port]}" if port in known_ports else f"{port} is open"
        print(msg)

CodePudding user response:

Adding to the answer of @rzz, who is right that the services could be configured on different ports: you can use the socket.getservbyport() function to determine the service name from the port without using a handmade chain of if statements. It does not necessarily have all the services that you want, so you'll want to catch the OSError and use the dictionary that the others are suggesting instead.

But, I am not sure that the major speed bottleneck you are having is the sequence of if statements. Are you saying that because you profiled the application or is it just a hunch?

The most likely reason is that you are doing all of those requests sequentially. You probably want to fire as many requests as you can concurrently, and then print the results as they come. Or, you add them to a structure and print them sorted as you wish at the end.

You can achieve that with system threads, or with async/await. I would prefer the second method since you're basically only I/O bound and you would avoid context switches overheads.

Here is a working example:

import asyncio
import socket

async def scan(host, port, timeout, results):
    try:
        await asyncio.wait_for(asyncio.open_connection(host, port), timeout=timeout)
        results[port] = "open"
    except TimeoutError:
        results[port] = "timeout"
    except Exception as e:  # you will want to better catch this and differentiate accordingly
        results[port] = str(e)


async def main():
    hostname = 'www.google.com'
    results = {}
    ports = [21, 80, 554]
    host = socket.gethostbyname(hostname)

    await asyncio.gather(*[scan(host, port, 2, results) for port in ports])

    for port in ports:
        print(f"Port {port} of service {socket.getservbyport(port)} result: {results[port]}.")

asyncio.run(main())
  • Related