top of page

Building a Port Scanner with Python

Jv Cyberguard




One of my goals for this year was to work on improving my programming skills. The way I have been approaching this is by revisiting programming fundamentals and trying to build scripts that potentially meet various security use cases.


The project that I most recently worked on was building a Port scanner. The topics in python I leveraged to complete this were:



Python for Defenders Part 1 and 2 by Michael Taggart: https://taggartinstitute.org/p/python-for-defenders-pt1


First a little about sockets:

A socket is an endpoint for sending and receiving data over the network . In Python’s socket module, the term “socket family” refers to the type of addresses your socket can communicate with. Here are some common examples:

  • AF_INET: This indicates IPv4 addresses (e.g., 127.0.0.1 or 192.168.1.10).

  • AF_INET6: This indicates IPv6 addresses (e.g., ::1 or 2001:db8::1).

  • AF_UNIX: This is for Unix domain sockets, which allow inter-process communication on the same machine without using network protocols.

When you create a socket like:

python
socket.socket(socket.AF_INET, socket.SOCK_STREAM)

we’re telling Python:

  1. Use IPv4 addresses (AF_INET).

  2. Use a stream-based protocol (TCP) via SOCK_STREAM.

Other socket families (like AF_INET6 for IPv6) are similarly specified if you want to handle different address formats.



Project Overview:


This project scans a specified host (or URL) by checking a range of ports to see which ones are open. It uses type hinting (e.g., ip: str) to improve code readability, a practice I picked up in my Python course.


We rely on the built-in socket module to handle all socket programming functions. By creating a TCP socket (IPv4) and attempting to connect to each port, we can detect whether that port is open or closed.


To accommodate different operating systems, we import errno. A closed or refused port might return 111 on Linux but 10061 on Windows. By comparing the connection result to errno.ECONNREFUSED, our code stays portable and easy to maintain.


For demonstration purposes, we’ll target the localhost (loopback) IP address 127.0.0.1. This makes it easy to test without needing an external server.


Functions

  • scan_port(ip, port): Attempts to connect to a specific port on the target IP. Returns 0 if the port is open; otherwise, returns an OS-dependent error code.

  • port_scanner(ip, port_range): Iterates through a range of ports, calls scan_port on each, and writes the results to a file. This helps you see exactly which ports are open or closed.


With that in mind, let’s look at the code that brings it all together.


import socket
import errno

ip: str = "127.0.0.1"
port_range: range = range(10, 85)

def scan_port(target: str, port: int) -> int:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.settimeout(1)
    result: int = s.connect_ex((target, port))
    s.close()
    return result

def port_scanner(ip: str, port_range: range) -> None:
    with open("scan1.txt", "w") as f:
        for port in port_range:
            verdict: int = scan_port(ip, port)
            if verdict == 0:
                f.write(f"{port} is open\n")
            elif verdict == 111:
                # For cross-platform, consider "verdict == errno.ECONNREFUSED"
                f.write(f"{port} is closed. Connection refused\n")
            else:
                f.write(f"{port} Error - Connection refused: {verdict}\n")


port_scanner(ip, port_range)


The comments didn't render properly on the webpage so I will place a screenshot of the code here.


To test, I opened a few terminal tabs and setup a netcat listener, as well as, a python http server to listen on port 80.






Ran the code.


Looking at the file we see the open ports.



That's a wrap up of the port scanning project that I've been working on. I still have lots to learn on my python journey, but we will keep taking it one project at a time and see where this journey leads - learning to leverage python for security engineering.


Until next time!

 
 
 

Comentarios


©2025 by The SOC spot

bottom of page