@kloak-it/tq-proxy
Version:
QT PROXY Server/Client
308 lines (306 loc) • 13 kB
JavaScript
"use strict";
/*!
* Copyright 2018 CoNET Technology Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.proxyServer = exports.isAllBlackedByFireWall = exports.getSslConnectFirstData = void 0;
const Net = __importStar(require("net"));
const Http = __importStar(require("http"));
const httpProxy_1 = __importDefault(require("./httpProxy"));
const Crypto = __importStar(require("crypto"));
const res = __importStar(require("./res"));
const Fs = __importStar(require("fs"));
const Path = __importStar(require("path"));
const Socks = __importStar(require("./socket5ForiOpn"));
const gateway_1 = __importDefault(require("./gateway"));
const Os = __importStar(require("os"));
const log_1 = require("../GateWay/log");
const safe_1 = __importDefault(require("colors/safe"));
const util_1 = require("util");
const whiteIpFile = 'whiteIpList.json';
Http.globalAgent.maxSockets = 1024;
const ipConnectResetTime = 1000 * 60 * 5;
let flag = 'w';
const QTGateFolder = Path.join(Os.homedir(), '.QTGate');
const proxyLogFile = Path.join(QTGateFolder, 'proxy.log');
const saveLog = (log) => {
const data = `${new Date().toUTCString()}: ${log}\r\n`;
Fs.appendFile(proxyLogFile, data, { flag: flag }, err => {
flag = 'a';
});
};
/**
* IPv6 support!
*/
const hostGlobalIpV6 = false;
const testGatewayDomainName = 'www.google.com';
const closeClientSocket = (socket, status = 404) => {
if (!socket || !socket.writable)
return;
let stat = res._HTTP_404;
switch (status) {
case 502:
stat = res._HTTP_502;
break;
case 599:
stat = res._HTTP_599;
break;
case 598:
stat = res._HTTP_598;
break;
case -200:
stat = res._HTTP_PROXY_200;
socket.write(stat);
return socket.resume();
default:
break;
}
return socket.end(stat);
};
const getSslConnectFirstData = (clientSocket, data, first, CallBack) => {
if (first) {
clientSocket.once('data', (_data) => {
return (0, exports.getSslConnectFirstData)(clientSocket, _data, false, CallBack);
});
return closeClientSocket(clientSocket, -200);
}
return CallBack(null, data);
};
exports.getSslConnectFirstData = getSslConnectFirstData;
const isAllBlackedByFireWall = (hostName, ip6, gatway, userAgent, domainListPool, CallBack) => {
const hostIp = domainListPool.get(hostName);
const now = new Date().getTime();
if (!hostIp || hostIp.expire < now)
return gatway.hostLookup(hostName, userAgent, (err, ipadd) => {
return CallBack(err, ipadd);
});
return CallBack(null, hostIp);
};
exports.isAllBlackedByFireWall = isAllBlackedByFireWall;
const isSslFromBuffer = (buffer) => {
const ret = /^\x16\x03|^\x80/.test(buffer);
return ret;
};
const httpProxy = (clientSocket, buffer, _gatway, debug) => {
if (!_gatway || typeof _gatway.requestGetWay !== 'function') {
console.log(safe_1.default.red(`httpProxy !gateWay stop SOCKET res._HTTP_PROXY_302 `));
return clientSocket.end(res._HTTP_PROXY_302());
}
const httpHead = new httpProxy_1.default(buffer);
const hostName = httpHead.host;
const userAgent = httpHead.headers['user-agent'];
const connect = (_, _data) => {
const uuuu = {
uuid: Crypto.randomBytes(10).toString('hex'),
host: hostName,
hostIPAddress: httpHead.hostIpAddress,
buffer: _data.toString('base64'),
cmd: httpHead.methods,
//ATYP: Rfc1928.ATYP.IP_V4,
port: httpHead.Port,
ssl: isSslFromBuffer(_data)
};
const requestObj = {
remotePort: clientSocket.remotePort,
remoteAddress: clientSocket.remoteAddress.split(':')[3],
targetHost: hostName,
targetPort: httpHead.Port,
methods: httpHead.methods,
uuid: uuuu.uuid
};
if (!_data || !_data.length) {
console.log(safe_1.default.red(`httpProxy got unknow request stop proxy request `));
closeClientSocket(clientSocket);
return console.log((0, util_1.inspect)(requestObj, false, 3, true));
}
if (_gatway && typeof _gatway.requestGetWay === 'function') {
return _gatway.requestGetWay(requestObj, uuuu, userAgent, clientSocket);
}
console.log(safe_1.default.red(`httpProxy _gatway have no ready!`));
return closeClientSocket(clientSocket);
};
if (httpHead.isConnect) {
return (0, exports.getSslConnectFirstData)(clientSocket, buffer, true, connect);
}
return connect(null, buffer);
};
const getPac = (hostIp, port, http, sock5) => {
const FindProxyForURL = `function FindProxyForURL ( url, host )
{
if ( isInNet ( dnsResolve( host ), "0.0.0.0", "255.0.0.0") ||
isInNet( dnsResolve( host ), "172.16.0.0", "255.240.255.0") ||
isInNet( dnsResolve( host ), "127.0.0.0", "255.255.255.0") ||
isInNet ( dnsResolve( host ), "192.168.0.0", "255.255.0.0" ) ||
isInNet ( dnsResolve( host ), "10.0.0.0", "255.0.0.0" )) {
return "DIRECT";
}
return "${http ? 'PROXY' : (sock5 ? 'SOCKS5' : 'SOCKS')} ${hostIp}:${port}";
}`;
//return "${ http ? 'PROXY': ( sock5 ? 'SOCKS5' : 'SOCKS' ) } ${ hostIp }:${ port.toString() }; ";
return res.Http_Pac(FindProxyForURL);
};
class proxyServer {
saveWhiteIpList() {
if (this.whiteIpList.length > 0) {
Fs.writeFile(Path.join(__dirname, whiteIpFile), JSON.stringify(this.whiteIpList), { encoding: 'utf8' }, err => {
if (err) {
return console.log(`saveWhiteIpList save file error : ${err.message}`);
}
});
}
}
constructor(proxyPort, // Proxy server listening port number
multipleGateway, // gateway server information
debug = false) {
this.proxyPort = proxyPort;
this.multipleGateway = multipleGateway;
this.debug = debug;
this.hostLocalIpv4 = [];
this.hostLocalIpv6 = null;
this.hostGlobalIpV4 = null;
this.hostGlobalIpV6 = null;
this.network = false;
this.getGlobalIpRunning = false;
this.server = null;
this.gateway = new gateway_1.default(this.multipleGateway, this.debug);
this.whiteIpList = [];
this.domainBlackList = [];
this.domainListPool = new Map();
this.checkAgainTimeOut = 1000 * 60 * 5;
this.connectHostTimeOut = 1000 * 5;
this.useGatWay = true;
this.clientSockets = new Set();
this.getGlobalIp = (gateWay) => {
if (this.getGlobalIpRunning) {
return console.log(`getGlobalIp getGlobalIpRunning === true!, skip!`);
}
this.getGlobalIpRunning = true;
(0, log_1.logger)(`doing getGlobalIp!`);
return gateWay.hostLookup(testGatewayDomainName, null, (err, data) => {
this.getGlobalIpRunning = false;
if (err) {
return (0, log_1.logger)('getGlobalIp ERROR:', err.message);
}
//console.log ( Util.inspect ( data ))
this.hostLocalIpv6 ? console.log(`LocalIpv6[ ${this.hostLocalIpv6} ]`) : null;
this.hostLocalIpv4.forEach(n => {
return console.log(`LocalIpv4[ ${n.address}]`);
});
this.hostGlobalIpV6 ? console.log(`GlobalIpv6[ ${this.hostGlobalIpV6} ]`) : null;
this.hostGlobalIpV4 ? console.log(`GlobalIpv4[ ${this.hostGlobalIpV4} ]`) : null;
const domain = data;
if (!domain) {
return console.log(`[] Gateway connect Error!`);
}
this.network = true;
console.log('*************** Gateway connect ready *************************');
});
};
(0, log_1.logger)(safe_1.default.blue(`proxyServer startup debug [${debug}]`));
this.getGlobalIp(this.gateway);
let socks = null;
this.server = Net.createServer(socket => {
const ip = socket.remoteAddress;
this.clientSockets.add(socket);
const isWhiteIp = this.whiteIpList.find(n => { return n === ip; }) ? true : false;
let agent = 'Mozilla/5.0';
// windows 7 GET PAC User-Agent: Mozilla/5.0 (compatible; IE 11.0; Win32; Trident/7.0)
// proxy auto setup support
socket.once('data', (data) => {
const dataStr = data.toString();
if (/^GET \/pac/.test(dataStr)) {
(0, log_1.logger)(safe_1.default.blue(dataStr));
const httpHead = new httpProxy_1.default(data);
agent = httpHead.headers['user-agent'];
const sock5 = /Firefox|Windows NT|WinHttp-Autoproxy-Service|Darwin/i.test(agent) && !/CFNetwork|WOW64/i.test(agent);
const ret = getPac(httpHead.host, this.proxyPort, /pacHttp/.test(dataStr), sock5);
console.log(`/GET \/pac from :[${socket.remoteAddress}] sock5 [${sock5}] agent [${agent}] httpHead.headers [${Object.keys(httpHead.headers)}]`);
console.log(dataStr);
console.log(ret);
return socket.end(ret);
}
switch (data.readUInt8(0)) {
case 0x4: {
return socks = new Socks.sockt4(socket, data, agent, this);
}
case 0x5: {
return socks = new Socks.socks5(socket, data, agent, this);
}
default: {
return httpProxy(socket, data, this.gateway, this.debug);
}
}
});
socket.on('error', err => {
socks = null;
});
socket.once('end', () => {
this.clientSockets.delete(socket);
socks = null;
});
});
this.server.on('error', err => {
(0, log_1.logger)(safe_1.default.red(`proxy server : ${err.message}`));
});
this.server.maxConnections = 65536;
this.server.listen(proxyPort, () => {
return (0, log_1.logger)(safe_1.default.blue(`proxy start success on port : [${proxyPort}]`));
});
}
exit() {
(0, log_1.logger)(safe_1.default.red(`************ proxyServer on exit ()`));
this.gateway = null;
}
close(Callback) {
(0, log_1.logger)(safe_1.default.red(`Local proxy doing close`));
this.server.close(err => {
(0, log_1.logger)(safe_1.default.red(`server.close success! now doing close all [${this.clientSockets.size}] sockets `));
return Callback();
});
this.clientSockets.forEach(n => {
(0, log_1.logger)(safe_1.default.blue(`close [${n.remotePort}]`));
if (typeof n.destroy === 'function') {
return n.destroy();
}
});
}
}
exports.proxyServer = proxyServer;