Skip to main content

Command Palette

Search for a command to run...

Building a Simple Nonconcurrent TCP Port Scanner in F#

Updated
4 min read
Building a Simple Nonconcurrent TCP Port Scanner in F#

Building a Simple Nonconcurrent TCP Port Scanner in F#

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#.


What Is a Port Scanner?

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.


Step 1: Generate Target Addresses

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

Understanding sprintf

The 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

Step 2: Attempt a TCP Connection

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.


Step 3: Always Close Connections

A successful connection consumes operating system resources.

Good network citizenship requires closing connections immediately after testing.

Conceptually:

client.Close()

Benefits:

  • Releases socket resources
  • Prevents connection leaks
  • Reduces system overhead
  • Mimics professional scanner behavior

Scanner Workflow

The complete nonconcurrent scanning algorithm is straightforward:

for each port
    generate address
    attempt TCP connection

    if success
        print OPEN
        close connection

    else
        continue

Why Is It Called Nonconcurrent?

Only one port is tested at a time.

Port 1  -> wait
Port 2  -> wait
Port 3  -> wait
...
Port 1024

Advantages:

  • Easy to understand
  • Easy to debug
  • Minimal code complexity

Disadvantages:

  • Slow
  • Network latency accumulates
  • Does not scale to large scans

Modern scanners solve this using:

  • Threads
  • Async I/O
  • Tasks
  • Event-driven networking

Educational Value

Even though professional scanners use concurrency, a nonconcurrent scanner teaches several important networking concepts:

  • TCP connection establishment
  • Socket programming
  • Port states
  • Error handling
  • Resource management
  • Network reconnaissance fundamentals

Understanding this sequential model makes it much easier to appreciate how high-performance scanners achieve their speed.


Key Takeaways

  • TCP scanners determine port availability by attempting connections.
  • A loop can generate target addresses for thousands of ports.
  • sprintf provides convenient address formatting in F#.
  • Successful connections should always be closed.
  • Nonconcurrent scanners are simple but relatively slow.
  • This approach forms the foundation for advanced concurrent port scanners.

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.