Skip to content

Random Port Generator — C source

Generate one or many random TCP/UDP port numbers across registered, ephemeral, or the full range - optionally unique. Runs entirely in your browser with crypto-grade randomness.

This is the C implementation — the same logic the interactive tool runs, in a shareable, citable form.

/*
 * random-port-generator — C port: random/dynamic port picker.
 *
 * Display port of the CosmoDev Random Port Generator tool — same contract as
 * cli/random-port-generator/random-port-generator.go (the live Go twin) and
 * src/lib/random-port.ts (the canonical TypeScript implementation). The
 * default rng draws 32 bits from OpenSSL RAND_bytes (a CSPRNG standing in
 * for the TS crypto.getRandomValues default) scaled into [0, 1). Invalid
 * custom ranges return -1 instead of throwing.
 */
#include <openssl/rand.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

/* Named ranges — `Range` in the Go twin; the zero value is Registered,
 * matching the Go twin and the TS default (registered ports). */
typedef enum { PORT_REGISTERED, PORT_EPHEMERAL, PORT_ANY, PORT_CUSTOM } port_range_t;

/* PortOptions in the TS lib (count/unique cover random_ports). min/max of 0
 * mean unset, falling back to the full-range defaults like
 * resolveRange({range:'custom'}) in the TS lib. */
typedef struct {
    port_range_t range;
    int min;             /* custom-range lower bound (PORT_CUSTOM only) */
    int max;             /* custom-range upper bound (PORT_CUSTOM only) */
    int count;           /* how many ports (random_ports); < 1 clamps to 1 */
    int unique;          /* dedupe (random_ports) */
    double (*rng)(void); /* injectable random in [0, 1); NULL = CSPRNG */
} port_options_t;

/* --------------------------------------------------------------- ranges --- */

/* resolveRange() in the TS lib. */
static void port_resolve_range(const port_options_t *opts, int *lo, int *hi)
{
    switch (opts->range) {
    case PORT_ANY:
        *lo = 1;
        *hi = 65535;
        return;
    case PORT_EPHEMERAL:
        *lo = 49152;
        *hi = 65535;
        return;
    case PORT_CUSTOM:
        *lo = opts->min ? opts->min : 1;
        *hi = opts->max ? opts->max : 65535;
        return;
    default: /* PORT_REGISTERED */
        *lo = 1024;
        *hi = 49151;
        return;
    }
}

/* assertRange() in the TS lib — the TS throw becomes a 0 verdict. */
static int port_range_ok(int lo, int hi)
{
    if (lo < 0 || lo > 65535 || hi > 65535 || lo > hi) {
        return 0;
    }
    return 1;
}

/* ----------------------------------------------------------------- random --- */

/* defaultRng() in the TS lib: 32 CSPRNG bits / 2^32, like the TS
 * crypto.getRandomValues(new Uint32Array(1))[0] / 2**32. */
static double default_rng(void)
{
    unsigned char buf[4];
    uint32_t v;

    if (RAND_bytes(buf, sizeof buf) != 1) {
        return 0.0; /* only on a broken CSPRNG; keep the tool alive */
    }
    v = (uint32_t) buf[0] | ((uint32_t) buf[1] << 8) |
        ((uint32_t) buf[2] << 16) | ((uint32_t) buf[3] << 24);
    return v / 4294967296.0;
}

/* randomPort() in the TS lib — Port() in the Go twin. -1 on a bad range. */
static int random_port(const port_options_t *opts)
{
    int lo, hi;
    double (*rng)(void) = opts->rng ? opts->rng : default_rng;

    port_resolve_range(opts, &lo, &hi);
    if (!port_range_ok(lo, hi)) {
        return -1;
    }
    return lo + (int) (rng() * (double) (hi - lo + 1));
}

/* randomPorts() in the TS lib — Ports() in the Go twin. Writes up to `cap`
 * ports into `out` and returns how many, or -1 on a bad range / no room. */
static int random_ports(const port_options_t *opts, int *out, int cap)
{
    int lo, hi, count, capacity, n, i, j, t;
    double (*rng)(void) = opts->rng ? opts->rng : default_rng;
    int *pool;

    port_resolve_range(opts, &lo, &hi);
    if (!port_range_ok(lo, hi)) {
        return -1;
    }
    count = opts->count < 1 ? 1 : opts->count;

    if (!opts->unique) {
        if (count > cap) {
            return -1;
        }
        for (i = 0; i < count; i++) {
            out[i] = random_port(opts);
        }
        return count;
    }

    /* Fisher-Yates partial shuffle over the range to pick n unique ports. */
    capacity = hi - lo + 1;
    n = count < capacity ? count : capacity;
    if (n > cap) {
        return -1;
    }
    pool = malloc((size_t) capacity * sizeof *pool);
    if (!pool) {
        return -1;
    }
    for (i = 0; i < capacity; i++) {
        pool[i] = lo + i;
    }
    for (i = 0; i < n; i++) {
        j = i + (int) (rng() * (double) (capacity - i));
        t = pool[i];
        pool[i] = pool[j];
        pool[j] = t;
    }
    for (i = 0; i < n; i++) {
        out[i] = pool[i];
    }
    free(pool);
    return n;
}

/* ------------------------------------------------------------------- main --- */

static double rng_zero(void) { return 0.0; }
static double rng_half(void) { return 0.5; }
static double rng_point_nine(void) { return 0.9; }

static void print_ports(const char *label, const int *ports, int n)
{
    int i;

    printf("%s = [", label);
    for (i = 0; i < n; i++) {
        printf("%s%d", i ? " " : "", ports[i]);
    }
    printf("]\n");
}

int main(void)
{
    /* Vectors from src/lib/random-port.test.ts — a fixed rng pins every draw. */
    port_options_t o;
    int lo, hi, buf[8], n;

    o = (port_options_t){ PORT_REGISTERED, 0, 0, 1, 0, rng_zero };
    printf("registered+0 = %d\n", random_port(&o)); /* 1024 */
    o.range = PORT_ANY;
    printf("any+0 = %d\n", random_port(&o)); /* 1 */
    o.range = PORT_EPHEMERAL;
    printf("ephemeral+0 = %d\n", random_port(&o)); /* 49152 */

    o = (port_options_t){ PORT_CUSTOM, 8000, 8000, 1, 0, rng_point_nine };
    printf("custom 8000-8000 @0.9 = %d\n", random_port(&o)); /* 8000 */

    o = (port_options_t){ PORT_CUSTOM, 0, 0, 1, 0, NULL };
    port_resolve_range(&o, &lo, &hi);
    printf("resolveRange(custom defaults) = %d..%d\n", lo, hi); /* 1..65535 */

    o = (port_options_t){ PORT_REGISTERED, 0, 0, 5, 0, rng_zero };
    n = random_ports(&o, buf, 8);
    print_ports("count=5 @0", buf, n); /* [1024 x5] */

    o = (port_options_t){ PORT_CUSTOM, 1, 10, 3, 1, rng_half };
    n = random_ports(&o, buf, 8);
    print_ports("unique 3 in 1..10 @0.5", buf, n); /* [6 1 7] */

    o = (port_options_t){ PORT_CUSTOM, 1, 3, 5, 1, rng_half };
    n = random_ports(&o, buf, 8);
    print_ports("unique 5 in 1..3 @0.5", buf, n); /* capped at capacity 3 */

    o = (port_options_t){ 0, 0, 0, 1, 0, NULL }; /* zero value = registered */
    printf("default rng registered = %d (in 1024..49151)\n", random_port(&o));

    o = (port_options_t){ PORT_CUSTOM, 70000, 0, 1, 0, NULL }; /* min above 65535 */
    printf("invalid: random_port -> %d\n", random_port(&o)); /* -1 */
    o = (port_options_t){ PORT_CUSTOM, 100, 50, 1, 0, NULL }; /* min above max */
    printf("invalid: random_ports -> %d\n", random_ports(&o, buf, 8)); /* -1 */
    return 0;
}

Also available in 13 other languages

Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →