Skip to content

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++20 port of src/lib/whois.ts; fetch omitted).
#include <algorithm>
#include <cctype>
#include <iostream>
#include <optional>
#include <string>
#include <vector>
enum class Kind { invalid, domain, ipv4, ipv6, asn };
struct Classified { Kind kind; std::string value, tld; };
static std::string lc(std::string s) {
    std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::tolower(c); });
    return s;
}
static bool label_ok(const std::string& s) { // a-z0-9 + inner '-', no edge '-'
    if (s.empty() || s.size() > 63 || !std::isalnum((unsigned char)s.front()) || !std::isalnum((unsigned char)s.back())) return false;
    for (size_t i = 1; i + 1 < s.size(); i++) if (!std::isalnum((unsigned char)s[i]) && s[i] != '-') return false;
    return true;
}
static bool v4(const std::string& h) { // four decimal octets <= 255, no leading zeros
    size_t i = 0;
    for (int o = 0; o < 4; o++) {
        if (!std::isdigit(h[i]) || (h[i] == '0' && std::isdigit(h[i + 1]))) return false;
        long v = 0;
        while (std::isdigit(h[i])) v = v * 10 + (h[i++] - '0');
        if (v > 255 || (o < 3 && h[i++] != '.')) return false;
    }
    return i == h.size();
}
static bool v6(const std::string& h) { // hex groups of 1-4, at most one "::" collapse
    if (h.find(':') == std::string::npos) return false;
    auto dd = h.find("::");
    if (dd != std::string::npos && h.find("::", dd + 1) != std::string::npos) return false;
    int g = 0;
    for (size_t i = 0; i < h.size();) {
        if (h[i] == ':') { i++; continue; }
        size_t len = 0;
        while (std::isxdigit((unsigned char)h[i + len])) len++;
        if (len < 1 || len > 4) return false;
        g++, i += len;
    }
    return g <= 8 && (dd != std::string::npos || g == 8);
}
static std::string host_of(std::string raw) { // strip scheme/userinfo/path/port -> bare host
    auto sep = raw.find("://");
    std::string s = sep == std::string::npos ? raw : raw.substr(sep + 3);
    if (s.starts_with("whois:") || s.starts_with("rdap:")) s = s.substr(s.find(':') + 1);
    if (auto at = s.rfind('@'); at != std::string::npos) s = s.substr(at + 1);
    for (char c : {'/', '?', '#'}) if (auto cut = s.find(c); cut != std::string::npos) s.resize(cut);
    if (s.starts_with('[')) s = s.substr(1, s.find(']') - 1); // [ipv6](:port)?
    else if (auto colon = s.rfind(':'); colon != std::string::npos && s.find(':') == colon) s.resize(colon); // host:port
    return lc(s);
}
static Classified classify(const std::string& raw) { // ASN (as?\d+) | IPv4 | IPv6 | domain
    std::string h = host_of(raw);
    if (h.empty()) return { Kind::invalid, "", "" };
    std::string asn = h.starts_with("as") ? h.substr(2) : h; // AS3356 / 3356
    if (!asn.empty() && asn.size() <= 10 && asn.find_first_not_of("0123456789") == std::string::npos && std::stol(asn) > 0)
        return { Kind::asn, std::to_string(std::stol(asn)), "" };
    if (v4(h)) return { Kind::ipv4, h, "" };
    if (v6(h)) return { Kind::ipv6, h, "" };
    while (!h.empty() && h.back() == '.') h.pop_back(); // trailing root dot(s)
    if (h.find('.') == std::string::npos || h.find("..") != std::string::npos) return { Kind::invalid, "", "" };
    std::string val, tld;
    for (size_t start = 0; start <= h.size();) { // split on '.', every label must be valid
        size_t end = h.find('.', start);
        std::string label = h.substr(start, end == std::string::npos ? end : end - start);
        if (!label_ok(label)) return { Kind::invalid, "", "" };
        val += label + ".", tld = label;
        if (end == std::string::npos) break;
        start = end + 1;
    }
    return { Kind::domain, val.substr(0, val.size() - 1), tld };
}
static std::optional<std::string> path_of(Kind k) { // the RDAP object path segment
    switch (k) {
        case Kind::domain: return "domain";
        case Kind::ipv4: case Kind::ipv6: return "ip";
        case Kind::asn: return "autnum";
        default: return std::nullopt;
    }
}
static std::string url(std::string base, const Classified& c) { // one slash before the object path
    while (!base.empty() && base.back() == '/') base.pop_back();
    return path_of(c.kind) ? base + "/" + *path_of(c.kind) + "/" + c.value : "";
}
static bool asn_range(const std::string& key, long a) { // asn.json keys: "lo-hi" or bare
    auto dash = key.find('-');
    return dash == std::string::npos ? std::stol(key) == a
                                     : a >= std::stol(key) && a <= std::stol(key.substr(dash + 1));
}
// Bootstrap (dns.json / asn.json): exact TLD match, or "lo-hi" ASN ranges.
static std::string resolve(const std::vector<std::pair<std::vector<std::string>, std::string>>& bs,
                           const std::string& tld, long asn) {
    for (auto& [keys, u] : bs) for (auto& k : keys) {
        if (!tld.empty() && lc(k) == tld) return u;
        if (tld.empty() && asn_range(k, asn)) return u;
    }
    return "";
}
int main() {
    std::vector<std::pair<std::vector<std::string>, std::string>> dns = { { { "org", "com" }, "https://rdap.org" } },
        asns = { { { "64496-64511" }, "https://rdap.arin.net/registry" } };
    for (std::string q : { "https://CosmoLabs.org/tools#whois", "AS3356", "192.0.2.1", "not_a_domain" }) {
        Classified c = classify(q);
        std::string base = c.kind == Kind::domain ? resolve(dns, c.tld, 0)
                           : c.kind == Kind::asn ? resolve(asns, "", std::stol(c.value)) : "";
        auto p = path_of(c.kind);
        std::cout << q << " -> " << (p ? *p : "invalid") << ' ' << (base.empty() ? "" : url(base, c)) << '\n';
    }
}

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 →