bugfix: DoT的DNS,配置了SNI但未生效的问题修复

master
王良 2025-05-16 18:10:05 +08:00
parent 1dc5895314
commit 71094c4758
8 changed files with 195 additions and 15 deletions

View File

@ -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",

View File

@ -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查询超时'))

View File

@ -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,
})
}

View File

@ -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)

View File

@ -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 }

View File

@ -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

View File

@ -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++;
}
}

View File

@ -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