From a53cd1b7717144d27a31c4e3cd8c93da2b5bc7b1 Mon Sep 17 00:00:00 2001 From: hippoz Date: Wed, 3 Feb 2021 02:18:28 +0200 Subject: [PATCH] proof-of-concept sending packets to client --- example/example.js | 1 + lib/Parser.js | 34 +++++++++++++++++++++++++++++++--- lib/Socket.js | 21 +++++++++++++++++++++ 3 files changed, 53 insertions(+), 3 deletions(-) diff --git a/example/example.js b/example/example.js index 8cc6810..ce4af09 100644 --- a/example/example.js +++ b/example/example.js @@ -8,6 +8,7 @@ const wormhole = new Wormhole({ urls: [ '/hello' ], httpServer }); wormhole.on('connect', ({ socket, accept, reject }) => { accept(); + socket.sendText('hello'); }); httpServer.listen(8080); \ No newline at end of file diff --git a/lib/Parser.js b/lib/Parser.js index 6a7ed20..43da7a4 100644 --- a/lib/Parser.js +++ b/lib/Parser.js @@ -14,6 +14,8 @@ class WebsocketFrame { 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; @@ -23,6 +25,30 @@ class WebsocketFrame { } } +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); + buf.writeUint16BE(this.PayloadLenEx); + } else if (this.PayloadLen === 127) { + lenBuffer = Buffer.alloc(8); + buf.writeBigUint64BE(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); @@ -42,18 +68,20 @@ const parseWebsocketFrame = (data) => { // Handle Payload len cases and set the masking key offset if (frame.PayloadLen === 126) { - frame.PayloadLen = data.getUint16(2); + 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.PayloadLen = data.getBigUint64(2); + 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; } const maskingKeyEnd = maskingKeyOffset + 4; // (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, maskingKeyEnd + frame.PayloadLen); // Create a new buffer that starts at the end of the masking key and ends at (the end of the masking key + payload length) + frame.PayloadData = data.buffer.slice(maskingKeyEnd, maskingKeyEnd + 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) { diff --git a/lib/Socket.js b/lib/Socket.js index c51f080..0bd1c49 100644 --- a/lib/Socket.js +++ b/lib/Socket.js @@ -16,6 +16,27 @@ class Socket { } } +Socket.prototype.sendText = function(text) { + const frame = new parser.WebsocketFrame(); + frame.FIN = 1; + frame.RSVx = 0; + frame.Opcode = 0x01; + + frame.MASK = 0; + const length = text.length; + if (length > 125) { + frame.PayloadLen = 126; + } else if (length >= 65536) { + frame.PayloadLen = 127; + } else if (length <= 125) { + frame.PayloadLen = length; + } + frame.PayloadLenEx = length; + frame.PayloadData = Buffer.from(text); + + this._socket.write(frame.toBuffer()); +}; + Socket.prototype._decodePayload = function(payload) { console.log(parser.parseWebsocketFrame(new DataView(payload))); };