Skip to content

Commit 4a3cae3

Browse files
feature: DoH类型的DNS,已支持SNI
1 parent b582ac6 commit 4a3cae3

File tree

7 files changed

+132
-20
lines changed

7 files changed

+132
-20
lines changed

packages/mitmproxy/src/lib/dns/base.js

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -103,19 +103,21 @@ module.exports = class BaseDNS {
103103
}
104104

105105
async _lookupWithPreSetIpList (hostname) {
106-
// 获取当前域名的预设IP列表
107-
let hostnamePreSetIpList = matchUtil.matchHostname(this.preSetIpList, hostname, `matched preSetIpList(${this.dnsName})`)
108-
if (hostnamePreSetIpList && (hostnamePreSetIpList.length > 0 || hostnamePreSetIpList.length === undefined)) {
109-
if (hostnamePreSetIpList.length > 0) {
110-
hostnamePreSetIpList = hostnamePreSetIpList.slice() // 复制一份列表数据,避免配置数据被覆盖
111-
} else {
112-
hostnamePreSetIpList = mapToList(hostnamePreSetIpList)
113-
}
106+
if (this.preSetIpList) {
107+
// 获取当前域名的预设IP列表
108+
let hostnamePreSetIpList = matchUtil.matchHostname(this.preSetIpList, hostname, `matched preSetIpList(${this.dnsName})`)
109+
if (hostnamePreSetIpList && (hostnamePreSetIpList.length > 0 || hostnamePreSetIpList.length === undefined)) {
110+
if (hostnamePreSetIpList.length > 0) {
111+
hostnamePreSetIpList = hostnamePreSetIpList.slice() // 复制一份列表数据,避免配置数据被覆盖
112+
} else {
113+
hostnamePreSetIpList = mapToList(hostnamePreSetIpList)
114+
}
114115

115-
if (hostnamePreSetIpList.length > 0) {
116-
hostnamePreSetIpList.isPreSet = true
117-
log.info(`[DNS-over-PreSet '${this.dnsName}'] 获取到该域名的预设IP列表: ${hostname} - ${JSON.stringify(hostnamePreSetIpList)}`)
118-
return hostnamePreSetIpList
116+
if (hostnamePreSetIpList.length > 0) {
117+
hostnamePreSetIpList.isPreSet = true
118+
log.info(`[DNS-over-PreSet '${this.dnsName}'] 获取到该域名的预设IP列表: ${hostname} - ${JSON.stringify(hostnamePreSetIpList)}`)
119+
return hostnamePreSetIpList
120+
}
119121
}
120122
}
121123

@@ -159,6 +161,10 @@ module.exports = class BaseDNS {
159161
}
160162

161163
_doDnsQuery (hostname, type = 'A', start) {
164+
if (start == null) {
165+
start = Date.now()
166+
}
167+
162168
return new Promise((resolve, reject) => {
163169
// 设置超时任务
164170
let isOver = false
Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,45 @@
11
const { promisify } = require('node:util')
22
const doh = require('dns-over-http')
33
const BaseDNS = require('./base')
4+
const HttpsAgent = require('../proxy/common/ProxyHttpsAgent')
5+
const Agent = require('../proxy/common/ProxyHttpAgent')
46

57
const dohQueryAsync = promisify(doh.query)
68

9+
function createAgent (dnsServer) {
10+
return new (dnsServer.startsWith('https:') ? HttpsAgent : Agent)({
11+
keepAlive: true,
12+
timeout: 20000,
13+
})
14+
}
15+
716
module.exports = class DNSOverHTTPS extends BaseDNS {
8-
constructor (dnsName, cacheSize, preSetIpList, dnsServer) {
17+
constructor (dnsName, cacheSize, preSetIpList, dnsServer, dnsServerName) {
918
super(dnsName, 'HTTPS', cacheSize, preSetIpList)
10-
this.dnsServer = dnsServer
19+
this.dnsServer = dnsServer.replace(/\s+/, '')
20+
this.dnsServerName = dnsServerName
1121
}
1222

1323
_dnsQueryPromise (hostname, type = 'A') {
14-
return dohQueryAsync({ url: this.dnsServer }, [{ type, name: hostname }])
24+
// 请求参数
25+
const options = {
26+
url: this.dnsServer,
27+
agent: createAgent(this.dnsServer),
28+
}
29+
if (this.dnsServerName) {
30+
// 设置SNI
31+
options.servername = this.dnsServerName
32+
options.rejectUnauthorized = false
33+
}
34+
35+
// DNS查询参数
36+
const questions = [
37+
{
38+
type,
39+
name: hostname,
40+
},
41+
]
42+
43+
return dohQueryAsync(options, questions)
1544
}
1645
}

packages/mitmproxy/src/lib/dns/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ module.exports = {
4848
}
4949

5050
// 基于 https
51-
dnsMap[provider] = new DNSOverHTTPS(provider, conf.cacheSize, preSetIpList, server)
51+
dnsMap[provider] = new DNSOverHTTPS(provider, conf.cacheSize, preSetIpList, server, conf.sni || conf.servername)
5252
} else {
5353
// 获取DNS端口
5454
let port = conf.port
@@ -64,7 +64,7 @@ module.exports = {
6464

6565
if (type === 'tls' || type === 'dot' || type === 'dns-over-tls') {
6666
// 基于 tls
67-
dnsMap[provider] = new DNSOverTLS(provider, conf.cacheSize, preSetIpList, server, port, conf.servername || conf.sni)
67+
dnsMap[provider] = new DNSOverTLS(provider, conf.cacheSize, preSetIpList, server, port, conf.sni || conf.servername)
6868
} else if (type === 'tcp') {
6969
// 基于 tcp
7070
dnsMap[provider] = new DNSOverTCP(provider, conf.cacheSize, preSetIpList, server, port)

packages/mitmproxy/src/lib/dns/tcp.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const defaultPort = 53 // TCP类型的DNS服务默认端口号
99
module.exports = class DNSOverTCP extends BaseDNS {
1010
constructor (dnsName, cacheSize, preSetIpList, dnsServer, dnsServerPort) {
1111
super(dnsName, 'TCP', cacheSize, preSetIpList)
12-
this.dnsServer = dnsServer
12+
this.dnsServer = dnsServer.replace(/\s+/, '')
1313
this.dnsServerPort = Number.parseInt(dnsServerPort) || defaultPort
1414
}
1515

packages/mitmproxy/src/lib/dns/tls.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const defaultPort = 853
66
module.exports = class DNSOverTLS extends BaseDNS {
77
constructor (dnsName, cacheSize, preSetIpList, dnsServer, dnsServerPort, dnsServerName) {
88
super(dnsName, 'TLS', cacheSize, preSetIpList)
9-
this.dnsServer = dnsServer
9+
this.dnsServer = dnsServer.replace(/\s+/, '')
1010
this.dnsServerPort = Number.parseInt(dnsServerPort) || defaultPort
1111
this.dnsServerName = dnsServerName
1212
}

packages/mitmproxy/src/lib/dns/udp.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const defaultPort = 53 // UDP类型的DNS服务默认端口号
88
module.exports = class DNSOverUDP extends BaseDNS {
99
constructor (dnsName, cacheSize, preSetIpList, dnsServer, dnsServerPort) {
1010
super(dnsName, 'UDP', cacheSize, preSetIpList)
11-
this.dnsServer = dnsServer
11+
this.dnsServer = dnsServer.replace(/\s+/, '')
1212
this.dnsServerPort = Number.parseInt(dnsServerPort) || defaultPort
1313

1414
this.isIPv6 = dnsServer.includes(':') && dnsServer.includes('[') && dnsServer.includes(']')
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import DNSOverHTTPS from "../src/lib/dns/https.js";
2+
3+
// 境外DNS的DoH配置sni测试
4+
const servers = [
5+
'https://dns.quad9.net/dns-query',
6+
'https://max.rethinkdns.com/dns-query',
7+
'https://sky.rethinkdns.com/dns-query',
8+
'https://doh.opendns.com/dns-query',
9+
'https://1.1.1.1/dns-query',
10+
'https://dns.cloudflare.com/dns-query',
11+
'https://cloudflare-dns.com/dns-query',
12+
'https://dns.google/dns-query',
13+
'https://dns.bebasid.com/unfiltered',
14+
'https://0ms.dev/dns-query',
15+
'https://dns.decloudus.com/dns-query',
16+
'https://wikimedia-dns.org/dns-query',
17+
'https://doh.applied-privacy.net/query',
18+
'https://private.canadianshield.cira.ca/dns-query',
19+
// 'https://dns.controld.com/comss', // 可直连,无需SNI
20+
'https://kaitain.restena.lu/dns-query',
21+
'https://doh.libredns.gr/dns-query',
22+
'https://doh.libredns.gr/ads',
23+
'https://dns.switch.ch/dns-query',
24+
'https://doh.nl.ahadns.net/dns-query',
25+
'https://doh.la.ahadns.net/dns-query',
26+
'https://dns.dnswarden.com/uncensored',
27+
'https://doh.ffmuc.net/dns-query',
28+
'https://dns.oszx.co/dns-query',
29+
'https://doh.tiarap.org/dns-query',
30+
'https://jp.tiarap.org/dns-query',
31+
'https://dns.adguard.com/dns-query',
32+
'https://rubyfish.cn/dns-query',
33+
'https://i.233py.com/dns-query'
34+
35+
]
36+
37+
const hostname1 = 'github.com'
38+
const sni = 'baidu.com'
39+
40+
console.log(`\n--------------- 测试DoH的SNI功能:共 ${servers.length} 个DoH服务 ---------------\n`)
41+
42+
let n = 0
43+
let success = 0
44+
let error = 0
45+
const arr = []
46+
47+
function count (isSuccess, i, doh, result) {
48+
n++
49+
if (isSuccess) {
50+
success++
51+
arr[i] = `${doh.dnsServer} : ${hostname1} -> ${result.answers[0].data}`;
52+
} else error++
53+
54+
if (n === servers.length) {
55+
console.info(`\n\n=============================================================================\n全部测完:总计:${servers.length}, 成功:${success},失败:${error}`);
56+
for (const item of arr) {
57+
if (item) {
58+
console.info(item);
59+
}
60+
}
61+
console.info('=============================================================================\n\n')
62+
}
63+
}
64+
65+
for (let i = 0; i < servers.length; i++) {
66+
const n = i;
67+
const doh = new DNSOverHTTPS(`dns${i}`, null, null, servers[i], sni)
68+
doh._doDnsQuery(hostname1)
69+
.then((result) => {
70+
// console.info(`===> test testDoH '${doh.dnsServer}': ${hostname1} ->`, result.answers, '\n\n')
71+
count(true, n, doh, result)
72+
})
73+
.catch((e) => {
74+
// console.error(`===> test testDoH '${doh.dnsServer}': ${hostname1} 失败:`, e, '\n\n')
75+
count(false)
76+
})
77+
}

0 commit comments

Comments
 (0)