Web relay websocket ping/pong forwarding, #4172

Ylian Saint-Hilaire committed Jun 28, 2022 at 15:42 UTC a383bd1cb3f794c59dbc88c5243aa54d15e8651d
1 file changed +43 -59
apprelays.js
+43 -59
@@ -260,42 +260,48 @@ module.exports.CreateWebRelay = function (parent, db, args, domain) {
260 // Setup opcode and payload
261 var op = 2, payload = data;
262 if (typeof data == 'string') { op = 1; payload = Buffer.from(data, 'binary'); } // Text frame
263 -
264 - // Select a random mask
265 - const mask = parent.parent.crypto.randomBytes(4)
266 -
267 - // Setup header and mask
268 - var header = null;
269 - if (data.length < 126) {
270 - header = Buffer.alloc(6); // Header (2) + Mask (4)
271 - header[0] = 0x80 + op; // FIN + OP
272 - header[1] = 0x80 + data.length; // Mask + Length
273 - mask.copy(header, 2, 0, 4); // Copy the mask
274 - } else if (data.length <= 0xFFFF) {
275 - header = Buffer.alloc(8); // Header (2) + Length (2) + Mask (4)
276 - header[0] = 0x80 + op; // FIN + OP
277 - header[1] = 0x80 + 126; // Mask + 126
278 - header.writeInt16BE(data.length, 2); // Payload size
279 - mask.copy(header, 4, 0, 4); // Copy the mask
280 - } else {
281 - header = Buffer.alloc(14); // Header (2) + Length (8) + Mask (4)
282 - header[0] = 0x80 + op; // FIN + OP
283 - header[1] = 0x80 + 127; // Mask + 127
284 - header.writeInt32BE(data.length, 6); // Payload size
285 - mask.copy(header, 10, 0, 4); // Copy the mask
286 - }
287 -
288 - // Mask the payload
289 - for (var i = 0; i < payload.length; i++) { payload[i] = (payload[i] ^ mask[i % 4]); }
290 -
291 - // Send the frame
292 - //console.log(obj.tunnelId, '-->', op, payload.length);
293 - send(Buffer.concat([header, payload]));
263 + sendWebSocketFrameToDevice(op, payload);
264 });
265 +
266 + obj.ws.on('ping', function (data) { sendWebSocketFrameToDevice(9, data); }); // Forward ping frame
267 + obj.ws.on('pong', function (data) { sendWebSocketFrameToDevice(10, data); }); // Forward pong frame
268 obj.ws.on('close', function () { obj.close(); });
269 obj.ws.on('error', function (err) { obj.close(); });
270 }
271
272 + function sendWebSocketFrameToDevice(op, payload) {
273 + // Select a random mask
274 + const mask = parent.parent.crypto.randomBytes(4)
275 +
276 + // Setup header and mask
277 + var header = null;
278 + if (payload.length < 126) {
279 + header = Buffer.alloc(6); // Header (2) + Mask (4)
280 + header[0] = 0x80 + op; // FIN + OP
281 + header[1] = 0x80 + payload.length; // Mask + Length
282 + mask.copy(header, 2, 0, 4); // Copy the mask
283 + } else if (payload.length <= 0xFFFF) {
284 + header = Buffer.alloc(8); // Header (2) + Length (2) + Mask (4)
285 + header[0] = 0x80 + op; // FIN + OP
286 + header[1] = 0x80 + 126; // Mask + 126
287 + header.writeInt16BE(payload.length, 2); // Payload size
288 + mask.copy(header, 4, 0, 4); // Copy the mask
289 + } else {
290 + header = Buffer.alloc(14); // Header (2) + Length (8) + Mask (4)
291 + header[0] = 0x80 + op; // FIN + OP
292 + header[1] = 0x80 + 127; // Mask + 127
293 + header.writeInt32BE(payload.length, 6); // Payload size
294 + mask.copy(header, 10, 0, 4); // Copy the mask
295 + }
296 +
297 + // Mask the payload
298 + for (var i = 0; i < payload.length; i++) { payload[i] = (payload[i] ^ mask[i % 4]); }
299 +
300 + // Send the frame
301 + //console.log(obj.tunnelId, '-->', op, payload.length);
302 + send(Buffer.concat([header, payload]));
303 + }
304 +
305 // Disconnect
306 obj.close = function (arg) {
307 if (obj.closed == true) return;
@@ -541,34 +547,12 @@ module.exports.CreateWebRelay = function (parent, db, args, domain) {
547
548 // Perform operation
549 switch (op) {
544 - case 0: { // Continue frame
545 - //console.log('continue', payload.length);
546 - break;
547 - }
548 - case 1: { // Text frame
549 - //console.log('text', payload.length);
550 - try { obj.ws.send(payload.toString('binary')); } catch (ex) { }
551 - break;
552 - }
553 - case 2: { // Binary frame
554 - //console.log('binary', payload.length);
555 - try { obj.ws.send(payload); } catch (ex) { }
556 - break;
557 - }
558 - case 8: { // Connection close
559 - obj.close();
560 - return;
561 - }
562 - case 9: { // Ping frame
563 - //console.log('ping', payload.length);
564 - // TODO
565 - break;
566 - }
567 - case 10: { // Pong frame
568 - //console.log('pong', payload.length);
569 - // TODO
570 - break;
571 - }
550 + case 0: { break; } // Continue frame (TODO)
551 + case 1: { try { obj.ws.send(payload.toString('binary')); } catch (ex) { } break; } // Text frame
552 + case 2: { try { obj.ws.send(payload); } catch (ex) { } break; } // Binary frame
553 + case 8: { obj.close(); return; } // Connection close
554 + case 9: { try { obj.ws.ping(payload); } catch (ex) { } break; } // Ping frame
555 + case 10: { try { obj.ws.pong(payload); } catch (ex) { } break; } // Pong frame
556 }
557 }
558 }