The internet's phone book
Computers route by IP address; people type names. DNS translates. No server knows every name: the root knows who runs .com, the .com servers know who runs sikau.com, and *that* server knows the answer. Resolvers follow these referrals, then cache answers so the next lookup is instant.
Following referrals
// Each name server knows some names, or which server to ask next (ns).
const ZONES = {
".": { com: { ns: "a.gtld" }, np: { ns: "np.ns" } },
"a.gtld": { "sikau.com": { ns: "ns.sikau" }, "example.com": { a: "93.184.216.34" } },
"ns.sikau": {
"www.sikau.com": { cname: "sikau.com" },
"sikau.com": { a: "203.0.113.7" },
"api.sikau.com": { a: "203.0.113.8" },
},
"np.ns": { "cafe.np": { a: "202.52.0.9" } },
};
function lookup(zones, name) {
const steps = [];
let server = ".";
for (let guard = 0; guard < 10; guard++) {
steps.push(server);
const zone = zones[server];
const exact = zone[name];
if (exact?.a) return { ip: exact.a, steps };
if (exact?.cname) {
const r = lookup(zones, exact.cname); // an alias: start again for the real name
return { ip: r.ip, steps: [...steps, ...r.steps], cname: exact.cname };
}
if (exact?.ns) {
server = exact.ns;
continue;
}
// A referral: the longest known suffix of the name that has a name server.
const parent = Object.keys(zone)
.filter((k) => name.endsWith("." + k) && zone[k].ns)
.sort((a, b) => b.length - a.length)[0];
if (!parent) return { ip: null, steps };
server = zone[parent].ns;
}
throw new Error("Too many referrals");
}
function createResolver(zones) {
const cache = new Map();
return {
resolve(name) {
if (cache.has(name)) return { ip: cache.get(name), queries: 0 };
const r = lookup(zones, name);
if (r.ip) cache.set(name, r.ip);
return { ip: r.ip, queries: r.steps.length };
},
};
}