Browse Source

1

pull/88/head
xiaojunnuo 3 years ago
parent
commit
cc46292cd9
  1. 6
      packages/core/src/config/index.js
  2. 12
      packages/core/start/mitmproxy.js
  3. 45
      packages/core/start/user_config.json5
  4. 42
      packages/gui/src/view/pages/server.vue
  5. 36
      packages/mitmproxy/src/lib/proxy/mitmproxy/createConnectHandler.js
  6. 169
      packages/mitmproxy/src/lib/proxy/tls/sniUtil.js
  7. 3
      packages/mitmproxy/src/utils/util.match.js

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

@ -157,9 +157,9 @@ module.exports = {
'pay.weixin.qq.com': true,
'www.baidu.com': true
},
sniList: {
'github.com': 'baidu.com'
},
// sniList: {
// 'github.com': 'abaidu.com'
// },
dns: {
providers: {
aliyun: {

12
packages/core/start/mitmproxy.js

@ -2,7 +2,7 @@
const server = require('@docmirror/mitmproxy')
const JSON5 = require('json5')
const path = require('path')
const home = process.env.HOME
const home = process.env.USER_HOME || process.env.HOME || 'C:/Users/xiaoj/'
let configPath = path.join(home, '.dev-sidecar/running.json')
if (process.argv && process.argv.length > 3) {
configPath = process.argv[2]
@ -11,9 +11,9 @@ if (process.argv && process.argv.length > 3) {
const fs = require('fs')
const configJson = fs.readFileSync(configPath)
const config = JSON5.parse(configJson)
const scriptDir = '../../gui/extra/scripts/'
config.setting.script.defaultDir = path.join(__dirname, scriptDir)
const pacFilePath = '../../gui/extra/pac/pac.txt'
config.plugin.overwall.pac.customPacFilePath = path.join(__dirname, pacFilePath)
// const scriptDir = '../../gui/extra/scripts/'
// config.setting.script.defaultDir = path.join(__dirname, scriptDir)
// const pacFilePath = '../../gui/extra/pac/pac.txt'
// config.plugin.overwall.pac.customPacFilePath = path.join(__dirname, pacFilePath)
config.setting.rootDir = path.join(__dirname, '../../gui/')
server.start(config)

45
packages/core/start/user_config.json5

@ -1,24 +1,31 @@
{
server: {
intercepts: {
'github1.githubassets.com': {
'.*': {
redirect: 'assets.fastgit.org',
test: 'https://github.githubassets.com/favicons/favicon.svg',
desc: '静态资源加速'
}
},
'github.githubassets.com': null,
'notify3.note.youdao.com': {
'/pushserver3/.*': {
abort: true
},
}
app: {
autoStart: {
enabled: true,
},
mode: 'safe',
},
plugin: {
node: {
enabled: true
}
}
}
setting: {
yarnRegistry: null,
},
},
git: {
enabled: true,
},
overwall: {
targets: {
'*yonsz.net': true,
'*bootstrapcdn.com': true,
'*cloudflare.com': true,
'help.yonsz.net': true,
},
},
},
server: {
intercept: {
enabled: false,
},
},
}

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

@ -110,28 +110,28 @@
</a-row>
</div>
</a-tab-pane>
<a-tab-pane tab="SNI" key="5">
<a-row style="margin-top:10px">
<a-col span="19">
<div>这里配置哪些域名要修改sni</div>
</a-col>
<a-col span="3">
<a-button style="margin-left:8px" type="primary" icon="plus" @click="addSniList()"/>
</a-col>
</a-row>
<a-row :gutter="10" style="margin-top: 10px" v-for="(item,index) of sniList" :key='index'>
<a-col :span="14">
<a-input v-model="item.key"></a-input>
</a-col>
<a-col :span="5">
<a-input v-model="item.value"></a-input>
</a-col>
<a-col :span="3">
<a-button type="danger" icon="minus" @click="deleteSniList(item,index)"/>
</a-col>
</a-row>
<!-- <a-tab-pane tab="SNI" key="5">-->
<!-- <a-row style="margin-top:10px">-->
<!-- <a-col span="19">-->
<!-- <div>这里配置哪些域名要修改sni</div>-->
<!-- </a-col>-->
<!-- <a-col span="3">-->
<!-- <a-button style="margin-left:8px" type="primary" icon="plus" @click="addSniList()"/>-->
<!-- </a-col>-->
<!-- </a-row>-->
<!-- <a-row :gutter="10" style="margin-top: 10px" v-for="(item,index) of sniList" :key='index'>-->
<!-- <a-col :span="14">-->
<!-- <a-input v-model="item.key"></a-input>-->
<!-- </a-col>-->
<!-- <a-col :span="5">-->
<!-- <a-input v-model="item.value"></a-input>-->
<!-- </a-col>-->
<!-- <a-col :span="3">-->
<!-- <a-button type="danger" icon="minus" @click="deleteSniList(item,index)"/>-->
<!-- </a-col>-->
<!-- </a-row>-->
</a-tab-pane>
<!-- </a-tab-pane>-->
<a-tab-pane tab="IP测速" key="6">
<div>
<a-alert type="info" message="对从dns获取到的ip进行测速,使用速度最快的ip进行访问。(对使用增强功能的域名没啥用)"></a-alert>

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

@ -7,7 +7,7 @@ const localIP = '127.0.0.1'
const defaultDns = require('dns')
const matchUtil = require('../../../utils/util.match')
const speedTest = require('../../speed/index.js')
const sniExtract = require('../tls/sniUtil.js')
function isSslConnect (sslConnectInterceptors, req, cltSocket, head) {
for (const intercept of sslConnectInterceptors) {
const ret = intercept(req, cltSocket, head)
@ -55,16 +55,11 @@ function connect (req, cltSocket, head, hostname, port, dnsConfig, sniRegexpMap)
let isDnsIntercept = null
const replaceSni = matchUtil.matchHostname(sniRegexpMap, hostname)
console.log('replaceSni', replaceSni, sniRegexpMap)
let servername = null
if (replaceSni) {
servername = replaceSni
}
try {
const options = {
port,
host: hostname,
connectTimeout: 10000,
servername
connectTimeout: 10000
}
if (dnsConfig) {
const dns = DnsUtil.hasDnsLookup(dnsConfig, hostname)
@ -100,6 +95,33 @@ function connect (req, cltSocket, head, hostname, port, dnsConfig, sniRegexpMap)
proxySocket.pipe(cltSocket)
cltSocket.pipe(proxySocket)
// let sniReplaced = false
// cltSocket.on('data', (chunk) => {
// // if (replaceSni && sniReplaced === false) {
// // const sniPackage = sniExtract(chunk)
// // if (sniPackage != null) {
// // sniReplaced = true
// // const bytes = Buffer.from(replaceSni)
// // const start = sniPackage.start
// // const length = sniPackage.length
// // for (let i = 0; i < length; i++) {
// // let char = 97 // a 的ascii
// // if (bytes.length > i) {
// // char = bytes[i]
// // }
// // chunk[start + i] = char
// // }
// // }
// // }
// if (sniReplaced === false) {
// sniReplaced = true
// chunk[chunk.length - 1] = 1
// }
// proxySocket.write(chunk)
// })
// cltSocket.on('end', () => {
// proxySocket.end()
// })
})
cltSocket.on('error', (e) => {

169
packages/mitmproxy/src/lib/proxy/tls/sniUtil.js

@ -0,0 +1,169 @@
module.exports = function extractSNI (data) {
/*
From https://tools.ietf.org/html/rfc5246:
enum {
hello_request(0), client_hello(1), server_hello(2),
certificate(11), server_key_exchange (12),
certificate_request(13), server_hello_done(14),
certificate_verify(15), client_key_exchange(16),
finished(20)
(255)
} HandshakeType;
struct {
HandshakeType msg_type;
uint24 length;
select (HandshakeType) {
case hello_request: HelloRequest;
case client_hello: ClientHello;
case server_hello: ServerHello;
case certificate: Certificate;
case server_key_exchange: ServerKeyExchange;
case certificate_request: CertificateRequest;
case server_hello_done: ServerHelloDone;
case certificate_verify: CertificateVerify;
case client_key_exchange: ClientKeyExchange;
case finished: Finished;
} body;
} Handshake;
struct {
uint8 major;
uint8 minor;
} ProtocolVersion;
struct {
uint32 gmt_unix_time;
opaque random_bytes[28];
} Random;
opaque SessionID<0..32>;
uint8 CipherSuite[2];
enum { null(0), (255) } CompressionMethod;
struct {
ProtocolVersion client_version;
Random random;
SessionID session_id;
CipherSuite cipher_suites<2..2^16-2>;
CompressionMethod compression_methods<1..2^8-1>;
select (extensions_present) {
case false:
struct {};
case true:
Extension extensions<0..2^16-1>;
};
} ClientHello;
*/
var end = data.length
// skip the record header
var pos = 5
// skip HandshakeType (you should already have verified this)
pos += 1
// skip handshake length
pos += 3
// skip protocol version (you should already have verified this)
pos += 2
// skip Random
pos += 32
// skip SessionID
if (pos > end - 1) return null
var sessionIdLength = data[pos]
pos += 1 + sessionIdLength
// skip CipherSuite
if (pos > end - 2) return null
var cipherSuiteLength = data[pos] << 8 | data[pos + 1]
pos += 2 + cipherSuiteLength
// skip CompressionMethod
if (pos > end - 1) return null
var compressionMethodLength = data[pos]
pos += 1 + compressionMethodLength
// verify extensions exist
if (pos > end - 2) return null
var extensionsLength = data[pos] << 8 | data[pos + 1]
pos += 2
// verify the extensions fit
var extensionsEnd = pos + extensionsLength
if (extensionsEnd > end) return null
end = extensionsEnd
/*
From https://tools.ietf.org/html/rfc5246
and http://tools.ietf.org/html/rfc6066:
struct {
ExtensionType extension_type;
opaque extension_data<0..2^16-1>;
} Extension;
enum {
signature_algorithms(13), (65535)
} ExtensionType;
enum {
server_name(0), max_fragment_length(1),
client_certificate_url(2), trusted_ca_keys(3),
truncated_hmac(4), status_request(5), (65535)
} ExtensionType;
struct {
NameType name_type;
select (name_type) {
case host_name: HostName;
} name;
} ServerName;
enum {
host_name(0), (255)
} NameType;
opaque HostName<1..2^16-1>;
struct {
ServerName server_name_list<1..2^16-1>
} ServerNameList;
*/
while (pos <= end - 4) {
var extensionType = data[pos] << 8 | data[pos + 1]
var extensionSize = data[pos + 2] << 8 | data[pos + 3]
pos += 4
if (extensionType === 0) { // ExtensionType was server_name(0)
// read ServerNameList length
if (pos > end - 2) return null
var nameListLength = data[pos] << 8 | data[pos + 1]
pos += 2
// verify we have enough bytes and loop over SeverNameList
var n = pos
pos += nameListLength
if (pos > end) return null
while (n < pos - 3) {
var nameType = data[n]
var nameLength = data[n + 1] << 8 | data[n + 2]
n += 3
// check if NameType is host_name(0)
if (nameType === 0) {
// verify we have enough bytes
if (n > end - nameLength) return null
// decode as ascii and return
const sniName = data.toString('ascii', n, n + nameLength)
return {
sniName,
start: n,
end: n + nameLength,
length: nameLength
}
} else {
n += nameLength
}
}
} else { // ExtensionType was something we are not interested in
pos += extensionSize
}
}
return null
}

3
packages/mitmproxy/src/utils/util.match.js

@ -24,6 +24,9 @@ function domainMapRegexply (hostMap) {
}
function matchHostname (hostMap, hostname) {
if (hostMap == null) {
return null
}
const value = hostMap[hostname]
if (value) {
return value

Loading…
Cancel
Save