refactor: handle more situations

This commit is contained in:
Derrick Hammer 2023-09-12 20:49:53 -04:00
parent 2f25241943
commit b33b70160e
Signed by: pcfreak30
GPG Key ID: C997C339BE476FF2
1 changed files with 135 additions and 90 deletions

View File

@ -33,6 +33,40 @@ interface HnsRecord {
} }
export default class Handshake extends AbstractResolverModule { export default class Handshake extends AbstractResolverModule {
async resolve(
domain: string,
options: ResolverOptions,
bypassCache: boolean,
): Promise<DNSResult> {
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<Set<string>> { private async buildBlacklist(): Promise<Set<string>> {
const blacklist = new Set<string>(); const blacklist = new Set<string>();
let resolvers = this.resolver.resolvers as unknown as Set<ResolverModule>; let resolvers = this.resolver.resolvers as unknown as Set<ResolverModule>;
@ -51,44 +85,34 @@ export default class Handshake extends AbstractResolverModule {
return blacklist; return blacklist;
} }
async resolve( async processRecords(
domain: string, hnsRecords: HnsRecord[],
options: ResolverOptions, options: ResolverOptions,
domain: string,
bypassCache: boolean, bypassCache: boolean,
): Promise<DNSResult> { ): Promise<DNSRecord[]> {
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();
}
let records: DNSRecord[] = []; let records: DNSRecord[] = [];
const hnsRecords = chainRecords.result?.records; const nsRecords = this.findRecordsByType(hnsRecords, "NS");
const nsServer = this.findNameserver(hnsRecords, options); const contentRecords = this.findRecordsByType(hnsRecords, "TXT");
if (!nsServer) { // Scenario: Content and NS Records Found (HIP-5)
const ns = this.findRecordsByType(hnsRecords, "NS"); 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( let result = await this.resolver.resolve(
ns[0].ns, hip5Record.ns,
{ {
...options, ...options,
options: { options: {
@ -99,40 +123,92 @@ export default class Handshake extends AbstractResolverModule {
); );
if (result.records.length) { if (result.records.length) {
records.push.apply(records, result.records); records.push(...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,
}),
);
} }
return records;
} }
} else { }
for (const type of [DNS_RECORD_TYPE.A, DNS_RECORD_TYPE.CNAME]) {
if (type === options.type) { // Scenario: Delegated Lookup (via NS)
const ret = await this.dnsQuery(domain, type); if (nsRecords && this.isNSHip5(nsRecords[0], options)) {
if (ret.length) { return await this.handleDelegatedLookup(
records.push({ nsRecords,
type, options,
value: ret.slice().shift().data.address, 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<DNSRecord[]> {
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<boolean> {
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<DNSRecord[]> {
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); return records;
if (0 < records.length) {
return resolveSuccess(records);
}
return resolverEmptyResponse();
} }
private async query(tld: string): Promise<HandshakeResponse> { private async query(tld: string): Promise<HandshakeResponse> {
@ -146,37 +222,6 @@ export default class Handshake extends AbstractResolverModule {
async ready() { async ready() {
return ((await client.status()) as any)?.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( private findRecordsByType(
records: HnsRecord[], records: HnsRecord[],
type: "NS" | "SYNTH4" | "SYNTH6" | "GLUE4" | "GLUE6" | "TXT", type: "NS" | "SYNTH4" | "SYNTH6" | "GLUE4" | "GLUE6" | "TXT",