Building a Simple Nonconcurrent TCP Port Scanner in F#

Search for a command to run...

No comments yet. Be the first to comment.
Modern software systems often separate control logic from high-performance execution logic. This design is common in networking, distributed systems, operating systems, storage engines, and embedded s

Introduction Engineering software often combines multiple programming languages to leverage their individual strengths. A common approach is to implement computational algorithms in native C or C++ wh

Deadlocks are one of the most common synchronization problems encountered in operating systems and concurrent programming. Although the concept is frequently introduced in textbooks, observing it insi

Memory and resource allocation are fundamental operations inside the Linux kernel. Whether assigning device IDs, managing CPU masks, allocating interrupt vectors, or tracking hardware resources, the k

The Linux scheduler is one of the most important components of the operating system. Every running program, background service, and kernel thread eventually interacts with the scheduler. In this artic

Port scanning is one of the most fundamental techniques in networking and security. Before diving into high-performance concurrent scanners, it's useful to understand how a basic scanner works by checking one port at a time.
In this article, we'll build the foundation of a nonconcurrent TCP port scanner using F#.
A port scanner attempts to connect to ports on a target system and determines whether those ports are:
| State | Meaning |
|---|---|
| Open | A service is listening and accepts connections |
| Closed | No service is listening |
| Filtered | Traffic is blocked by a firewall or filtering device |
TCP ports range from:
1 - 65535
For demonstration purposes, we'll scan only:
1 - 1024
These are commonly known as the well-known ports.
Before attempting any network connections, we need a way to generate:
hostname:port
combinations.
In F#, a simple loop can accomplish this.
open System
for i in 1 .. 1024 do
let address = sprintf "scanme.nmap.org:%d" i
printfn "%s" address
Example output:
scanme.nmap.org:1
scanme.nmap.org:2
scanme.nmap.org:3
...
scanme.nmap.org:1024
sprintfThe sprintf function formats strings similarly to C's printf.
let address = sprintf "scanme.nmap.org:%d" i
Here:
| Component | Purpose |
|---|---|
%d |
Integer placeholder |
i |
Current port number |
sprintf |
Returns a formatted string |
For port 80, the generated string becomes:
scanme.nmap.org:80
A TCP scanner determines whether a port is open by trying to establish a connection.
Conceptually:
Connect to target port
|
+-- Success -> Open
|
+-- Error -> Closed/Filtered
In .NET, this is typically done using:
System.Net.Sockets.TcpClient
Minimal example:
let client = new System.Net.Sockets.TcpClient()
If the connection succeeds, the port is likely open.
A successful connection consumes operating system resources.
Good network citizenship requires closing connections immediately after testing.
Conceptually:
client.Close()
Benefits:
The complete nonconcurrent scanning algorithm is straightforward:
for each port
generate address
attempt TCP connection
if success
print OPEN
close connection
else
continue
Only one port is tested at a time.
Port 1 -> wait
Port 2 -> wait
Port 3 -> wait
...
Port 1024
Advantages:
Disadvantages:
Modern scanners solve this using:
Even though professional scanners use concurrency, a nonconcurrent scanner teaches several important networking concepts:
Understanding this sequential model makes it much easier to appreciate how high-performance scanners achieve their speed.
sprintf provides convenient address formatting in F#.A nonconcurrent scanner may not be the fastest tool in a security engineer's toolkit, but it provides an excellent introduction to how network discovery and TCP-based reconnaissance actually work under the hood.