UNPKG

@kloak-it/tq-proxy

Version:
308 lines (306 loc) 13 kB
"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;