Sitelet https://github.com/yufi113/anyproxy/commit/d51cb0ce4b169602353d9b17fb49feee75e22fee
Skip to content

Commit d51cb0c

Browse files
committed
using SNI when intercepting https requests
1 parent 6d84a95 commit d51cb0c

6 files changed

Lines changed: 105 additions & 187 deletions

File tree

‎CHANGELOG‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
10 Feb 2015: anyproxy 3.2.0:
2+
3+
* using SNI when intercepting https requests
4+
15
28 Jan 2015: anyproxy 3.1.2:
26

37
* thanks to iconv-lite, almost webpage with any charset can be correctly decoded in web interface.

‎README.md‎

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,15 @@ A fully configurable proxy in NodeJS, which can handle HTTPS requests perfectly.
2323
------------
2424
* 支持https明文代理
2525
* 支持低网速模拟
26-
* 全流程开放,可以用javascript控制代理流程中的任意步骤。搭建前端个性化调试环境的利器。
27-
* 提供web版界面,供观测请求情况
26+
* 全流程开放,可以用javascript控制代理流程中的任意步骤,搭建前端个性化调试环境
27+
* 提供web版界面,观测请求情况
2828

2929
Feature
3030
------------
3131
* work as http or https proxy
32-
* fully configurable, you can modify a request at any stage by your own javascript code
33-
* when working as https proxy, it can generate and intercept https requests for any domain without complaint by browser (after you trust its root CA)
32+
* fully configurable, you could modify a request at any stage with your customized javascript code
33+
* when working as https proxy, it could generate and intercept https requests for any domain without complaint by browser (after you trust its root CA)
3434
* a web interface for you to watch realtime request details, where html string with (almost) any charset could be shown correctly
35-
* (beta)a web UI interface for you to replace some remote response with local data
3635

3736
![screenshot](http://gtms01.alicdn.com/tps/i1/TB1IdgqGXXXXXa9apXXLExM2pXX-854-480.gif)
3837

‎diff.txt‎

Lines changed: 0 additions & 64 deletions
This file was deleted.

‎lib/certMgr.js‎

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ var exec = require('child_process').exec,
1010

1111
//TODO : unstable in windows
1212
var certDir = path.join(util.getUserHome(),"/.anyproxy_certs/"),
13-
cmdDir = path.join(__dirname,"..","./cert/"),
14-
cmd_genRoot = path.join(cmdDir,"./gen-rootCA"),
15-
cmd_genCert = path.join(cmdDir,"./gen-cer"),
16-
asyncTaskMgr = new asyncTask();
13+
cmdDir = path.join(__dirname,"..","./cert/"),
14+
cmd_genRoot = path.join(cmdDir,"./gen-rootCA"),
15+
cmd_genCert = path.join(cmdDir,"./gen-cer"),
16+
createCertTaskMgr = new asyncTask();
1717

1818
if(!fs.existsSync(certDir)){
1919
try{
@@ -26,26 +26,31 @@ if(!fs.existsSync(certDir)){
2626
}
2727
}
2828

29-
function getCertificate(hostname,cb){
29+
function getCertificate(hostname,certCallback){
30+
3031
var keyFile = path.join(certDir , "__hostname.key".replace(/__hostname/,hostname) ),
3132
crtFile = path.join(certDir , "__hostname.crt".replace(/__hostname/,hostname) );
3233

33-
if(!fs.existsSync(keyFile) || !fs.existsSync(crtFile)){
34-
asyncTaskMgr.addTask(hostname,function(cb){
34+
createCertTaskMgr.addTask(hostname,function(callback){
35+
if(!fs.existsSync(keyFile) || !fs.existsSync(crtFile)){
3536
createCert(hostname,function(err){
36-
cb(err ? err : null);
37+
if(err){
38+
callback(err);
39+
}else{
40+
callback(null , fs.readFileSync(keyFile) , fs.readFileSync(crtFile));
41+
}
3742
});
38-
},function(err){
39-
if(!err){
40-
cb(null , fs.readFileSync(keyFile) , fs.readFileSync(crtFile) );
41-
}else{
42-
cb(err);
43-
}
44-
});
43+
}else{
44+
callback(null , fs.readFileSync(keyFile) , fs.readFileSync(crtFile));
45+
}
4546

46-
}else{
47-
cb(null , fs.readFileSync(keyFile) , fs.readFileSync(crtFile) );
48-
}
47+
},function(err,keyContent,crtContent){
48+
if(!err){
49+
certCallback(null ,keyContent,crtContent);
50+
}else{
51+
certCallback(err);
52+
}
53+
});
4954
}
5055

5156
function createCert(hostname,callback){

‎lib/httpsServerMgr.js‎

Lines changed: 45 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -5,100 +5,61 @@ var getPort = require('./getPort'),
55
https = require('https'),
66
fs = require('fs'),
77
net = require('net'),
8-
url = require('url'),
8+
tls = require('tls'),
99
color = require('colorful'),
1010
certMgr = require("./certMgr"),
1111
asyncTask = require("async-task-mgr");
1212

13-
var DEFAULT_RELEASE_TIME = 2000;//120*1000;
14-
15-
var asyncTaskMgr = new asyncTask();
16-
17-
module.exports =function(){
18-
var self = this;
19-
self.serverList = {
20-
/* schema sample
21-
"www.example.com":{
22-
port:123,
23-
server : serverInstance,
24-
lastestUse: 99999 //unix time stamp
13+
//using sni to avoid multiple ports
14+
function SNIPrepareCert(serverName,SNICallback){
15+
var keyContent, crtContent,ctx;
16+
17+
async.series([
18+
function(callback){
19+
certMgr.getCertificate(serverName,function(err,key,crt){
20+
if(err){
21+
callback(err);
22+
}else{
23+
keyContent = key;
24+
crtContent = crt;
25+
callback();
26+
}
27+
});
28+
},
29+
function(callback){
30+
try{
31+
ctx = tls.createSecureContext({
32+
key :keyContent,
33+
cert :crtContent
34+
});
35+
callback();
36+
}catch(e){
37+
callback(e);
38+
}
2539
}
26-
*/
27-
};
28-
29-
//fetch a port for https server with hostname
30-
this.fetchPort = function(hostname,userRequesthandler,userCB){
31-
var serverInfo = self.serverList[hostname],
32-
port;
33-
34-
//server exists
35-
if(serverInfo){
36-
serverInfo.lastestUse = new Date().getTime();
37-
port = serverInfo.port;
38-
userCB && userCB(null,port);
39-
40-
//create server with corresponding CA
40+
],function(err,result){
41+
if(!err){
42+
var tipText = "proxy server for __NAME established".replace("__NAME",serverName);
43+
console.log(color.yellow(color.bold("[internal https]")) + color.yellow(tipText));
44+
SNICallback(null,ctx);
4145
}else{
42-
43-
asyncTaskMgr.addTask(hostname, createServer ,userCB);
44-
45-
function createServer(cb){
46-
async.series([
47-
//find a clean port
48-
function(callback){
49-
getPort(function(cleanPort){
50-
port = cleanPort;
51-
callback(null,port);
52-
});
53-
}
54-
55-
//create server
56-
,function(callback){
57-
58-
certMgr.getCertificate(hostname,function(err,keyContent,crtContent){
59-
var server = createHttpsServer(port,keyContent,crtContent,userRequesthandler);
60-
self.serverList[hostname] = {
61-
port : port,
62-
server : server,
63-
lastestUse : new Date().getTime()
64-
};
65-
66-
var tipText = "https server @port __portNum for __HOST established".replace(/__portNum/,port).replace(/__HOST/,hostname);
67-
console.log(color.yellow(color.bold("[internal https]")) + color.yellow(tipText));
68-
callback && callback(null,port);
69-
70-
});
71-
}
72-
],function(err,result){
73-
cb && cb(err,result[result.length - 1]);
74-
75-
});
76-
}
46+
console.log("err occurred when prepare certs for SNI - " + e);
7747
}
78-
};
48+
});
49+
}
7950

80-
//clear servers which have been idle for some time
81-
setInterval(function(){
82-
var timeNow = new Date().getTime();
83-
for(var serverName in self.serverList){
84-
var item = self.serverList[serverName];
85-
if( (timeNow - item.lastestUse) > DEFAULT_RELEASE_TIME){
86-
item.server.close();
87-
asyncTaskMgr.removeTask(serverName);
88-
delete self.serverList[serverName];
51+
//config.port - port to start https server
52+
//config.handler - request handler
53+
module.exports =function(config){
54+
var self = this;
8955

90-
console.log(color.yellow(color.bold("[internal https]")) + color.yellow("https server released : " + serverName));
91-
}
92-
}
56+
if(!config || !config.port ){
57+
throw(new Error("please assign a port"));
58+
}
9359

94-
},DEFAULT_RELEASE_TIME);
60+
https.createServer({
61+
SNICallback : SNIPrepareCert
62+
},config.handler).listen(config.port);
9563
}
9664

97-
function createHttpsServer(port,keyContent,crtContent,userRequesthandler){
98-
return https.createServer({
99-
key : keyContent,
100-
cert: crtContent
101-
},userRequesthandler).listen(port);
102-
103-
}
10465

‎lib/requestHandler.js‎

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ var http = require("http"),
99
color = require("colorful"),
1010
Buffer = require('buffer').Buffer,
1111
util = require("./util"),
12+
getPort = require("./getPort"),
1213
Stream = require("stream"),
1314
httpsServerMgr = require("./httpsServerMgr");
1415

15-
var httpsServerMgrInstance = new httpsServerMgr(),
16-
defaultRule = require("./rule_default.js"),
16+
var defaultRule = require("./rule_default.js"),
1717
userRule = defaultRule; //init
1818

1919
function userRequestHandler(req,userRes){
@@ -270,26 +270,40 @@ function connectReqHandler(req, socket, head){
270270
};
271271
resourceInfoId = GLOBAL.recorder.appendRecord(resourceInfo);
272272

273-
var proxyPort, proxyHost;
273+
var proxyPort,
274+
proxyHost,
275+
internalHttpsPort,
276+
httpsServerMgrInstance;
277+
278+
274279
async.series([
275280

276-
//find port
281+
//check if internal https server exists
282+
function(callback){
283+
if(internalHttpsPort){
284+
callback();
285+
}else{
286+
getPort(function(port){
287+
internalHttpsPort = port;
288+
httpsServerMgrInstance = new httpsServerMgr({
289+
port :port,
290+
handler :userRequestHandler
291+
});
292+
callback();
293+
});
294+
}
295+
},
296+
297+
//determine the target server
277298
function(callback){
278299

279300
if(shouldIntercept){
280-
//TODO : remote port other than 433
281-
httpsServerMgrInstance.fetchPort(host,userRequestHandler,function(err,port){
282-
if(!err && port){
283-
proxyPort = port;
284-
proxyHost = "127.0.0.1";
285-
callback();
286-
}else{
287-
callback(err);
288-
}
289-
});
301+
proxyPort = internalHttpsPort;
302+
proxyHost = "127.0.0.1";
303+
callback();
290304

291305
}else{
292-
proxyPort = targetPort;
306+
proxyPort = 443;
293307
proxyHost = host;
294308

295309
callback();
@@ -305,7 +319,6 @@ function connectReqHandler(req, socket, head){
305319
if(GLOBAL._throttle && !shouldIntercept ){
306320
var readable = conn.pipe(GLOBAL._throttle.throttle());
307321
readable.pipe(socket);
308-
309322
socket.pipe(conn);
310323
}else{
311324
conn.pipe(socket);

0 commit comments

Comments
 (0)