From 32cffc19c7b20db2f979974860632b8f92f151cd Mon Sep 17 00:00:00 2001 From: Micooz Date: Sat, 10 Jun 2017 16:23:51 +0800 Subject: [PATCH] chore(lib): update --- lib/core/balancer.js | 2 +- lib/core/client-proxy.js | 2 +- lib/core/config.js | 2 +- lib/core/constants.js | 1 + lib/core/hub.js | 2 +- lib/core/index.js | 2 +- lib/core/logger.js | 1 + lib/core/middleware.js | 2 +- lib/core/socket.js | 2 +- lib/presets/ss-base.js | 2 +- lib/proxies/socks4/RequestMessage.js | 2 +- 11 files changed, 11 insertions(+), 9 deletions(-) create mode 100644 lib/core/constants.js create mode 100644 lib/core/logger.js diff --git a/lib/core/balancer.js b/lib/core/balancer.js index 86bf17c..062f3b7 100755 --- a/lib/core/balancer.js +++ b/lib/core/balancer.js @@ -1 +1 @@ -'use strict';Object.defineProperty(exports,'__esModule',{value:true});exports.Balancer=undefined;var _net=require('net');var _net2=_interopRequireDefault(_net);var _winston=require('winston');var _winston2=_interopRequireDefault(_winston);function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}const QUERY_INTERVAL=12e4;const now=()=>new Date().getTime();class Balancer{static start(servers,interval=QUERY_INTERVAL){if(servers.length<1){throw Error('servers cannot be empty')}this._servers=servers;this._pings=this._servers.map(()=>0);this._startQuery(interval)}static destroy(){this._stopQuery()}static getFastest(){let index=0;const pings=this._pings;for(let i=0;i0&&ping1){if(this._timer!==null){this._stopQuery()}this._timer=setInterval(()=>this._query(),interval);this._query()}}static _stopQuery(){clearInterval(this._timer);this._timer=null}static _query(){this._servers.map((server,i)=>{const sstr=`${server.host}:${server.port}`;_winston2.default.verbose(`[balancer] querying ${sstr}`);const startTime=now();const socket=_net2.default.connect({host:server.host,port:server.port},()=>{const ping=now()-startTime;this._pings[i]=ping;_winston2.default.verbose(`[balancer] ${sstr} = ${ping}ms`);socket.end()});socket.on('error',()=>{this._pings[i]=-1;_winston2.default.warn(`[balancer] ${sstr} lost connection`)})})}}exports.Balancer=Balancer;Balancer._servers=[];Balancer._pings=[];Balancer._timer=null; \ No newline at end of file +'use strict';Object.defineProperty(exports,'__esModule',{value:true});exports.Balancer=undefined;var _net=require('net');var _net2=_interopRequireDefault(_net);var _logger=require('./logger');var _logger2=_interopRequireDefault(_logger);function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}const QUERY_INTERVAL=12e4;const now=()=>new Date().getTime();class Balancer{static start(servers,interval=QUERY_INTERVAL){if(servers.length<1){throw Error('servers cannot be empty')}this._servers=servers;this._pings=this._servers.map(()=>0);this._startQuery(interval)}static destroy(){this._stopQuery()}static getFastest(){let index=0;const pings=this._pings;for(let i=0;i0&&ping1){if(this._timer!==null){this._stopQuery()}this._timer=setInterval(()=>this._query(),interval);this._query()}}static _stopQuery(){clearInterval(this._timer);this._timer=null}static _query(){this._servers.map((server,i)=>{const sstr=`${server.host}:${server.port}`;_logger2.default.verbose(`[balancer] querying ${sstr}`);const startTime=now();const socket=_net2.default.connect({host:server.host,port:server.port},()=>{const ping=now()-startTime;this._pings[i]=ping;_logger2.default.verbose(`[balancer] ${sstr} = ${ping}ms`);socket.end()});socket.on('error',()=>{this._pings[i]=-1;_logger2.default.warn(`[balancer] ${sstr} lost connection`)})})}}exports.Balancer=Balancer;Balancer._servers=[];Balancer._pings=[];Balancer._timer=null; \ No newline at end of file diff --git a/lib/core/client-proxy.js b/lib/core/client-proxy.js index 69e0ac9..ab150c8 100644 --- a/lib/core/client-proxy.js +++ b/lib/core/client-proxy.js @@ -1 +1 @@ -'use strict';Object.defineProperty(exports,'__esModule',{value:true});exports.ClientProxy=undefined;var _blinksocksUtils=require('blinksocks-utils');var _socks=require('../proxies/socks5');var _socks2=require('../proxies/socks4');var _http=require('../proxies/http');var _common=require('../proxies/common');class ClientProxy{constructor(props){this._socksTcpReady=false;this._socksUdpReady=false;this._httpReady=false;this.onHandshakeDone=props.onHandshakeDone}isDone(){return[this._socksTcpReady,this._socksUdpReady,this._httpReady].some(v=>!!v)}makeHandshake(socket,buffer){this._trySocksHandshake(socket,buffer);if(!this.isDone()){this._tryHttpHandshake(socket,buffer)}}_trySocksHandshake(socket,buffer){if(!this.isDone()){this._trySocks5Handshake(socket,buffer)}if(!this.isDone()){this._trySocks4Handshake(socket,buffer)}}_trySocks4Handshake(socket,buffer){const request=_socks2.RequestMessage.parse(buffer);if(request!==null){const CMD=request.CMD,DSTIP=request.DSTIP,DSTADDR=request.DSTADDR,DSTPORT=request.DSTPORT;if(CMD===_common.REQUEST_COMMAND_CONNECT){const addr={type:DSTADDR.length>0?_common.ATYP_DOMAIN:_common.ATYP_V4,host:DSTADDR.length>0?DSTADDR:DSTIP,port:DSTPORT};this.onHandshakeDone(addr,()=>{const message=new _socks2.ReplyMessage({CMD:_common.REPLY_GRANTED});socket.write(message.toBuffer());this._socksTcpReady=true})}}}_trySocks5Handshake(socket,buffer){const identifier=_socks.IdentifierMessage.parse(buffer);if(identifier!==null){const message=new _socks.SelectMessage;socket.write(message.toBuffer());return}const request=_socks.RequestMessage.parse(buffer);if(request!==null){const type=request.CMD;switch(type){case _common.REQUEST_COMMAND_UDP:case _common.REQUEST_COMMAND_CONNECT:{const addr={type:request.ATYP,host:request.DSTADDR,port:request.DSTPORT};this.onHandshakeDone(addr,()=>{const message=new _socks.ReplyMessage({REP:_common.REPLY_SUCCEEDED});socket.write(message.toBuffer());if(type===_common.REQUEST_COMMAND_CONNECT){this._socksTcpReady=true}else{this._socksUdpReady=true}});break}default:{const message=new _socks.ReplyMessage({REP:_common.REPLY_COMMAND_NOT_SUPPORTED});socket.write(message.toBuffer());break}}}}_tryHttpHandshake(socket,buffer){const request=_http.HttpRequestMessage.parse(buffer);if(request!==null){const METHOD=request.METHOD,HOST=request.HOST;const addr=(0,_blinksocksUtils.parseURI)(HOST.toString());this.onHandshakeDone(addr,onForward=>{if(METHOD.toString()==='CONNECT'){const message=new _http.ConnectReplyMessage;socket.write(message.toBuffer())}else{onForward(buffer)}this._httpReady=true})}}}exports.ClientProxy=ClientProxy; \ No newline at end of file +'use strict';Object.defineProperty(exports,'__esModule',{value:true});exports.ClientProxy=undefined;var _ip=require('ip');var _ip2=_interopRequireDefault(_ip);var _blinksocksUtils=require('blinksocks-utils');var _socks=require('../proxies/socks5');var _socks2=require('../proxies/socks4');var _http=require('../proxies/http');var _common=require('../proxies/common');function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}class ClientProxy{constructor(props){this._socksTcpReady=false;this._socksUdpReady=false;this._httpReady=false;this.onHandshakeDone=props.onHandshakeDone}isDone(){return[this._socksTcpReady,this._socksUdpReady,this._httpReady].some(v=>!!v)}makeHandshake(socket,buffer){this._trySocksHandshake(socket,buffer);if(!this.isDone()){this._tryHttpHandshake(socket,buffer)}}_trySocksHandshake(socket,buffer){if(!this.isDone()){this._trySocks5Handshake(socket,buffer)}if(!this.isDone()){this._trySocks4Handshake(socket,buffer)}}_trySocks4Handshake(socket,buffer){const request=_socks2.RequestMessage.parse(buffer);if(request!==null){const CMD=request.CMD,DSTIP=request.DSTIP,DSTADDR=request.DSTADDR,DSTPORT=request.DSTPORT;if(CMD===_common.REQUEST_COMMAND_CONNECT){const addr={type:DSTADDR.length>0?_common.ATYP_DOMAIN:_common.ATYP_V4,host:DSTADDR.length>0?DSTADDR:DSTIP,port:DSTPORT};if(addr.type!==_common.ATYP_DOMAIN){addr.host=_ip2.default.toString(addr.host)}this.onHandshakeDone(addr,()=>{const message=new _socks2.ReplyMessage({CMD:_common.REPLY_GRANTED});socket.write(message.toBuffer());this._socksTcpReady=true})}}}_trySocks5Handshake(socket,buffer){const identifier=_socks.IdentifierMessage.parse(buffer);if(identifier!==null){const message=new _socks.SelectMessage;socket.write(message.toBuffer());return}const request=_socks.RequestMessage.parse(buffer);if(request!==null){const type=request.CMD;switch(type){case _common.REQUEST_COMMAND_UDP:case _common.REQUEST_COMMAND_CONNECT:{const addr={type:request.ATYP,host:request.DSTADDR,port:request.DSTPORT};if(addr.type!==_common.ATYP_DOMAIN){addr.host=_ip2.default.toString(addr.host)}this.onHandshakeDone(addr,()=>{const message=new _socks.ReplyMessage({REP:_common.REPLY_SUCCEEDED});socket.write(message.toBuffer());if(type===_common.REQUEST_COMMAND_CONNECT){this._socksTcpReady=true}else{this._socksUdpReady=true}});break}default:{const message=new _socks.ReplyMessage({REP:_common.REPLY_COMMAND_NOT_SUPPORTED});socket.write(message.toBuffer());break}}}}_tryHttpHandshake(socket,buffer){const request=_http.HttpRequestMessage.parse(buffer);if(request!==null){const METHOD=request.METHOD,HOST=request.HOST;const addr=(0,_blinksocksUtils.parseURI)(HOST.toString());this.onHandshakeDone(addr,onForward=>{if(METHOD.toString()==='CONNECT'){const message=new _http.ConnectReplyMessage;socket.write(message.toBuffer())}else{onForward(buffer)}this._httpReady=true})}}}exports.ClientProxy=ClientProxy; \ No newline at end of file diff --git a/lib/core/config.js b/lib/core/config.js index 56896bd..b2033fc 100755 --- a/lib/core/config.js +++ b/lib/core/config.js @@ -1 +1 @@ -'use strict';Object.defineProperty(exports,'__esModule',{value:true});exports.Config=exports.DEFAULT_LOG_LEVEL=undefined;var _fs=require('fs');var _fs2=_interopRequireDefault(_fs);var _os=require('os');var _os2=_interopRequireDefault(_os);var _path=require('path');var _path2=_interopRequireDefault(_path);var _winston=require('winston');var _winston2=_interopRequireDefault(_winston);var _blinksocksUtils=require('blinksocks-utils');function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}const HOME_DIR=_os2.default.homedir();const BLINKSOCKS_DIR=_path2.default.join(HOME_DIR,'.blinksocks');const LOG_PATH=_path2.default.join(BLINKSOCKS_DIR,'logs');const DEFAULT_LOG_LEVEL=exports.DEFAULT_LOG_LEVEL='error';function mkdir(dir){try{_fs2.default.lstatSync(dir)}catch(err){if(err.code==='ENOENT'){_fs2.default.mkdirSync(dir)}}}class Config{static validate(json){if(typeof json!=='object'||Array.isArray(json)){throw Error('Invalid configuration file')}if(typeof json.host!=='string'||json.host===''){throw Error('\'host\' must be provided and is not empty')}if(!(0,_blinksocksUtils.isValidPort)(json.port)){throw Error('\'port\' is invalid')}if(typeof json.servers!=='undefined'){if(!Array.isArray(json.servers)){throw Error('\'servers\' must be provided as an array')}const servers=json.servers.filter(server=>server.enabled===true);if(servers.length<1){throw Error('\'servers\' must have at least one enabled item')}servers.forEach(this.validateServer)}else{this.validateServer(json)}if(typeof json.redirect==='string'&&json.redirect!==''){const address=json.redirect.split(':');if(address.length!==2||!(0,_blinksocksUtils.isValidPort)(+address[1])){throw Error('\'redirect\' is an invalid address')}}if(typeof json.timeout!=='number'){throw Error('\'timeout\' must be a number')}if(json.timeout<1){throw Error('\'timeout\' must be greater than 0')}if(json.timeout<60){console.warn(`==> [config] 'timeout' is too short, is ${json.timeout}s expected?`)}}static validateServer(server){if(typeof server.transport!=='string'){throw Error('\'server.transport\' must be a string')}if(!['tcp','udp'].includes(server.transport.toLowerCase())){throw Error('\'server.transport\' must be one of "tcp" or "udp"')}if(typeof server.host!=='string'||server.host===''){throw Error('\'server.host\' must be provided and is not empty')}if(!(0,_blinksocksUtils.isValidPort)(server.port)){throw Error('\'server.port\' is invalid')}if(typeof server.key!=='string'){throw Error('\'server.key\' must be a string')}if(server.key===''){throw Error('\'server.key\' cannot be empty')}if(!Array.isArray(server.presets)){throw Error('\'server.presets\' must be an array')}if(server.presets.length<1){throw Error('\'server.presets\' must contain at least one preset')}var _iteratorNormalCompletion=true;var _didIteratorError=false;var _iteratorError=undefined;try{for(var _iterator=server.presets[Symbol.iterator](),_step;!(_iteratorNormalCompletion=(_step=_iterator.next()).done);_iteratorNormalCompletion=true){const preset=_step.value;const name=preset.name,params=preset.params;if(typeof name==='undefined'){throw Error('\'server.presets[].name\' must be a string')}if(name===''){throw Error('\'server.presets[].name\' cannot be empty')}if(typeof params!=='object'){throw Error('\'server.presets[].params\' must be an object')}const ps=require(`../presets/${preset.name}`).default;if(name!==server.presets[0].name){delete new ps(params||{})}}}catch(err){_didIteratorError=true;_iteratorError=err}finally{try{if(!_iteratorNormalCompletion&&_iterator.return){_iterator.return()}}finally{if(_didIteratorError){throw _iteratorError}}}}static init(json){this.validate(json);global.__LOCAL_HOST__=json.host;global.__LOCAL_PORT__=json.port;if(typeof json.servers!=='undefined'){global.__SERVERS__=json.servers;global.__IS_SERVER__=false;global.__IS_CLIENT__=true}else{global.__IS_SERVER__=true;global.__IS_CLIENT__=false;this.initServer(json)}global.__REDIRECT__=json.redirect;global.__TIMEOUT__=json.timeout;global.__PROFILE__=!!json.profile;global.__IS_WATCH__=!!json.watch;global.__LOG_LEVEL__=this.setUpLogger(json.log_level||DEFAULT_LOG_LEVEL);global.__ALL_CONFIG__=json}static initServer(server){this.validateServer(server);global.__TRANSPORT__=server.transport;global.__SERVER_HOST__=server.host;global.__SERVER_PORT__=server.port;global.__KEY__=server.key;global.__PRESETS__=server.presets}static setUpLogger(level=''){mkdir(BLINKSOCKS_DIR);mkdir(LOG_PATH);let _level=level.toLowerCase();switch(_level){case'silly':case'debug':case'verbose':case'info':case'warn':case'error':break;default:_level=DEFAULT_LOG_LEVEL;break;}_winston2.default.configure({level:_level,transports:[new _winston2.default.transports.Console({colorize:true,prettyPrint:true}),new _winston2.default.transports.File({filename:_path2.default.join(LOG_PATH,`blinksocks-${__IS_CLIENT__?'client':'server'}.log`),maxsize:2*1024*1024,silent:['test','debug'].includes(process.env.NODE_ENV)})]});return _level}}exports.Config=Config; \ No newline at end of file +'use strict';Object.defineProperty(exports,'__esModule',{value:true});exports.Config=undefined;var _fs=require('fs');var _fs2=_interopRequireDefault(_fs);var _blinksocksUtils=require('blinksocks-utils');var _constants=require('./constants');function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function mkdir(dir){try{_fs2.default.lstatSync(dir)}catch(err){if(err.code==='ENOENT'){_fs2.default.mkdirSync(dir)}}}mkdir(_constants.BLINKSOCKS_DIR);mkdir(_constants.LOG_DIR);class Config{static validate(json){if(typeof json!=='object'||Array.isArray(json)){throw Error('Invalid configuration file')}if(typeof json.host!=='string'||json.host===''){throw Error('\'host\' must be provided and is not empty')}if(!(0,_blinksocksUtils.isValidPort)(json.port)){throw Error('\'port\' is invalid')}if(typeof json.servers!=='undefined'){if(!Array.isArray(json.servers)){throw Error('\'servers\' must be provided as an array')}const servers=json.servers.filter(server=>server.enabled===true);if(servers.length<1){throw Error('\'servers\' must have at least one enabled item')}servers.forEach(this.validateServer)}else{this.validateServer(json)}if(typeof json.redirect==='string'&&json.redirect!==''){const address=json.redirect.split(':');if(address.length!==2||!(0,_blinksocksUtils.isValidPort)(+address[1])){throw Error('\'redirect\' is an invalid address')}}if(typeof json.timeout!=='number'){throw Error('\'timeout\' must be a number')}if(json.timeout<1){throw Error('\'timeout\' must be greater than 0')}if(json.timeout<60){console.warn(`==> [config] 'timeout' is too short, is ${json.timeout}s expected?`)}}static validateServer(server){if(typeof server.transport!=='string'){throw Error('\'server.transport\' must be a string')}if(!['tcp','udp'].includes(server.transport.toLowerCase())){throw Error('\'server.transport\' must be one of "tcp" or "udp"')}if(typeof server.host!=='string'||server.host===''){throw Error('\'server.host\' must be provided and is not empty')}if(!(0,_blinksocksUtils.isValidPort)(server.port)){throw Error('\'server.port\' is invalid')}if(typeof server.key!=='string'){throw Error('\'server.key\' must be a string')}if(server.key===''){throw Error('\'server.key\' cannot be empty')}if(!Array.isArray(server.presets)){throw Error('\'server.presets\' must be an array')}if(server.presets.length<1){throw Error('\'server.presets\' must contain at least one preset')}var _iteratorNormalCompletion=true;var _didIteratorError=false;var _iteratorError=undefined;try{for(var _iterator=server.presets[Symbol.iterator](),_step;!(_iteratorNormalCompletion=(_step=_iterator.next()).done);_iteratorNormalCompletion=true){const preset=_step.value;const name=preset.name,params=preset.params;if(typeof name==='undefined'){throw Error('\'server.presets[].name\' must be a string')}if(name===''){throw Error('\'server.presets[].name\' cannot be empty')}if(typeof params!=='object'||params===null){throw Error('\'server.presets[].params\' must be an object and not null')}const ps=require(`../presets/${preset.name}`).default;if(name!==server.presets[0].name){delete new ps(params||{})}}}catch(err){_didIteratorError=true;_iteratorError=err}finally{try{if(!_iteratorNormalCompletion&&_iterator.return){_iterator.return()}}finally{if(_didIteratorError){throw _iteratorError}}}}static init(json){this.validate(json);global.__LOCAL_HOST__=json.host;global.__LOCAL_PORT__=json.port;if(typeof json.servers!=='undefined'){global.__SERVERS__=json.servers.filter(server=>server.enabled);global.__IS_CLIENT__=true}else{global.__IS_CLIENT__=false;this.initServer(json)}global.__IS_SERVER__=!global.__IS_CLIENT__;global.__REDIRECT__=json.redirect;global.__TIMEOUT__=json.timeout;global.__PROFILE__=!!json.profile;global.__IS_WATCH__=!!json.watch;global.__LOG_LEVEL__=json.log_level||_constants.DEFAULT_LOG_LEVEL;global.__ALL_CONFIG__=json}static initServer(server){this.validateServer(server);global.__TRANSPORT__=server.transport;global.__SERVER_HOST__=server.host;global.__SERVER_PORT__=server.port;global.__KEY__=server.key;global.__PRESETS__=server.presets}}exports.Config=Config; \ No newline at end of file diff --git a/lib/core/constants.js b/lib/core/constants.js new file mode 100644 index 0000000..775fc96 --- /dev/null +++ b/lib/core/constants.js @@ -0,0 +1 @@ +'use strict';var _os=require('os');var _os2=_interopRequireDefault(_os);var _path=require('path');var _path2=_interopRequireDefault(_path);function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}const HOME_DIR=_os2.default.homedir();const BLINKSOCKS_DIR=_path2.default.join(HOME_DIR,'.blinksocks');const LOG_DIR=_path2.default.join(BLINKSOCKS_DIR,'logs');const LOG_FILE_PATH=_path2.default.join(LOG_DIR,typeof process.env.RUN_AS==='undefined'?'blinksocks.log':{'server':'blinksocks-server.log','client':'blinksocks-client.log'}[process.env.RUN_AS]);const LOG_FILE_MAX_SIZE=2*1024*1024;const DEFAULT_LOG_LEVEL='info';module.exports={HOME_DIR,BLINKSOCKS_DIR,LOG_DIR,LOG_FILE_PATH,LOG_FILE_MAX_SIZE,DEFAULT_LOG_LEVEL}; \ No newline at end of file diff --git a/lib/core/hub.js b/lib/core/hub.js index f47ee34..9b13ffb 100755 --- a/lib/core/hub.js +++ b/lib/core/hub.js @@ -1 +1 @@ -'use strict';Object.defineProperty(exports,'__esModule',{value:true});exports.Hub=undefined;var _net=require('net');var _net2=_interopRequireDefault(_net);var _winston=require('winston');var _winston2=_interopRequireDefault(_winston);var _config=require('./config');var _socket=require('./socket');var _profile=require('./profile');var _balancer=require('./balancer');function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}const nextId=function(){let i=0;return()=>{if(i>Number.MAX_SAFE_INTEGER-1){i=0}return++i}}();class Hub{constructor(config){this._hub=null;this._sockets=[];this._isClosed=false;if(typeof config!=='undefined'){_config.Config.init(config)}this._hub=_net2.default.createServer();this._hub.on('close',this.onClose.bind(this));this._hub.on('connection',this.onConnect.bind(this));this.onSocketClose=this.onSocketClose.bind(this)}onClose(){if(!this._isClosed){console.info('==> [hub] shutdown');if(__IS_CLIENT__){_balancer.Balancer.destroy();console.info('==> [balancer] stopped')}if(__PROFILE__){console.info('==> [profile] saving...');_profile.Profile.save();_profile.Profile.stop();console.info('==> [profile] stopped')}this._isClosed=true;this._sockets.forEach(socket=>socket.destroy());this._sockets=[]}}onSocketClose(socket){this._sockets=this._sockets.filter(({id})=>id!==socket.id);_profile.Profile.connections=this._sockets.length}onConnect(socket){const id=nextId();const instance=new _socket.Socket({id,socket,onClose:this.onSocketClose});this._sockets.push(instance);_winston2.default.info(`[hub] [${socket.remoteAddress}:${socket.remotePort}] connected`);_profile.Profile.connections+=1}run(callback){const options={host:__LOCAL_HOST__,port:__LOCAL_PORT__};this._hub.listen(options,()=>{console.info('==> [hub] use configuration:');console.info(JSON.stringify(__ALL_CONFIG__,null,' '));console.info(`==> [hub] is running as: ${__IS_SERVER__?'Server':'Client'}`);console.info('==> [hub] is listening on:',this._hub.address());if(__IS_CLIENT__){console.info('==> [balancer] started');_balancer.Balancer.start(__SERVERS__)}if(__PROFILE__){console.info('==> [profile] started');_profile.Profile.start()}if(typeof callback==='function'){callback()}})}terminate(){this._hub.close();this.onClose()}}exports.Hub=Hub; \ No newline at end of file +'use strict';Object.defineProperty(exports,'__esModule',{value:true});exports.Hub=undefined;var _net=require('net');var _net2=_interopRequireDefault(_net);var _logger=require('./logger');var _logger2=_interopRequireDefault(_logger);var _config=require('./config');var _socket=require('./socket');var _profile=require('./profile');var _balancer=require('./balancer');function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}const nextId=function(){let i=0;return()=>{if(i>Number.MAX_SAFE_INTEGER-1){i=0}return++i}}();class Hub{constructor(config){this._hub=null;this._sockets=[];this._isClosed=false;if(typeof config!=='undefined'){_config.Config.init(config)}_logger2.default.level=__LOG_LEVEL__;this._hub=_net2.default.createServer();this._hub.on('close',this.onClose.bind(this));this._hub.on('connection',this.onConnect.bind(this));this.onSocketClose=this.onSocketClose.bind(this)}onClose(){if(!this._isClosed){_logger2.default.info('==> [hub] shutdown');if(__IS_CLIENT__){_balancer.Balancer.destroy();_logger2.default.info('==> [balancer] stopped')}if(__PROFILE__){_logger2.default.info('==> [profile] saving...');_profile.Profile.save();_profile.Profile.stop();_logger2.default.info('==> [profile] stopped')}this._isClosed=true;this._sockets.forEach(socket=>socket.destroy());this._sockets=[]}}onSocketClose(socket){this._sockets=this._sockets.filter(({id})=>id!==socket.id);_profile.Profile.connections=this._sockets.length}onConnect(socket){const id=nextId();const instance=new _socket.Socket({id,socket,onClose:this.onSocketClose});this._sockets.push(instance);_logger2.default.info(`[hub] [${socket.remoteAddress}:${socket.remotePort}] connected`);_profile.Profile.connections+=1}run(callback){const options={host:__LOCAL_HOST__,port:__LOCAL_PORT__};this._hub.listen(options,()=>{_logger2.default.info(`==> [hub] use configuration: ${JSON.stringify(__ALL_CONFIG__)}`);_logger2.default.info(`==> [hub] running as: ${__IS_SERVER__?'Server':'Client'}`);_logger2.default.info(`==> [hub] listening on: ${JSON.stringify(this._hub.address())}`);if(__IS_CLIENT__){_logger2.default.info('==> [balancer] started');_balancer.Balancer.start(__SERVERS__)}if(__PROFILE__){_logger2.default.info('==> [profile] started');_profile.Profile.start()}if(typeof callback==='function'){callback()}})}terminate(){this._hub.close();this.onClose()}}exports.Hub=Hub; \ No newline at end of file diff --git a/lib/core/index.js b/lib/core/index.js index 677a5d8..0a3ac73 100644 --- a/lib/core/index.js +++ b/lib/core/index.js @@ -1 +1 @@ -'use strict';Object.defineProperty(exports,'__esModule',{value:true});var _balancer=require('./balancer');Object.keys(_balancer).forEach(function(key){if(key==='default'||key==='__esModule')return;Object.defineProperty(exports,key,{enumerable:true,get:function get(){return _balancer[key]}})});var _config=require('./config');Object.keys(_config).forEach(function(key){if(key==='default'||key==='__esModule')return;Object.defineProperty(exports,key,{enumerable:true,get:function get(){return _config[key]}})});var _hub=require('./hub');Object.keys(_hub).forEach(function(key){if(key==='default'||key==='__esModule')return;Object.defineProperty(exports,key,{enumerable:true,get:function get(){return _hub[key]}})}); \ No newline at end of file +'use strict';Object.defineProperty(exports,'__esModule',{value:true});var _balancer=require('./balancer');Object.keys(_balancer).forEach(function(key){if(key==='default'||key==='__esModule')return;Object.defineProperty(exports,key,{enumerable:true,get:function get(){return _balancer[key]}})});var _config=require('./config');Object.keys(_config).forEach(function(key){if(key==='default'||key==='__esModule')return;Object.defineProperty(exports,key,{enumerable:true,get:function get(){return _config[key]}})});var _hub=require('./hub');Object.keys(_hub).forEach(function(key){if(key==='default'||key==='__esModule')return;Object.defineProperty(exports,key,{enumerable:true,get:function get(){return _hub[key]}})});var _logger=require('./logger');Object.keys(_logger).forEach(function(key){if(key==='default'||key==='__esModule')return;Object.defineProperty(exports,key,{enumerable:true,get:function get(){return _logger[key]}})}); \ No newline at end of file diff --git a/lib/core/logger.js b/lib/core/logger.js new file mode 100644 index 0000000..8b29881 --- /dev/null +++ b/lib/core/logger.js @@ -0,0 +1 @@ +'use strict';Object.defineProperty(exports,'__esModule',{value:true});exports.logger=undefined;var _winston=require('winston');var _winston2=_interopRequireDefault(_winston);var _constants=require('./constants');function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}const instance=new _winston2.default.Logger({transports:[new _winston2.default.transports.Console({colorize:true,prettyPrint:true}),new _winston2.default.transports.File({filename:_constants.LOG_FILE_PATH,maxsize:_constants.LOG_FILE_MAX_SIZE,silent:['test','debug'].includes(process.env.NODE_ENV)})]});const logger=exports.logger=instance;exports.default=instance; \ No newline at end of file diff --git a/lib/core/middleware.js b/lib/core/middleware.js index 3197e61..b941955 100644 --- a/lib/core/middleware.js +++ b/lib/core/middleware.js @@ -1 +1 @@ -'use strict';Object.defineProperty(exports,'__esModule',{value:true});exports.Middleware=exports.MIDDLEWARE_DIRECTION_DOWNWARD=exports.MIDDLEWARE_DIRECTION_UPWARD=undefined;exports.createMiddleware=createMiddleware;var _events=require('events');var _events2=_interopRequireDefault(_events);var _winston=require('winston');var _winston2=_interopRequireDefault(_winston);function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}const MIDDLEWARE_DIRECTION_UPWARD=exports.MIDDLEWARE_DIRECTION_UPWARD=1;const MIDDLEWARE_DIRECTION_DOWNWARD=exports.MIDDLEWARE_DIRECTION_DOWNWARD=-1;class Middleware extends _events2.default{constructor(impl){super();this._broadcast=null;this._impl=null;this._impl=impl}subscribe(receiver){this._broadcast=receiver}onNotified(action){return this._impl.onNotified(action)}write(direction,{buffer,direct,fail}){const type={[MIDDLEWARE_DIRECTION_UPWARD]:'Out',[MIDDLEWARE_DIRECTION_DOWNWARD]:'In'}[direction];const broadcast=this._broadcast;const next=buf=>{const args={buffer:buf,next:processed=>this.emit(`next_${direction}`,processed),broadcast,direct,fail};const ret=__IS_CLIENT__?this._impl[`client${type}`](args):this._impl[`server${type}`](args);if(typeof ret!=='undefined'){args.next(ret)}};const r=this._impl[`before${type}`]({buffer,next,broadcast,direct,fail});if(typeof r!=='undefined'){next(r)}}}exports.Middleware=Middleware;function createMiddleware(name,params={}){try{const ImplClass=require(`../presets/${name}`).default;const impl=new ImplClass(params);checkMiddleware(ImplClass.name,impl);return new Middleware(impl)}catch(err){_winston2.default.error(err.message);process.exit(-1)}return null}function checkMiddleware(name,impl){const requiredMethods=['clientOut','serverIn','serverOut','clientIn'];if(requiredMethods.some(method=>typeof impl[method]!=='function')){throw Error(`all methods [${requiredMethods.toString()}] in ${name} must be implemented`)}return true} \ No newline at end of file +'use strict';Object.defineProperty(exports,'__esModule',{value:true});exports.Middleware=exports.MIDDLEWARE_DIRECTION_DOWNWARD=exports.MIDDLEWARE_DIRECTION_UPWARD=undefined;exports.createMiddleware=createMiddleware;var _events=require('events');var _events2=_interopRequireDefault(_events);var _logger=require('./logger');var _logger2=_interopRequireDefault(_logger);function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}const MIDDLEWARE_DIRECTION_UPWARD=exports.MIDDLEWARE_DIRECTION_UPWARD=1;const MIDDLEWARE_DIRECTION_DOWNWARD=exports.MIDDLEWARE_DIRECTION_DOWNWARD=-1;class Middleware extends _events2.default{constructor(impl){super();this._broadcast=null;this._impl=null;this._impl=impl}subscribe(receiver){this._broadcast=receiver}onNotified(action){return this._impl.onNotified(action)}write(direction,{buffer,direct,fail}){const type={[MIDDLEWARE_DIRECTION_UPWARD]:'Out',[MIDDLEWARE_DIRECTION_DOWNWARD]:'In'}[direction];const broadcast=this._broadcast;const next=buf=>{const args={buffer:buf,next:processed=>this.emit(`next_${direction}`,processed),broadcast,direct,fail};const ret=__IS_CLIENT__?this._impl[`client${type}`](args):this._impl[`server${type}`](args);if(typeof ret!=='undefined'){args.next(ret)}};const r=this._impl[`before${type}`]({buffer,next,broadcast,direct,fail});if(typeof r!=='undefined'){next(r)}}}exports.Middleware=Middleware;function createMiddleware(name,params={}){try{const ImplClass=require(`../presets/${name}`).default;const impl=new ImplClass(params);checkMiddleware(ImplClass.name,impl);return new Middleware(impl)}catch(err){_logger2.default.error(err.message);process.exit(-1)}return null}function checkMiddleware(name,impl){const requiredMethods=['clientOut','serverIn','serverOut','clientIn'];if(requiredMethods.some(method=>typeof impl[method]!=='function')){throw Error(`all methods [${requiredMethods.toString()}] in ${name} must be implemented`)}return true} \ No newline at end of file diff --git a/lib/core/socket.js b/lib/core/socket.js index bea43bb..65340a3 100755 --- a/lib/core/socket.js +++ b/lib/core/socket.js @@ -1 +1 @@ -'use strict';Object.defineProperty(exports,'__esModule',{value:true});exports.Socket=undefined;var _slicedToArray=function(){function sliceIterator(arr,i){var _arr=[];var _n=true;var _d=false;var _e=undefined;try{for(var _i=arr[Symbol.iterator](),_s;!(_n=(_s=_i.next()).done);_n=true){_arr.push(_s.value);if(i&&_arr.length===i)break}}catch(err){_d=true;_e=err}finally{try{if(!_n&&_i['return'])_i['return']()}finally{if(_d)throw _e}}return _arr}return function(arr,i){if(Array.isArray(arr)){return arr}else if(Symbol.iterator in Object(arr)){return sliceIterator(arr,i)}else{throw new TypeError('Invalid attempt to destructure non-iterable instance')}}}();var _extends=Object.assign||function(target){for(var i=1;i=MAX_BUFFERED_SIZE){this._bsocket.pause()}}onBackward(buffer){if(__IS_CLIENT__){this.clientIn(buffer)}else{if(this._isRedirect){this.bsocketWritable&&this._bsocket.write(buffer);return}this.serverOut(buffer)}if(this._bsocket&&this._bsocket.bufferSize>=MAX_BUFFERED_SIZE){this._fsocket.pause()}}onError(err){_winston2.default.warn(`[socket] [${this.remote}] ${err.code} - ${err.message}`);_profile.Profile.errors+=1}onForwardSocketDrain(){if(this._bsocket!==null&&!this._bsocket.destroyed){this._bsocket.resume()}}onForwardSocketTimeout({host,port}){_winston2.default.warn(`[socket] [${host}:${port}] timeout: no I/O on the connection for ${__TIMEOUT__}s`);this.onForwardSocketClose()}onForwardSocketClose(){if(this._fsocket!==null&&!this._fsocket.destroyed){this._fsocket.destroy()}if(__IS_CLIENT__&&this._tracks.length>0){this.dumpTrack()}this._fsocket=null}onBackwardSocketDrain(){if(this._fsocket!==null&&!this._fsocket.destroyed){this._fsocket.resume()}}onBackwardSocketTimeout({host,port}){_winston2.default.warn(`[socket] [${host}:${port}] timeout: no I/O on the connection for ${__TIMEOUT__}s`);this.onBackwardSocketClose()}onBackwardSocketClose(){if(this._bsocket!==null&&!this._bsocket.destroyed){this._bsocket.destroy()}this.onForwardSocketClose();if(__IS_SERVER__&&this._tracks.length>0){this.dumpTrack()}this._bsocket=null;this._onClose(this)}onHandshakeDone(addr,callback){const server=_balancer.Balancer.getFastest();if(lastServer===null||!(0,_lodash2.default)(server,lastServer)){_config.Config.initServer(server);lastServer=server;_winston2.default.info(`[balancer] use: ${__SERVER_HOST__}:${__SERVER_PORT__}`)}var _ref=[addr.host.toString(),addr.port.readUInt16BE(0)];const dstHost=_ref[0],dstPort=_ref[1];return this.connect({host:__SERVER_HOST__,port:__SERVER_PORT__,dstHost,dstPort},()=>{this.createPipe(addr);this._tracks.push(`${dstHost}:${dstPort}`);this._isHandshakeDone=true;callback(this.onForward)})}clientOut(buffer){if(this.fsocketWritable){try{this._pipe.feed(_middleware.MIDDLEWARE_DIRECTION_UPWARD,buffer)}catch(err){_winston2.default.error(`[socket] [${this.remote}]`,err)}}}serverIn(buffer){if(this.fsocketWritable||!this._isHandshakeDone){try{this._pipe.feed(_middleware.MIDDLEWARE_DIRECTION_DOWNWARD,buffer);this._tracks.push(TRACK_CHAR_DOWNLOAD);this._tracks.push(buffer.length)}catch(err){_winston2.default.error(`[socket] [${this.remote}]`,err)}}}serverOut(buffer){if(this.bsocketWritable){try{this._pipe.feed(_middleware.MIDDLEWARE_DIRECTION_UPWARD,buffer)}catch(err){_winston2.default.error(`[socket] [${this.remote}]`,err)}}}clientIn(buffer){if(this.bsocketWritable){try{this._pipe.feed(_middleware.MIDDLEWARE_DIRECTION_DOWNWARD,buffer);this._tracks.push(TRACK_CHAR_DOWNLOAD);this._tracks.push(buffer.length)}catch(err){_winston2.default.error(`[socket] [${this.remote}]`,err)}}}send(direction,buffer){if(direction===_middleware.MIDDLEWARE_DIRECTION_UPWARD){if(__IS_CLIENT__){this.clientForward(buffer)}else{this.serverBackward(buffer)}}else{if(__IS_CLIENT__){this.clientBackward(buffer)}else{this.serverForward(buffer)}}_profile.Profile.totalOut+=buffer.length}clientForward(buffer){if(this.fsocketWritable){this._fsocket.write(buffer);this._tracks.push(TRACK_CHAR_UPLOAD);this._tracks.push(buffer.length)}}serverForward(buffer){if(this.fsocketWritable){this._fsocket.write(buffer)}}serverBackward(buffer){if(this.bsocketWritable){this._bsocket.write(buffer);this._tracks.push(TRACK_CHAR_UPLOAD);this._tracks.push(buffer.length)}}clientBackward(buffer){if(this.bsocketWritable){this._bsocket.write(buffer)}}connect({host,port,dstHost,dstPort},callback){var _this=this;return _asyncToGenerator(function*(){if(host&&port){if(__IS_CLIENT__){_winston2.default.info(`[socket] [${_this.remote}] request: ${dstHost}:${dstPort}, connecting to: ${host}:${port}`)}else{_winston2.default.info(`[socket] [${_this.remote}] connecting to: ${host}:${port}`)}_this._tracks.push(`${host}:${port}`);try{const ip=yield dnsCache.get(host);_this._fsocket=_net2.default.connect({host:ip,port},callback);_this._fsocket.on('error',_this.onError);_this._fsocket.on('close',_this.onForwardSocketClose);_this._fsocket.on('timeout',_this.onForwardSocketTimeout.bind(_this,{host,port}));_this._fsocket.on('data',_this.onBackward);_this._fsocket.on('drain',_this.onForwardSocketDrain);_this._fsocket.setTimeout(__TIMEOUT__*1e3)}catch(err){_winston2.default.error(`[socket] [${_this.remote}] connect to ${host}:${port} failed due to: ${err.message}`)}}else{_winston2.default.warn(`unexpected host=${host} port=${port}`);_this.onBackwardSocketClose()}})()}createPipe(addr){const presets=__PRESETS__.map((preset,i)=>(0,_middleware.createMiddleware)(preset.name,_extends({},preset.params,i===0?addr:{})));this._pipe=new _pipe.Pipe({onNotified:this.onPipeNotified.bind(this)});this._pipe.setMiddlewares(_middleware.MIDDLEWARE_DIRECTION_UPWARD,presets);this._pipe.on(`next_${_middleware.MIDDLEWARE_DIRECTION_UPWARD}`,buf=>this.send(_middleware.MIDDLEWARE_DIRECTION_UPWARD,buf));this._pipe.on(`next_${_middleware.MIDDLEWARE_DIRECTION_DOWNWARD}`,buf=>this.send(_middleware.MIDDLEWARE_DIRECTION_DOWNWARD,buf))}onPipeNotified(action){if(__IS_SERVER__&&action.type===_defs.SOCKET_CONNECT_TO_DST){var _action$payload=action.payload;const targetAddress=_action$payload.targetAddress,onConnected=_action$payload.onConnected;return this.connect(targetAddress,()=>{this._isHandshakeDone=true;onConnected()})}if(action.type===_defs.PROCESSING_FAILED){return this.onPresetFailed(action)}}onPresetFailed(action){var _action$payload2=action.payload;const message=_action$payload2.message,orgData=_action$payload2.orgData;if(__IS_SERVER__&&__REDIRECT__!==''&&this._fsocket===null){var _REDIRECT__$split=__REDIRECT__.split(':'),_REDIRECT__$split2=_slicedToArray(_REDIRECT__$split,2);const host=_REDIRECT__$split2[0],port=_REDIRECT__$split2[1];_winston2.default.error(`[socket] [${this.remote}] connection is redirected to ${host}:${port} due to: ${message}`);this.connect({host,port},()=>{this._isRedirect=true;this.fsocketWritable&&this._fsocket.write(orgData)})}else{const timeout=(0,_blinksocksUtils.getRandomInt)(10,40);_winston2.default.error(`[socket] [${this.remote}] connection will be closed in ${timeout}s due to: ${message}`);setTimeout(()=>{this.onForwardSocketClose();this.onBackwardSocketClose()},timeout*1e3)}_profile.Profile.fatals+=1}dumpTrack(){let strs=[];let dp=0,db=0;let up=0,ub=0;let ud='';var _iteratorNormalCompletion=true;var _didIteratorError=false;var _iteratorError=undefined;try{for(var _iterator=this._tracks[Symbol.iterator](),_step;!(_iteratorNormalCompletion=(_step=_iterator.next()).done);_iteratorNormalCompletion=true){const el=_step.value;if(el===TRACK_CHAR_UPLOAD||el===TRACK_CHAR_DOWNLOAD){if(ud===el){continue}ud=el}if(Number.isInteger(el)){if(ud===TRACK_CHAR_DOWNLOAD){dp+=1;db+=el}if(ud===TRACK_CHAR_UPLOAD){up+=1;ub+=el}}strs.push(el)}}catch(err){_didIteratorError=true;_iteratorError=err}finally{try{if(!_iteratorNormalCompletion&&_iterator.return){_iterator.return()}}finally{if(_didIteratorError){throw _iteratorError}}}const perSize=Math.floor(TRACK_MAX_SIZE/2);if(strs.length>TRACK_MAX_SIZE){strs=strs.slice(0,perSize).concat([' ... ']).concat(strs.slice(-perSize))}const summary=__IS_CLIENT__?`out/in = ${up}/${dp}, ${ub}b/${db}b`:`in/out = ${dp}/${up}, ${db}b/${ub}b`;_winston2.default.info(`[socket] [${this.remote}] closed with summary(${summary}) abstract(${strs.join(' ')})`);this._tracks=[]}destroy(){this.onForwardSocketClose();this.onBackwardSocketClose()}}exports.Socket=Socket; \ No newline at end of file +'use strict';Object.defineProperty(exports,'__esModule',{value:true});exports.Socket=undefined;var _slicedToArray=function(){function sliceIterator(arr,i){var _arr=[];var _n=true;var _d=false;var _e=undefined;try{for(var _i=arr[Symbol.iterator](),_s;!(_n=(_s=_i.next()).done);_n=true){_arr.push(_s.value);if(i&&_arr.length===i)break}}catch(err){_d=true;_e=err}finally{try{if(!_n&&_i['return'])_i['return']()}finally{if(_d)throw _e}}return _arr}return function(arr,i){if(Array.isArray(arr)){return arr}else if(Symbol.iterator in Object(arr)){return sliceIterator(arr,i)}else{throw new TypeError('Invalid attempt to destructure non-iterable instance')}}}();var _extends=Object.assign||function(target){for(var i=1;i=MAX_BUFFERED_SIZE){this._bsocket.pause()}}onBackward(buffer){if(__IS_CLIENT__){this.clientIn(buffer)}else{if(this._isRedirect){this.bsocketWritable&&this._bsocket.write(buffer);return}this.serverOut(buffer)}if(this._bsocket&&this._bsocket.bufferSize>=MAX_BUFFERED_SIZE){this._fsocket.pause()}}onError(err){_logger2.default.warn(`[socket] [${this.remote}] ${err.code} - ${err.message}`);_profile.Profile.errors+=1}onForwardSocketDrain(){if(this._bsocket!==null&&!this._bsocket.destroyed){this._bsocket.resume()}else{this.onForwardSocketClose()}}onForwardSocketTimeout({host,port}){_logger2.default.warn(`[socket] [${host}:${port}] timeout: no I/O on the connection for ${__TIMEOUT__}s`);this.onForwardSocketClose()}onForwardSocketClose(){if(this._fsocket!==null&&!this._fsocket.destroyed){this._fsocket.destroy()}if(this._bsocket&&this._bsocket.bufferSize<=0){this.onBackwardSocketClose()}if(__IS_CLIENT__&&this._tracks.length>0){this.dumpTrack()}this._fsocket=null}onBackwardSocketDrain(){if(this._fsocket!==null&&!this._fsocket.destroyed){this._fsocket.resume()}else{this.onBackwardSocketClose()}}onBackwardSocketTimeout({host,port}){_logger2.default.warn(`[socket] [${host}:${port}] timeout: no I/O on the connection for ${__TIMEOUT__}s`);this.onBackwardSocketClose()}onBackwardSocketClose(){if(this._bsocket!==null&&!this._bsocket.destroyed){this._bsocket.destroy()}if(this._fsocket&&this._fsocket.bufferSize<=0){this.onForwardSocketClose()}if(__IS_SERVER__&&this._tracks.length>0){this.dumpTrack()}this._bsocket=null;this._onClose(this)}onHandshakeDone(addr,callback){const server=_balancer.Balancer.getFastest();if(lastServer===null||!(0,_lodash2.default)(server,lastServer)){_config.Config.initServer(server);lastServer=server;_logger2.default.info(`[balancer] use: ${__SERVER_HOST__}:${__SERVER_PORT__}`)}var _ref=[addr.host.toString(),addr.port.readUInt16BE(0)];const dstHost=_ref[0],dstPort=_ref[1];return this.connect({host:__SERVER_HOST__,port:__SERVER_PORT__,dstHost,dstPort},()=>{this.createPipe(addr);this._tracks.push(`${dstHost}:${dstPort}`);this._isHandshakeDone=true;callback(this.onForward)})}clientOut(buffer){if(this.fsocketWritable){try{this._pipe.feed(_middleware.MIDDLEWARE_DIRECTION_UPWARD,buffer)}catch(err){_logger2.default.error(`[socket] [${this.remote}]`,err)}}}serverIn(buffer){if(this.fsocketWritable||!this._isHandshakeDone){try{this._pipe.feed(_middleware.MIDDLEWARE_DIRECTION_DOWNWARD,buffer);this._tracks.push(TRACK_CHAR_DOWNLOAD);this._tracks.push(buffer.length)}catch(err){_logger2.default.error(`[socket] [${this.remote}]`,err)}}}serverOut(buffer){if(this.bsocketWritable){try{this._pipe.feed(_middleware.MIDDLEWARE_DIRECTION_UPWARD,buffer)}catch(err){_logger2.default.error(`[socket] [${this.remote}]`,err)}}}clientIn(buffer){if(this.bsocketWritable){try{this._pipe.feed(_middleware.MIDDLEWARE_DIRECTION_DOWNWARD,buffer);this._tracks.push(TRACK_CHAR_DOWNLOAD);this._tracks.push(buffer.length)}catch(err){_logger2.default.error(`[socket] [${this.remote}]`,err)}}}send(direction,buffer){if(direction===_middleware.MIDDLEWARE_DIRECTION_UPWARD){if(__IS_CLIENT__){this.clientForward(buffer)}else{this.serverBackward(buffer)}}else{if(__IS_CLIENT__){this.clientBackward(buffer)}else{this.serverForward(buffer)}}_profile.Profile.totalOut+=buffer.length}clientForward(buffer){if(this.fsocketWritable){this._fsocket.write(buffer);this._tracks.push(TRACK_CHAR_UPLOAD);this._tracks.push(buffer.length)}}serverForward(buffer){if(this.fsocketWritable){this._fsocket.write(buffer)}}serverBackward(buffer){if(this.bsocketWritable){this._bsocket.write(buffer);this._tracks.push(TRACK_CHAR_UPLOAD);this._tracks.push(buffer.length)}}clientBackward(buffer){if(this.bsocketWritable){this._bsocket.write(buffer)}}connect({host,port,dstHost,dstPort},callback){var _this=this;return _asyncToGenerator(function*(){if(host&&port){if(__IS_CLIENT__){_logger2.default.info(`[socket] [${_this.remote}] request: ${dstHost}:${dstPort}, connecting to: ${host}:${port}`)}else{_logger2.default.info(`[socket] [${_this.remote}] connecting to: ${host}:${port}`)}_this._tracks.push(`${host}:${port}`);try{const ip=yield dnsCache.get(host);_this._fsocket=_net2.default.connect({host:ip,port},callback);_this._fsocket.on('error',_this.onError);_this._fsocket.on('close',_this.onForwardSocketClose);_this._fsocket.on('timeout',_this.onForwardSocketTimeout.bind(_this,{host,port}));_this._fsocket.on('data',_this.onBackward);_this._fsocket.on('drain',_this.onForwardSocketDrain);_this._fsocket.setTimeout(__TIMEOUT__*1e3)}catch(err){_logger2.default.error(`[socket] [${_this.remote}] connect to ${host}:${port} failed due to: ${err.message}`)}}else{_logger2.default.warn(`unexpected host=${host} port=${port}`);_this.onBackwardSocketClose()}})()}createPipe(addr){const presets=__PRESETS__.map((preset,i)=>(0,_middleware.createMiddleware)(preset.name,_extends({},preset.params,i===0?addr:{})));this._pipe=new _pipe.Pipe({onNotified:this.onPipeNotified.bind(this)});this._pipe.setMiddlewares(_middleware.MIDDLEWARE_DIRECTION_UPWARD,presets);this._pipe.on(`next_${_middleware.MIDDLEWARE_DIRECTION_UPWARD}`,buf=>this.send(_middleware.MIDDLEWARE_DIRECTION_UPWARD,buf));this._pipe.on(`next_${_middleware.MIDDLEWARE_DIRECTION_DOWNWARD}`,buf=>this.send(_middleware.MIDDLEWARE_DIRECTION_DOWNWARD,buf))}onPipeNotified(action){if(__IS_SERVER__&&action.type===_defs.SOCKET_CONNECT_TO_DST){var _action$payload=action.payload;const targetAddress=_action$payload.targetAddress,onConnected=_action$payload.onConnected;return this.connect(targetAddress,()=>{this._isHandshakeDone=true;onConnected()})}if(action.type===_defs.PROCESSING_FAILED){return this.onPresetFailed(action)}}onPresetFailed(action){var _action$payload2=action.payload;const message=_action$payload2.message,orgData=_action$payload2.orgData;if(__IS_SERVER__&&__REDIRECT__!==''&&this._fsocket===null){var _REDIRECT__$split=__REDIRECT__.split(':'),_REDIRECT__$split2=_slicedToArray(_REDIRECT__$split,2);const host=_REDIRECT__$split2[0],port=_REDIRECT__$split2[1];_logger2.default.error(`[socket] [${this.remote}] connection is redirected to ${host}:${port} due to: ${message}`);this.connect({host,port},()=>{this._isRedirect=true;this.fsocketWritable&&this._fsocket.write(orgData)})}else{const timeout=(0,_blinksocksUtils.getRandomInt)(10,40);_logger2.default.error(`[socket] [${this.remote}] connection will be closed in ${timeout}s due to: ${message}`);setTimeout(()=>{this.onForwardSocketClose();this.onBackwardSocketClose()},timeout*1e3)}_profile.Profile.fatals+=1}dumpTrack(){let strs=[];let dp=0,db=0;let up=0,ub=0;let ud='';var _iteratorNormalCompletion=true;var _didIteratorError=false;var _iteratorError=undefined;try{for(var _iterator=this._tracks[Symbol.iterator](),_step;!(_iteratorNormalCompletion=(_step=_iterator.next()).done);_iteratorNormalCompletion=true){const el=_step.value;if(el===TRACK_CHAR_UPLOAD||el===TRACK_CHAR_DOWNLOAD){if(ud===el){continue}ud=el}if(Number.isInteger(el)){if(ud===TRACK_CHAR_DOWNLOAD){dp+=1;db+=el}if(ud===TRACK_CHAR_UPLOAD){up+=1;ub+=el}}strs.push(el)}}catch(err){_didIteratorError=true;_iteratorError=err}finally{try{if(!_iteratorNormalCompletion&&_iterator.return){_iterator.return()}}finally{if(_didIteratorError){throw _iteratorError}}}const perSize=Math.floor(TRACK_MAX_SIZE/2);if(strs.length>TRACK_MAX_SIZE){strs=strs.slice(0,perSize).concat([' ... ']).concat(strs.slice(-perSize))}const summary=__IS_CLIENT__?`out/in = ${up}/${dp}, ${ub}b/${db}b`:`in/out = ${dp}/${up}, ${db}b/${ub}b`;_logger2.default.info(`[socket] [${this.remote}] closed with summary(${summary}) abstract(${strs.join(' ')})`);this._tracks=[]}destroy(){this.onForwardSocketClose();this.onBackwardSocketClose()}}exports.Socket=Socket; \ No newline at end of file diff --git a/lib/presets/ss-base.js b/lib/presets/ss-base.js index e2a328e..d2a2b11 100755 --- a/lib/presets/ss-base.js +++ b/lib/presets/ss-base.js @@ -1 +1 @@ -'use strict';Object.defineProperty(exports,'__esModule',{value:true});var _ip=require('ip');var _ip2=_interopRequireDefault(_ip);var _blinksocksUtils=require('blinksocks-utils');var _defs=require('./defs');function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}const ATYP_V4=1;const ATYP_V6=4;const ATYP_DOMAIN=3;class SSBasePreset extends _defs.IPreset{constructor(addr){super();this._isHandshakeDone=false;this._isAddressReceived=false;this._atyp=ATYP_V4;this._addr=null;this._port=null;this._staging=Buffer.alloc(0);if(__IS_CLIENT__){const type=addr.type,host=addr.host,port=addr.port;this._atyp=type;this._addr=host;this._port=port}}clientOut({buffer}){if(!this._isHandshakeDone){this._isHandshakeDone=true;return Buffer.from([this._atyp,...(this._atyp===ATYP_DOMAIN?(0,_blinksocksUtils.numberToBuffer)(this._addr.length,1):[]),...this._addr,...this._port,...buffer])}else{return buffer}}serverIn({buffer,next,broadcast,fail}){if(!this._isHandshakeDone){if(this._isAddressReceived){this._staging=Buffer.concat([this._staging,buffer]);return}if(buffer.length<7){fail(`invalid length: ${buffer.length}`);return}const atyp=buffer[0];let addr;let port;let offset=3;switch(atyp){case ATYP_V4:addr=_ip2.default.toString(buffer.slice(1,5));port=buffer.slice(5,7).readUInt16BE(0);offset+=4;break;case ATYP_V6:if(buffer.length<19){fail(`invalid length: ${buffer.length}`);return}addr=_ip2.default.toString(buffer.slice(1,17));port=buffer.slice(16,18).readUInt16BE(0);offset+=16;break;case ATYP_DOMAIN:const domainLen=buffer[1];if(buffer.length{next(Buffer.concat([this._staging,data]));this._isHandshakeDone=true;this._staging=null}}});this._isAddressReceived=true}else{return buffer}}}exports.default=SSBasePreset; \ No newline at end of file +'use strict';Object.defineProperty(exports,'__esModule',{value:true});var _ip=require('ip');var _ip2=_interopRequireDefault(_ip);var _blinksocksUtils=require('blinksocks-utils');var _defs=require('./defs');function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}const ATYP_V4=1;const ATYP_V6=4;const ATYP_DOMAIN=3;class SSBasePreset extends _defs.IPreset{constructor(addr){super();this._isHandshakeDone=false;this._isAddressReceived=false;this._atyp=ATYP_V4;this._host=null;this._port=null;this._staging=Buffer.alloc(0);if(__IS_CLIENT__){const type=addr.type,host=addr.host,port=addr.port;this._atyp=type;this._port=port;this._host=host;if(!(host instanceof Buffer)){if(type===ATYP_DOMAIN){this._host=Buffer.from(host)}else{this._host=_ip2.default.toBuffer(host)}}}}clientOut({buffer}){if(!this._isHandshakeDone){this._isHandshakeDone=true;return Buffer.from([this._atyp,...(this._atyp===ATYP_DOMAIN?(0,_blinksocksUtils.numberToBuffer)(this._host.length,1):[]),...this._host,...this._port,...buffer])}else{return buffer}}serverIn({buffer,next,broadcast,fail}){if(!this._isHandshakeDone){if(this._isAddressReceived){this._staging=Buffer.concat([this._staging,buffer]);return}if(buffer.length<7){fail(`invalid length: ${buffer.length}`);return}const atyp=buffer[0];let addr;let port;let offset=3;switch(atyp){case ATYP_V4:addr=_ip2.default.toString(buffer.slice(1,5));port=buffer.slice(5,7).readUInt16BE(0);offset+=4;break;case ATYP_V6:if(buffer.length<19){fail(`invalid length: ${buffer.length}`);return}addr=_ip2.default.toString(buffer.slice(1,17));port=buffer.slice(16,18).readUInt16BE(0);offset+=16;break;case ATYP_DOMAIN:const domainLen=buffer[1];if(buffer.length{next(Buffer.concat([this._staging,data]));this._isHandshakeDone=true;this._staging=null}}});this._isAddressReceived=true}else{return buffer}}}exports.default=SSBasePreset; \ No newline at end of file diff --git a/lib/proxies/socks4/RequestMessage.js b/lib/proxies/socks4/RequestMessage.js index bd2bada..b608af8 100644 --- a/lib/proxies/socks4/RequestMessage.js +++ b/lib/proxies/socks4/RequestMessage.js @@ -1 +1 @@ -'use strict';Object.defineProperty(exports,'__esModule',{value:true});exports.RequestMessage=undefined;var _extends=Object.assign||function(target){for(var i=1;i