Building a Simple Port Scanner in C++ thumbnail

Building a Simple Port Scanner in C++

⏱ approx. 10 min views 431 likes 1 LOG_DATE:2025-11-06
TOC

Overview #

In this experiment I built a "port scanner" in C++ using the WinSock API (Windows Sockets) — a small tool that checks whether a given IP and port are open.

The goal: by walking through the mechanics of TCP connect attempts, learn the basics of network programming and socket-level communication.

In security work, port scanning is one of the most fundamental techniques attackers use to see what services (open ports) are alive on a target server. Understanding how it works is essential for understanding why firewalls and intrusion-detection systems matter.# What is a port scanner?

A port scanner is a program that probes a host on the network to see which TCP/UDP ports are reachable (open). It's used to figure out what services a server is offering — port 80 / 443 for a web server, port 22 for SSH, and so on.

It's a legitimate tool for sysadmins doing network management or vulnerability assessment. It's also abused by attackers as part of reconnaissance — the lead-up to unauthorized access.

How it works #

There are several scan techniques — TCP SYN scan, TCP Connect scan, and many others. We'll use the simplest and most straightforward one: TCP Connect scan.

We implement it on top of WinSock (Windows Sockets), the network communication facility Windows provides.

TCP Connect scan #

A TCP Connect scan attempts a full TCP connection — the same three-way handshake that ordinary TCP traffic uses — to the target port.

  1. The client (the scanner) creates a socket (socket function).
  2. It sends a connection request to the target IP and port (connect function).
  3. If it succeeds: connect returns success — the OS completed the three-way handshake. The port is OPEN.
  4. If it fails: connect returns an error — the server didn't respond, or sent back an RST. The port is either CLOSED or filtered (by a firewall, etc.).

C++ code walkthrough #

main — initialization and argument parsing #

The program's entry point. First, WSAStartup initializes WinSock for use. Then we parse command-line arguments using argc (count) and argv (array), accepting -ip <IP> and -p <PORT>.

std::stoi converts the port number from string to int. We then check that the arguments are valid and proceed with the scan.

int main(int argc, char* argv[]) {

    WSADATA wsaData;
    int result = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (result != 0) {
        std::cerr << "WSAStartup failed: " << result << std::endl;
        return 1;
    }

    std::string targetIP;
    int targetPort = 0; // initialize to 0

    // --- parse command-line arguments ---
    for (int i = 1; i < argc; ++i) { // skip argv[0] (the program name)
        std::string arg = argv[i];

        if (arg == "-ip") {
            if (i + 1 < argc) { // is there an IP address after -ip?
                targetIP = argv[i + 1]; // grab the next argument as the IP
                i++; // advance past the IP
            }
        } else if (arg == "-p") {
            if (i + 1 < argc) { // is there a port number after -p?
                try {
                    // Convert the next argument (string) to int
                    targetPort = std::stoi(argv[i + 1]);
                    i++; // advance past the port
                } catch (const std::exception&) {
                    // stoi failed (e.g. "-p abc")
                    std::cerr << "Error: Invalid port number." << std::endl;
                    WSACleanup();
                    return 1;
                }
            }
        }
    }

    // Make sure we got both an IP and a port
    if (targetIP.empty() || targetPort == 0) {
        std::cerr << "Usage: " << (argc > 0 ? argv[0] : "program.exe") << " -ip <ip_address> -p <port>" << std::endl;
        WSACleanup();
        return 1;
    }

    // --- run the scan ---
    // (omitted) ...

    WSACleanup(); // clean up WinSock when we're done
    return 0;
}
</port></ip_address>

isPortOpen — the actual scanning function #

This is the heart of the scan.

  • socket(AF_INET, SOCK_STREAM, 0): create a TCP (SOCK_STREAM) socket for IPv4 (AF_INET).
  • sockaddr_in: struct that holds the destination's information. Set sin_family (AF_INET), sin_port (port number), and sin_addr (IP address).
  • inet_pton(...): convert a string-formatted IP address like "127.0.0.1" into the binary form the network expects, and store it in the struct.
  • connect(...): actually try to connect. If this doesn't return SOCKET_ERROR, the connection succeeded — the port is OPEN.
  • closesocket(sock): always close the socket and release its resources, whether the connection succeeded or not.
bool isPortOpen(const std::string& ip, int port) {
    SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock == INVALID_SOCKET) {
        return false;
    }

    sockaddr_in server_addr;
    ZeroMemory(&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port); // convert port number to network byte order

    // Convert IP address string to binary form
    if (inet_pton(AF_INET, ip.c_str(), &server_addr.sin_addr) <= 0) {
        closesocket(sock);
        return false;
    }

    bool isOpen = false;
    // Try to connect
    if (connect(sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) != SOCKET_ERROR) {
        isOpen = true; // connection succeeded
    }

    closesocket(sock); // close the socket
    return isOpen;
}

Ethical notes #

This code is for educational purposes — to learn network programming and security fundamentals.

Running a port scan against a network or server you don't manage, without permission, is generally considered a violation of terms of service and unauthorized access (or preparation for it). It can be treated as offensive reconnaissance and lead to legal action or access bans. When you run it, do so only against localhost (127.0.0.1) or an environment you control.

Why port scanning is dangerous #

Attackers use port scans to find a way in. Once they spot an open port, they try to identify the service version behind it — Apache 2.4.41, OpenSSH 8.0, that kind of thing.

If that specific version has a known vulnerability (a security hole), the attacker exploits it to break into the system. Port scanning is the first step in that chain.

Defense #

Defending against port scans is fundamental server hygiene.

  • Firewalls: the most basic measure. Any port that doesn't need to be exposed externally should be closed (blocked) at a firewall — Windows Defender Firewall, your network appliance, whatever you use.
  • Run as few services as possible: keep the running services (and therefore open ports) on each server to the minimum.
  • IDS/IPS (intrusion detection / prevention): opening a large number of ports in rapid succession (a scan) is an abnormal traffic pattern. An IDS/IPS detects it and can automatically block traffic from the source.
  • The "TCP Connect scan" used here is also one of the easier scans to detect, because it leaves connection-attempt entries in the target's logs.

COMMENTS 1

くまん
すごいね~

Post a comment