Chore: make sure random DoH Servers are different
Some checks are pending
Build / Build (push) Waiting to run
Build / Diff output (push) Blocked by required conditions
Build / Deploy to Cloudflare Pages (push) Blocked by required conditions
Build / Deploy to GitHub and GitLab (push) Blocked by required conditions

This commit is contained in:
SukkaW 2025-02-14 23:44:02 +08:00
parent 26eb05572f
commit 65558592cb
4 changed files with 42 additions and 37 deletions

View File

@ -1,6 +1,7 @@
import tldts from 'tldts-experimental'; import tldts from 'tldts-experimental';
import { looseTldtsOpt } from '../constants/loose-tldts-opt'; import { looseTldtsOpt } from '../constants/loose-tldts-opt';
import picocolors from 'picocolors'; import picocolors from 'picocolors';
import { pickRandom, pickOne } from 'foxts/pick-random';
import DNS2 from 'dns2'; import DNS2 from 'dns2';
import asyncRetry from 'async-retry'; import asyncRetry from 'async-retry';
@ -82,15 +83,14 @@ const domesticDohServers: Array<[string, DNS2.DnsResolver]> = ([
}) })
] as const); ] as const);
function createResolve(server: Array<[string, DNS2.DnsResolver]>): DNS2.DnsResolver<DnsResponse> { async function $resolve(name: string, type: DNS2.PacketQuestion, server: [string, DNS2.DnsResolver]) {
return async (...args) => {
try { try {
return await asyncRetry(async () => { return await asyncRetry(async () => {
const [dohServer, dohClient] = server[Math.floor(Math.random() * server.length)]; const [dohServer, dohClient] = server;
try { try {
return { return {
...await dohClient(...args), ...await dohClient(name, type),
dns: dohServer dns: dohServer
} satisfies DnsResponse; } satisfies DnsResponse;
} catch (e) { } catch (e) {
@ -99,15 +99,11 @@ function createResolve(server: Array<[string, DNS2.DnsResolver]>): DNS2.DnsResol
} }
}, { retries: 5 }); }, { retries: 5 });
} catch (e) { } catch (e) {
console.log('[doh error]', ...args, e); console.log('[doh error]', name, type, e);
throw e; throw e;
} }
};
} }
const resolve = createResolve(dohServers);
const domesticResolve = createResolve(domesticDohServers);
async function getWhois(domain: string) { async function getWhois(domain: string) {
return asyncRetry(() => whoiser.domain(domain, { raw: true }), { retries: 5 }); return asyncRetry(() => whoiser.domain(domain, { raw: true }), { retries: 5 });
} }
@ -147,9 +143,10 @@ export async function isDomainAlive(domain: string, isSuffix: boolean): Promise<
const aaaaDns: string[] = []; const aaaaDns: string[] = [];
// test 2 times before make sure record is empty // test 2 times before make sure record is empty
const servers = pickRandom(dohServers, 3);
for (let i = 0; i < 2; i++) { for (let i = 0; i < 2; i++) {
// eslint-disable-next-line no-await-in-loop -- sequential // eslint-disable-next-line no-await-in-loop -- sequential
const aRecords = (await resolve($domain, 'A')); const aRecords = (await $resolve($domain, 'A', servers[i]));
if (aRecords.answers.length > 0) { if (aRecords.answers.length > 0) {
return onDomainAlive(domain); return onDomainAlive(domain);
} }
@ -158,7 +155,7 @@ export async function isDomainAlive(domain: string, isSuffix: boolean): Promise<
} }
for (let i = 0; i < 2; i++) { for (let i = 0; i < 2; i++) {
// eslint-disable-next-line no-await-in-loop -- sequential // eslint-disable-next-line no-await-in-loop -- sequential
const aaaaRecords = (await resolve($domain, 'AAAA')); const aaaaRecords = (await $resolve($domain, 'AAAA', servers[i]));
if (aaaaRecords.answers.length > 0) { if (aaaaRecords.answers.length > 0) {
return onDomainAlive(domain); return onDomainAlive(domain);
} }
@ -167,13 +164,13 @@ export async function isDomainAlive(domain: string, isSuffix: boolean): Promise<
} }
// only then, let's test once with domesticDohServers // only then, let's test once with domesticDohServers
const aRecords = (await domesticResolve($domain, 'A')); const aRecords = (await $resolve($domain, 'A', pickOne(domesticDohServers)));
if (aRecords.answers.length > 0) { if (aRecords.answers.length > 0) {
return onDomainAlive(domain); return onDomainAlive(domain);
} }
aDns.push(aRecords.dns); aDns.push(aRecords.dns);
const aaaaRecords = (await domesticResolve($domain, 'AAAA')); const aaaaRecords = (await $resolve($domain, 'AAAA', pickOne(domesticDohServers)));
if (aaaaRecords.answers.length > 0) { if (aaaaRecords.answers.length > 0) {
return onDomainAlive(domain); return onDomainAlive(domain);
} }
@ -194,7 +191,7 @@ async function isApexDomainAlive(apexDomain: string): Promise<[string, boolean]>
if (apexDomainNsResolvePromiseMap.has(apexDomain)) { if (apexDomainNsResolvePromiseMap.has(apexDomain)) {
resp = await apexDomainNsResolvePromiseMap.get(apexDomain)!; resp = await apexDomainNsResolvePromiseMap.get(apexDomain)!;
} else { } else {
const promise = resolve(apexDomain, 'NS'); const promise = $resolve(apexDomain, 'NS', pickOne(dohServers));
apexDomainNsResolvePromiseMap.set(apexDomain, promise); apexDomainNsResolvePromiseMap.set(apexDomain, promise);
resp = await promise; resp = await promise;
} }

7
Build/mod.d.ts vendored
View File

@ -1,9 +1,9 @@
import 'dns2'; import 'dns2';
declare module 'dns2' { declare module 'dns2' {
import DNS from 'dns2'; import DNS2 from 'dns2';
declare namespace DNS { declare namespace DNS2 {
interface DoHClientOption { interface DoHClientOption {
/** @example dns.google.com */ /** @example dns.google.com */
dns: string, dns: string,
@ -13,6 +13,7 @@ declare module 'dns2' {
get?: (url: string) => any get?: (url: string) => any
} }
export type PacketQuestion = keyof typeof Packet.TYPE;
export type DnsResolver<T = DnsResponse> = (name: string, type: PacketQuestion) => Promise<T>; export type DnsResolver<T = DnsResponse> = (name: string, type: PacketQuestion) => Promise<T>;
declare function DOHClient(opt: DoHClientOption): DnsResolver; declare function DOHClient(opt: DoHClientOption): DnsResolver;
@ -20,5 +21,5 @@ declare module 'dns2' {
export type $DnsResponse = DnsResponse; export type $DnsResponse = DnsResponse;
} }
export = DNS; export = DNS2;
} }

View File

@ -31,7 +31,7 @@
"fast-cidr-tools": "^0.3.1", "fast-cidr-tools": "^0.3.1",
"fast-fifo": "^1.3.2", "fast-fifo": "^1.3.2",
"fdir": "^6.4.3", "fdir": "^6.4.3",
"foxts": "^1.2.0", "foxts": "^1.3.0",
"hash-wasm": "^4.12.0", "hash-wasm": "^4.12.0",
"json-stringify-pretty-compact": "3.0.0", "json-stringify-pretty-compact": "3.0.0",
"picocolors": "^1.1.1", "picocolors": "^1.1.1",

17
pnpm-lock.yaml generated
View File

@ -53,8 +53,8 @@ importers:
specifier: ^6.4.3 specifier: ^6.4.3
version: 6.4.3(picomatch@4.0.2) version: 6.4.3(picomatch@4.0.2)
foxts: foxts:
specifier: ^1.2.0 specifier: ^1.3.0
version: 1.2.0 version: 1.3.0
hash-wasm: hash-wasm:
specifier: ^4.12.0 specifier: ^4.12.0
version: 4.12.0 version: 4.12.0
@ -1094,8 +1094,8 @@ packages:
resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==}
engines: {node: '>=14'} engines: {node: '>=14'}
foxts@1.2.0: foxts@1.3.0:
resolution: {integrity: sha512-GQp91VeREH/fglR4KY5479nATcqnDLBWqEMhPFCD8qd48hA7DB9TD5Q9czC54j9ez9WGHjjTcbjokNWZsSI2Nw==} resolution: {integrity: sha512-TaqRKSTN6uQNFXma3jZQfH2fBqNoePlHJtEGWuaFGWI0lSpe/7UnugEpcZzKy2m5J8EVKdxJl+uPZbHGFNLntQ==}
fs-constants@1.0.0: fs-constants@1.0.0:
resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
@ -1647,6 +1647,9 @@ packages:
engines: {node: '>=14.17'} engines: {node: '>=14.17'}
hasBin: true hasBin: true
uncrypto@0.1.3:
resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==}
undici-cache-store-better-sqlite3@0.1.1: undici-cache-store-better-sqlite3@0.1.1:
resolution: {integrity: sha512-F/PxGx+QY3oScnFQ/YGbN7AYtqKLTzps0izFi8LddDaobso3SYXyQORuPcnUd2JbqpdxmLLkvS2zSvG6VPPMpw==} resolution: {integrity: sha512-F/PxGx+QY3oScnFQ/YGbN7AYtqKLTzps0izFi8LddDaobso3SYXyQORuPcnUd2JbqpdxmLLkvS2zSvG6VPPMpw==}
peerDependencies: peerDependencies:
@ -2780,7 +2783,9 @@ snapshots:
cross-spawn: 7.0.6 cross-spawn: 7.0.6
signal-exit: 4.1.0 signal-exit: 4.1.0
foxts@1.2.0: {} foxts@1.3.0:
dependencies:
uncrypto: 0.1.3
fs-constants@1.0.0: {} fs-constants@1.0.0: {}
@ -3349,6 +3354,8 @@ snapshots:
typescript@5.7.3: {} typescript@5.7.3: {}
uncrypto@0.1.3: {}
undici-cache-store-better-sqlite3@0.1.1(undici@7.3.0(patch_hash=594ba65c371ae78d02b8402fbad8cdc3617510f9ee2390a258e0ce5742a93926)): undici-cache-store-better-sqlite3@0.1.1(undici@7.3.0(patch_hash=594ba65c371ae78d02b8402fbad8cdc3617510f9ee2390a258e0ce5742a93926)):
dependencies: dependencies:
better-sqlite3: 11.8.1 better-sqlite3: 11.8.1