Building a Command-Line Network Port Scanner in Python
In this tutorial, we’ll explore network programming in Python by building a command-line network port scanner. This tool will allow us to specify a target host and a range of ports to scan, and will report back on which ports are open and which are closed.
Prerequisites
Before we start, ensure that you have:
- A basic understanding of Python programming.
- Familiarity with networking concepts.
- Python installed on your machine. If you don’t have Python installed, you can download it from Python’s official website.
What is a Port Scanner?
A port scanner is a software application designed to probe a server or host for open ports. This information can be used by administrators to verify security policies of their networks and by attackers to identify running services on a host with the view to compromise it.
Now, let’s get started with our code.
Steps
- Import necessary libraries
- Initialize argparse and parse arguments
- Define the port scanning function
- Define the thread worker function
- Populate the queue with ports to scan
- Create threads and start the scan-
- Wait for all threads to finish and print the results
Importing the required modules
First, we need to import the necessary libraries:
python
import socketimport threadingimport argparsefrom queue import Queue
Setting up The Options Menu With Argparse
We’re using Python’s built-in socket, threading, argparse and queue libraries. socket is used to create and interact with network sockets. threading is used to create and manage threads. argparse is used to handle command-line arguments, and Queue from the queue library is used to distribute work among multiple threads.
Now, we’ll set up our argument parser and define our port scanning function. Here is where we can define the help menu that will be shown to make using the tool easier. We choose -t to define our target ip address and -r for a range of ports to scan.
python
# Initialize argparse and setup the help menu to be triggered for the scannerparser = argparse.ArgumentParser(description="Port scanner. Scans the specified host for open ports.")parser.add_argument("-t", "--target", help="Specify the target. Default is localhost.", default="127.0.0.1")parser.add_argument("-r", "--range", help="Specify the range of ports to be scanned. Default is 1-1024.", default="1-1024")# Parse argumentsargs = parser.parse_args()# Extract host and port range from argumentshost = args.targetport_range = args.range.split("-")start_port = int(port_range[0])end_port = int(port_range[1])# Initialize queues and lists for handling portsqueue = Queue()open_ports = []closed_ports = []# The actual port scanning functiondef port_scanner(port):try:# Create a new socket using the AF_INET address family (IPv4) and the SOCK_STREAM socket type (TCP)socket_obj = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# Try to connect to the host using that socketsocket_obj.connect((host, port))return Trueexcept:return False
This block initializes our argument parser, which allows us to take inputs directly from the command line. It also sets up our port scanner function, which tries to connect to a given port and returns True if successful (the port is open), and False if not (the port is closed).
Implementing Threading
Next, we’ll add threading and our user-friendly output:
python
# The function to handle threadingdef worker():while not queue.empty():port = queue.get()if port_scanner(port):print(f"Port {port} is open!")open_ports.append(port)else:print(f"Port {port} is NOT open!")closed_ports.append(port)
Next, we create a list to store our threads and create new threads targeting our worker function:
python
# Creating a new thread for each of the port in the queuethread_list = []for t in range(200): # Creating 200 threadsthread = threading.Thread(target=worker)thread_list.append(thread)
Here, we’re creating 200 threads (this number can be adjusted as needed) and setting their target function as our worker function defined earlier. Each of these threads, when started, will work concurrently, taking ports from the queue and scanning them.
Initiate the Scan and Start Threading!
Now we’re ready to start our scan:
python
# Starting the scanprint(f"Starting the scan on host: {host}, over port range: {start_port}-{end_port}")# Starting all threadsfor thread in thread_list:thread.start()
After creating them and initiating the scan, we start all our threads:
python
# Starting all threadsfor thread in thread_list:thread.start()
Each thread will start working on the worker function concurrently, taking ports from the queue and scanning them.
After starting them, we also want to wait for all threads to finish before we proceed. We do this by joining all threads:
python
# Waiting for all threads to finishfor thread in thread_list:thread.join()
The join() function makes sure that the main program waits for all threads to complete their tasks before it continues. This is necessary because we want to make sure that all ports are scanned before we print out the results.
Once all threads have finished their work, we print the results: print(“Scan finished!“)
python
# Printing the resultsprint(f"Open ports on {host} are: {open_ports}")# print(f"Closed ports on {host} are: {closed_ports}")
Putting it All Together
Have a look at our finished Network Port scanner!
python
# Import necessary librariesimport socketimport threadingimport argparsefrom queue import Queue# Initialize argparseparser = argparse.ArgumentParser(description="Port scanner. Scans the specified host for open ports.")parser.add_argument("-t", "--target", help="Specify the target. Default is localhost.", default="127.0.0.1")parser.add_argument("-r", "--range", help="Specify the range of ports to be scanned. Default is 1-1024.", default="1-1024")# Parse argumentsargs = parser.parse_args()# Extract host and port range from argumentshost = args.targetport_range = args.range.split("-")start_port = int(port_range[0])end_port = int(port_range[1])# Initialize queues and lists for handling portsqueue = Queue()open_ports = []closed_ports = []# Define the port scanning functiondef port_scanner(port):try:# Create a new socket using the AF_INET address family (IPv4) and the SOCK_STREAM socket type (TCP)socket_obj = socket.socket(socket.AF_INET, socket.SOCK_STREAM)socket_obj.settimeout(1)# Try to connect to the host using that socketsocket_obj.connect((host, port))return Trueexcept:return False# Define the thread worker functiondef worker():while not queue.empty():port = queue.get()if port_scanner(port):print(f"Port {port} is open!")open_ports.append(port)else:print(f"Port {port} is NOT open!")closed_ports.append(port)# Populate the queue with ports to scanfor port in range(start_port, end_port+1):queue.put(port)# Create threadsthread_list = []for t in range(200): # Creating 200 threadsthread = threading.Thread(target=worker)thread_list.append(thread)# Start the scanprint(f"Starting the scan on host: {host}, over port range: {start_port}-{end_port}")# Start all threadsfor thread in thread_list:thread.start()# Wait for all threads to finishfor thread in thread_list:thread.join()# Print the resultsprint("Scan finished!")print(f"Open ports on {host} are: {open_ports}")print(f"Closed ports on {host} are: {closed_ports}")
And that’s it! You’ve just built a multi-threaded network port scanner in Python that scans a specified range of ports on a target host and reports back the open and closed ports. Remember, it’s important to use tools like this responsibly, and only on networks where you have permission to do so.