WHOIS & RDAP Lookup — C source
Look up registration data for any domain, IP address, or ASN — straight from your browser to the authoritative RDAP registry over HTTPS. No backend, no database of lookups, fully private.
This is the C implementation — the same logic the interactive tool runs, in a shareable, citable form.
/* whois — RDAP input classification + URL building (WHOIS tool, C port of src/lib/whois.ts; fetch omitted). */
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef enum { INV, DOM, V4, V6, ASN } Kind;
typedef struct { Kind k; char value[256], tld[64]; } Classified;
static void lc(char *s) { for (; *s; s++) *s = (char)tolower((unsigned char)*s); }
static bool label_ok(const char *s, size_t n) { /* a-z0-9 + inner '-', no edge '-' */
return n && n <= 63 && isalnum((unsigned char)s[0]) && isalnum((unsigned char)s[n - 1])
&& strspn(s, "abcdefghijklmnopqrstuvwxyz0123456789-") == n;
}
static bool v4(const char *s) { /* four decimal octets <= 255, no leading zeros */
int o = 0;
while (*s) {
int len = strspn(s, "0123456789");
if (len < 1 || len > 3 || (len > 1 && *s == '0') || strtol(s, NULL, 10) > 255) return false;
s += len, o++;
if (*s == '.') s++;
}
return o == 4 && !*s;
}
static bool v6(const char *s) { /* hex groups of 1-4, at most one "::" collapse */
const char *dd = strstr(s, "::");
if (!strchr(s, ':') || (dd && strstr(dd + 1, "::"))) return false;
int g = 0;
for (const char *p = s; *p; p++) {
if (*p == ':') continue;
int len = strspn(p, "0123456789abcdefABCDEF");
if (len < 1 || len > 4) return false;
g++, p += len - 1;
}
return g <= 8 && (dd || g == 8);
}
static void host_of(const char *raw, char *out, size_t cap) { /* scheme/userinfo/path/port -> host */
char b[512], *s = b, *t;
snprintf(b, sizeof b, "%s", raw);
if ((t = strstr(s, "://"))) s = t + 3;
else if (!strncasecmp(s, "whois:", 6) || !strncasecmp(s, "rdap:", 5)) s = strchr(s, ':') + 1;
if ((t = strrchr(s, '@'))) s = t + 1;
if ((t = strpbrk(s, "/?#"))) *t = '\0';
if (*s == '[') { if ((t = strchr(++s, ']'))) *t = '\0'; } /* [ipv6](:port)? */
else if ((t = strrchr(s, ':')) && strchr(s, ':') == t) *t = '\0'; /* host:port */
lc(s);
snprintf(out, cap, "%s", s);
}
static Classified classify(const char *raw) { /* ASN (as?\d+) | IPv4 | IPv6 | domain; never fails */
Classified c = { INV, "", "" };
char host[512], val[256] = "";
host_of(raw, host, sizeof host);
if (!*host) return c;
const char *a = !strncasecmp(host, "as", 2) ? host + 2 : host; /* AS3356 / 3356 */
bool digits = *a && strlen(a) <= 10;
for (const char *p = a; *p && digits; p++) digits = isdigit((unsigned char)*p) != false;
if (digits && strtol(a, NULL, 10) > 0) { c.k = ASN, sprintf(c.value, "%ld", strtol(a, NULL, 10)); return c; }
if (v4(host)) { c.k = V4, strcpy(c.value, host); return c; }
if (v6(host)) { c.k = V6, strcpy(c.value, host); return c; }
size_t n = strlen(host);
while (n && host[n - 1] == '.') host[--n] = '\0'; /* trailing root dot(s) */
if (!strchr(host, '.') || strstr(host, "..")) return c; /* need a dot; no empty label */
for (char *tok = strtok(host, "."); tok; tok = strtok(NULL, ".")) {
if (!label_ok(tok, strlen(tok))) return c;
strcat(val, tok), strcat(val, "."), strcpy(c.tld, tok);
}
val[strlen(val) - 1] = '\0'; /* a dot survived -> >= 2 labels */
return c.k = DOM, strcpy(c.value, val), c;
}
static const char *path_of(Kind k) { /* the RDAP object path segment */
return k == DOM ? "domain" : k == ASN ? "autnum" : (k == V4 || k == V6) ? "ip" : NULL;
}
static void url(const char *base, Classified c, char *out, size_t cap) { /* one slash before the path */
size_t n = strlen(base);
while (n && base[n - 1] == '/') n--;
snprintf(out, cap, "%.*s/%s/%s", (int)n, base, path_of(c.k), c.value);
}
static bool asn_range(const char *key, long a) { /* asn.json keys: "lo-hi" or bare */
const char *d = strchr(key, '-');
return d ? a >= strtol(key, NULL, 10) && a <= strtol(d + 1, NULL, 10) : strtol(key, NULL, 10) == a;
}
int main(void) { /* bootstrap excerpts (dns.json / asn.json) -> authoritative RDAP base */
const char *qs[] = { "https://CosmoLabs.org/tools#whois", "AS3356", "192.0.2.1", "not_a_domain" };
for (unsigned i = 0; i < 4; i++) {
Classified c = classify(qs[i]);
char u[512] = "";
if (c.k == DOM && !strcasecmp(c.tld, "org")) url("https://rdap.pir.org/rdap/", c, u, sizeof u);
else if (c.k == ASN && asn_range("64496-64511", atol(c.value))) url("https://rdap.arin.net/registry", c, u, sizeof u);
printf("%-34s %-8s %s\n", qs[i], path_of(c.k) ? path_of(c.k) : "invalid", u);
}
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 →