UNPKG

@kloak-it/tq-proxy

Version:
425 lines (424 loc) 17.1 kB
"use strict"; /*! * Copyright 2017 Vpn.Email network security technology Canada Inc. All Rights Reserved. * * Vpn.Email network technolog Canada Ltd. * 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.Base64MediaFileStream3 = exports.encryptMediaFileStream = exports.getDecryptClientStreamFromHttp = exports.decryptStream = exports.encryptStream = void 0; const crypto = __importStar(require("crypto")); const Async = __importStar(require("async")); const Stream = __importStar(require("stream")); const fs_1 = require("fs"); const child_process_1 = require("child_process"); const Uuid = __importStar(require("node-uuid")); const safe_1 = __importDefault(require("colors/safe")); const log_1 = require("./log"); const EOF = Buffer.from('\r\n\r\n', 'utf8'); const HTTP_HEADER = Buffer.from(`HTTP/1.1 200 OK\r\nDate: ${new Date().toUTCString()}\r\nContent-Type: text/html\r\nTransfer-Encoding: chunked\r\nConnection: keep-alive\r\nVary: Accept-Encoding\r\n\r\n`); class encryptStream extends Stream.Transform { initCrypt(CallBack) { return Async.waterfall([ next => crypto.randomBytes(64, next), (_salt, next) => { this.salt = _salt; crypto.randomBytes(12, next); }, (_iv, next) => { this.iv = _iv; try { crypto.pbkdf2(this.password, this.salt, 2145, 32, 'sha512', next); } catch (ex) { return next(ex); } } ], (err, derivedKey) => { if (err) { console.log(safe_1.default.red(`encryptStream init error ${err.message} TRY again`)); return this.initCrypt(CallBack); } this.derivedKey = derivedKey; return CallBack(); }); } constructor(socket, id, debug, password, random, download, httpHeader, CallBack) { super(); this.socket = socket; this.id = id; this.debug = debug; this.password = password; this.random = random; this.download = download; this.httpHeader = httpHeader; this.ERR = null; this.first = 0; this.derivedKey = null; this.dataCount = false; this.initCrypt(CallBack); } _transform(chunk, encode, cb) { this.first++; if (this.debug) { (0, log_1.logger)(safe_1.default.blue(`${this.id} encryptStream got Buffer from Target【${safe_1.default.red(this.first.toString())}】`)); (0, log_1.hexDebug)(chunk); } if (this.first < 5) { const cipher = crypto.createCipheriv('aes-256-gcm', this.derivedKey, this.iv); let _text = Buffer.concat([Buffer.alloc(4, 0), chunk]); _text.writeUInt32BE(chunk.length, 0); if (chunk.length < this.random) { _text = Buffer.concat([_text, Buffer.allocUnsafe(Math.random() * 200)]); } const _buf = Buffer.concat([cipher.update(_text), cipher.final()]); const _buf1 = Buffer.concat([cipher.getAuthTag(), _buf]); if (this.dataCount) { //console.log ( `**** encryptStream ID[${ this.id }] dataCount is [true]! data.length`) this.download(_buf1.length); } if (this.first === 1) { const black = Buffer.concat([this.salt, this.iv, _buf1]).toString('base64'); if (!this.httpHeader) { const _buf4 = Buffer.from(black, 'utf8'); const _buffer = Buffer.concat([HTTP_HEADER, _buf4, EOF]); (0, log_1.logger)(safe_1.default.green(`encryptStream [${this.id}]client <--- Target _buffer = [${_buffer.length}] 【${safe_1.default.red(this.first.toString())}】`)); if (this.debug) { (0, log_1.logger)(_buffer.toString()); (0, log_1.hexDebug)(_buffer); } return cb(null, _buffer); } const _data = this.httpHeader(black); (0, log_1.logger)(safe_1.default.green(`encryptStream [${this.id}]to client client <--- Target _buffer = [${_data.length}] 【${safe_1.default.red(this.first.toString())}】`)); return cb(null, _data); } if (this.debug) { (0, log_1.logger)(safe_1.default.blue(`${this.id} [${safe_1.default.red(this.first.toString())}] encryptStream send data`)); (0, log_1.hexDebug)(_buf1); } const _buf2 = _buf1.toString('base64'); return cb(null, Buffer.from(_buf2 + EOF)); } const _buf2 = Buffer.from(chunk.toString('base64') + EOF); if (this.debug) { (0, log_1.logger)(safe_1.default.blue(`${this.id} encryptStream got Buffer from Target【${safe_1.default.red(this.first.toString())}】Sent direct to Client`)); (0, log_1.hexDebug)(_buf2); } if (this.socket.writable) { return cb(null, _buf2); } return cb(new Error(`${this.id} encryptStream writable = false `)); } } exports.encryptStream = encryptStream; class decryptStream extends Stream.Transform { _decrypt(decodeBuffer, CallBack) { return crypto.pbkdf2(this.password, this.salt, 2145, 32, 'sha512', (err, derivedKey) => { if (err) { (0, log_1.logger)(safe_1.default.red(`**** decryptStream crypto.pbkdf2 ERROR: ${err.message}`)); return CallBack(err); } this.derivedKey = derivedKey; try { this.decipher = crypto.createDecipheriv('aes-256-gcm', this.derivedKey, this.iv); // @ts-ignore this.decipher.setAuthTag(decodeBuffer.slice(0, 16)); } catch (ex) { (0, log_1.logger)(safe_1.default.red(`${this.id} 【${this.first}】 decryptStream crypto.setAuthTag got Error ${ex.message}`)); (0, log_1.hexDebug)(decodeBuffer); return CallBack(new Error(`${this.id} class decryptStream firstProcess crypto.createDecipheriv Error]`)); } let _Buf = null; try { _Buf = Buffer.concat([this.decipher.update(decodeBuffer.slice(16)), this.decipher.final()]); } catch (ex) { (0, log_1.logger)(safe_1.default.red(`${this.id} 【${this.first}】 decryptStream crypto.createDecipheriv Error ${ex.message}`)); (0, log_1.hexDebug)(decodeBuffer); return CallBack(new Error(`class decryptStream firstProcess _decrypt error`)); } const length = _Buf.readUInt32BE(0) + 4; const uuu = _Buf.slice(4, length); return CallBack(null, uuu); }); } firstProcess(decodeBuffer, CallBack) { if (decodeBuffer.length < 76) { return CallBack(new Error(`Unknow connect!`)); } this.salt = decodeBuffer.slice(0, 64); this.iv = decodeBuffer.slice(64, 76); return this._decrypt(decodeBuffer.slice(76), CallBack); } constructor(id, debug, password, upload) { super(); this.id = id; this.debug = debug; this.password = password; this.upload = upload; this.first = 0; this.derivedKey = null; this.decipher = null; this.text = ''; } _transform(chunk, encode, cb) { this.text += chunk.toString(); const line = this.text.split('\r\n\r\n'); if (line.length < 2) { return cb(); } const callback = (err, data) => { if (err) { return cb(err); } if (this.debug) { (0, log_1.logger)(`[${this.id}] decryptStream push data = [${data.length}] 【${safe_1.default.red(this.first.toString())}】`); (0, log_1.hexDebug)(data); } this.push(data); return this._transform(Buffer.from(''), encode, cb); }; const firstLine = line.shift(); const _chunk = Buffer.from(firstLine, 'base64'); if (this.debug) { (0, log_1.logger)(safe_1.default.green(`${this.id} decryptStream got DATA ${_chunk.length}`)); (0, log_1.hexDebug)(_chunk); } this.text = line.join('\r\n\r\n'); this.first++; if (this.first < 5) { if (this.first === 1) { return this.firstProcess(_chunk, callback); } return this._decrypt(_chunk, callback); } return callback(null, _chunk); } _flush(cb) { if (this.text.length) { (0, log_1.logger)(safe_1.default.red(`${this.id} decryptStream on _flush [${this.text.length}]`)); const _chunk = Buffer.from(this.text, 'base64'); if (this.first < 5) { return this._decrypt(_chunk, (err, data) => { if (err) { return cb(err); } this.push(_chunk); cb(); }); } this.push(_chunk); } cb(); } } exports.decryptStream = decryptStream; class encode extends Stream.Transform { constructor() { super(); this.kk = null; } _transform(chunk, encode, cb) { let start = chunk.slice(0); while (start.length) { const point = start.indexOf(0x0a); if (point < 0) { this.push(start); break; } const _buf = start.slice(0, point); this.push(_buf); start = start.slice(point + 1); } return cb(); } } class encodeHex extends Stream.Transform { constructor() { super(); } _transform(chunk, encode, cb) { return cb(null, chunk.toString('utf8')); } } class getDecryptClientStreamFromHttp extends Stream.Transform { constructor() { super(); this.first = true; this.text = ''; } getBlock(block) { const uu = block.split('\r\n'); if (uu.length !== 2) { return null; } const length = parseInt(uu[0], 16); const text = uu[1]; if (length === text.length) { return text; } console.log(`length[${length}] !== text.length [${text.length}]`); return null; } _transform(chunk, encode, cb) { this.text += chunk.toString('utf8'); const line = this.text.split('\r\n\r\n'); while (this.first && line.length > 1 || !this.first && line.length) { if (this.first) { this.first = false; line.shift(); } const _text = line.shift(); if (!_text.length) continue; const text = this.getBlock(_text); if (!text) { // middle data can't get block if (line.length) { console.log('getDecryptStreamFromHttp have ERROR:\n*****************************\n'); console.log(text); return this.unpipe(); } this.text = _text; return cb(); } this.push(Buffer.from(text, 'base64')); } this.text = ''; return cb(); } } exports.getDecryptClientStreamFromHttp = getDecryptClientStreamFromHttp; const tenMbyte = 10240000; class saveBlockFile extends Stream.Writable { constructor(fileName, data) { super(); this.fileName = fileName; this.data = data; this.length = 0; this.fileAddTag = 0; this._chunk = Buffer.allocUnsafe(0); } _write(chunk, encode, callback) { this.length += chunk.length; this._chunk = Buffer.concat([this._chunk, chunk]); if (this.length < tenMbyte) { return callback(); } const cipher = crypto.createCipheriv(this.data.algorithm, this.data.derivedKey, this.data.iv); const _data = Buffer.concat([cipher.update(this._chunk), cipher.final()]); const fileName = this.fileName + '.' + this.fileAddTag++; this.data.files.push(fileName); // @ts-ignore this.data.getAuthTag.push(cipher.getAuthTag().toString('base64')); return (0, fs_1.writeFile)(fileName, this._chunk.toString('base64'), err => { this._chunk = Buffer.allocUnsafe(0); this.length = 0; if (err) { return callback(err); } return callback(); }); } } const encryptMediaFileStream = (fileName, password, CallBack) => { let enCryptoData = { salt: null, iv: null, iterations: 100000, keylen: 32, digest: 'sha512', derivedKey: null, algorithm: 'aes-256-gcm', files: [], getAuthTag: [] }; let cipher = null; Async.waterfall([ next => crypto.randomBytes(64, next), (_salt, next) => { enCryptoData.salt = _salt; crypto.randomBytes(12, next); }, (_iv, next) => { enCryptoData.iv = _iv; crypto.pbkdf2(password, enCryptoData.salt, enCryptoData.iterations, enCryptoData.keylen, enCryptoData.digest, next); } ], (err, derivedKey) => { if (err) { return CallBack(err); } enCryptoData.derivedKey = derivedKey; const readFile = (0, fs_1.createReadStream)(fileName); const writeFile = new saveBlockFile(fileName, enCryptoData); readFile.once('close', () => { console.log(`readFile.once close`); return CallBack(null, enCryptoData); }); readFile.pipe(writeFile); }); }; exports.encryptMediaFileStream = encryptMediaFileStream; const addHeaderForBase64File = (addText, fileName, CallBack) => { const tempFile = 'temp/' + Uuid.v4(); const cmd = `echo -n '${addText}' | cat - ${fileName} > ${tempFile} && echo -n "\r\n\r\n" >> ${tempFile} && mv -f ${tempFile} ${fileName} `; return (0, child_process_1.exec)(cmd, CallBack); }; const Base64MediaFileStream3 = (fileName, domainName, CallBack) => { const text = `Content-Type: application/octet-stream\r\nContent-Disposition: attachment\r\nMessage-ID:<${Uuid.v4()}@>${domainName}\r\nContent-Transfer-Encoding: base64\r\nMIME-Version: 1.0\r\n\r\n`; const cmd = `base64 ${fileName} | split -b 10MB -d --verbose - ${fileName}. | sed "s/creating file '//g" | sed "s/'//g" `; Async.series([ next => (0, child_process_1.exec)(cmd, next), next => (0, child_process_1.exec)(`rm ${fileName}`, next) ], (err, data) => { if (err) { return CallBack(err); } const files = data[0][0].split('\n'); if (!files[files.length - 1].length) { files.pop(); } return Async.eachSeries(files, (n, next) => { return addHeaderForBase64File(text, n, next); }, err => { return CallBack(null, files); }); }); }; exports.Base64MediaFileStream3 = Base64MediaFileStream3;