@kloak-it/tq-proxy
Version:
QT PROXY Server/Client
425 lines (424 loc) • 17.1 kB
JavaScript
"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;