From 71094c475879f569df980eece19176b00e05e7ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=89=AF?= <841369634@qq.com> Date: Fri, 16 May 2025 18:10:05 +0800 Subject: [PATCH] =?UTF-8?q?bugfix:=20DoT=E7=9A=84DNS=EF=BC=8C=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E4=BA=86SNI=E4=BD=86=E6=9C=AA=E7=94=9F=E6=95=88?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mitmproxy/package.json | 1 - packages/mitmproxy/src/lib/dns/base.js | 2 +- packages/mitmproxy/src/lib/dns/https.js | 2 +- packages/mitmproxy/src/lib/dns/tls.js | 5 +- .../src/lib/dns/util/dns-over-tls.js | 81 +++++++++++++ .../mitmproxy/test/dnsTest-abroad-doh-sni.mjs | 2 +- .../mitmproxy/test/dnsTest-abroad-dot-sni.mjs | 107 ++++++++++++++++++ pnpm-lock.yaml | 10 -- 8 files changed, 195 insertions(+), 15 deletions(-) create mode 100644 packages/mitmproxy/src/lib/dns/util/dns-over-tls.js create mode 100644 packages/mitmproxy/test/dnsTest-abroad-dot-sni.mjs diff --git a/packages/mitmproxy/package.json b/packages/mitmproxy/package.json index b2d53a3..9367be8 100644 --- a/packages/mitmproxy/package.json +++ b/packages/mitmproxy/package.json @@ -18,7 +18,6 @@ "axios": "^1.7.7", "baidu-aip-sdk": "^4.16.16", "dns-over-http": "^0.2.0", - "dns-over-tls": "^0.0.9", "is-browser": "^2.1.0", "json5": "^2.2.3", "lodash": "^4.17.21", diff --git a/packages/mitmproxy/src/lib/dns/base.js b/packages/mitmproxy/src/lib/dns/base.js index a71e2e2..7632fa1 100644 --- a/packages/mitmproxy/src/lib/dns/base.js +++ b/packages/mitmproxy/src/lib/dns/base.js @@ -159,7 +159,7 @@ module.exports = class BaseDNS { return new Promise((resolve, reject) => { // 设置超时任务 let isOver = false - const timeout = 6000 + const timeout = 8000 const timeoutId = setTimeout(() => { if (!isOver) { reject(new Error('DNS查询超时')) diff --git a/packages/mitmproxy/src/lib/dns/https.js b/packages/mitmproxy/src/lib/dns/https.js index d5e2d76..07f2639 100644 --- a/packages/mitmproxy/src/lib/dns/https.js +++ b/packages/mitmproxy/src/lib/dns/https.js @@ -9,7 +9,7 @@ const dohQueryAsync = promisify(doh.query) function createAgent (dnsServer) { return new (dnsServer.startsWith('https:') ? HttpsAgent : Agent)({ keepAlive: true, - timeout: 20000, + timeout: 4000, }) } diff --git a/packages/mitmproxy/src/lib/dns/tls.js b/packages/mitmproxy/src/lib/dns/tls.js index dfb7024..0194b4a 100644 --- a/packages/mitmproxy/src/lib/dns/tls.js +++ b/packages/mitmproxy/src/lib/dns/tls.js @@ -1,4 +1,4 @@ -const dnstls = require('dns-over-tls') +const dnstls = require('./util/dns-over-tls') const BaseDNS = require('./base') const defaultPort = 853 @@ -16,10 +16,13 @@ module.exports = class DNSOverTLS extends BaseDNS { host: this.dnsServer, port: this.dnsServerPort, servername: this.dnsServerName || this.dnsServer, + rejectUnauthorized: !this.dnsServerName, name: hostname, klass: 'IN', type, + + timeout: 4000, } return dnstls.query(options) diff --git a/packages/mitmproxy/src/lib/dns/util/dns-over-tls.js b/packages/mitmproxy/src/lib/dns/util/dns-over-tls.js new file mode 100644 index 0000000..8194f00 --- /dev/null +++ b/packages/mitmproxy/src/lib/dns/util/dns-over-tls.js @@ -0,0 +1,81 @@ +/** + * 由于组件 `dns-over-tls@0.0.9` 不支持 `rejectUnauthorized` 和 `timeout` 两个参数,所以将源码复制过来,并简化了代码。 + */ +const dnsPacket = require('dns-packet') +const tls_1 = require('node:tls') +const randi = require('random-int') + +const TWO_BYTES = 2 + +function getDnsQuery ({ type, name, klass, id }) { + return { + id, + type: 'query', + flags: dnsPacket.RECURSION_DESIRED, + questions: [{ class: klass, name, type }], + } +} + +function query ({ host, servername, type, name, klass, port, rejectUnauthorized, timeout }) { + return new Promise((resolve, reject) => { + if (!host || !servername || !name) { + throw new Error('At least host, servername and name must be set.') + } + + let response = Buffer.alloc(0) + let packetLength = 0 + const dnsQuery = getDnsQuery({ id: randi(0x0, 0xFFFF), type, name, klass }) + const dnsQueryBuf = dnsPacket.streamEncode(dnsQuery) + const socket = tls_1.connect({ host, port, servername, rejectUnauthorized, timeout }) + + // 超时处理 + let isFinished = false + let interval + if (timeout > 0) { + interval = setInterval(() => { + if (!isFinished) { + socket.destroy((...args) => { + console.info('socket destory callback args:', args) + }) + + reject(new Error('DNS查询超时')) + } + }, timeout) + } + + socket.on('secureConnect', () => socket.write(dnsQueryBuf)) + socket.on('data', (data) => { + if (timeout) { + isFinished = true + clearInterval(interval) + } + + if (response.length === 0) { + packetLength = data.readUInt16BE(0) + if (packetLength < 12) { + reject(new Error('Below DNS minimum packet length (DNS Header is 12 bytes)')) + } + response = Buffer.from(data) + } else { + response = Buffer.concat([response, data]) + } + + if (response.length === packetLength + TWO_BYTES) { + socket.destroy() + resolve(dnsPacket.streamDecode(response)) + } else { + reject(new Error('响应长度不正确')) + } + }) + socket.on('error', (err) => { + if (timeout) { + isFinished = true + clearInterval(interval) + } + reject(err) + }) + }) +} + +exports.query = query +exports.default = { query } diff --git a/packages/mitmproxy/test/dnsTest-abroad-doh-sni.mjs b/packages/mitmproxy/test/dnsTest-abroad-doh-sni.mjs index 9c98873..39fd2be 100644 --- a/packages/mitmproxy/test/dnsTest-abroad-doh-sni.mjs +++ b/packages/mitmproxy/test/dnsTest-abroad-doh-sni.mjs @@ -37,7 +37,7 @@ const servers = [ const hostname1 = 'github.com' const sni = 'baidu.com' -console.log(`\n--------------- 测试DoH的SNI功能:共 ${servers.length} 个DoH服务 ---------------\n`) +console.log(`\n--------------- 测试DoH的SNI功能:共 ${servers.length} 个服务,${hostnames.length} 个域名,SNI: ${sni || '无'} ---------------\n`) let n = 0 let success = 0 diff --git a/packages/mitmproxy/test/dnsTest-abroad-dot-sni.mjs b/packages/mitmproxy/test/dnsTest-abroad-dot-sni.mjs new file mode 100644 index 0000000..c6cfddb --- /dev/null +++ b/packages/mitmproxy/test/dnsTest-abroad-dot-sni.mjs @@ -0,0 +1,107 @@ +import DNSOverTLS from "../src/lib/dns/tls.js"; + +// 境外DNS的DoT配置sni测试 +const servers = [ + // 'dot.360.cn', + + '1.1.1.1', // 可直连,无需SNI(有时候可以,有时候不行) + 'one.one.one.one', + 'cloudflare-dns.com', + 'security.cloudflare-dns.com', + 'family.cloudflare-dns.com', + '1dot1dot1dot1.cloudflare-dns.com', + + 'dot.sb', + '185.222.222.222', + '45.11.45.11', + + 'dns.adguard.com', + 'dns.adguard-dns.com', + 'dns-family.adguard.com', + 'family.adguard-dns.com', + 'dns-unfiltered.adguard.com', + 'unfiltered.adguard-dns.com', + 'dns.bebasid.com', + 'unfiltered.dns.bebasid.com', + 'antivirus.bebasid.com', + 'internetsehat.bebasid.com', + 'family-adblock.bebasid.com', + 'oisd.dns.bebasid.com', + 'hagezi.dns.bebasid.com', + 'dns.cfiec.net', + 'dns.opendns.com', + 'familyshield.opendns.com', + 'sandbox.opendns.com', + 'family-filter-dns.cleanbrowsing.org', + 'adult-filter-dns.cleanbrowsing.org', + 'security-filter-dns.cleanbrowsing.org', + 'p0.freedns.controld.com', + 'p1.freedns.controld.com', + 'p2.freedns.controld.com', + 'p3.freedns.controld.com', + 'dns.decloudus.com', + 'getdnsapi.net', + 'dnsovertls.sinodun.com', + 'dnsovertls1.sinodun.com', + 'dns.de.futuredns.eu.org', + 'dns.us.futuredns.eu.org', + 'unicast.censurfridns.dk', +] + +const hostnames = [ + 'github.com', + 'mvnrepository.com', +] +const sni = 'baidu.com' +// const sni = '' + +console.log(`\n--------------- 测试DoT的SNI功能:共 ${servers.length} 个服务,${hostnames.length} 个域名,SNI: ${sni || '无'} ---------------\n`) + +let n = 0 +let success = 0 +let error = 0 +const arr = [] + +function count (isSuccess, hostname, idx, dns, result, cost) { + if (isSuccess) { + success++ + const ipList = [] + for (const answer of result.answers) { + ipList[ipList.length] = answer.data; + } + arr[idx] = `${dns.dnsServer} : ${hostname} -> [ ${ipList.join(', ')} ] , cost: ${cost} ms`; + } else { + error++ + } + + n++ + + if (n === servers.length * hostnames.length) { + console.info(`\n\n=============================================================================\n全部测完:总计:${servers.length * hostnames.length}, 成功:${success},失败:${error}`); + for (const item of arr) { + if (item) { + console.info(item); + } + } + console.info('=============================================================================\n\n') + } +} + +let x = 0; +for (let i = 0; i < servers.length; i++) { + for (const hostname of hostnames) { + const dns = new DNSOverTLS(`dns-${i}-${hostname}`, null, null, servers[i], null, sni) + const start = Date.now() + const idx = x; + dns._doDnsQuery(hostname) + .then((result) => { + console.info(`===> ${dns.dnsServer}: ${hostname} ->`, result.answers, '\n\n') + count(true, hostname, idx, dns, result, Date.now() - start) + }) + .catch((e) => { + console.error(`===> ${dns.dnsServer}: ${hostname} 失败:`, e, '\n\n') + count(false, hostname) + }) + x++; + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e46e3cc..6f06d8c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -169,9 +169,6 @@ importers: dns-over-http: specifier: ^0.2.0 version: 0.2.0 - dns-over-tls: - specifier: ^0.0.9 - version: 0.0.9 is-browser: specifier: ^2.1.0 version: 2.1.0 @@ -3045,9 +3042,6 @@ packages: dns-over-http@0.2.0: resolution: {integrity: sha512-K+SyN2L3ljxJ2MFtOv/vRS+3/YEMLvOuH7MrmO5ejaubi4w02/DLqzoK1kBGKlQrT9ND57pbapeDf+ue8AElEA==} - dns-over-tls@0.0.9: - resolution: {integrity: sha512-IdI/Qgku2KQPLtUBsC6HDdK6bWIZADQMOYWtqJzRivV5Z+EDKIVAaC+tWE6lJ9vn11qj5L39ZaIaTj/14Lzgkw==} - dns-packet@4.2.0: resolution: {integrity: sha512-bn1AKpfkFbm0MIioOMHZ5qJzl2uypdBwI4nYNsqvhjsegBhcKJUlCrMPWLx6JEezRjxZmxhtIz/FkBEur2l8Cw==} engines: {node: '>=4'} @@ -10553,10 +10547,6 @@ snapshots: transitivePeerDependencies: - supports-color - dns-over-tls@0.0.9: - dependencies: - dns-packet: 5.6.1 - dns-packet@4.2.0: dependencies: ip: 1.1.9