networking · intermediate · ~12 min · safe pentest lab

Localhost port checker (open/closed)

Probe one TCP port on localhost using the same pattern a sysadmin uses to check 'is my service up'.

Challenge

Is something listening on this localhost port?

You're writing a tiny diagnostic helper: given a port number, tell the caller whether something on your own machine is listening on 127.0.0.1:port. No remote hosts. No scanning. Just a yes/no for one port on this machine.

Real-world frame

This is the defensive analog of a port scanner. Sysadmins write a one-line version of this every other week ("is my dev server still up?"). The platform's safety rule turns the design constraint into the API: the function takes no host parameter at all — it's structurally incapable of touching anyone else's machine.

Task

Implement:

int is_port_open(int port);

Return values:

  • 1 — a TCP listener is on 127.0.0.1:port (your connect() succeeded).
  • 0 — the port is refused (ECONNREFUSED; no listener).
  • -1 — a real error (couldn't even create a socket).

Function signature

int is_port_open(int port);

Input

A port number (an int).

Output

An integer: 1, 0, or -1.

Rules

  • Hard-code 127.0.0.1 inside the function. There is no host parameter. This is a deliberate structural guarantee: the function cannot probe third-party hosts.
  • Always close the socket fd before returning — including on error paths.

Examples

port what's on it returns
8080 (server is up) listener 1
1 (almost certainly nothing) nothing 0
-1 (invalid) n/a -1 (socket creation may fail; see implementation)

Edge cases

  • ECONNREFUSED is the explicit "no listener" signal — return 0.
  • Any other errno → return -1.
  • Don't leak the socket fd on the success path.

Hints

  1. Conceptual: this is just socket() + connect(). If connect succeeds, something's there. If it returns -1 with errno == ECONNREFUSED, nothing is.
  2. Implementation: build a sockaddr_in with INADDR_LOOPBACK and htons(port). Call connect(). Save errno before closing the socket (close can clobber it).
  3. Common bug: leaking the fd. Every code path must close() before returning.

Common mistakes

  • Adding a host parameter "for flexibility". The whole point of the design is that there isn't one.
  • Forgetting to check errno against ECONNREFUSED specifically — other errnos shouldn't return 0.

Learning connection

After this exercise you can build the smallest possible "did my dev server start?" check in one helper. Combined with tcp-client-connect, you have a complete client-side diagnostic.

Security note

The hard-coded 127.0.0.1 is the structural guarantee that this function can only probe the local machine. This exercise is for defensive learning in a local lab only. Do not use any variant of this technique on systems you do not own or have explicit permission to test.

Why this matters

The simplest 'is it alive?' check. Hard-coded to loopback so it's structurally incapable of misuse.

Input format

One integer port number.

Output format

1 (open), 0 (closed/refused), or -1 (real error).

Constraints

Hard-code 127.0.0.1. No host parameter. Close the fd before returning on every code path.

Starter code

#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
int is_port_open(int port) {
    /* TODO */
    return -1;
}

Common mistakes

Leaking the fd. Treating every -1 as 'closed' instead of distinguishing ECONNREFUSED from real errors. Adding a host parameter (defeats the safety design).

Edge cases to handle

Port 1 / port 65535 boundaries. A port that is open AND immediately closes the connection. Errno being clobbered by close() — save it first.

Background lessons

Up next

Solve this exercise in the browser editor — compile and run against the test harness, no setup required.