feat(domain-intelligence): add domain intelligence lookups with RDAP and DNS enrichment

This commit is contained in:
2026-04-13 16:51:41 +00:00
parent 7e973b842c
commit a694b0c8ae
9 changed files with 995 additions and 22 deletions
+22 -7
View File
@@ -50,6 +50,13 @@ export interface IIpIntelligenceOptions {
dbMaxAge?: number;
/** Timeout (ms) for RDAP/DNS/CDN requests. Default: 5000 */
timeout?: number;
/**
* Optional injected smartdns client. When provided, IpIntelligence will
* not create or destroy its own client (the owner — typically SmartNetwork —
* manages lifecycle). When omitted, a short-lived client is created per
* Team Cymru lookup and destroyed in finally.
*/
dnsClient?: InstanceType<typeof plugins.smartdns.dnsClientMod.Smartdns>;
}
// CDN URLs for GeoLite2 MMDB files (served via jsDelivr from npm packages)
@@ -93,9 +100,13 @@ export class IpIntelligence {
private bootstrapEntries: IBootstrapEntry[] | null = null;
private bootstrapPromise: Promise<void> | null = null;
// Optional injected smartdns client (shared by SmartNetwork)
private readonly sharedDnsClient: InstanceType<typeof plugins.smartdns.dnsClientMod.Smartdns> | null;
constructor(options?: IIpIntelligenceOptions) {
this.dbMaxAge = options?.dbMaxAge ?? DEFAULT_DB_MAX_AGE;
this.timeout = options?.timeout ?? DEFAULT_TIMEOUT;
this.sharedDnsClient = options?.dnsClient ?? null;
}
/**
@@ -385,16 +396,19 @@ export class IpIntelligence {
* Response: "ASN | prefix | CC | rir | date"
*/
private async queryTeamCymru(ip: string): Promise<{ asn: number; prefix: string; country: string } | null> {
let dnsClient: InstanceType<typeof plugins.smartdns.dnsClientMod.Smartdns> | null = null;
const external = this.sharedDnsClient !== null;
let dnsClient = this.sharedDnsClient;
try {
const reversed = ip.split('.').reverse().join('.');
const queryName = `${reversed}.origin.asn.cymru.com`;
dnsClient = new plugins.smartdns.dnsClientMod.Smartdns({
strategy: 'prefer-system',
allowDohFallback: true,
timeoutMs: this.timeout,
});
if (!dnsClient) {
dnsClient = new plugins.smartdns.dnsClientMod.Smartdns({
strategy: 'prefer-system',
allowDohFallback: true,
timeoutMs: this.timeout,
});
}
const records = await dnsClient.getRecordsTxt(queryName);
if (!records || records.length === 0) return null;
@@ -418,7 +432,8 @@ export class IpIntelligence {
this.logger.debug?.(`Team Cymru DNS query failed for ${ip}: ${err.message}`);
return null;
} finally {
if (dnsClient) {
// Only destroy clients we created ourselves; leave injected ones alone.
if (!external && dnsClient) {
dnsClient.destroy();
}
}