diff --git a/src/resolver.ts b/src/resolver.ts index 756948d..c967201 100644 --- a/src/resolver.ts +++ b/src/resolver.ts @@ -33,6 +33,40 @@ interface HnsRecord { } export default class Handshake extends AbstractResolverModule { + async resolve( + domain: string, + options: ResolverOptions, + bypassCache: boolean, + ): Promise { + options.options = options.options || {}; + + if (await this.shouldBypassResolution(domain)) { + return resolverEmptyResponse(); + } + + const chainRecords = await this.query(getTld(domain)); + if (chainRecords.error) { + return resolverError(chainRecords.error); + } + + const hnsRecords = chainRecords.result?.records; + if (!hnsRecords || !hnsRecords.length) { + return resolverEmptyResponse(); + } + + let records = await this.processRecords( + hnsRecords, + options, + domain, + bypassCache, + ); + records = ensureUniqueRecords(records); + + return records.length > 0 + ? resolveSuccess(records) + : resolverEmptyResponse(); + } + private async buildBlacklist(): Promise> { const blacklist = new Set(); let resolvers = this.resolver.resolvers as unknown as Set; @@ -51,44 +85,34 @@ export default class Handshake extends AbstractResolverModule { return blacklist; } - async resolve( - domain: string, + async processRecords( + hnsRecords: HnsRecord[], options: ResolverOptions, + domain: string, bypassCache: boolean, - ): Promise { - options.options = options.options || {}; - const tld = getTld(domain); - - const blacklist = await this.buildBlacklist(); - - if (blacklist.has(tld)) { - return resolverEmptyResponse(); - } - - if (isIp(domain)) { - return resolverEmptyResponse(); - } - - const chainRecords = await this.query(tld); - if (chainRecords.error) { - return resolverError(chainRecords.error); - } - - if (!chainRecords.result?.records.length) { - return resolverEmptyResponse(); - } - + ): Promise { let records: DNSRecord[] = []; - const hnsRecords = chainRecords.result?.records; - const nsServer = this.findNameserver(hnsRecords, options); + const nsRecords = this.findRecordsByType(hnsRecords, "NS"); + const contentRecords = this.findRecordsByType(hnsRecords, "TXT"); - if (!nsServer) { - const ns = this.findRecordsByType(hnsRecords, "NS"); + // Scenario: Content and NS Records Found (HIP-5) + if ( + nsRecords && + contentRecords && + options.type === DNS_RECORD_TYPE.CONTENT + ) { + return this.handleContentRecords(contentRecords, options); + } - if (ns && this.isNSHip5(ns[0], options)) { + // Scenario: HIP-5 Compliance + if (nsRecords) { + const hip5Record = nsRecords.find((record) => + this.isNSHip5(record, options), + ); + if (hip5Record) { let result = await this.resolver.resolve( - ns[0].ns, + hip5Record.ns, { ...options, options: { @@ -99,40 +123,92 @@ export default class Handshake extends AbstractResolverModule { ); if (result.records.length) { - records.push.apply(records, result.records); - } - } else { - const content = this.findRecordsByType(hnsRecords, "TXT"); - if (content && [DNS_RECORD_TYPE.CONTENT].includes(options.type)) { - content.forEach((record) => - records.push({ - type: DNS_RECORD_TYPE.CONTENT, - value: record.txt.slice().pop() as string, - }), - ); + records.push(...result.records); } + return records; } - } else { - for (const type of [DNS_RECORD_TYPE.A, DNS_RECORD_TYPE.CNAME]) { - if (type === options.type) { - const ret = await this.dnsQuery(domain, type); - if (ret.length) { - records.push({ - type, - value: ret.slice().shift().data.address, - }); - } + } + + // Scenario: Delegated Lookup (via NS) + if (nsRecords && this.isNSHip5(nsRecords[0], options)) { + return await this.handleDelegatedLookup( + nsRecords, + options, + domain, + bypassCache, + ); + } + + // Scenario: Content Records + if (contentRecords) { + return this.handleContentRecords(contentRecords, options); + } + + // Scenario: Direct DNS Query + return await this.handleWithNameserver(domain, options); + } + + // Handle Content Records + handleContentRecords( + contentRecords: HnsRecord[], + options: ResolverOptions, + ): DNSRecord[] { + let records: DNSRecord[] = []; + if (options.type === DNS_RECORD_TYPE.CONTENT) { + contentRecords.forEach((record) => + records.push({ + type: DNS_RECORD_TYPE.CONTENT, + value: record.txt?.slice().pop() as string, + }), + ); + } + return records; + } + + // Handle HIP-5 NS Delegation + async handleDelegatedLookup( + nsRecords: HnsRecord[], + options: ResolverOptions, + domain: string, + bypassCache: boolean, + ): Promise { + let records: DNSRecord[] = []; + const result = await this.resolver.resolve( + nsRecords[0].ns as string, + { ...options, options: { domain } }, + bypassCache, + ); + + if (result.records.length) { + records.push(...result.records); + } + return records; + } + + // Check if the resolution should be bypassed + async shouldBypassResolution(domain: string): Promise { + const tld = getTld(domain); + const blacklist = await this.buildBlacklist(); + return blacklist.has(tld) || isIp(domain); + } + + // Handle the case where a nameserver is found + async handleWithNameserver( + domain: string, + options: ResolverOptions, + ): Promise { + let records: DNSRecord[] = []; + + for (const type of [DNS_RECORD_TYPE.A, DNS_RECORD_TYPE.CNAME]) { + if (type === options.type) { + const ret = await this.dnsQuery(domain, type); + if (ret.length) { + records.push({ type, value: ret.slice().shift().data.address }); } } } - records = ensureUniqueRecords(records); - - if (0 < records.length) { - return resolveSuccess(records); - } - - return resolverEmptyResponse(); + return records; } private async query(tld: string): Promise { @@ -146,37 +222,6 @@ export default class Handshake extends AbstractResolverModule { async ready() { return ((await client.status()) as any)?.ready; } - - private findNameserver( - records: HnsRecord[], - options: ResolverOptions, - ): HnsRecord | false { - const synth4 = this.findRecordsByType(records, "SYNTH4"); - const synth6 = this.findRecordsByType(records, "SYNTH6"); - const synth = synth4 || synth6; - const glue4 = this.findRecordsByType(records, "GLUE4"); - const glue6 = this.findRecordsByType(records, "GLUE6"); - const glue = glue4 || glue6; - - const ns = this.findRecordsByType(records, "NS"); - - if (synth) { - return synth[0]; - } - - if (glue) { - return glue[0]; - } - - if (ns) { - if (!this.isNSHip5(ns[0], options)) { - return ns[0]; - } - } - - return false; - } - private findRecordsByType( records: HnsRecord[], type: "NS" | "SYNTH4" | "SYNTH6" | "GLUE4" | "GLUE6" | "TXT",