feat(domain-intelligence): add domain intelligence lookups with RDAP and DNS enrichment
This commit is contained in:
@@ -2,10 +2,14 @@ import * as plugins from './smartnetwork.plugins.js';
|
||||
import { CloudflareSpeed } from './smartnetwork.classes.cloudflarespeed.js';
|
||||
import { PublicIp } from './smartnetwork.classes.publicip.js';
|
||||
import { IpIntelligence, type IIpIntelligenceResult } from './smartnetwork.classes.ipintelligence.js';
|
||||
import { DomainIntelligence, type IDomainIntelligenceResult } from './smartnetwork.classes.domainintelligence.js';
|
||||
import { getLogger } from './logging.js';
|
||||
import { NetworkError } from './errors.js';
|
||||
import { RustNetworkBridge } from './smartnetwork.classes.rustbridge.js';
|
||||
|
||||
/** Type alias for the shared Smartdns client instance */
|
||||
type TSmartdnsClient = InstanceType<typeof plugins.smartdns.dnsClientMod.Smartdns>;
|
||||
|
||||
/**
|
||||
* Configuration options for SmartNetwork
|
||||
*/
|
||||
@@ -54,6 +58,8 @@ export class SmartNetwork {
|
||||
private rustBridge: RustNetworkBridge;
|
||||
private bridgeStarted = false;
|
||||
private ipIntelligence: IpIntelligence | null = null;
|
||||
private domainIntelligence: DomainIntelligence | null = null;
|
||||
private dnsClient: TSmartdnsClient | null = null;
|
||||
|
||||
constructor(options?: SmartNetworkOptions) {
|
||||
this.options = options || {};
|
||||
@@ -73,13 +79,24 @@ export class SmartNetwork {
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the Rust binary bridge.
|
||||
* Stop the Rust binary bridge and tear down the shared Smartdns client.
|
||||
* Call this before your Node process exits if you've used any DNS or
|
||||
* Rust-backed features, otherwise the smartdns Rust backend may keep
|
||||
* the event loop alive.
|
||||
*/
|
||||
public async stop(): Promise<void> {
|
||||
if (this.bridgeStarted) {
|
||||
await this.rustBridge.stop();
|
||||
this.bridgeStarted = false;
|
||||
}
|
||||
if (this.dnsClient) {
|
||||
this.dnsClient.destroy();
|
||||
this.dnsClient = null;
|
||||
// Intelligence instances hold a stale reference to the destroyed
|
||||
// client; drop them so the next call rebuilds with a fresh one.
|
||||
this.ipIntelligence = null;
|
||||
this.domainIntelligence = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -91,6 +108,23 @@ export class SmartNetwork {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazily create the shared Smartdns client. The Rust backend inside
|
||||
* Smartdns is only spawned on first query that requires it (NS/MX/SOA
|
||||
* with prefer-system strategy, or any query with doh/udp strategy).
|
||||
* The client is destroyed by stop().
|
||||
*/
|
||||
private ensureDnsClient(): TSmartdnsClient {
|
||||
if (!this.dnsClient) {
|
||||
this.dnsClient = new plugins.smartdns.dnsClientMod.Smartdns({
|
||||
strategy: 'prefer-system',
|
||||
allowDohFallback: true,
|
||||
timeoutMs: 5000,
|
||||
});
|
||||
}
|
||||
return this.dnsClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get network speed via Cloudflare speed test (pure TS, no Rust needed).
|
||||
*/
|
||||
@@ -310,16 +344,15 @@ export class SmartNetwork {
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve DNS records (A, AAAA, MX) — uses smartdns, no Rust needed.
|
||||
* Resolve DNS records (A, AAAA, MX) via the shared smartdns client.
|
||||
* The client is lifecycle-managed by start()/stop() — MX queries spawn
|
||||
* the smartdns Rust bridge, which is torn down by stop().
|
||||
*/
|
||||
public async resolveDns(
|
||||
host: string,
|
||||
): Promise<{ A: string[]; AAAA: string[]; MX: { exchange: string; priority: number }[] }> {
|
||||
try {
|
||||
const dnsClient = new plugins.smartdns.dnsClientMod.Smartdns({
|
||||
strategy: 'prefer-system',
|
||||
allowDohFallback: true,
|
||||
});
|
||||
const dnsClient = this.ensureDnsClient();
|
||||
|
||||
const [aRecords, aaaaRecords, mxRecords] = await Promise.all([
|
||||
dnsClient.getRecordsA(host).catch((): any[] => []),
|
||||
@@ -404,7 +437,7 @@ export class SmartNetwork {
|
||||
*/
|
||||
public async getIpIntelligence(ip: string): Promise<IIpIntelligenceResult> {
|
||||
if (!this.ipIntelligence) {
|
||||
this.ipIntelligence = new IpIntelligence();
|
||||
this.ipIntelligence = new IpIntelligence({ dnsClient: this.ensureDnsClient() });
|
||||
}
|
||||
const fetcher = () => this.ipIntelligence!.getIntelligence(ip);
|
||||
if (this.options.cacheTtl && this.options.cacheTtl > 0) {
|
||||
@@ -413,6 +446,22 @@ export class SmartNetwork {
|
||||
return fetcher();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get domain intelligence: registrar, registrant, nameservers, registration
|
||||
* events, status flags, DNSSEC, and abuse contact via RDAP. Pure TS, no
|
||||
* Rust needed.
|
||||
*/
|
||||
public async getDomainIntelligence(domain: string): Promise<IDomainIntelligenceResult> {
|
||||
if (!this.domainIntelligence) {
|
||||
this.domainIntelligence = new DomainIntelligence({ dnsClient: this.ensureDnsClient() });
|
||||
}
|
||||
const fetcher = () => this.domainIntelligence!.getIntelligence(domain);
|
||||
if (this.options.cacheTtl && this.options.cacheTtl > 0) {
|
||||
return this.getCached(`domainIntelligence:${domain}`, fetcher);
|
||||
}
|
||||
return fetcher();
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal caching helper
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user