Implement WHOIS referral chain collection and merging logic (fixes #11)

- Introduced `collectWhoisReferralChain` to gather WHOIS responses while avoiding contradictory data from registrars.
- Updated `lookupDomain` to utilize the new chain collection method, ensuring TLD responses are prioritized.
- Added `mergeWhoisRecords` function to consolidate WHOIS data from multiple sources.
- Enhanced tests for referral handling and merging behavior, ensuring accurate data retention across scenarios.
This commit is contained in:
2025-10-20 14:08:26 -04:00
parent b00d0bbb0a
commit 2a1b7529cc
8 changed files with 250 additions and 20 deletions
+44
View File
@@ -46,6 +46,50 @@ export async function followWhoisReferrals(
return current;
}
/**
* Collect the WHOIS referral chain starting from the TLD server.
* Always includes the initial TLD response; may include one or more registrar responses.
* Stops on contradiction (registrar claims availability) or failures.
*/
export async function collectWhoisReferralChain(
initialServer: string,
domain: string,
opts?: LookupOptions,
): Promise<WhoisQueryResult[]> {
const results: WhoisQueryResult[] = [];
const maxHops = Math.max(0, opts?.maxWhoisReferralHops ?? 2);
const first = await whoisQuery(initialServer, domain, opts);
results.push(first);
if (opts?.followWhoisReferral === false || maxHops === 0) return results;
const visited = new Set<string>([normalize(first.serverQueried)]);
let current = first;
let hops = 0;
while (hops < maxHops) {
const next = extractWhoisReferral(current.text);
if (!next) break;
const normalized = normalize(next);
if (visited.has(normalized)) break;
visited.add(normalized);
try {
const res = await whoisQuery(next, domain, opts);
// If registrar claims availability while TLD indicated registered, stop.
const registeredBefore = !isWhoisAvailable(current.text);
const registeredAfter = !isWhoisAvailable(res.text);
if (registeredBefore && !registeredAfter) {
// Do not adopt or append contradictory registrar; keep authoritative TLD only.
break;
}
results.push(res);
current = res;
} catch {
break;
}
hops += 1;
}
return results;
}
function normalize(server: string): string {
return server.replace(/^whois:\/\//i, "").toLowerCase();
}