Main code:
import sys
import socket
from datetime import datetime
import threading
# Function to scan a port
def scan_port(target,port):
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(1)
result = s.connect_ex((target,port)) #error indictor - if 0, port is open
if result == 0:
print(f"Port {port} is open")
s.close()
except socket.error as e:
print(f"Socket error on port {port}: {e}")
except Exception as e:
print(f"Unexpected error on port {port}: {e}")
#Main function - argument validation and target definition
def main():
if len(sys.argv) == 2:
target = sys.argv[1]
else:
print("Invalid number of arguments.")
print("Usage: python.exe scanner.py <target>")
sys.exit(1)
# Resolve the target hostname to an IP address
try:
target_ip = socket.gethostbyname(target)
except socket.gaierror:
print(f"Error: Unable to resolve hostname {target}")
sys.exit(1)
# Add a pretty banner
print("-" * 50)
print(f"Scanning target {target_ip}")
print(f"Time started: {datetime.now()}")
print("-" * 50)
try:
# Use multithreading to scan ports concurrently
threads = []
for port in range(1,65536):
thread = threading.Thread(target=scan_port, args=(target_ip, port))
threads.append(thread)
thread.start()
# Wait for all threads to complete
for thread in threads:
thread.join()
except KeyboardInterrupt:
print("\nExiting program.")
sys.exit(0)
except socket.error as e:
print(f"Socket error: {e}")
sys.exit(1)
print("\nScan completed!")
if __name__ == "__main__":
main()
sys → built-in module that lets you interact with the Python interpreter.
- We’ll use it for
sys.argv(command-line arguments) andsys.exit().
import socket
- Imports the socket module, which lets you create network connections (TCP/UDP).
from datetime import datetime
from ... import ...→ import just a specific thing from a module.datetimemodule → deals with date and time.datetimeclass → represents a specific point in time.- We'll use
datetime.now()to print when the scan starts.
import threading
- Imports the threading module, which allows you to run many functions “at the same time” (concurrently).
- We’ll use it to speed up scanning ports.
def scan_port(target,port):
-
def→ defines a function. -
scan_port→ function name. -
(target, port)→ parameters:target= IP address or hostname (string).port= port number (integer). This function will scan one single port on the target.
try: Starts a try block → we expect some code might raise errors (e.g., network issues). If something goes wrong, we’ll catch it with except.
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s → variable (the socket object). socket.socket() → creates a new socket. AF = Address Family INET = Internet (IPv4) Means: use IPv4 addresses. socket.SOCK_STREAM Type of socket: TCP (stream-based). So this line creates a TCP IPv4 socket that we’ll use to talk to one port.
s.settimeout(1)
s.settimeout(1) → sets a timeout of 1 second. If the connection takes more than 1 second, we stop waiting and move on. This prevents the scanner from hanging forever on a slow or filtered port.
result = s.connect_ex((target,port)) #error indictor - if 0, port is open
s.connect_ex(...):- This tries to connect to
(target, port)but: - Instead of raising an exception on failure, it returns an error code.
0means success → port is open.- Non-zero means failure → port is closed/filtered/etc.
- This tries to connect to
(target, port)→ a tuple containing:- target IP or hostname
- port number
result = ...→ store the return value inresult.
if result == 0:
print(f"Port {port} is open")
if result == 0:→ check if the connect was successful.print(f"...")→ f-string.
Inside the f-string:
"Port {port} is open"→{port}is replaced by the actual port number.- Example:
Port 80 is open
s.close()
Close the socket. Very important to free system resources.
except socket.error as e:
print(f"Socket error on port {port}: {e}")
except socket.error as e: Catches errors related specifically to the socket module. e → exception object (contains error message). print(f"...{e}") → show which port and what error happened.
def main():
#Main function - argument validation and target definition
if len(sys.argv) == 2:
target = sys.argv[1]
else:
print("Invalid number of arguments.")
print("Usage: python.exe scanner.py <target>")
sys.exit(1)
Imagine you run this in the terminal:
python scanner.py 192.168.1.1
Then:
sys.argv == ["scanner.py", "192.168.1.1"]
-
sys.argv→ a list of what you typed afterpython -
sys.argv[0]→"scanner.py"(the script name) -
sys.argv[1]→"192.168.1.1"(the target you typed)
So:
sys.argv[0] # script name
sys.argv[1] # target
len(sys.argv) = how many items are in that list.
Example:
-
If you run:
python scanner.py 192.168.1.1
→sys.argv = ["scanner.py", "192.168.1.1"]
→len(sys.argv) == 2 -
If you run:
python scanner.py(no target)
→sys.argv = ["scanner.py"]
→len(sys.argv) == 1
if len(sys.argv) == 2:
Read this like normal English:
“If the number of arguments is exactly 2…”
2 things =
-
Script name
-
Target
So this checks:
“Did the user give one target (like an IP or domain)?”
`target = sys.argv[1]`
If there are 2 items:
-
Take the second one →
sys.argv[1] -
Save it into a variable called
target
Example:
python scanner.py google.com
Then:
sys.argv[1] == "google.com" target = "google.com"
Now your script knows what to scan.
If len(sys.argv) != 2 (not equal to 2), that means:
-
User didn’t give a target
or -
Gave too many things
Then this runs:
else:
print("Invalid number of arguments.")
print("Usage: python.exe scanner.py <target>")
sys.exit(1)
-
print("Invalid number of arguments.")
→ Tell the user: “You didn’t type it correctly.” -
print("Usage: python.exe scanner.py <target>")
→ Show them how to type it correctly.
Example:python.exe scanner.py 192.168.1.1 -
sys.exit(1)
→ Stop the program right now.
→1means “there was an error”.
(By convention:0= OK, non-zero = problem)
# Resolve the target hostname to an IP address
try:
target_ip = socket.gethostbyname(target)
-
socket.gethostbyname(target):-
If
targetis a hostname (likegoogle.com):- It will resolve DNS and return an IP address (e.g.,
142.250.182.78).
- It will resolve DNS and return an IP address (e.g.,
-
If
targetis already an IP, it just returns it.
-
-
target_ip = ...→ store the result.
except socket.gaierror:
print(f"Error: Unable to resolve hostname {target}")
sys.exit(1)
socket.gaierror is a specific type of error from the socket module.
It happens when DNS resolution fails — meaning Python can’t turn a hostname into an IP address.
This error happens if DNS resolution fails (invalid domain).
gai = Get Address Info So gaierror = get-address-info error.
# Add a pretty banner
print("-" * 50)
print(f"Scanning target {target_ip}")
print(f"Time started: {datetime.now()}")
print("-" * 50)
try:
# Use multithreading to scan ports concurrently
threads = []
-
try:→ wrap the whole scanning logic to catch interrupts or errors. -
Comment: we’re going to use threads to speed up scanning.
-
threads = []- Create an empty list to store thread objects.
for port in range(1,65536):
for port in range(1, 65536):
-
Loops from port
1up to65535. -
rangestops before65536. -
This covers all valid TCP ports (1–65535).
thread = threading.Thread(target=scan_port, args=(target_ip, port))
A thread is like starting a second worker that can run code at the same time as the main program.
Instead of scanning ports one by one, threads let us scan many ports at the same time, making it much faster.
-
Call the
Threadclass from thethreadingmodule. -
This creates a new thread object, but doesn’t start it yet.
-
targetmeans:What function should this thread run?
-
Here,
scan_portis the name of a function. -
So the thread will execute the
scan_port()function.
-
argsmeans the arguments to send to that function. -
Since
scan_port()expects two parameters:def scan_port(target, port): -
We provide them inside parentheses
( )as a tuple:-
target_ip→ the IP to scan -
port→ the specific port number
-
“Create a new thread that will call the function scan_port() and give it the values target_ip and port.”
threads.append(thread)
-
Add the newly created thread to the
threadslist. -
We track them so we can later
.join()them (wait for them to finish).thread.start()
Because this is inside the for loop, you’re creating a new thread for each port.
# Wait for all threads to complete
for thread in threads:
thread.join()
-
for thread in threads:→ loop over the list of all thread objects. -
thread.join()→ wait for each thread to finish before continuing.- This ensures that all ports have been scanned before we print "Scan completed!".
except KeyboardInterrupt:
print("\nExiting program.")
sys.exit(0)
-
except KeyboardInterrupt:- This happens when you press
Ctrl + Cin the terminal.
- This happens when you press
-
When pressed:
-
Print message.
-
Exit gracefully with
sys.exit(0).
-
except socket.error as e:
print(f"Socket error: {e}")
sys.exit(1)
-
Catch any global socket errors in the scan loop.
-
Print the error and exit with status
1.
if __name__ == "__main__":
main()
In every Python file, there is a built-in variable called __name__.
Python automatically sets __name__ depending on how the file is being run.
| How the file is used | What __name__ becomes |
|---|---|
The file is run directly (like python scanner.py) |
"__main__" |
| The file is imported into another Python file | The file’s name, e.g., "scanner" |
if __name__ == "__main__":
This means:
Check if this file is being run directly (not imported).
If the answer is yes, then execute:
main()
Which means:
Run the main() function
Because sometimes a Python file can be:
python scanner.py
import scanner
If another script imports it, we do NOT want to automatically start scanning ports immediately — that would be chaos 😅
So this code prevents main() from running unless the script is executed directly.
print("Hello from script1")
import script1
If you run:
python script2.py
Output:
Hello from script1
Because imported file runs automatically.
Sometimes we don’t want that behavior.
So we modify script1:
def hello():
print("Hello from script1")
if __name__ == "__main__":
hello()
Now running script2 prints nothing unless we call hello() manually.
-
Reads the target from the command line.
-
Resolves the target to an IP.
-
Prints a header with time.
-
For each port from
1to65535:-
Starts a new thread running
scan_port(target_ip, port). -
Each thread:
-
Tries to connect to that port.
-
If success → prints “Port X is open”.
-
-
-
Waits for all threads to finish.
-
Prints “Scan completed!”.