networking · beginner · ~12 min · safe pentest lab

Open a listening TCP socket on localhost

Bind a TCP socket to loopback, set SO_REUSEADDR, and put it in listen mode.

Challenge

Open a listening TCP socket — on localhost only

Every TCP server starts with the same four-call dance: socket → setsockopt → bind → listen. After that, the socket is ready to accept connections. Your job is to wrap that dance into one helper.

Real-world frame

This is the prologue every server program executes — nginx, sshd, your own toy chat server, all of them. Done once, used everywhere.

Task

Implement:

int open_listener(int port);

It should:

  1. Create a TCP socket (AF_INET, SOCK_STREAM).
  2. Set SO_REUSEADDR so a quick restart doesn't fail with EADDRINUSE.
  3. Bind it to 127.0.0.1:port.
  4. Call listen(fd, 16).
  5. Return the listening file descriptor, or -1 on any failure.

Pass port = 0 to let the kernel pick an unused ephemeral port. The harness uses this to avoid colliding with other ports.

Function signature

int open_listener(int port);

Rules

  • Bind to INADDR_LOOPBACK (127.0.0.1) — never INADDR_ANY. This is the platform's safety rule: traffic never leaves the machine.
  • Close the fd on any error path. Leaking sockets is a quiet bug that catches up with you on the 10 000th test run.

Examples

port what happens returns
8080 (and nothing else is on it) binds + listens new fd (>=0)
0 kernel picks free port new fd; check with getsockname
privileged port like 22 without root bind fails with EACCES -1

Edge cases

  • EADDRINUSE on rapid relaunch → set SO_REUSEADDR before bind.
  • EACCES on ports < 1024 → use a high port (8080+) for labs.

Hints

  1. Conceptual: the four calls are in fixed order. Get any one wrong and the next one returns -1.
  2. Implementation: socket() first, then setsockopt(..., SO_REUSEADDR, ...), then memset a sockaddr_in and fill family + port (htons) + addr (htonl(INADDR_LOOPBACK)), then bind, then listen.
  3. Common bug: forgetting htons on the port. Without it the kernel binds to a weird port number (the bytes are swapped on little-endian CPUs).

Common mistakes

  • Forgetting SO_REUSEADDR. Your test rerun fails with EADDRINUSE.
  • Using INADDR_ANY. The lab policy is loopback only.
  • Forgetting to close the fd on the error path.

Learning connection

After this you'll connect from a client (tcp-client-connect), accept (echo-server), and eventually thread-per-connection (multi-client-server-threads).

Security note

The harness verifies the bound address is 127.0.0.1. Submissions that bind to INADDR_ANY will fail the loopback check. This exercise is for defensive learning in a local lab only. Do not run it on systems you do not own.

Why this matters

One helper, used by every TCP server you'll ever write. Master the four-call prologue once and you stop fumbling it.

Input format

One integer port. 0 means 'let the kernel pick'.

Output format

A non-negative fd on success, -1 on any failure.

Constraints

Loopback only (INADDR_LOOPBACK). SO_REUSEADDR must be set before bind.

Starter code

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

Common mistakes

Forgetting SO_REUSEADDR. Using INADDR_ANY (breaks the loopback-only rule). Missing htons/htonl.

Edge cases to handle

port=0 (kernel picks). port<1024 without root (EACCES). port already taken (EADDRINUSE).

Background lessons

Up next

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