feature: DoH类型的DNS,已支持SNI
parent
b582ac63ad
commit
4a3cae392f
|
@ -103,19 +103,21 @@ module.exports = class BaseDNS {
|
|||
}
|
||||
|
||||
async _lookupWithPreSetIpList (hostname) {
|
||||
// 获取当前域名的预设IP列表
|
||||
let hostnamePreSetIpList = matchUtil.matchHostname(this.preSetIpList, hostname, `matched preSetIpList(${this.dnsName})`)
|
||||
if (hostnamePreSetIpList && (hostnamePreSetIpList.length > 0 || hostnamePreSetIpList.length === undefined)) {
|
||||
if (hostnamePreSetIpList.length > 0) {
|
||||
hostnamePreSetIpList = hostnamePreSetIpList.slice() // 复制一份列表数据,避免配置数据被覆盖
|
||||
} else {
|
||||
hostnamePreSetIpList = mapToList(hostnamePreSetIpList)
|
||||
}
|
||||
if (this.preSetIpList) {
|
||||
// 获取当前域名的预设IP列表
|
||||
let hostnamePreSetIpList = matchUtil.matchHostname(this.preSetIpList, hostname, `matched preSetIpList(${this.dnsName})`)
|
||||
if (hostnamePreSetIpList && (hostnamePreSetIpList.length > 0 || hostnamePreSetIpList.length === undefined)) {
|
||||
if (hostnamePreSetIpList.length > 0) {
|
||||
hostnamePreSetIpList = hostnamePreSetIpList.slice() // 复制一份列表数据,避免配置数据被覆盖
|
||||
} else {
|
||||
hostnamePreSetIpList = mapToList(hostnamePreSetIpList)
|
||||
}
|
||||
|
||||
if (hostnamePreSetIpList.length > 0) {
|
||||
hostnamePreSetIpList.isPreSet = true
|
||||
log.info(`[DNS-over-PreSet '${this.dnsName}'] 获取到该域名的预设IP列表: ${hostname} - ${JSON.stringify(hostnamePreSetIpList)}`)
|
||||
return hostnamePreSetIpList
|
||||
if (hostnamePreSetIpList.length > 0) {
|
||||
hostnamePreSetIpList.isPreSet = true
|
||||
log.info(`[DNS-over-PreSet '${this.dnsName}'] 获取到该域名的预设IP列表: ${hostname} - ${JSON.stringify(hostnamePreSetIpList)}`)
|
||||
return hostnamePreSetIpList
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -159,6 +161,10 @@ module.exports = class BaseDNS {
|
|||
}
|
||||
|
||||
_doDnsQuery (hostname, type = 'A', start) {
|
||||
if (start == null) {
|
||||
start = Date.now()
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
// 设置超时任务
|
||||
let isOver = false
|
||||
|
|
|
@ -1,16 +1,45 @@
|
|||
const { promisify } = require('node:util')
|
||||
const doh = require('dns-over-http')
|
||||
const BaseDNS = require('./base')
|
||||
const HttpsAgent = require('../proxy/common/ProxyHttpsAgent')
|
||||
const Agent = require('../proxy/common/ProxyHttpAgent')
|
||||
|
||||
const dohQueryAsync = promisify(doh.query)
|
||||
|
||||
function createAgent (dnsServer) {
|
||||
return new (dnsServer.startsWith('https:') ? HttpsAgent : Agent)({
|
||||
keepAlive: true,
|
||||
timeout: 20000,
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = class DNSOverHTTPS extends BaseDNS {
|
||||
constructor (dnsName, cacheSize, preSetIpList, dnsServer) {
|
||||
constructor (dnsName, cacheSize, preSetIpList, dnsServer, dnsServerName) {
|
||||
super(dnsName, 'HTTPS', cacheSize, preSetIpList)
|
||||
this.dnsServer = dnsServer
|
||||
this.dnsServer = dnsServer.replace(/\s+/, '')
|
||||
this.dnsServerName = dnsServerName
|
||||
}
|
||||
|
||||
_dnsQueryPromise (hostname, type = 'A') {
|
||||
return dohQueryAsync({ url: this.dnsServer }, [{ type, name: hostname }])
|
||||
// 请求参数
|
||||
const options = {
|
||||
url: this.dnsServer,
|
||||
agent: createAgent(this.dnsServer),
|
||||
}
|
||||
if (this.dnsServerName) {
|
||||
// 设置SNI
|
||||
options.servername = this.dnsServerName
|
||||
options.rejectUnauthorized = false
|
||||
}
|
||||
|
||||
// DNS查询参数
|
||||
const questions = [
|
||||
{
|
||||
type,
|
||||
name: hostname,
|
||||
},
|
||||
]
|
||||
|
||||
return dohQueryAsync(options, questions)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ module.exports = {
|
|||
}
|
||||
|
||||
// 基于 https
|
||||
dnsMap[provider] = new DNSOverHTTPS(provider, conf.cacheSize, preSetIpList, server)
|
||||
dnsMap[provider] = new DNSOverHTTPS(provider, conf.cacheSize, preSetIpList, server, conf.sni || conf.servername)
|
||||
} else {
|
||||
// 获取DNS端口
|
||||
let port = conf.port
|
||||
|
@ -64,7 +64,7 @@ module.exports = {
|
|||
|
||||
if (type === 'tls' || type === 'dot' || type === 'dns-over-tls') {
|
||||
// 基于 tls
|
||||
dnsMap[provider] = new DNSOverTLS(provider, conf.cacheSize, preSetIpList, server, port, conf.servername || conf.sni)
|
||||
dnsMap[provider] = new DNSOverTLS(provider, conf.cacheSize, preSetIpList, server, port, conf.sni || conf.servername)
|
||||
} else if (type === 'tcp') {
|
||||
// 基于 tcp
|
||||
dnsMap[provider] = new DNSOverTCP(provider, conf.cacheSize, preSetIpList, server, port)
|
||||
|
|
|
@ -9,7 +9,7 @@ const defaultPort = 53 // TCP类型的DNS服务默认端口号
|
|||
module.exports = class DNSOverTCP extends BaseDNS {
|
||||
constructor (dnsName, cacheSize, preSetIpList, dnsServer, dnsServerPort) {
|
||||
super(dnsName, 'TCP', cacheSize, preSetIpList)
|
||||
this.dnsServer = dnsServer
|
||||
this.dnsServer = dnsServer.replace(/\s+/, '')
|
||||
this.dnsServerPort = Number.parseInt(dnsServerPort) || defaultPort
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ const defaultPort = 853
|
|||
module.exports = class DNSOverTLS extends BaseDNS {
|
||||
constructor (dnsName, cacheSize, preSetIpList, dnsServer, dnsServerPort, dnsServerName) {
|
||||
super(dnsName, 'TLS', cacheSize, preSetIpList)
|
||||
this.dnsServer = dnsServer
|
||||
this.dnsServer = dnsServer.replace(/\s+/, '')
|
||||
this.dnsServerPort = Number.parseInt(dnsServerPort) || defaultPort
|
||||
this.dnsServerName = dnsServerName
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ const defaultPort = 53 // UDP类型的DNS服务默认端口号
|
|||
module.exports = class DNSOverUDP extends BaseDNS {
|
||||
constructor (dnsName, cacheSize, preSetIpList, dnsServer, dnsServerPort) {
|
||||
super(dnsName, 'UDP', cacheSize, preSetIpList)
|
||||
this.dnsServer = dnsServer
|
||||
this.dnsServer = dnsServer.replace(/\s+/, '')
|
||||
this.dnsServerPort = Number.parseInt(dnsServerPort) || defaultPort
|
||||
|
||||
this.isIPv6 = dnsServer.includes(':') && dnsServer.includes('[') && dnsServer.includes(']')
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
import DNSOverHTTPS from "../src/lib/dns/https.js";
|
||||
|
||||
// 境外DNS的DoH配置sni测试
|
||||
const servers = [
|
||||
'https://dns.quad9.net/dns-query',
|
||||
'https://max.rethinkdns.com/dns-query',
|
||||
'https://sky.rethinkdns.com/dns-query',
|
||||
'https://doh.opendns.com/dns-query',
|
||||
'https://1.1.1.1/dns-query',
|
||||
'https://dns.cloudflare.com/dns-query',
|
||||
'https://cloudflare-dns.com/dns-query',
|
||||
'https://dns.google/dns-query',
|
||||
'https://dns.bebasid.com/unfiltered',
|
||||
'https://0ms.dev/dns-query',
|
||||
'https://dns.decloudus.com/dns-query',
|
||||
'https://wikimedia-dns.org/dns-query',
|
||||
'https://doh.applied-privacy.net/query',
|
||||
'https://private.canadianshield.cira.ca/dns-query',
|
||||
// 'https://dns.controld.com/comss', // 可直连,无需SNI
|
||||
'https://kaitain.restena.lu/dns-query',
|
||||
'https://doh.libredns.gr/dns-query',
|
||||
'https://doh.libredns.gr/ads',
|
||||
'https://dns.switch.ch/dns-query',
|
||||
'https://doh.nl.ahadns.net/dns-query',
|
||||
'https://doh.la.ahadns.net/dns-query',
|
||||
'https://dns.dnswarden.com/uncensored',
|
||||
'https://doh.ffmuc.net/dns-query',
|
||||
'https://dns.oszx.co/dns-query',
|
||||
'https://doh.tiarap.org/dns-query',
|
||||
'https://jp.tiarap.org/dns-query',
|
||||
'https://dns.adguard.com/dns-query',
|
||||
'https://rubyfish.cn/dns-query',
|
||||
'https://i.233py.com/dns-query'
|
||||
|
||||
]
|
||||
|
||||
const hostname1 = 'github.com'
|
||||
const sni = 'baidu.com'
|
||||
|
||||
console.log(`\n--------------- 测试DoH的SNI功能:共 ${servers.length} 个DoH服务 ---------------\n`)
|
||||
|
||||
let n = 0
|
||||
let success = 0
|
||||
let error = 0
|
||||
const arr = []
|
||||
|
||||
function count (isSuccess, i, doh, result) {
|
||||
n++
|
||||
if (isSuccess) {
|
||||
success++
|
||||
arr[i] = `${doh.dnsServer} : ${hostname1} -> ${result.answers[0].data}`;
|
||||
} else error++
|
||||
|
||||
if (n === servers.length) {
|
||||
console.info(`\n\n=============================================================================\n全部测完:总计:${servers.length}, 成功:${success},失败:${error}`);
|
||||
for (const item of arr) {
|
||||
if (item) {
|
||||
console.info(item);
|
||||
}
|
||||
}
|
||||
console.info('=============================================================================\n\n')
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < servers.length; i++) {
|
||||
const n = i;
|
||||
const doh = new DNSOverHTTPS(`dns${i}`, null, null, servers[i], sni)
|
||||
doh._doDnsQuery(hostname1)
|
||||
.then((result) => {
|
||||
// console.info(`===> test testDoH '${doh.dnsServer}': ${hostname1} ->`, result.answers, '\n\n')
|
||||
count(true, n, doh, result)
|
||||
})
|
||||
.catch((e) => {
|
||||
// console.error(`===> test testDoH '${doh.dnsServer}': ${hostname1} 失败:`, e, '\n\n')
|
||||
count(false)
|
||||
})
|
||||
}
|
Loading…
Reference in New Issue