wormhole/lib/Parser.js
2021-03-25 20:24:43 +02:00

133 lines
No EOL
5 KiB
JavaScript

const constants = require("./constants");
const { createLog } = require('./logger');
const parserLog = createLog([ 'Wormhole', 'Parser' ]);
class WebsocketFrame {
constructor() {
// Just defining the structure of the WebsocketFrame,
// I could actually just use a normal object but I
// might need to add some methods to this in the future
// so it's good enough
// 1 byte - first byte
this.FIN = undefined;
this.RSVx = undefined;
this.Opcode = undefined;
// 1 byte - second byte
this.MASK = undefined;
this.PayloadLen = undefined;
this.PayloadLenEx = undefined; // This is for when PayloadLen needs to be extended
// 4 bytes - masking key
this.MaskingKey = undefined;
// (Extension Data + Application Data[PayloadLen]) (kind of) - payload data
// TODO: Separate extension data from application data
this.PayloadData = undefined;
}
}
WebsocketFrame.prototype.toBuffer = function() {
const packet = [];
const firstByte = this.FIN << 0x07 | this.RSVx << 0x06 | this.Opcode;
const secondByte = this.MASK << 0x07 | this.PayloadLen;
const headerBuffer = Buffer.from([firstByte, secondByte]);
packet.push(headerBuffer);
let lenBuffer;
if (this.PayloadLen === 126) {
lenBuffer = Buffer.alloc(2);
lenBuffer.writeUint16BE(this.PayloadLenEx);
} else if (this.PayloadLen === 127) {
lenBuffer = Buffer.alloc(8);
lenBuffer.writeBigUint64BE(BigInt(this.PayloadLenEx));
}
lenBuffer && packet.push(lenBuffer);
this.MASK && packet.push(this.MaskingKey);
packet.push(this.PayloadData);
return Buffer.concat(packet);
};
const parseWebsocketFrame = (data) => {
const firstByte = data.getUint8(0);
const secondByte = data.getUint8(1);
const frame = new WebsocketFrame();
// 1 byte - first byte
frame.FIN = (firstByte >> 7); // >> 7 - Get most significant bit (FIN)
frame.RSVx = (firstByte & 0x70) >> 4; // 0x70[0b01110000] - Get the 3 bits after the most significant bit (RSVx)
frame.Opcode = (firstByte & 0x0F); // 0xF[0b00001111] - Get the last 4 bits (Opcode)
// 1 byte - second byte
frame.MASK = (secondByte >> 7); // >> 7 - Get most significant bit (MASK)
frame.PayloadLen = (secondByte & 0x7F); // 0x7F[0b01111111] - Get last 7 bits (Payload len)
let maskingKeyOffset = 2; // By default, theres a 2 byte offset. We will modify this in the cases below.
// Handle Payload len cases and set the masking key offset
if (frame.PayloadLen === 126) {
frame.PayloadLenEx = data.getUint16(2);
maskingKeyOffset = 4; // 4 byte offset, because we also read 2 bytes with getUint16 above (2 bytes (normal size) + 2 bytes)
} else if (frame.PayloadLen === 127) {
frame.PayloadLenEx = data.getBigUint64(2);
maskingKeyOffset = 10; // 10 byte offset, because we also read 8 bytes with getBigUint64 above (2 bytes (normal size) + 8 bytes)
} else {
frame.PayloadLenEx = frame.PayloadLen;
}
let maskingKeyEnd = frame.MASK ? maskingKeyOffset + 4 : maskingKeyOffset; // (4 bytes because it is a 32 bit value)
if (frame.MASK) frame.MaskingKey = data.buffer.slice(maskingKeyOffset, maskingKeyEnd); // Create a new buffer starting at the masking key offset and ending at the masking key end (duh).
// TODO: Separate extension data from application data
frame.PayloadData = data.buffer.slice(maskingKeyEnd, Number(BigInt(maskingKeyEnd) + BigInt(frame.PayloadLenEx))); // Create a new buffer that starts at the end of the masking key and ends at (the end of the masking key + payload length)
// TODO: implement unmasked string decoding
if (frame.MASK) {
switch (frame.Opcode) {
case constants.opcodes.TEXT_FRAME: { // Denotes a text frame
const payloadDataView = new DataView(frame.PayloadData);
const maskingKeyView = new DataView(frame.MaskingKey);
let decoded = '';
for (let i = 0; i < frame.PayloadData.byteLength; i++) {
decoded += (String.fromCharCode(payloadDataView.getUint8(i) ^ maskingKeyView.getUint8(i % 4)));
}
frame.DecodedPayloadData = decoded;
break;
}
case constants.opcodes.BINARY_FRAME: { // Denotes a binary frame
const payloadDataView = new DataView(frame.PayloadData);
const maskingKeyView = new DataView(frame.MaskingKey);
const decoded = [];
for (let i = 0; i < frame.PayloadData.byteLength; i++) {
decoded.push(payloadDataView.getUint8(i) ^ maskingKeyView.getUint8(i % 4));
}
frame.DecodedPayloadData = Uint8Array.from(decoded);
break;
}
}
}
return frame;
}
module.exports = {
WebsocketFrame,
parseWebsocketFrame
};