refactor: handle more situations
This commit is contained in:
parent
2f25241943
commit
b33b70160e
225
src/resolver.ts
225
src/resolver.ts
|
@ -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",
|
||||||
|
|
Loading…
Reference in New Issue