"use strict";
var __assign = (this && this.__assign) || function () {
    __assign = Object.assign || function(t) {
        for (var s, i = 1, n = arguments.length; i < n; i++) {
            s = arguments[i];
            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
                t[p] = s[p];
        }
        return t;
    };
    return __assign.apply(this, arguments);
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __generator = (this && this.__generator) || function (thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (g && (g = 0, op[0] && (_ = 0)), _) try {
            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [op[0] & 2, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProxyService = void 0;
// const net = require('net');
var net = require("net");
var uid_1 = require("uid");
var ssh_service_1 = require("./ssh.service");
var moment = require("moment");
// const dgram = require('dgram')
var dgram = require("dgram");
var http = require("http");
var https = require("https");
var url = require("url");
var forge = require('node-forge');
var pki = forge.pki;
// const http = require('http')
// var http = require("http");
// var url = require("url");
var g_server;
var g_httpsServer;
var g_socketServer;
var g_hostPortMap = {};
var root_key = "-----BEGIN RSA PRIVATE KEY-----\nProc-Type: 4,ENCRYPTED\nDEK-Info: AES-128-CBC,E4461FB6CA608D76C1CABF103CE877F3\n\nQRHBuwmCdCSWNxZi0EbI8cGfenkeXFrk5zDq6bU9ze77mmaXEkimaiCNN/JU8ezc\nX1vBC5K4q/VYzzWi3twssj9AR8SYpnm4Eg0l1D0VNyzPTJMbciHj/y0J86JfKVXE\nt4gR4xEmQWpyBXe6lrPlgZhlqx0eJh0QYHgqAiYG/nVklZ11FXykthRGSIHglA/k\no3kMYcaLaekHXbvTNQjgpvg+DBVtMRmJKHgY0ZHmoU35ub9Z+GP7pCnatuv2b6XV\nHXHPVqzXrAGHziaI9MBSh/HA/0d1/TAJj48EWZaPnBIlyMhR3wBg8CYu8fR5zMdE\n1M7y6wy9rLA9kolcV9TMQvL5eEJmF9mOpFmYpKepHT+21rXrj1RIMvPFIxuL/B4H\nAOdaHtQkgiWKQJgcsOOQLGupRuAetAPn59VEb10i6nOtd4m8ZTcFXD0C7nqSk42P\nZndZzxsrChLS9WafyrJs67maHqnQYK0wovyOEb4TAquxqZluurs6FHJ8HvHKH9cI\nQ+6fEXTt1hIlfPH8FZDBUuAqPLC4zm3vTO0vykNlSGMMKzfXifH+uK8ee5uE0grw\nMMA+tcovp7qUeRW+HniGgMfaCzIX3+XDkxVE8BnsbkTezOP4Ms77ThbMZYK77EjM\nuTX0bPq5fLjdkP1fgFc64a5Alioe1SxOVzsEW108vBUZnLcr+SqSxL3V/NnSZFjb\n3dV1NvZGqzhRSX9l93/2lKUTSy8wVvmLmLA5LAglofuli4rEBV+PLTJFCLi0706K\n5o1jB01elpMFhuMQI96jqPfl6wEolWTcSosR1G4x7XkqYiKaS/nfYkipNTssAIWQ\nHwQ93mUdUOaYIXshtDf3wu8PZhxYDPEnS0/ygTd8DsuNzdXpizy08bJfHqIh7u+0\nFE++ZrY9/3C8HiT2ghnajF1MxwLT2Xw7Vk1s0yEi60VY5HoPoQrcdN8M9lwju/MS\nvCUcstkjax0WGJCCi8AzhaU3JE2gz1GFc2xPbjRnSXl/qihiNQpT4caoovLeoS1B\nj+0plGEUePM5Dbubn0ccGjh8AN+eZNgBsgqO2vq9yPcokH4nHhItQTOrSQVgicmU\nW8H6Jes1XPX5YH47HvfFbhodlF6CHIXJ9FjVWL4pNfrzfExyGYhCCoyTuycqYq7q\nRm7BCCo1VsFkTtEL9iRiER/IzQqoep+H+UKsV6+HTD8R8sMuJF49xYrKl0jwUEJA\n1wi3VyZDoGI8x9wn7TzmR++UJrboZR5pvL3tnDWCtfQnMMTsnLi5PZMbJKvW3g89\nKF2nhbZo9P6rwll8Z/Cu9bQMy1uYZ6jVWwUNJOQy12sOvWIYv12H/VQRgy++79++\n3Zaiumlc+YBTORBItFslxaYRrjv7OVJbKSJvNUaBfRk2gsqjfOtOM+dgqKOm7FEs\naANa8yCi67EIZzhbtu25Kmo+lCmIporX2wF2MWvVmNV9Bj9IU+tuUcf4tHkQCYTx\n7gIOyJuXkgbJ/MNcCEMQuxNC551EPaJKkWrwDpC24usbwufsZuXZ7yealpJrjWP1\n5zstzxbQgqf56HOHwGX+vQur55qnO8qqUIU6LeT6xZWrm0rcjH1vORvJ7ZfolMZM\n-----END RSA PRIVATE KEY-----\n";
var root_cert = "-----BEGIN CERTIFICATE-----\nMIIDWDCCAkACCQDNbyWUa7SohjANBgkqhkiG9w0BAQUFADBuMQswCQYDVQQGEwJD\nTjELMAkGA1UECAwCR0QxCzAJBgNVBAcMAkdaMQswCQYDVQQKDAJZUzELMAkGA1UE\nCwwCSVQxDTALBgNVBAMMBFlTQ0ExHDAaBgkqhkiG9w0BCQEWDWNhQHl1bnNlci5j\nb20wHhcNMjMwMzA3MDU0MTUyWhcNNDMwMzAyMDU0MTUyWjBuMQswCQYDVQQGEwJD\nTjELMAkGA1UECAwCR0QxCzAJBgNVBAcMAkdaMQswCQYDVQQKDAJZUzELMAkGA1UE\nCwwCSVQxDTALBgNVBAMMBFlTQ0ExHDAaBgkqhkiG9w0BCQEWDWNhQHl1bnNlci5j\nb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC81oax0qQQnGcU9yT7\n4Dm1f4A5yiNj3xyThjU8Tg1NF7x9AejEPN0eBLC6ziGfEssDe+nCl3nIo+IUL8Fa\nEd2x5MnZSpYM82uzGE5j3K9Nwen6qyb/9bTGVR9IOkZSFrf37wHHFu8SStBpcr8M\nMit7fPFDwIUQGpfgqwV5rVQhS4GaCqccMB4kCuuXjUOWJs22sz3WgHxn7/OG7Z0c\nKlC8PzAeqL/ow6V+g6BZzSs2WnCQxIULih8KxLrU1l242GSODZGqUiig9zCpbLDt\ntly50g/QXUouUJNdkVamiMXtvWMl9ojsFFtsS+trS87bNFLyMxLVllfAdmvHJB+Z\nMqaBAgMBAAEwDQYJKoZIhvcNAQEFBQADggEBABBJsSNC6/5oxcDo+Uc1rArSQyU+\n4ZS7i77MfTIR8UDxcA8h5OutJhl2Jhufck5dDyek7/mmK8lL5GPcC4Mu9ENBzCtT\npB0YgFC7CtkB8qrDSGHic9opi1lOSLedHaOVljLUEFBCWlSidX3DleMawvlga7R9\nSQEaX/5sEe9ejDaL33qDSTOvfsdWyL+SloorsmtcdPAFNPbYmmAw2ALpbS130fP9\nO8Womkh1nXN2WKalJfTkmpf4gWjjDuVov45hFX2XuTGrQYh6aZT3PAWIwPmLqIgY\ntLR7grk6AUHuQcTOWDQrM9Q6TUh3sGAZEJU5AeLvAfFHY3QCfl1jpLm+EFs=\n-----END CERTIFICATE-----\n";
function parseHeaders(rawHeaders) {
    var headers = [];
    for (var i = 0; i < rawHeaders.length; i += 2) {
        headers.push({
            key: rawHeaders[i],
            value: rawHeaders[i + 1],
        });
    }
    return headers;
}
function createUdpServer() {
    var udp_server = dgram.createSocket('udp4');
    udp_server.on('message', function (msg, remote) {
        console.log('udp_server/message', "".concat(remote.address, ":").concat(remote.port), msg.toJSON());
        // +-----+------+------+----------+----------+----------+
        // | RSV | FRAG | ATYP | DST.ADDR | DST.PORT |   DATA   |
        // +-----+------+------+----------+----------+----------+
        // |  2  |  1   |  1   | Variable |    2     | Variable |
        // +-----+------+------+----------+----------+----------+
        var RSV = msg.slice(0, 0 + 2);
        console.log('RSV', RSV);
        var FRAG = msg[2];
        console.log('FRAG', FRAG);
        var ATYP = msg[3];
        console.log('ATYP', ATYP);
        // IPV4: X'01'
        // 域名: X'03'
        // IPV6: X'04'
        var toAddr;
        var addrLength;
        if (ATYP == 1) {
            toAddr = msg.slice(4, 4 + 4).join('.');
            addrLength = 4;
        }
        else if (ATYP == 4) {
            console.log('no support UDP IPv6');
            return;
        }
        else if (ATYP == 3) {
            var domainLen = msg[4];
            toAddr = msg.slice(5, 5 + domainLen).toString("binary");
            addrLength = 1 + domainLen;
        }
        console.log('addrLength', addrLength);
        console.log('toAddr', toAddr);
        var portStart = 4 + addrLength;
        console.log('portStart', portStart);
        var toPort = msg.slice(portStart, portStart + 2).readUInt16BE(0);
        console.log('toPort', toPort);
        var dataStart = portStart + 2;
        var content = msg.slice(dataStart);
        console.log('content', content.toString());
        var udpClient = dgram.createSocket('udp4');
        // const message = Buffer.from('Hello2');
        udpClient.send(content, toPort, toAddr, function (err) {
            if (err) {
                console.log('发送失败');
            }
            else {
                console.log('发送成功');
            }
            udpClient.close();
        });
        {
            var info = g_hostPortMap["".concat(toAddr, ":").concat(toPort)];
            console.log('info', info, g_hostPortMap);
            if (info) {
                var webSocketId = info.webSocketId, clientId = info.clientId;
                var time = moment().format('YYYY-MM-DD HH:mm:ss');
                (0, ssh_service_1.sendTcpMsg)({
                    webSocketId: webSocketId,
                    message: {
                        type: 'udpClientSent',
                        data: {
                            id: (0, uid_1.uid)(8),
                            time: time,
                            // content,
                            // contentType,
                            type: 'udpClientSent',
                            toAddr: toAddr,
                            toPort: toPort,
                            content: content.toString(),
                            // host: remoteAddr,
                            // port: remotePort,
                            clientId: clientId,
                        }
                    }
                });
            }
        }
    });
    udp_server.bind(1, '0.0.0.0');
}
createUdpServer();
var ProxyService = /** @class */ (function () {
    function ProxyService() {
    }
    ProxyService.prototype.create = function (body) {
        return __awaiter(this, void 0, void 0, function () {
            var _a, host, _b, port, webSocketId;
            return __generator(this, function (_c) {
                _a = body.host, host = _a === void 0 ? '0.0.0.0' : _a, _b = body.port, port = _b === void 0 ? 6666 : _b, webSocketId = body.webSocketId;
                console.log('proxy/create');
                if (g_server) {
                    g_server.close();
                }
                // 服务器会在获得所有 HTTP 请求头（而不是请求正文时）时调用回调。
                g_server = http.createServer(function (req, res) {
                    console.log('======== proxy start');
                    // console.log('run',)
                    //客户端请求有两种方式，可以是对象，也可以是url字符串
                    //1.这里采取的是对象形式，包括url对象以及headers
                    console.log('req', req.method, req.url);
                    // console.log('url', req.url)
                    var options = url.parse(req.url);
                    console.log('options', options);
                    options['headers'] = req.headers;
                    console.log('req.headers', req.headers);
                    // console.log('req.headers', req.bo)
                    //2.如果采取字符串形式，就传入一个完整的url字符串，node会自动解析成url对象
                    var _reqData = '';
                    var _resData = '';
                    //通过客户端请求新建一个代理服务器
                    //代理请求仿照本地请求头的数据
                    var proxyRequest = http.request(options, function (proxyResponse) {
                        console.log('-------->');
                        console.log('res', proxyResponse.statusCode, proxyResponse.statusMessage);
                        console.log('res.headers', proxyResponse.headers);
                        proxyResponse.on('data', function (chunk) {
                            // console.log('proxyResponse length:', chunk.length);
                            // console.log('res.body:', chunk.toString());
                            _resData += chunk.toString();
                            // res.write(chunk, 'binary');
                            res.write(chunk, 'binary');
                            // res.write(chunk.toString().replace('description', 'DDD'), 'utf8', () => {
                            //     // console.log("Writing string Data...");
                            // });
                        });
                        //当代理请求不再收到新的数据，告知本地res数据写入完毕。
                        proxyResponse.on('end', function () {
                            console.log('======== proxy end');
                            res.end();
                            var time = moment().format('YYYY-MM-DD HH:mm:ss');
                            (0, ssh_service_1.sendTcpMsg)({
                                webSocketId: webSocketId,
                                message: {
                                    type: 'request',
                                    data: {
                                        id: (0, uid_1.uid)(8),
                                        time: time,
                                        // content: '123',
                                        // contentType,
                                        type: 'request',
                                        request: {
                                            method: req.method,
                                            host: options.host,
                                            path: options.path,
                                            httpVersion: req.httpVersion,
                                            headers: parseHeaders(req.rawHeaders),
                                            content: _reqData,
                                        },
                                        response: {
                                            httpVersion: proxyResponse.httpVersion,
                                            statusCode: proxyResponse.statusCode,
                                            statusMessage: proxyResponse.statusMessage,
                                            headers: parseHeaders(proxyResponse.rawHeaders),
                                            content: _resData,
                                        },
                                        // host,
                                        // port,
                                    }
                                }
                            });
                        });
                        res.writeHead(proxyResponse.statusCode, proxyResponse.headers);
                    });
                    //data只有当请求体数据进来时才会触发
                    //尽管没有请求体数据进来，data还是要写，否则不会触发end事件
                    req.on('data', function (chunk) {
                        console.log('in request length:', chunk.length);
                        _reqData += chunk.toString();
                        proxyRequest.write(chunk, 'binary');
                    });
                    req.on('end', function () {
                        //向proxy发送求情，这里end方法必须被调用才能发起代理请求
                        //所有的客户端请求都需要通过end来发起
                        proxyRequest.end();
                        console.log('req.body', _reqData);
                    });
                })
                    // .listen(7880)
                    .listen(port, host, function () {
                    console.log('listening');
                    var time = moment().format('YYYY-MM-DD HH:mm:ss');
                    (0, ssh_service_1.sendTcpMsg)({
                        webSocketId: webSocketId,
                        message: {
                            type: 'listening',
                            data: {
                                id: (0, uid_1.uid)(8),
                                time: time,
                                // content,
                                // contentType,
                                type: 'listening',
                                host: host,
                                port: port,
                            }
                        }
                    });
                })
                    .on('close', function () {
                    console.log('proxy/close');
                    var time = moment().format('YYYY-MM-DD HH:mm:ss');
                    (0, ssh_service_1.sendTcpMsg)({
                        webSocketId: webSocketId,
                        message: {
                            type: 'close',
                            data: {
                                id: (0, uid_1.uid)(8),
                                time: time,
                                // content,
                                // contentType,
                                type: 'close',
                                host: host,
                                port: port,
                            }
                        }
                    });
                });
                // const time = moment().format('YYYY-MM-DD HH:mm:ss')
                // sendTcpMsg({
                //     webSocketId,
                //     message: {
                //         type: 'listening',
                //         data: {
                //             id: uid(8),
                //             time,
                //             // content,
                //             // contentType,
                //             type: 'listening',
                //             host,
                //             port,
                //         }
                //     }
                // })
                console.log('proxy service start');
                return [2 /*return*/, {}];
            });
        });
    };
    ProxyService.prototype.createHttpsProxy = function (body) {
        return __awaiter(this, void 0, void 0, function () {
            /**
             * 根据CA证书生成一个伪造的https服务
             * @param  {[type]} ca         [description]
             * @param  {[type]} targetDomain     [description]
             * @param  {[type]} successFun [description]
             * @return {[type]}            [description]
             */
            function createFakeHttpsWebSite(targetDomain, targetPort, successFun) {
                // console.log('domain', domain)
                // console.log('caKey', caKey)
                var fakeCertObj = createFakeCertificateByDomain(caKey, caCert, targetDomain);
                // console.log('fakeCertObj', fakeCertObj)
                var fakeServer = new https.Server({
                    key: fakeCertObj.key,
                    cert: fakeCertObj.cert,
                    // SNICallback: (hostname, done) => {
                    //     let certObj = createFakeCertificateByDomain(caKey, caCert, hostname)
                    //     done(null, tls.createSecureContext({
                    //         key: pki.privateKeyToPem(certObj.key),
                    //         cert: pki.certificateToPem(certObj.cert)
                    //     }))
                    // }
                    // key: fs.readFileSync('./myssl_private.key'),
                    // cert: fs.readFileSync('./myssl_full_chain.pem'),
                });
                fakeServer.listen(0, function () {
                    var address = fakeServer.address();
                    successFun(address['port']);
                });
                fakeServer.on('request', function (clientReq, clientRes) {
                    // console.log('======== request start',)
                    // console.log('url', clientReq.method, clientReq.url)
                    // console.log('req.data', req.data)
                    // req.socket.on('data', (data) => {
                    //     console.log('req.socket/data', data.toString())
                    // })
                    // 解析客户端请求
                    var urlObject = url.parse(clientReq.url);
                    var reqOpts = {
                        protocol: 'https:',
                        hostname: targetDomain,
                        // hostname: clientReq.headers.host.split(':')[0],
                        port: targetPort,
                        // port: clientReq.headers.host.split(':')[1] || 80,
                        method: clientReq.method,
                        // url: clientReq.url,
                        path: urlObject.path,
                        // host: targetDomain,
                        headers: clientReq.headers,
                    };
                    // console.log('reqOpts', targetDomain, targetPort, reqOpts)
                    var otherData = {};
                    // TODO
                    // otherData.connect = str
                    // otherData.connectEstablished = connectEstablished
                    var allReqData = '';
                    var response = {
                        httpVersion: '??',
                        statusCode: -1,
                        statusMessage: '??',
                        headers: [],
                        content: '??',
                    };
                    // let _serverRes
                    var serverReq = https.request(reqOpts, function (serverRes) {
                        // _serverRes = serverRes
                        response.httpVersion = serverRes.httpVersion;
                        response.statusCode = serverRes.statusCode;
                        response.statusMessage = serverRes.statusMessage;
                        response.headers = parseHeaders(serverRes.rawHeaders);
                        // console.log('中间/res', serverRes.statusCode)
                        clientRes.writeHead(serverRes.statusCode, serverRes.headers);
                        console.log('');
                        var allResData = '';
                        // called when a data chunk is received.
                        serverRes.on('data', function (chunk) {
                            allResData += chunk;
                            clientRes.write(chunk);
                        });
                        // called when the complete response is received.
                        serverRes.on('end', function () {
                            // console.log('ddd', JSON.stringify(allResData))
                            // console.log('server/end', )
                            response.content = allResData;
                            clientRes.end();
                            var time = moment().format('YYYY-MM-DD HH:mm:ss');
                            (0, ssh_service_1.sendTcpMsg)({
                                webSocketId: webSocketId,
                                message: {
                                    type: 'request',
                                    data: __assign({ id: (0, uid_1.uid)(8), time: time, 
                                        // content: '123',
                                        // contentType,
                                        type: 'request', request: {
                                            method: clientReq.method,
                                            host: "".concat(targetDomain, ":").concat(targetPort),
                                            path: clientReq.url,
                                            httpVersion: clientReq.httpVersion,
                                            headers: parseHeaders(clientReq.rawHeaders),
                                            content: allReqData,
                                        }, response: response }, otherData)
                                }
                            });
                        });
                    })
                        .on("error", function (err) {
                        console.log("Error: ", err.message);
                        clientRes.end();
                    });
                    // if (clientReq.method == 'GET') {
                    //     serverReq.end()
                    // }
                    clientReq.on('data', function (data) {
                        // console.log('req.socket/data', data.toString())
                        allReqData += data.toString();
                        serverReq.write(data);
                    });
                    clientReq.on('end', function (data) {
                        // console.log('client/end')
                        serverReq.end();
                    });
                    // .end()
                    // clientRes.writeHead(200, {})
                    // clientRes.write('proxy hello')
                    // clientRes.end()
                });
                // fakeServer.on('con')
                fakeServer.on('error', function (e) {
                    console.error('fakeServer/error', e);
                });
            }
            // https://github.com/wuchangming/https-mitm-proxy-handbook
            /**
             * 根据所给域名生成对应证书
             * @param  {[type]} caKey  [description]
             * @param  {[type]} caCert [description]
             * @param  {[type]} domain [description]
             * @return {[type]}        [description]
             */
            function createFakeCertificateByDomain(caKey, caCert, domain) {
                var keys = pki.rsa.generateKeyPair(2046);
                var cert = pki.createCertificate();
                cert.publicKey = keys.publicKey;
                cert.serialNumber = (new Date()).getTime() + '';
                cert.validity.notBefore = new Date();
                cert.validity.notBefore.setFullYear(cert.validity.notBefore.getFullYear() - 1);
                cert.validity.notAfter = new Date();
                cert.validity.notAfter.setFullYear(cert.validity.notAfter.getFullYear() + 1);
                var attrs = [{
                        name: 'commonName',
                        value: domain
                    }, {
                        name: 'countryName',
                        value: 'CN'
                    }, {
                        shortName: 'ST',
                        value: 'GuangDong'
                    }, {
                        name: 'localityName',
                        value: 'ShengZhen'
                    }, {
                        name: 'organizationName',
                        value: 'https-mitm-proxy-handbook'
                    }, {
                        shortName: 'OU',
                        value: 'https://github.com/wuchangming/https-mitm-proxy-handbook'
                    }];
                cert.setIssuer(caCert.subject.attributes);
                cert.setSubject(attrs);
                cert.setExtensions([{
                        name: 'basicConstraints',
                        critical: true,
                        cA: false
                    },
                    {
                        name: 'keyUsage',
                        critical: true,
                        digitalSignature: true,
                        contentCommitment: true,
                        keyEncipherment: true,
                        dataEncipherment: true,
                        keyAgreement: true,
                        keyCertSign: true,
                        cRLSign: true,
                        encipherOnly: true,
                        decipherOnly: true
                    },
                    {
                        name: 'subjectAltName',
                        altNames: [{
                                type: 2,
                                value: domain
                            }]
                    },
                    {
                        name: 'subjectKeyIdentifier'
                    },
                    {
                        name: 'extKeyUsage',
                        serverAuth: true,
                        clientAuth: true,
                        codeSigning: true,
                        emailProtection: true,
                        timeStamping: true
                    },
                    {
                        name: 'authorityKeyIdentifier'
                    }]);
                cert.sign(caKey, forge.md.sha256.create());
                var certPem = pki.certificateToPem(cert);
                // console.log('certPem', certPem)
                var keyPem = pki.privateKeyToPem(keys.privateKey);
                // console.log('keyPem', keyPem)
                return {
                    key: keyPem,
                    // cert: cert
                    cert: certPem,
                };
            }
            var _a, host, _b, port, webSocketId, caCertPem, caKeyPem, caCert, caKey, httpTunnel;
            return __generator(this, function (_c) {
                _a = body.host, host = _a === void 0 ? '0.0.0.0' : _a, _b = body.port, port = _b === void 0 ? 6667 : _b, webSocketId = body.webSocketId;
                if (g_httpsServer) {
                    g_httpsServer.close();
                    g_httpsServer = null;
                }
                caCertPem = Buffer.from(root_cert);
                caKeyPem = Buffer.from(root_key);
                caCert = forge.pki.certificateFromPem(caCertPem);
                caKey = forge.pki.decryptRsaPrivateKey(caKeyPem, 'Test@2022');
                httpTunnel = new http.Server();
                g_httpsServer = httpTunnel;
                httpTunnel.listen(port, host, function () {
                    // console.log(`简易HTTPS中间人代理启动成功，端口：${port}`);
                    console.log("server is listening on ".concat(host, ":").concat(port));
                    var time = moment().format('YYYY-MM-DD HH:mm:ss');
                    (0, ssh_service_1.sendTcpMsg)({
                        webSocketId: webSocketId,
                        message: {
                            type: 'listening',
                            data: {
                                id: (0, uid_1.uid)(8),
                                time: time,
                                // content,
                                // contentType,
                                type: 'listening',
                                host: host,
                                port: port,
                            }
                        }
                    });
                });
                // https的请求通过http隧道方式转发
                httpTunnel.on('connect', function (req, cltSocket, head) {
                    // connect to an origin server
                    var srvUrl = url.parse("http://".concat(req.url));
                    // cltSocket.
                    // console.log('', )
                    createFakeHttpsWebSite(srvUrl.hostname, srvUrl.port, function (port) {
                        // console.log('onSuccess', port)
                        var srvSocket = net.connect(port, '127.0.0.1', function () {
                            cltSocket.write('HTTP/1.1 200 Connection Established\r\n' +
                                'Proxy-agent: MITM-proxy\r\n' +
                                '\r\n');
                            srvSocket.write(head);
                            srvSocket.pipe(cltSocket);
                            cltSocket.pipe(srvSocket);
                        });
                        srvSocket.on('error', function (e) {
                            console.error('error?', e);
                        });
                    });
                })
                    .on('error', function (e) {
                    // if (e.code == 'EADDRINUSE') {
                    //     console.error('HTTP中间人代理启动失败！！');
                    //     console.error(`端口：${port}，已被占用。`);
                    // } else {
                    // }
                    console.error(e);
                })
                    .on('close', function () {
                    console.log('proxy/close');
                    var time = moment().format('YYYY-MM-DD HH:mm:ss');
                    (0, ssh_service_1.sendTcpMsg)({
                        webSocketId: webSocketId,
                        message: {
                            type: 'close',
                            data: {
                                id: (0, uid_1.uid)(8),
                                time: time,
                                // content,
                                // contentType,
                                type: 'close',
                                host: host,
                                port: port,
                            }
                        }
                    });
                });
                return [2 /*return*/, {}];
            });
        });
    };
    ProxyService.prototype.createHttpsProxyUseTcp = function (body) {
        return __awaiter(this, void 0, void 0, function () {
            var _a, host, _b, port, webSocketId, server;
            return __generator(this, function (_c) {
                _a = body.host, host = _a === void 0 ? '0.0.0.0' : _a, _b = body.port, port = _b === void 0 ? 6667 : _b, webSocketId = body.webSocketId;
                server = net.createServer(function (socket) {
                    console.log('client connected');
                    var remoteSocket;
                    var otherData = {};
                    socket.on('end', function () {
                        console.log('client disconnected');
                    });
                    socket.on('data', function (data) {
                        var str = data.toString();
                        // console.log('data/', str);
                        console.log('data/');
                        // CONNECT local.yunser.com:9443 HTTP/1.1
                        // Host: local.yunser.com:9443
                        // User-Agent: curl/7.64.1
                        // Proxy-Connection: Keep-Alive
                        if (str.includes('CONNECT ')) {
                            otherData.connect = str;
                            console.log('isConnect');
                            var _url = str.split('\n')[0].split(/\s+/)[1];
                            console.log('url', _url);
                            var urlObj = url.parse('http://' + _url);
                            console.log('obj', urlObj.hostname, urlObj.port);
                            var pSock = net.connect(parseInt(urlObj.port), urlObj.hostname, function () {
                                var connectEstablished = 'HTTP/1.1 200 Connection Established\r\n\r\n';
                                socket.write(connectEstablished);
                                otherData.connectEstablished = connectEstablished;
                                console.log('server-Connection Established');
                                // pSock.pipe(socket);
                            });
                            // console.log('pSock', pSock)
                            remoteSocket = pSock
                                .on('error', function (err) {
                                console.log('server-connect-error');
                                socket.end();
                            })
                                .on('data', function (remoteData) {
                                console.log('remoteData');
                                // console.log('remoteData', remoteData.toString())
                                socket.write(remoteData);
                            })
                                .on('close', function (remoteData) {
                                console.log('remot/close');
                                remoteSocket = null;
                            });
                            // socket.pipe(pSock);
                        }
                        else {
                            // console.log('no-handled data', )
                            if (remoteSocket) {
                                try {
                                    remoteSocket.write(data);
                                }
                                catch (err) {
                                    // TODO 经常触发，先 try 后面再看
                                }
                                console.log('wited');
                            }
                            else {
                                console.log('remoteSocket/null?');
                            }
                        }
                    });
                    socket.on('close', function () {
                        console.log('client/close');
                        var time = moment().format('YYYY-MM-DD HH:mm:ss');
                        (0, ssh_service_1.sendTcpMsg)({
                            webSocketId: webSocketId,
                            message: {
                                type: 'request',
                                data: __assign({ id: (0, uid_1.uid)(8), time: time, 
                                    // content: '123',
                                    // contentType,
                                    type: 'request', request: {
                                        // method: req.method,
                                        // url: req.url,
                                        // httpVersion: req.httpVersion,
                                        // headers: parseHeaders(req.rawHeaders),
                                        // content: _reqData,
                                        method: '??',
                                        url: '??',
                                        httpVersion: '??',
                                        headers: [],
                                        content: '??',
                                    }, response: {
                                        // httpVersion: proxyResponse.httpVersion,
                                        // statusCode: proxyResponse.statusCode,
                                        // statusMessage: proxyResponse.statusMessage,
                                        // headers: parseHeaders(proxyResponse.rawHeaders),
                                        // content: _resData,
                                        httpVersion: '??',
                                        statusCode: '??',
                                        statusMessage: '??',
                                        headers: [],
                                        content: '??',
                                    } }, otherData)
                            }
                        });
                    });
                    //   socket.write('hello\r\n');
                    //   socket.pipe(socket);
                })
                    .on('close', function () {
                    console.log('proxy/close');
                    var time = moment().format('YYYY-MM-DD HH:mm:ss');
                    (0, ssh_service_1.sendTcpMsg)({
                        webSocketId: webSocketId,
                        message: {
                            type: 'close',
                            data: {
                                id: (0, uid_1.uid)(8),
                                time: time,
                                // content,
                                // contentType,
                                type: 'close',
                                host: host,
                                port: port,
                            }
                        }
                    });
                });
                g_httpsServer = server;
                server.listen(port, host, function () {
                    console.log("server is listening on ".concat(host, ":").concat(port));
                    var time = moment().format('YYYY-MM-DD HH:mm:ss');
                    (0, ssh_service_1.sendTcpMsg)({
                        webSocketId: webSocketId,
                        message: {
                            type: 'listening',
                            data: {
                                id: (0, uid_1.uid)(8),
                                time: time,
                                // content,
                                // contentType,
                                type: 'listening',
                                host: host,
                                port: port,
                            }
                        }
                    });
                });
                return [2 /*return*/, {}];
            });
        });
    };
    ProxyService.prototype.close = function (body) {
        return __awaiter(this, void 0, void 0, function () {
            return __generator(this, function (_a) {
                if (g_server) {
                    g_server.close();
                }
                return [2 /*return*/];
            });
        });
    };
    ProxyService.prototype.closeHttpsProxy = function (body) {
        return __awaiter(this, void 0, void 0, function () {
            return __generator(this, function (_a) {
                if (g_httpsServer) {
                    g_httpsServer.close();
                    g_httpsServer = null;
                }
                return [2 /*return*/];
            });
        });
    };
    ProxyService.prototype.createSocket = function (body) {
        return __awaiter(this, void 0, void 0, function () {
            var host, port, webSocketId, server;
            return __generator(this, function (_a) {
                host = body.host, port = body.port, webSocketId = body.webSocketId;
                if (g_socketServer) {
                    g_socketServer.close();
                }
                server = net.createServer(function (socket) {
                    var clientId = (0, uid_1.uid)(8);
                    var clientRemote;
                    console.log('======== socks start', clientId);
                    console.log('client_socket/create');
                    var addr = socket.address();
                    console.log('client_socket/addr', addr.family, addr.address, addr.port);
                    // IPv6 ::ffff:127.0.0.1 6668
                    // 连接成功后进行协商认证，发送一个数据包
                    // +-----+----------+----------+
                    // | VER | NMETHODS | METHODS  |
                    // +-----+----------+----------+
                    // |  1  |    1     | 1 to 255 |
                    // +-----+----------+----------+
                    // VER： 协议版本号
                    // NMETHODS：客户端支持的认证方法数量
                    // METHODS：每个byte对应一个认证方法
                    // X'00' 不需要身份验证(NO AUTHENTICATION REQUIRED)
                    // X'01' GSSAPI
                    // X'02' 用户密码认证(USERNAME/PASSWORD)
                    // X'03' to X'7F' IANA ASSIGNED
                    // X'80' to X'FE' RESERVED FOR PRIVATE METHODS
                    socket.once('data', function (data) {
                        console.log('tcp_proxy/once_data', JSON.stringify(data));
                        if (!data) {
                            console.log('tcp_proxy/destroy/no_data');
                            return socket.destroy();
                        }
                        if (data[0] !== 0x05) {
                            console.log('tcp_proxy/destroy/no_v5', data.toString());
                            return socket.destroy();
                        }
                        console.log('is_v5');
                        // 代理服务器返回一个应答数据包
                        // +-----+--------+
                        // | VER | METHOD |
                        // +-----+--------+
                        // |  1  |   1    |
                        // +-----+--------+
                        // VER: 指定协议版本号，此处为X'05'
                        // METHOD: 指定认证方法。该方法应从客户端提供的认证方法中挑选一个，或者是X'FF'用以拒绝认证
                        socket.write(Buffer.from([5, 0]), function (err) {
                            if (err) {
                                console.log('tcp_proxy/write error');
                                socket.destroy();
                                return;
                            }
                            console.log('socks5 协商认证完成');
                            // once和on不同的地方就是，once只监听一次，会在回调函数执行完毕后，取消监听。
                            socket.once('data', function (data) {
                                console.log('tcp_proxy/请求代理 data', JSON.stringify(data));
                                // console.log('tcp_proxy/buff_data', data.data)
                                // console.log(JSON.stringify(buff));
                                // [5,1,0,3,24,119,119,119,46,103,111,111,103,108,101,45,97,110,97,108,121,116,105,99,115,46,99,111,109,1,187]
                                // console.log('version', data.slice(0, 0 + 1))
                                // console.log('RESPONSE', data.slice(1, 1 + 1))
                                // console.log('RSV', data.slice(2, 2 + 1))
                                // console.log('ADDRESS_TYPE', data.slice(3, 3 + 1))
                                // console.log('BND.ADDR', data.slice(4, data.length - 3))
                                // console.log('BND.PORT', data.slice(data.length - 2, data.length))
                                // console.log('BND.PORT2', data.slice(data.length - 2, data.length).toString())
                                // 请求代理
                                // 客户端在认证成功之后，需要发送一个数据包来请求服务端:
                                // +-----+-----+-------+------+----------+----------+
                                // | VER | CMD |  RSV  | ATYP | DST.ADDR | DST.PORT |
                                // +-----+-----+-------+------+----------+----------+
                                // |  1  |  1  | X'00' |  1   | Variable |    2     |
                                // +-----+-----+-------+------+----------+----------+
                                // VER: 协议版本号，此处为X'05'
                                // CMD: 指定代理方式
                                if (data.length < 7) {
                                    console.log('数据长度不对');
                                    return socket.destroy();
                                }
                                if (data[1] == 0x01) {
                                    // CONNECT
                                    try {
                                        var addrtype = data[3]; // ADDRESS_TYPE 目标服务器地址类型
                                        console.log('addrtype', addrtype);
                                        var addrLen = void 0;
                                        if (addrtype === 3) { //0x03 域名地址(没有打错，就是没有0x02)，
                                            addrLen = data[4]; //域名地址的第1个字节为域名长度，剩下字节为域名名称字节数组
                                        }
                                        else if (addrtype !== 1 && addrtype !== 4) {
                                            return socket.destroy();
                                        }
                                        var remotePort_1 = data.readUInt16BE(data.length - 2); //最后两位为端口值
                                        var remoteAddr_1;
                                        if (addrtype === 1) { // 0x01 IP V4地址
                                            remoteAddr_1 = data.slice(4, 4 + 4).join('.');
                                        }
                                        else if (addrtype === 4) { //0x04 IP V6地址
                                            return socket.write(Buffer.from([0x05, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])); //不支持IP V6
                                        }
                                        else { //0x03 域名地址(没有打错，就是没有0x02)，域名地址的第1个字节为域名长度，剩下字节为域名名称字节数组
                                            remoteAddr_1 = data.slice(5, 5 + addrLen).toString("binary");
                                        }
                                        console.log("remote_info : ".concat(remoteAddr_1, ":").concat(remotePort_1));
                                        {
                                            var time = moment().format('YYYY-MM-DD HH:mm:ss');
                                            (0, ssh_service_1.sendTcpMsg)({
                                                webSocketId: webSocketId,
                                                message: {
                                                    type: 'clientConnected',
                                                    data: {
                                                        id: (0, uid_1.uid)(8),
                                                        time: time,
                                                        // content,
                                                        // contentType,
                                                        type: 'clientConnected',
                                                        host: remoteAddr_1,
                                                        port: remotePort_1,
                                                        clientId: clientId,
                                                    }
                                                }
                                            });
                                        }
                                        var remote_1 = net.connect(remotePort_1, remoteAddr_1, function () {
                                            console.log("connected : ".concat(remoteAddr_1, ":").concat(remotePort_1));
                                            // 告诉客户端代理成功
                                            // +-----+-----+-------+------+----------+----------+
                                            // | VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
                                            // +-----+-----+-------+------+----------+----------+
                                            // |  1  |  1  | X'00' |  1   | Variable |    2     |
                                            // +-----+-----+-------+------+----------+----------+
                                            socket.write(Buffer.from([
                                                0x05,
                                                0x00,
                                                0x00,
                                                0x01,
                                                // 绑定地址
                                                0x00, 0x00, 0x00, 0x00,
                                                // 绑定端口
                                                0x00, 0x00
                                            ]), function (err) {
                                                if (err) {
                                                    console.log("\u544A\u8BC9\u5BA2\u6237\u7AEF\u4EE3\u7406\u6210\u529F error:".concat(err.message));
                                                    return socket.destroy();
                                                }
                                                // socket.pipe(remote);
                                                console.log('告诉客户端代理成功/pined');
                                                // remote.pipe(socket);
                                                socket.on('data', function (data) {
                                                    // console.log('tcp_proxy/on_data', data.toString())
                                                    console.log('tcp_proxy/on_data', '...');
                                                    var time = moment().format('YYYY-MM-DD HH:mm:ss');
                                                    (0, ssh_service_1.sendTcpMsg)({
                                                        webSocketId: webSocketId,
                                                        message: {
                                                            type: 'clientSent',
                                                            data: {
                                                                id: (0, uid_1.uid)(8),
                                                                time: time,
                                                                content: data.toString(),
                                                                // contentType,
                                                                type: 'clientSent',
                                                                // host,
                                                                // port,
                                                                clientId: clientId,
                                                            }
                                                        }
                                                    });
                                                    if (!data) {
                                                        console.log('tcp_proxy/on_data/destroy');
                                                        return socket.destroy();
                                                    }
                                                    remote_1.write(data);
                                                });
                                            });
                                        });
                                        clientRemote = remote_1;
                                        remote_1.on('data', function (data) {
                                            console.log('remote/ondata', '...');
                                            // console.log('remote/ondata', data.toString())
                                            socket.write(data);
                                            var time = moment().format('YYYY-MM-DD HH:mm:ss');
                                            (0, ssh_service_1.sendTcpMsg)({
                                                webSocketId: webSocketId,
                                                message: {
                                                    type: 'clientReceived',
                                                    data: {
                                                        id: (0, uid_1.uid)(8),
                                                        time: time,
                                                        content: data.toString(),
                                                        // contentType,
                                                        type: 'clientReceived',
                                                        // host,
                                                        // port,
                                                        clientId: clientId,
                                                    }
                                                }
                                            });
                                        });
                                        remote_1.on('error', function (err) {
                                            console.log("\u8FDE\u63A5\u5230\u8FDC\u7A0B\u670D\u52A1\u5668 ".concat(remoteAddr_1, ":").concat(remotePort_1, " \u5931\u8D25,\u5931\u8D25\u4FE1\u606F:").concat(err.message));
                                            remote_1.destroy();
                                            socket.destroy();
                                        });
                                    }
                                    catch (e) {
                                        console.log('err', e);
                                        console.log(e);
                                    }
                                }
                                else if (data[1] == 0x03) {
                                    // UDP ASSOCIATE
                                    console.log('UDP ASSOCIATE');
                                    try {
                                        var addrtype = data[3]; // ADDRESS_TYPE 目标服务器地址类型
                                        console.log('addrtype', addrtype);
                                        var addrLen = void 0;
                                        if (addrtype === 3) { //0x03 域名地址(没有打错，就是没有0x02)，
                                            addrLen = data[4]; //域名地址的第1个字节为域名长度，剩下字节为域名名称字节数组
                                        }
                                        else if (addrtype !== 1 && addrtype !== 4) {
                                            return socket.destroy();
                                        }
                                        var remotePort = data.readUInt16BE(data.length - 2); //最后两位为端口值
                                        var remoteAddr = void 0;
                                        if (addrtype === 1) { // 0x01 IP V4地址
                                            remoteAddr = data.slice(4, 4 + 4).join('.');
                                        }
                                        else if (addrtype === 4) { //0x04 IP V6地址
                                            return socket.write(Buffer.from([0x05, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])); //不支持IP V6
                                        }
                                        else { //0x03 域名地址(没有打错，就是没有0x02)，域名地址的第1个字节为域名长度，剩下字节为域名名称字节数组
                                            remoteAddr = data.slice(5, 5 + addrLen).toString("binary");
                                        }
                                        console.log("remote_info : ".concat(remoteAddr, ":").concat(remotePort));
                                        g_hostPortMap["".concat(remoteAddr, ":").concat(remotePort)] = {
                                            webSocketId: webSocketId,
                                            clientId: clientId,
                                        };
                                        {
                                            // const time = moment().format('YYYY-MM-DD HH:mm:ss')
                                            // sendTcpMsg({
                                            //     webSocketId,
                                            //     message: {
                                            //         type: 'clientConnected',
                                            //         data: {
                                            //             id: uid(8),
                                            //             time,
                                            //             // content,
                                            //             // contentType,
                                            //             type: 'clientConnected',
                                            //             host: remoteAddr,
                                            //             port: remotePort,
                                            //             clientId,
                                            //         }
                                            //     }
                                            // })
                                        }
                                        socket.write(Buffer.from([
                                            0x05,
                                            0x00,
                                            0x00,
                                            0x01,
                                            // 绑定地址
                                            0x00, 0x00, 0x00, 0x00,
                                            // 绑定端口
                                            // 0 端口会导致客户端 [ERR_SOCKET_BAD_PORT]: Port should be > 0 and < 65536. Received 0
                                            0x00, 0x01
                                        ]), function (err) {
                                            if (err) {
                                                console.log("\u544A\u8BC9\u5BA2\u6237\u7AEF\u4EE3\u7406\u6210\u529F error:".concat(err.message));
                                                return socket.destroy();
                                            }
                                            // socket.pipe(remote);
                                            console.log('告诉客户端代理成功/pined');
                                            // remote.pipe(socket);
                                            socket.on('data', function (data) {
                                                console.log('tcp_proxy/on_data??', data.toString());
                                            });
                                        });
                                    }
                                    catch (e) {
                                        console.log('err', e);
                                        console.log(e);
                                    }
                                }
                                else {
                                    console.log('no_support_CMD');
                                    return socket.destroy();
                                }
                            });
                        });
                    });
                    socket.on('error', function (err) {
                        console.log("client_socket/on_error:".concat(err.message));
                    });
                    socket.on('close', function () {
                        console.log('client_socket/on_close', clientId);
                        if (clientRemote) {
                            clientRemote.end(function () {
                                clientRemote.destroy();
                                clientRemote = null;
                            });
                        }
                        {
                            var time = moment().format('YYYY-MM-DD HH:mm:ss');
                            (0, ssh_service_1.sendTcpMsg)({
                                webSocketId: webSocketId,
                                message: {
                                    type: 'clientClose',
                                    data: {
                                        id: (0, uid_1.uid)(8),
                                        time: time,
                                        // content,
                                        // contentType,
                                        type: 'clientClose',
                                        // host,
                                        // port,
                                        clientId: clientId,
                                    }
                                }
                            });
                        }
                    });
                });
                g_socketServer = server;
                server.listen(port, host, function () {
                    console.log('listened');
                    var time = moment().format('YYYY-MM-DD HH:mm:ss');
                    (0, ssh_service_1.sendTcpMsg)({
                        webSocketId: webSocketId,
                        message: {
                            type: 'listening',
                            data: {
                                id: (0, uid_1.uid)(8),
                                time: time,
                                // content,
                                // contentType,
                                type: 'listening',
                                host: host,
                                port: port,
                            }
                        }
                    });
                })
                    .on('close', function () {
                    console.log('tcp_proxy/close');
                    var time = moment().format('YYYY-MM-DD HH:mm:ss');
                    (0, ssh_service_1.sendTcpMsg)({
                        webSocketId: webSocketId,
                        message: {
                            type: 'close',
                            data: {
                                id: (0, uid_1.uid)(8),
                                time: time,
                                // content,
                                // contentType,
                                type: 'close',
                                host: host,
                                port: port,
                            }
                        }
                    });
                });
                console.log('tcp_proxy/listen at', port);
                return [2 /*return*/, {}];
            });
        });
    };
    ProxyService.prototype.closeSocket = function (body) {
        return __awaiter(this, void 0, void 0, function () {
            return __generator(this, function (_a) {
                if (g_socketServer) {
                    g_socketServer.close();
                }
                return [2 /*return*/];
            });
        });
    };
    ProxyService.prototype.getRootCert = function (body) {
        return __awaiter(this, void 0, void 0, function () {
            return __generator(this, function (_a) {
                return [2 /*return*/, {
                        cert: root_cert,
                    }];
            });
        });
    };
    return ProxyService;
}());
exports.ProxyService = ProxyService;
//# sourceMappingURL=proxy.service.js.map