2018-02-17 04:19:31 +00:00
|
|
|
import { IPresetAddressing } from './defs';
|
2018-02-04 14:33:01 +00:00
|
|
|
import {
|
|
|
|
AdvancedBuffer,
|
|
|
|
dumpHex,
|
|
|
|
getRandomChunks,
|
|
|
|
isValidHostname,
|
|
|
|
isValidPort,
|
|
|
|
numberToBuffer as ntb,
|
|
|
|
} from '../utils';
|
|
|
|
|
2017-12-25 08:42:01 +00:00
|
|
|
const CMD_NEW_CONN = 0x00;
|
|
|
|
const CMD_DATA_FRAME = 0x01;
|
|
|
|
const CMD_CLOSE_CONN = 0x02;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @description
|
2018-02-04 14:33:01 +00:00
|
|
|
* Multiplexing protocol.
|
2017-12-25 08:42:01 +00:00
|
|
|
*
|
|
|
|
* @protocol
|
|
|
|
*
|
|
|
|
* # New Connection (client -> server)
|
|
|
|
* +-------+-------+------+----------+----------+
|
|
|
|
* | CMD | CID | ALEN | DST.ADDR | DST.PORT |
|
2018-02-04 14:33:01 +00:00
|
|
|
* +-------+-------+------+----------+----------+ + [Data Frames]
|
|
|
|
* | 0x0 | 4 | 1 | Variable | 2 |
|
2017-12-25 08:42:01 +00:00
|
|
|
* +-------+-------+------+----------+----------+
|
|
|
|
*
|
|
|
|
* # Data Frames (client <-> server)
|
|
|
|
* +-------+-------+------------+-------------+
|
|
|
|
* | CMD | CID | DATA LEN | DATA |
|
|
|
|
* +-------+-------+------------+-------------+
|
2018-02-04 14:33:01 +00:00
|
|
|
* | 0x1 | 4 | 2 | Variable |
|
2017-12-25 08:42:01 +00:00
|
|
|
* +-------+-------+------------+-------------+
|
|
|
|
*
|
2018-02-04 14:33:01 +00:00
|
|
|
* # Close Connection (client <-> server)
|
|
|
|
* +-------+-------+
|
|
|
|
* | CMD | CID |
|
|
|
|
* +-------+-------+
|
|
|
|
* | 0x2 | 4 |
|
|
|
|
* +-------+-------+
|
|
|
|
*
|
2017-12-25 08:42:01 +00:00
|
|
|
*/
|
|
|
|
export default class MuxPreset extends IPresetAddressing {
|
|
|
|
|
2018-05-04 14:08:36 +00:00
|
|
|
static isPrivate = true;
|
|
|
|
|
2017-12-25 08:42:01 +00:00
|
|
|
_adBuf = null;
|
|
|
|
|
2018-02-15 03:01:24 +00:00
|
|
|
onInit() {
|
2018-02-17 04:19:31 +00:00
|
|
|
this._adBuf = new AdvancedBuffer({ getPacketLength: this.onReceiving.bind(this) });
|
2017-12-25 08:42:01 +00:00
|
|
|
this._adBuf.on('data', this.onChunkReceived.bind(this));
|
|
|
|
}
|
|
|
|
|
|
|
|
onDestroy() {
|
|
|
|
this._adBuf.clear();
|
|
|
|
this._adBuf = null;
|
|
|
|
}
|
|
|
|
|
2018-02-17 04:19:31 +00:00
|
|
|
onReceiving(buffer, { fail }) {
|
2018-02-04 14:33:01 +00:00
|
|
|
if (buffer.length < 5) {
|
2017-12-25 08:42:01 +00:00
|
|
|
return; // too short, continue to recv
|
|
|
|
}
|
|
|
|
const cmd = buffer[0];
|
|
|
|
switch (cmd) {
|
|
|
|
case CMD_NEW_CONN:
|
2018-02-04 14:33:01 +00:00
|
|
|
if (buffer.length < 8 + buffer[5]) {
|
2017-12-26 08:00:18 +00:00
|
|
|
return;
|
|
|
|
}
|
2018-02-04 14:33:01 +00:00
|
|
|
return 8 + buffer[5];
|
2017-12-25 08:42:01 +00:00
|
|
|
case CMD_DATA_FRAME:
|
2018-02-04 14:33:01 +00:00
|
|
|
if (buffer.length < 7) {
|
2017-12-26 08:00:18 +00:00
|
|
|
return;
|
|
|
|
}
|
2018-02-04 14:33:01 +00:00
|
|
|
return 7 + buffer.readUInt16BE(5);
|
2017-12-25 08:42:01 +00:00
|
|
|
case CMD_CLOSE_CONN:
|
2018-02-04 14:33:01 +00:00
|
|
|
return 5;
|
2017-12-25 08:42:01 +00:00
|
|
|
default:
|
|
|
|
fail(`unknown cmd=${cmd} dump=${dumpHex(buffer)}`);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-04 14:08:36 +00:00
|
|
|
onChunkReceived(chunk, { fail }) {
|
2017-12-25 08:42:01 +00:00
|
|
|
const cmd = chunk[0];
|
2018-02-04 14:33:01 +00:00
|
|
|
const cid = chunk.slice(1, 5).toString('hex');
|
2017-12-25 08:42:01 +00:00
|
|
|
switch (cmd) {
|
|
|
|
case CMD_NEW_CONN: {
|
2018-02-06 13:29:54 +00:00
|
|
|
const hostBuf = chunk.slice(6, -2);
|
|
|
|
const host = hostBuf.toString();
|
2018-02-04 14:33:01 +00:00
|
|
|
const port = chunk.readUInt16BE(6 + chunk[5]);
|
2018-01-18 10:03:01 +00:00
|
|
|
if (!isValidHostname(host) || !isValidPort(port)) {
|
2018-02-06 13:29:54 +00:00
|
|
|
return fail(`invalid host or port, host=${dumpHex(hostBuf)} port=${port}`);
|
2018-01-18 10:03:01 +00:00
|
|
|
}
|
2018-05-04 14:08:36 +00:00
|
|
|
return this.muxNewConn({ cid, host, port });
|
2017-12-25 08:42:01 +00:00
|
|
|
}
|
|
|
|
case CMD_DATA_FRAME: {
|
2018-02-04 14:33:01 +00:00
|
|
|
const dataLen = chunk.readUInt16BE(5);
|
2018-05-04 14:08:36 +00:00
|
|
|
return this.muxDataFrame({ cid, data: chunk.slice(-dataLen) });
|
2017-12-25 08:42:01 +00:00
|
|
|
}
|
|
|
|
case CMD_CLOSE_CONN:
|
2018-05-04 14:08:36 +00:00
|
|
|
return this.muxCloseConn({ cid });
|
2017-12-25 08:42:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
createDataFrames(cid, data) {
|
|
|
|
const chunks = getRandomChunks(data, 0x0800, 0x3fff).map((chunk) =>
|
2018-02-04 14:33:01 +00:00
|
|
|
Buffer.concat([ntb(CMD_DATA_FRAME, 1), cid, ntb(chunk.length), chunk])
|
2017-12-25 08:42:01 +00:00
|
|
|
);
|
|
|
|
return Buffer.concat(chunks);
|
|
|
|
}
|
|
|
|
|
|
|
|
createNewConn(host, port, cid) {
|
|
|
|
const _host = Buffer.from(host);
|
|
|
|
const _port = ntb(port);
|
2018-02-04 14:33:01 +00:00
|
|
|
return Buffer.concat([ntb(CMD_NEW_CONN, 1), cid, ntb(_host.length, 1), _host, _port]);
|
2017-12-25 08:42:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
createCloseConn(cid) {
|
2018-02-04 14:33:01 +00:00
|
|
|
return Buffer.concat([ntb(CMD_CLOSE_CONN, 1), cid]);
|
2017-12-25 08:42:01 +00:00
|
|
|
}
|
|
|
|
|
2018-02-17 04:19:31 +00:00
|
|
|
clientOut({ buffer, fail }, { host, port, cid, isClosing }) {
|
2017-12-25 08:42:01 +00:00
|
|
|
if (cid !== undefined) {
|
2018-02-04 14:33:01 +00:00
|
|
|
const _cid = Buffer.from(cid, 'hex');
|
2018-06-24 04:18:39 +00:00
|
|
|
if (isClosing) {
|
|
|
|
return this.createCloseConn(_cid);
|
|
|
|
}
|
2018-02-04 14:33:01 +00:00
|
|
|
const dataFrames = this.createDataFrames(_cid, buffer);
|
2017-12-25 08:42:01 +00:00
|
|
|
if (host && port) {
|
2018-02-04 14:33:01 +00:00
|
|
|
return Buffer.concat([this.createNewConn(host, port, _cid), dataFrames]);
|
2017-12-25 08:42:01 +00:00
|
|
|
}
|
|
|
|
return dataFrames;
|
2018-02-04 14:33:01 +00:00
|
|
|
} else {
|
|
|
|
fail(`cid is not provided, drop buffer=${dumpHex(buffer)}`);
|
2017-12-25 08:42:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-17 04:19:31 +00:00
|
|
|
serverOut({ buffer, fail }, { cid, isClosing }) {
|
2017-12-25 08:42:01 +00:00
|
|
|
if (cid !== undefined) {
|
2018-02-04 14:33:01 +00:00
|
|
|
const _cid = Buffer.from(cid, 'hex');
|
2017-12-25 08:42:01 +00:00
|
|
|
if (isClosing) {
|
2018-02-04 14:33:01 +00:00
|
|
|
return this.createCloseConn(_cid);
|
2017-12-25 08:42:01 +00:00
|
|
|
}
|
2018-02-04 14:33:01 +00:00
|
|
|
return this.createDataFrames(_cid, buffer);
|
|
|
|
} else {
|
|
|
|
fail(`cid is not provided, drop buffer=${dumpHex(buffer)}`);
|
2017-12-25 08:42:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-04 14:08:36 +00:00
|
|
|
beforeIn({ buffer, fail }) {
|
|
|
|
this._adBuf.put(buffer, { fail });
|
2017-12-25 08:42:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|