@kloak-it/tq-proxy
Version:
QT PROXY Server/Client
384 lines (378 loc) • 14.8 kB
JavaScript
"use strict";
/*!
* Copyright 2018 CoNET Technology Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.sockt4 = exports.socks5 = void 0;
const Rfc1928 = __importStar(require("./rfc1928"));
const res = __importStar(require("./res"));
const Crypto = __importStar(require("crypto"));
const httpProxy_1 = __importDefault(require("./httpProxy"));
const Util = __importStar(require("util"));
const log_1 = require("../GateWay/log");
const safe_1 = __importDefault(require("colors/safe"));
// socks 5 headers
const server_res = {
NO_AUTHENTICATION_REQUIRED: Buffer.from('0500', 'hex')
};
const isSslFromBuffer = (buffer) => {
const ret = buffer[0] === 0x16 && buffer[1] === 0x03;
return ret;
};
const getHostNameFromSslConnection = (buffer) => {
if (!isSslFromBuffer(buffer)) {
return null;
}
const lengthPoint = buffer.readInt16BE(0x95);
const serverName = buffer.slice(0x97, 0x97 + lengthPoint);
// 00000090 00 02 01 00 00 0A 00 08 00 06 00 1D 00 17 00 18 ................
// use IP address
if (lengthPoint === 0x0A00 && serverName[0] === 0x8 && serverName[1] === 0x0) {
return null;
}
(0, log_1.hexDebug)(serverName);
(0, log_1.logger)(`getHostNameFromSslConnection lengthPoint[${lengthPoint.toString(16)}] === 0x0A ${lengthPoint === 0x0A00} serverName[0] [${serverName[0].toString(16)}] serverName[0] === 0x8 ${serverName[0] === 0x8} && serverName[1] [${serverName[1].toString(16)}] === 0x06 [${serverName[1] === 0x0}] `);
return serverName.toString();
};
class socks5 {
stopConnection(req) {
req.REP = Rfc1928.Replies.COMMAND_NOT_SUPPORTED_or_PROTOCOL_ERROR;
return this.socket.write(req.buffer);
}
closeSocks5(buffer) {
//console.log (`close proxy socket!`)
if (this.socket) {
if (this.socket.writable) {
this.socket.end(buffer);
}
if (typeof this.socket.removeAllListeners === 'function')
this.socket.removeAllListeners();
}
}
connectStat3(req) {
let userAgent = '';
switch (req.cmd) {
case Rfc1928.CMD.CONNECT: {
break;
}
case Rfc1928.CMD.BIND: {
}
case Rfc1928.CMD.UDP_ASSOCIATE: {
}
default: {
return this.stopConnection(req);
}
}
const uuuu = {
uuid: this.uuid,
host: req.host,
hostIPAddress: req.hostAddress,
buffer: '',
cmd: this._cmd,
port: req.port,
ssl: false
};
const requestObj = {
remotePort: this.socket.remotePort,
remoteAddress: this.socket.remoteAddress,
targetHost: uuuu.host,
targetPort: uuuu.port,
methods: '',
socks: 'Sock5',
uuid: uuuu.uuid
};
this.socket.once('data', (_data) => {
// gateway shutdown
if (!this.proxyServer.gateway) {
//console.log (`SOCK5 !this.proxyServer.gateway STOP sokcet! res.HTTP_403`)
return this.socket.end(res._HTTP_PROXY_302());
}
if (this.debug) {
(0, log_1.logger)(`connectStat3 buffer`);
(0, log_1.hexDebug)(_data);
}
uuuu.ssl = isSslFromBuffer(_data);
if (!uuuu.ssl) {
const httpHeader = new httpProxy_1.default(_data);
uuuu.host = httpHeader.host || uuuu.host;
if (!httpHeader.host) {
(0, log_1.logger)(safe_1.default.red(`Socks 5 have no host [header]`));
(0, log_1.logger)(Util.inspect(httpHeader.headers, false, 3, true));
}
userAgent = httpHeader.headers['user-agent'];
requestObj.methods = httpHeader.methods;
}
uuuu.buffer = _data.toString('base64');
if (this.debug) {
(0, log_1.logger)(Util.inspect(uuuu));
(0, log_1.logger)(Util.inspect(requestObj));
}
return this.proxyServer.gateway.requestGetWay(requestObj, uuuu, userAgent, this.socket);
});
req.REP = Rfc1928.Replies.GRANTED;
return this.socket.write(req.buffer);
}
udpProcess(data) {
data.REP = Rfc1928.Replies.GRANTED;
return this.socket.write(data.buffer);
}
connectStat2(data) {
if (this.debug) {
(0, log_1.hexDebug)(data);
}
const req = new Rfc1928.Requests(data);
this.ATYP = req.ATYP;
this.host = req.domainName;
this.port = req.port;
this.cmd = req.cmd;
this.targetIpV4 = req.ATYP_IP4Address;
//.serverIP = this.socket.localAddress.split (':')[3]
// IPv6 not support!
switch (this.cmd) {
case Rfc1928.CMD.CONNECT: {
this.keep = true;
this._cmd = 'CONNECT';
break;
}
case Rfc1928.CMD.BIND: {
this._cmd = 'BIND';
break;
}
case Rfc1928.CMD.UDP_ASSOCIATE: {
this._cmd = 'UDP_ASSOCIATE';
//logger( `Rfc1928.CMD.UDP_ASSOCIATE data[${ data.toString ('hex')}]` )
break;
}
default: {
this._cmd = 'UNKNOW';
(0, log_1.logger)(`Socks 5 unknow cmd: `, data.toString('hex'), Util.inspect(req, false, 3, true));
break;
}
}
// IPv6 not support
// if ( req.IPv6 ) {
// this.keep = false
// }
const obj = { ATYP: this.ATYP, host: this.host, hostType: typeof this.host, port: this.port, targetIpV4: this.targetIpV4, cmd: this._cmd, buffer: data.toString('hex') };
if (!this.keep) {
req.REP = Rfc1928.Replies.COMMAND_NOT_SUPPORTED_or_PROTOCOL_ERROR;
if (this.debug) {
(0, log_1.logger)(safe_1.default.red(`Rfc1928.Replies.COMMAND_NOT_SUPPORTED_or_PROTOCOL_ERROR STOP socks 5 connecting.`));
(0, log_1.logger)(Util.inspect(obj));
}
return this.closeSocks5(req.buffer);
}
if (this.cmd === Rfc1928.CMD.UDP_ASSOCIATE) {
return (0, log_1.logger)('this.cmd === Rfc1928.CMD.UDP_ASSOCIATE skip!');
}
return this.connectStat3(req);
}
constructor(socket, data, agent, proxyServer) {
this.socket = socket;
this.data = data;
this.agent = agent;
this.proxyServer = proxyServer;
this.host = null;
this.ATYP = null;
this.port = null;
this.cmd = null;
this._cmd = '';
this.targetIpV4 = null;
this.keep = false;
this.clientIP = this.socket.remoteAddress.split(':')[3] || this.socket.remoteAddress;
this.debug = this.proxyServer.debug;
this.uuid = Crypto.randomBytes(10).toString('hex');
if (this.debug) {
(0, log_1.logger)(safe_1.default.yellow(`new socks v5`));
(0, log_1.hexDebug)(data);
}
this.socket.once('data', (chunk) => {
return this.connectStat2(chunk);
});
this.socket.write(server_res.NO_AUTHENTICATION_REQUIRED);
this.socket.resume();
}
}
exports.socks5 = socks5;
class sockt4 {
constructor(socket, buffer, agent, proxyServer) {
this.socket = socket;
this.buffer = buffer;
this.agent = agent;
this.proxyServer = proxyServer;
this.req = new Rfc1928.socket4Requests(this.buffer);
this.host = this.req.domainName;
this.port = this.req.port;
this.uuid = Crypto.randomBytes(10).toString('hex');
this.cmd = this.req.cmd;
this._cmd = '';
this.targetIpV4 = this.req.targetIp;
this.keep = false;
this.debug = false;
this.id = safe_1.default.blue(`[${this.uuid}] [${this.socket.remoteAddress}:${this.socket.remotePort}] --> [${this.host}:${this.port}]`);
this.debug = proxyServer.debug;
this.socket.pause();
if (this.debug) {
(0, log_1.logger)(safe_1.default.yellow(`new socks v4`));
(0, log_1.hexDebug)(buffer);
}
switch (this.cmd) {
case Rfc1928.CMD.CONNECT: {
this.keep = true;
this._cmd = 'CONNECT';
if (this.debug) {
(0, log_1.logger)(safe_1.default.gray(`${this.id} sockt4 got Rfc1928 command ${safe_1.default.magenta('CONNECT')}`));
}
break;
}
case Rfc1928.CMD.BIND: {
this._cmd = 'BIND';
if (this.debug) {
(0, log_1.logger)(safe_1.default.gray(`${this.id} sockt4 got Rfc1928 command ${safe_1.default.magenta('BIND')}`));
}
break;
}
case Rfc1928.CMD.UDP_ASSOCIATE: {
if (this.debug) {
(0, log_1.logger)(safe_1.default.gray(`${this.id} sockt4 got Rfc1928 command ${safe_1.default.magenta('UDP_ASSOCIATE')}`));
}
this._cmd = 'UDP_ASSOCIATE';
break;
}
default: {
(0, log_1.logger)(safe_1.default.red(`${this.id} sockt4 got Rfc1928 unknow command [${this.cmd}]`));
this._cmd = 'UNKNOW';
break;
}
}
if (!this.keep) {
this.debug ? (0, log_1.logger)(safe_1.default.red(`STOP session`)) : null;
this.socket.end(this.req.request_failed);
return;
}
this.connectStat2();
}
connectStat2() {
this.socket.once('data', (_data) => {
if (this.debug) {
(0, log_1.logger)(`SOCK4 connectStat2 [${this.host || this.targetIpV4}] get data`);
(0, log_1.hexDebug)(_data);
}
if (!this.proxyServer.gateway) {
(0, log_1.logger)(safe_1.default.red(`SOCK4 !this.proxyServer.gateway STOP sokcet! res.HTTP_403`));
return this.socket.end(res._HTTP_PROXY_302());
}
this.connect(_data);
});
const buffer = this.req.request_4_granted('0.0.0.255', this.port);
this.socket.write(buffer);
return this.socket.resume();
}
connect(buffer) {
const isSsl = isSslFromBuffer(buffer);
let userAgent = '';
let methods = 'GET';
let httpHeader = null;
const uuuu = {
uuid: this.uuid,
host: this.host,
hostIPAddress: this.req.targetIp,
buffer: buffer.toString('base64'),
cmd: this._cmd,
port: this.req.port,
ssl: isSslFromBuffer(buffer)
};
if (!isSsl) {
httpHeader = new httpProxy_1.default(buffer);
uuuu.host = httpHeader.host;
userAgent = httpHeader.headers['user-agent'];
methods = httpHeader.methods;
}
const requestObj = {
remotePort: this.socket.remotePort,
remoteAddress: this.socket.remoteAddress,
targetHost: uuuu.host,
targetPort: uuuu.port,
methods: httpHeader ? httpHeader.methods : 'CONNECT',
socks: this.req.targetIp ? 'Sock4' : 'Sock4a',
uuid: uuuu.uuid
};
if (this.debug) {
(0, log_1.logger)(Util.inspect(uuuu, false, 3, true));
(0, log_1.logger)(Util.inspect(requestObj, false, 3, true));
}
if (this.proxyServer.gateway && typeof this.proxyServer.gateway.requestGetWay === 'function') {
return this.proxyServer.gateway.requestGetWay(requestObj, uuuu, userAgent, this.socket);
}
return this.socket.end(this.req.request_failed);
}
}
exports.sockt4 = sockt4;
/*
export class UdpDgram {
private server: Dgram.Socket = null
public port = 0
private createDgram () {
this.server = Dgram.createSocket ( 'udp4' )
this.server.once ( 'error', err => {
console.log ( 'server.once error close server!', err )
this.server.close ()
})
this.server.on ( 'message', ( msg: Buffer, rinfo ) => {
console.log(`UdpDgram server msg: ${ msg.toString('hex') } from ${ rinfo.address }:${ rinfo.port }`)
})
this.server.once ( 'listening', () => {
const address = this.server.address()
this.port = address.port
console.log ( `server listening ${ address.address }:${ address.port }` )
})
this.server.bind ({ port: 0 } , ( err, kkk ) => {
if ( err ) {
return console.log ( `server.bind ERROR`, err )
}
console.log ( kkk )
})
}
constructor () {
this.createDgram ()
}
}
*/