Browse Source

refactor: dns优选

pull/180/head
xiaojunnuo 4 years ago
parent
commit
dfd2db5285
  1. 2
      packages/core/src/config/index.js
  2. 2
      packages/core/src/index.js
  3. 1
      packages/core/src/modules/plugin/node/index.js
  4. 4
      packages/core/src/modules/server/index.js
  5. 19
      packages/gui/src/background.js
  6. 3
      packages/gui/src/bridge/index.js
  7. 2
      packages/gui/src/view/event.js
  8. 20
      packages/gui/src/view/mixins/plugin.js
  9. 16
      packages/gui/src/view/pages/plugin/node.vue
  10. 4
      packages/gui/src/view/pages/proxy.vue
  11. 26
      packages/gui/src/view/pages/server.vue
  12. 108
      packages/mitmproxy/src/lib/dns/base.js
  13. 13
      packages/mitmproxy/src/lib/dns/https.js
  14. 1
      packages/mitmproxy/src/lib/dns/index.js
  15. 6
      packages/mitmproxy/src/lib/proxy/common/util.js
  16. 10
      packages/mitmproxy/src/lib/proxy/mitmproxy/createConnectHandler.js
  17. 0
      packages/mitmproxy/test/dnsTest.js
  18. 23
      packages/mitmproxy/test/dnsTest.mjs

2
packages/core/src/config/index.js

@ -24,7 +24,7 @@ module.exports = {
'.*': { proxy: 'raw.fastgit.org' }
},
'github.githubassets.com': {
'.*': { proxy: 'assets.fastgit.org' }
'.*': { proxy: 'assets.fastgit.org', test: 'https://github.githubassets.com/favicons/favicon.svg' }
},
'customer-stories-feed.github.com': {
'.*': { proxy: 'customer-stories-feed.fastgit.org' }

2
packages/core/src/index.js

@ -8,7 +8,7 @@ process.on('uncaughtException', function (err) {
// console.error(err.errno)
return
}
console.error('uncaughtException',err)
console.error('uncaughtException', err)
})
process.on('unhandledRejection', (reason, p) => {

1
packages/core/src/modules/plugin/node/index.js

@ -137,6 +137,7 @@ const NodePlugin = function (context) {
]
const ret = await shell.exec(cmds, { type: 'cmd' })
event.fire('status', { key: 'plugin.node.enabled', value: false })
console.info('关闭【NPM】代理成功')
return ret
}
}

4
packages/core/src/modules/server/index.js

@ -97,9 +97,9 @@ const serverApi = {
}
})
},
async restart () {
async restart ({ mitmproxyPath }) {
await serverApi.kill()
await serverApi.start()
await serverApi.start({ mitmproxyPath })
},
getServer () {
return server

19
packages/gui/src/background.js

@ -35,7 +35,7 @@ function setTray (app) {
}
]
// 设置系统托盘图标
const iconPath = path.join(__dirname, '../extra/favicon.ico')
const iconPath = path.join(__dirname, '../extra/icons/128x128.png')
const appTray = new Tray(iconPath)
// 图标的上下文菜单
@ -44,14 +44,25 @@ function setTray (app) {
// 设置托盘悬浮提示
appTray.setToolTip('DevSidecar-开发者边车辅助工具')
// 设置托盘菜单
appTray.setContextMenu(contextMenu)
// 单击托盘小图标显示应用
appTray.on('click', () => {
// 显示主程序
win.show()
})
// 设置托盘菜单
// appTray.setContextMenu(contextMenu)
// appTray.on('double-click', function () {
// console.log('double click')
// win.show()
// })
appTray.on('right-click', function (event, bounds) {
setTimeout(function () {
appTray.popUpContextMenu(contextMenu)
}, 200)
})
return appTray
}

3
packages/gui/src/bridge/index.js

@ -21,6 +21,9 @@ const localApi = {
server: {
start () {
return DevSidecar.api.server.start({ mitmproxyPath })
},
restart () {
return DevSidecar.api.server.restart({ mitmproxyPath })
}
},
config: {

2
packages/gui/src/view/event.js

@ -22,7 +22,7 @@ function register (app) {
}
function handleServerStartError (err, app) {
if (err.message.indexOf('listen EADDRINUSE') >= 0) {
if (err.message && err.message.indexOf('listen EADDRINUSE') >= 0) {
app.$confirm({
title: '端口被占用,代理服务启动失败',
content: '是否要杀掉占用进程?',

20
packages/gui/src/view/mixins/plugin.js

@ -10,7 +10,8 @@ export default {
config: undefined,
status: status,
labelCol: { span: 4 },
wrapperCol: { span: 20 }
wrapperCol: { span: 20 },
applyLoading: false
}
},
created () {
@ -27,12 +28,17 @@ export default {
}
})
},
apply () {
return this.saveConfig().then(() => {
if (this.applyAfter) {
return this.applyAfter()
}
})
async apply () {
this.applyLoading = true
await this.applyBefore()
await this.saveConfig()
if (this.applyAfter) {
await this.applyAfter()
}
this.applyLoading = false
},
async applyBefore () {
},
reloadDefault (key) {
this.$api.config.resetDefault(key).then(ret => {

16
packages/gui/src/view/pages/plugin/node.vue

@ -59,8 +59,8 @@
</div>
<template slot="footer">
<div class="footer-bar">
<a-button class="md-mr-10" @click="reloadDefault('plugin.node')">恢复默认</a-button>
<a-button type="primary" @click="apply()">应用</a-button>
<a-button class="md-mr-10" icon="sync" @click="reloadDefault('server')">恢复默认</a-button>
<a-button :loading="applyLoading" icon="check" type="primary" @click="apply()">应用</a-button>
</div>
</template>
</ds-container>
@ -91,13 +91,13 @@ export default {
this.npmVariables = ret
})
},
onSwitchRegistry (event) {
return this.setRegistry(event.target.value).then(() => {
this.$message.success('切换成功')
})
async onSwitchRegistry (event) {
await this.setRegistry(event.target.value)
this.$message.success('切换成功')
},
setRegistry (registry) {
return this.$api.plugin.node.setRegistry(registry)
async setRegistry (registry) {
this.apply()
await this.$api.plugin.node.setRegistry(registry)
},
setNpmVariableAll () {
this.saveConfig().then(() => {

4
packages/gui/src/view/pages/proxy.vue

@ -21,8 +21,8 @@
</div>
<template slot="footer">
<div class="footer-bar">
<a-button class="md-mr-10" @click="reloadDefault('proxy')">恢复默认</a-button>
<a-button type="primary" @click="apply()">应用</a-button>
<a-button class="md-mr-10" icon="sync" @click="reloadDefault('server')">恢复默认</a-button>
<a-button :loading="applyLoading" icon="check" type="primary" @click="apply()">应用</a-button>
</div>
</template>
</ds-container>

26
packages/gui/src/view/pages/server.vue

@ -69,8 +69,8 @@
</div>
<template slot="footer">
<div class="footer-bar">
<a-button class="md-mr-10" @click="reloadDefault('server')">恢复默认</a-button>
<a-button type="primary" @click="apply()">应用</a-button>
<a-button class="md-mr-10" icon="sync" @click="reloadDefault('server')">恢复默认</a-button>
<a-button :loading="applyLoading" icon="check" type="primary" @click="apply()">应用</a-button>
</div>
</template>
</ds-container>
@ -109,8 +109,28 @@ export default {
})
}
},
applyAfter () {
async applyBefore () {
const dnsMapping = {}
for (const item of this.dnsMappings) {
if (item.key) {
dnsMapping[item.key] = item.value
}
}
this.config.server.dns.mapping = dnsMapping
},
async applyAfter () {
if (this.status.server.enabled) {
return this.$api.server.restart()
}
},
deleteDnsMapping (item, index) {
this.dnsMappings.splice(index, 1)
},
restoreDefDnsMapping (item, index) {
},
addDnsMapping () {
this.dnsMappings.unshift({ key: '', value: 'usa' })
}
}
}

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

@ -1,11 +1,72 @@
const LRU = require('lru-cache')
const { isIP } = require('validator')
// const { isIP } = require('validator')
const getLogger = require('../utils/logger')
const logger = getLogger('dns')
const cacheSize = 1024
function _isIP (v) {
return v && isIP(v)
// eslint-disable-next-line no-unused-vars
// function _isIP (v) {
// return v && isIP(v)
// }
class IpCache {
constructor (hostname) {
this.hostname = hostname
this.count = {}
this.lookupCount = 0
this.createTime = new Date()
}
/**
* 获取到新的ipList
* @param ipList
*/
setIpList (ipList) {
this.ip = ipList.shift()
this.ipList = ipList
this.lookupCount++
this.doCount(this.ip, false)
}
/**
* 换下一个ip
* @param count
*/
changeNext (count) {
count.keepErrorCount = 0 // 清空连续失败
if (this.ipList > 0) {
this.ip = this.ipList.shift()
} else {
this.ip = null
}
}
/**
* 记录使用次数或错误次数
* @param ip
* @param isError
*/
doCount (ip, isError) {
let count = this.count[ip]
if (count == null) {
count = this.count[ip] = { total: 0, error: 0, keepErrorCount: 0, successRate: 0 }
}
if (isError) {
count.error++
count.keepErrorCount++
} else {
count.total++
}
count.successRate = 1 - (count.error / count.total)
if (isError && this.ip === ip) {
if (count.keepErrorCount >= 5) {
this.changeNext(count)
}
if (count.successRate < 0.51) {
this.changeNext(count)
}
}
}
}
module.exports = class BaseDNS {
@ -13,30 +74,39 @@ module.exports = class BaseDNS {
this.cache = new LRU(cacheSize)
}
count (hostname, ip, isError = true) {
const ipCache = this.cache.get(hostname)
if (ipCache) {
ipCache.doCount(ip, isError)
}
}
async lookup (hostname) {
try {
let ip = this.cache.get(hostname)
if (ip) {
return ip
let ipCache = this.cache.get(hostname)
if (ipCache) {
if (ipCache.ip != null) {
ipCache.doCount(ipCache.ip, false)
return ipCache.ip
}
} else {
ipCache = new IpCache(hostname)
this.cache.set(hostname, ipCache)
}
const t = new Date()
ip = hostname
for (let depth = 0; !_isIP(ip) && depth < 5; depth++) {
ip = await this._lookup(ip).catch(error => {
logger.debug(ip, error)
return ip
})
let ipList = await this._lookup(hostname)
if (ipList == null) {
// 没有获取到ipv4地址
ipList = []
}
ipList.push(hostname) // 把原域名加入到统计里去
if (!_isIP(ip)) {
throw new Error(`BAD IP FORMAT (${ip})`)
}
ipCache.setIpList(ipList)
logger.debug(`[DNS] ${hostname} -> ${ipCache.ip} (${new Date() - t} ms)`)
logger.debug(`[DNS] ${hostname} -> ${ip} (${new Date() - t} ms)`)
this.cache.set(hostname, ip)
return ip
return ipCache.ip
} catch (error) {
logger.debug(`[DNS] cannot resolve hostname ${hostname} (${error})`)
return hostname

13
packages/mitmproxy/src/lib/dns/https.js

@ -12,6 +12,17 @@ module.exports = class DNSOverHTTPS extends BaseDNS {
async _lookup (hostname) {
const result = await dohQueryAsync({ url: this.dnsServer }, [{ type: 'A', name: hostname }])
return result.answers[0].data
if (result.answers.length === 0) {
// 说明没有获取到ip
console.log('该域名没有ip地址解析', hostname)
return []
}
const ret = result.answers.filter(item => { return item.type === 'A' }).map(item => { return item.data })
if (ret.length === 0) {
console.log('该域名没有ipv4地址解析', hostname)
} else {
console.log('获取到域名地址:', hostname, JSON.stringify(ret))
}
return ret
}
}

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

@ -23,7 +23,6 @@ module.exports = {
}
}
if (providerName) {
console.log('匹配到dns:', providerName, hostname)
return dnsConfig.providers[providerName]
}
}

6
packages/mitmproxy/src/lib/proxy/common/util.js

@ -6,13 +6,13 @@ const tunnelAgent = require('tunnel-agent')
const util = exports
const httpsAgent = new HttpsAgent({
keepAlive: true,
timeout: 5000,
timeout: 15000,
keepAliveTimeout: 60000, // free socket keepalive for 30 seconds
rejectUnauthorized: false
})
const httpAgent = new Agent({
keepAlive: true,
timeout: 5000,
timeout: 15000,
keepAliveTimeout: 60000 // free socket keepalive for 30 seconds
})
let socketId = 0
@ -34,7 +34,7 @@ util.getOptionsFormRequest = (req, ssl, externalProxy = null) => {
try {
externalProxyUrl = externalProxy(req, ssl)
} catch (e) {
console.error('externalProxy',e)
console.error('externalProxy', e)
}
}
}

10
packages/mitmproxy/src/lib/proxy/mitmproxy/createConnectHandler.js

@ -21,7 +21,7 @@ module.exports = function createConnectHandler (sslConnectInterceptor, fakeServe
const dns = DnsUtil.hasDnsLookup(dnsConfig, hostname)
if (dns) {
dns.lookup(hostname).then(ip => {
connect(req, cltSocket, head, ip, srvUrl.port)
connect(req, cltSocket, head, ip, srvUrl.port, { dns, hostname, ip })
})
}
}
@ -30,7 +30,7 @@ module.exports = function createConnectHandler (sslConnectInterceptor, fakeServe
}
}
function connect (req, cltSocket, head, hostname, port) {
function connect (req, cltSocket, head, hostname, port, isDnsIntercept) {
// tunneling https
// console.log('connect:', hostname, port)
const start = new Date().getTime()
@ -57,6 +57,12 @@ function connect (req, cltSocket, head, hostname, port) {
const end = new Date().getTime()
console.error('代理连接失败:', e.message, hostname, port, (end - start) + 'ms')
cltSocket.destroy()
if (isDnsIntercept) {
const { dns, ip, hostname } = isDnsIntercept
dns.count(hostname, ip, true)
console.error('记录ip失败次数,用于优选ip:', hostname, ip)
}
})
return proxySocket
} catch (error) {

0
packages/mitmproxy/test/dnsTest.js

23
packages/mitmproxy/test/dnsTest.mjs

@ -0,0 +1,23 @@
import dns from '../src/lib/dns/index.js'
const dnsProviders = dns.initDNS({
aliyun: {
type: 'https',
server: 'https://dns.alidns.com/dns-query',
cacheSize: 1000
},
usa: {
type: 'https',
server: 'https://cloudflare-dns.com/dns-query',
cacheSize: 1000
}
})
// let hostname = 'www.yonsz.com'
// dnsProviders.usa.lookup(hostname)
// const hostname = 'api.github.com'
// dnsProviders.usa.lookup(hostname)
const hostname1 = 'api.github.com'
dnsProviders.aliyun.lookup(hostname1)
Loading…
Cancel
Save