- Python program to check if a network port is open
- What is Socket Programming
- Checking if the network port is open using Python programming
- Test TCP ports with Python and Scapy
- Get started with a TCP port check
- Great Linux resources
- Meet Scapy
- Try a simple interactive TCP port scanner
- Create a Scapy-flavored custom port check
- What to learn next
Python program to check if a network port is open
In this Python programming article, you are going to learn how to check if a particular port is open. We are using socket.socket() and socket.connect_ex() to check if a network port is open. Before going into programming first let’s understand what is Socket Programming and some basic terminology of socket programming.
Before we go forward, we first need to know about socket programming.
What is Socket Programming
Socket Programming is a way of connecting two nodes on a network in order to communicate with each other. One socket or node listens to a particular port at an IP address and the other socket reaches out to the other port for creating a connection. In simple terms, they are called client-server programming. They are the backbones behind web browsing.
Python has a socket library for implementing socket programming. Let’s make a simple socket
import socket as sock s = sock.socket(sock.AF_INET, sock.SOCK_STREAM)
Here we made a socket and passed two parameters. The first parameter is AF_INET refers to address-family ipv4 and the second parameter SOCK_STREAM refers to the connection-oriented TCP protocol.
Checking if the network port is open using Python programming
Below are given steps to perform our task in Python:
1) First import the socket library as sock.
2) Create a socket object by calling sock.socket(family, type) with the family set to sock.AF_INET and the type set to SOCK_STREAM.
3) Create a destination as a tuple containing the IP address and the desired port number.
4) To check port is open call sock.connect_ex(destination) which returns 0 if the port is open, 1 if the port is close.
5) After checking the port is open, then socket by using socket.close().
import socket as sock create_socket = sock.socket(sock.AF_INET, sock.SOCK_STREAM) destination = ("127.0.0.1", 80) result = create_socket.connect_ex(destination) if result == 0: print("Port is open") else: print("Port is not open") create_socket.close()
Test TCP ports with Python and Scapy
In Stop using Telnet to test ports, I explored several alternative commands and scripts to test TCP connectivity. These commands and scripts range from basic tests to more sophisticated checks, but they are limited to the features provided by supporting tools like Netcat.
There is another option when you want exceptional control and flexibility for your TCP port checks: Do it yourself. Programming languages like Python offer socket programming APIs and access to sophisticated frameworks like Scapy to accomplish just that.
Get started with a TCP port check
Start with a simple TCP port check in Python:
#!/usr/bin/env python3 """ VERY simple port TCP port check https://docs.python.org/3/library/socket.html Author: Jose Vicente Nunez """ import socket from pathlib import Path from typing import Dict, List from argparse import ArgumentParser def load_machines_port(the_data_file: Path) -> Dict[str, List[int]]: port_data = <> with open(the_data_file, 'r') as d_scan: for line in d_scan: host, ports = line.split() port_data[host] = [int(p) for p in ports.split(',')] return port_data def test_port(address: str, dest_port: int) -> bool: try: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: if sock.connect_ex((address, dest_port)) == 0: return True return False except (OSError, ValueError): return False if __name__ == "__main__": PARSER = ArgumentParser(description=__doc__) PARSER.add_argument("scan_file", type=Path, help="Scan file with list of hosts and ports") ARGS = PARSER.parse_args() data = load_machines_port(ARGS.scan_file) for machine in data: for port in data[machine]: try: results = test_port(machine, port) except (OSError, ValueError): results = False if results: print(f":: OK") else: print(f":: ERROR")
This application opens the socket and assumes that any error means the port is closed.
$ ./tcp_port_scan.py port_scan.csv google.com:80: OK amazon.com:80: OK raspberrypi:22: OK raspberrypi:9090: OK raspberrypi:8086: OK raspberrypi:21: ERROR dmaf5:22: OK dmaf5:80: ERROR
It works as expected. But what if you could use a framework that allows you to skip all the boilerplate while doing more complex things?
Great Linux resources
Meet Scapy
Scapy describes itself as «a Python program that enables the user to send, sniff and dissect, and forge network packets.» Using this capability, you can build tools that can probe, scan, test, or discover networks.
Most Linux distributions have a package for Scapy. On Fedora, install it like this:
$ sudo dnf install -y python3-scapy.noarch
Scapy requires elevated privileges to run. If you decide to use pip , you may do the following:
sudo -i python3 -m venv /usr/local/scapy . /usr/local/scapy/bin/activate pip install --upgrade pip pip install wheel pip install scapy
Just remember to activate your virtual environment before calling Scapy if you install it that way. You can use Scapy as a library or as an interactive shell. Next, I’ll show you a few applications.
Try a simple interactive TCP port scanner
In the interactive mode, you call the Scapy terminal as root, as it requires elevated privileges.
For that, you will add layers. First, add an IP network layer:
Next, send the packets and capture answered and unanswered results:
Then analyze the answered results, filtering only open ports:
ans.summary(lfilter = lambda s,r: r.sprintf("%TCP.flags%") == "SA",prn=lambda s,r: r.sprintf("%TCP.sport% is open"))
$ sudo scapy3 -H >>> (ans, notanws) = sr(IP(dst="raspberrypi.home")/TCP(dport=[22,3000,8086])) Begin emission: Finished sending 3 packets. Received 5 packets, got 3 answers, remaining 0 packets >>> ans.summary(lfilter = lambda s,r: r.sprintf("%TCP.flags%") == "SA",prn=lambda s,r: r.sprintf("%TCP.sport% is open")) ssh is open hbci is open d_s_n is open
Not bad for just two lines of code, compared to 46 from the first Python script.
Next. you’ll create an automated port scanner, using what you learned before.
Create a Scapy-flavored custom port check
The interactive shell is nice when you are exploring and experimenting to find the best way to tackle a problem. But once you come up with a solution, you can make it a script:
#!/usr/bin/env -S sudo python3 """ VERY simple port TCP port check, using Scapy * https://scapy.readthedocs.io/en/latest/usage.html * https://scapy.readthedocs.io/en/latest/api/scapy.html * https://0xbharath.github.io/art-of-packet-crafting-with-scapy/scapy/sending_recieving/index.html * Please check out the original script: https://thepacketgeek.com/scapy/building-network-tools/part-10/ Author: Jose Vicente Nunez """ import os import sys import traceback from enum import IntEnum from pathlib import Path from random import randint from typing import Dict, List from argparse import ArgumentParser from scapy.layers.inet import IP, TCP, ICMP from scapy.packet import Packet from scapy.sendrecv import sr1, sr NON_PRIVILEGED_LOW_PORT = 1025 NON_PRIVILEGED_HIGH_PORT = 65534 ICMP_DESTINATION_UNREACHABLE = 3 class TcpFlags(IntEnum): """ https://www.wireshark.org/docs/wsug_html_chunked/ChAdvTCPAnalysis.html """ SYNC_ACK = 0x12 RST_PSH = 0x14 class IcmpCodes(IntEnum): """ ICMP codes, to decide https://www.ibm.com/docs/en/qsip/7.4?topic=applications-icmp-type-code-ids """ Host_is_unreachable = 1 Protocol_is_unreachable = 2 Port_is_unreachable = 3 Communication_with_destination_network_is_administratively_prohibited = 9 Communication_with_destination_host_is_administratively_prohibited = 10 Communication_is_administratively_prohibited = 13 FILTERED_CODES = [x.value for x in IcmpCodes] class RESPONSES(IntEnum): """ Customized responses for our port check """ FILTERED = 0 CLOSED = 1 OPEN = 2 ERROR = 3 def load_machines_port(the_data_file: Path) -> Dict[str, List[int]]: port_data = <> with open(the_data_file, 'r') as d_scan: for line in d_scan: host, ports = line.split() port_data[host] = [int(p) for p in ports.split(',')] return port_data def test_port( address: str, dest_ports: int, verbose: bool = False ) -> RESPONSES: """ Test the address + port combination :param address: Host to check :param dest_ports: Ports to check :return: Answer and Unanswered packets (filtered) """ src_port = randint(NON_PRIVILEGED_LOW_PORT, NON_PRIVILEGED_HIGH_PORT) ip = IP(dst=address) ports = TCP(sport=src_port, dport=dest_ports, flags="S") reset_tcp = TCP(sport=src_port, dport=dest_ports, flags="S") packet: Packet = ip / ports verb_level = 0 if verbose: verb_level = 99 packet.show() try: answered = sr1( packet, verbose=verb_level, retry=1, timeout=1, threaded=True ) if not answered: return RESPONSES.FILTERED elif answered.haslayer(TCP): if answered.getlayer(TCP).flags == TcpFlags.SYNC_ACK: rst_packet = ip / reset_tcp sr(rst_packet, timeout=1, verbose=verb_level) return RESPONSES.OPEN elif answered.getlayer(TCP).flags == TcpFlags.RST_PSH: return RESPONSES.CLOSED elif answered.haslayer(ICMP): icmp_type = answered.getlayer(ICMP).type icmp_code = int(answered.getlayer(ICMP).code) if icmp_type == ICMP_DESTINATION_UNREACHABLE and icmp_code in FILTERED_CODES: return RESPONSES.FILTERED except TypeError: traceback.print_exc(file=sys.stdout) return RESPONSES.ERROR if __name__ == "__main__": if os.getuid() != 0: raise EnvironmentError(f"Sorry, you need to be root to run this program!") PARSER = ArgumentParser(description=__doc__) PARSER.add_argument("--verbose", action="store_true", help="Toggle verbose mode on/ off") PARSER.add_argument("scan_file", type=Path, help="Scan file with list of hosts and ports") ARGS = PARSER.parse_args() data = load_machines_port(ARGS.scan_file) for machine in data: m_ports = data[machine] for dest_port in m_ports: ans = test_port(address=machine, dest_ports=dest_port, verbose=ARGS.verbose) print(f" -> :")
This script is more complex than the first, which uses Python alone, but it offers a more detailed explanation of the analyzed ports. You can run it like this: ./tcp_port_scan_scapy.py port_scan.csv :
$ ./tcp_port_scan_scapy.py port_scan.csv OPEN -> google.com:80 OPEN -> amazon.com:80 OPEN -> raspberrypi:22 OPEN -> raspberrypi:9090 OPEN -> raspberrypi:8086 CLOSED -> raspberrypi:21 FILTERED -> dmaf5:22 FILTERED -> dmaf5:80
The results for my system show one connection closed and two of them possibly filtered.
The real power of Scapy is the level of customization you now have from a familiar language like Python. The shell mode is particularly important as you can troubleshoot network problems easily while doing some exploration work.
What to learn next
Developing a TCP port scanner using a programming language like Python provides a level of flexibility and customization that is hard to achieve with scripting alone. By adding a specialized library like Scapy, you can perform even more complex network packet manipulation. Read this tutorial for Scapy, and you’ll be amazed at what you can do.