Python Port Scanner

Below you will find an example of how to code a basic port scanner using Python. I coded this in the hope it would serve as an introduction to Python while creating something a bit more interesting than a guess the number game or something like that.

I will give you the code first. If you are just after a Python port scanner or the comments are enough then great, If need a bit more of an explanation of how this is actually working then read on.

If you haven’t already got Python installed take a look here Python 3 Installation & Setup Guide or I am a big fan of Microsoft Visual Studio Code

If you are after a book to help to get you started with Python or coding in general, I really like the below. I purchased this one for my son a few months back and he really loves it. Beginner’s Step-by-Step Coding Course: Learn Computer Programming the Easy Way

Port Scanner Code

import socket
import sys
import os
import datetime

# Clear the screen
def cls():
    os.system('cls' if os.name=='nt' else 'clear')

cls()

# Defining a target host, start and end ports
if len(sys.argv) == 4: 
    # We have command line options so lets use those

    # translate hostname to IPv4 
    remoteServerIP = str(socket.gethostbyname(sys.argv[1]))

    if len(remoteServerIP) == 0:
        print("A hostname or IP address is required!")
        sys.exit()

    startPort = int((sys.argv[2]))
    endPort = int((sys.argv[3]))
else: 
    # We don't have the correct number of command line options so prompt for input
    print("Command Line Usage: PortScanner.py <host> <start port> <end port>\n")

    remoteServer = input("Enter a remote hostname or IP to scan: ")
    remoteServerIP = socket.gethostbyname(remoteServer)

    if len(remoteServerIP) == 0:
        print("A hostname or IP address is required!")
        sys.exit()

    startPort = input("Start at port (0): ")
    endPort = input("End at port (1023): ")

    # If the user did not provide ports use the defaults
    if not startPort:
        startPort = 0

    if not endPort:
        endPort = 1023

# Print a banner with information on which host we are about to scan
openPortCount = 0
startTime = datetime.datetime.now()

print("=" * 60)
print("Please wait, scanning remote host", remoteServerIP)
print("Scanning started at: " + startTime.strftime("%x %X"))
print("Please CTRL+C to cancel")
print("=" * 60)
print("Open Ports:")

# Scan the ports using the range function to loop 
try:
    for port in range(int(startPort),int(endPort)):  
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        result = sock.connect_ex((remoteServerIP, port))
        if result == 0:
            print(" Port {}: 	 Open".format(port))
            openPortCount += 1
        sock.close()

except KeyboardInterrupt:
    print("You pressed Ctrl+C")
    sys.exit()

except socket.gaierror:
    print("Error - Could not resolve hostname.")
    sys.exit()

except socket.error:
    print("Error - Couldn't connect to server")
    sys.exit()

endTime = datetime.datetime.now()
runTime = endTime - startTime
runTime_in_s =  runTime.total_seconds()

runtimeDays    = divmod(runTime_in_s, 86400)        # Get days (without [0]!)
runtimeHours   = divmod(runtimeDays[1], 3600)               # Use remainder of days to calc hours
runtimeMinutes = divmod(runtimeHours[1], 60)                # Use remainder of hours to calc minutes
runtimeSeconds = divmod(runtimeMinutes[1], 1)               # Use remainder of minutes to calc seconds

# Print end information to screen
print("-" * 60)
print("Scanning complete")
print(" Duration: %d days, %d hours, %d minutes and %d seconds" % (runtimeDays[0], runtimeHours[0], runtimeMinutes[0], runtimeSeconds[0]))
print(" Ports scanned " + str(startPort) + " to " + str(endPort))
print(" " + str(openPortCount) + " open port(s) found")
print("-" * 60)

# Wait for the users input to terminate the program
input("Press the enter key to exit")

What Should Happen

The code gives you the option with either passing in command-line arguments, such as PortScanner.py host start_port end_port

Or if you just run the script you will be prompted to enter the remote host, a start and end port. The start and end port are optional if you leave then black default values of 0 to 1023 will be used.

Code Explanation

I will do my best to explain the code but if you need anything more then please comment below


import socket
import sys
import os
import datetime

Here we import all of the modules we will need.

  • socket – Gives us access to the network.
  • sys – Gives us access to system functions such as exiting the program and getting command line arguments
  • os – Used so we can detect what OS we are running on. More on this later.
  • datetime – Used to get and manipulation the date and time.

# Clear the screen
def cls():
    os.system('cls' if os.name=='nt' else 'clear')

cls()

Here we are just clearing the display. It is a bit more complex than you would expect as the method is different depending on if we are on Windows, Mac, or Linux. That is why we need to import the OS module.

We use def to define a function called CLS. Whenever we need to clear the screen we can call the code of our function with just the cls() command. The function itself just checks if we are on Windows os.name==’nt’. If so the command to clear the screen with cls. If we are not on Windows the command is clear


# Defining a target host, start and end ports
if len(sys.argv) == 4: 
    # We have command line options so lets use those

    # translate hostname to IPv4 
    remoteServerIP = str(socket.gethostbyname(sys.argv[1]))

    if len(remoteServerIP) == 0:
        print("A hostname or IP address is required!")
        sys.exit()

    startPort = int((sys.argv[2]))
    endPort = int((sys.argv[3]))
else: 
    # We don't have the correct number of command line options so prompt for input
    print("Command Line Usage: PortScanner.py <host> <start port> <end port>\n")

    remoteServer = input("Enter a remote hostname or IP to scan: ")
    remoteServerIP = socket.gethostbyname(remoteServer)

    if len(remoteServerIP) == 0:
        print("A hostname or IP address is required!")
        sys.exit()

    startPort = input("Start at port (0): ")
    endPort = input("End at port (1023): ")

    # If the user did not provide ports use the defaults
    if not startPort:
        startPort = 0

    if not endPort:
        endPort = 1023

Here we have an if function if len(sys.argv) == 4: We are checking if we have been passed 4 system arguments using the len (length) function on sys.argv.

If the length of sys.argv is four we set the variables remoteServerIP, startPort and endPort to be the values that we were provided.

Why are checking the four variables when we only need three? In Python sys.argv[0] is the script name. User passed variables start at 1

If anything other than four arguments was passed, we ignore the arguments. Instead, we prompt the user for input to populate the variables that way.

  • len() – Gets the length/size of something
  • sys.argv[x] – Gets the command line argument where x is the position.
  • socket.gethostbyname – Uses the imported socket module to convert a host name to an IP address if it is not already one.
  • str() – Coverts an input or a variable to a string
  • int() – Coverts an input or a variable to an integer (number)
  • # – Lines starting we a hash are comments
  • print(“something”) – Outputs/prints a message on the screen
  • variable = input(“something”) – Prompts the user for input, storing the input in variable

# Print a banner with information on which host we are about to scan
openPortCount = 0
startTime = datetime.datetime.now()

print("=" * 60)
print("Please wait, scanning remote host", remoteServerIP)
print("Scanning started at: " + startTime.strftime("%x %X"))
print("Please CTRL+C to cancel")
print("=" * 60)
print("Open Ports:")

Here we just print out some information for the user.

  • startTime = datetime.datetime.now() – Uses the datetime module to store the current datetime in the startTime variable. We will use this later to calculate the script duration
  • print(“=” * 60) – Prints an equals sign 60 times
  • startTime.strftime(“%x %X”) – Prints the start time in the local computers date and time format


# Scan the ports using the range function to loop 
try:
    for port in range(int(startPort),int(endPort)):  
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        result = sock.connect_ex((remoteServerIP, port))
        if result == 0:
            print(" Port {}: 	 Open".format(port))
            openPortCount += 1
        sock.close()

except KeyboardInterrupt:
    print("You pressed Ctrl+C")
    sys.exit()

except socket.gaierror:
    print("Error - Could not resolve hostname.")
    sys.exit()

except socket.error:
    print("Error - Couldn't connect to server")
    sys.exit()

The main part of our code. We use a for loop to loop through all ports in the range from our startPort to our endPort variables.

For each port, we try and open a socket to it. If the result is zero it means a socket was successfully opened. So the remote host is listening on that port. Print that as an open port and increase the openPortCount variable by 1 (+= 1).

We then close the port with sock.close()

The code is wrapped in a try block. So it will try out code but if any of the exceptions at the bottom are hit we can cleanly manage the exception


endTime = datetime.datetime.now()
runTime = endTime - startTime
runTime_in_s =  runTime.total_seconds()

runtimeDays    = divmod(runTime_in_s, 86400)        # Get days (without [0]!)
runtimeHours   = divmod(runtimeDays[1], 3600)               # Use remainder of days to calc hours
runtimeMinutes = divmod(runtimeHours[1], 60)                # Use remainder of hours to calc minutes
runtimeSeconds = divmod(runtimeMinutes[1], 1)               # Use remainder of minutes to calc seconds

Here we are calculating the duration the script has been running and format the result into something easily readable.

I have a separate post that goes into this in more details


# Print end information to screen
print("-" * 60)
print("Scanning complete")
print(" Duration: %d days, %d hours, %d minutes and %d seconds" % (runtimeDays[0], runtimeHours[0], runtimeMinutes[0], runtimeSeconds[0]))
print(" Ports scanned " + str(startPort) + " to " + str(endPort))
print(" " + str(openPortCount) + " open port(s) found")
print("-" * 60)

# Wait for the users input to terminate the program
input("Press the enter key to exit")

Finally, we just print out some useful information.

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.