From a62f8efaad5271266c4e263959b2fd9069efa9c9 Mon Sep 17 00:00:00 2001 From: InDate Date: Tue, 20 Oct 2026 08:24:05 +0800 Subject: [PATCH] Take the packet end from the length field, not a suffix search parsePacket located the frame end with buffer.indexOf('0000AA55'), then '00009966', searched from byte 0. A sequence number of 0x0000AA55, or 0x00009966 on protocol 3.5, holds those bytes at offset 4 or 6, so the search returns the sequence field, the buffer is cut to 8 or 10 bytes, and the next readUInt32BE throws ERR_OUT_OF_RANGE ("must be >= 0 and <= 6. Received 8" on 3.5). Every device whose counter passes those values drops that frame. The frame length now comes from the header: 16 + length for 55AA frames, 22 + length for 6699 frames, matching what the encoders write. Bytes past it go to leftover as before; a buffer shorter than it throws "Packet missing payload". Tests cover 3.1, 3.3, 3.4 and 3.5 with both sequence numbers, two packets back to back. Five of the eight fail on master. Claude-Session: https://claude.ai/code/session_01Mi7yrmdxpownpnTsMGLHJW --- lib/message-parser.js | 17 +++++++++++------ test/parser.js | 21 +++++++++++++++++++++ 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/lib/message-parser.js b/lib/message-parser.js index 414ad1e..3f69061 100644 --- a/lib/message-parser.js +++ b/lib/message-parser.js @@ -118,14 +118,19 @@ class MessageParser { // Check for extra data let leftover = false; - let suffixLocation = buffer.indexOf('0000AA55', 0, 'hex'); - if (suffixLocation === -1) {// Couldn't find 0000AA55 during parse - suffixLocation = buffer.indexOf('00009966', 0, 'hex'); + // Frame end comes from the length field: a search for the suffix bytes + // also matches a sequence number or ciphertext holding those bytes + const frameLength = prefix === 0x00006699 ? + buffer.readUInt32BE(14) + 22 : // Header (18) + length + suffix (4) + buffer.readUInt32BE(12) + 16; // Header (16) + length + + if (buffer.length < frameLength) { + throw new TypeError(`Packet missing payload: frame has length ${frameLength}.`); } - if (suffixLocation !== buffer.length - 4) { - leftover = buffer.slice(suffixLocation + 4); - buffer = buffer.slice(0, suffixLocation + 4); + if (buffer.length > frameLength) { + leftover = buffer.slice(frameLength); + buffer = buffer.slice(0, frameLength); } // Check for suffix diff --git a/test/parser.js b/test/parser.js index 44dad1b..26bfcbe 100644 --- a/test/parser.js +++ b/test/parser.js @@ -209,3 +209,24 @@ test('decode message with two packets 2', t => { t.deepEqual(parsed.payload, payload); t.is(parsed.commandByte, 10); }); + +for (const version of ['3.1', '3.3', '3.4', '3.5']) { + for (const sequenceN of [0x0000AA55, 0x00009966]) { + test(`decode two packets with protocol ${version} and sequence number holding suffix bytes 0x${sequenceN.toString(16)}`, t => { + const payload = {devId: '002004265ccf7fb1b659', dps: {1: true, 2: 0}}; + + const parser = new MessageParser({key: 'bbe88b3f4106d354', version}); + const baseline = parser.parse(parser.encode({data: payload, commandByte: CommandType.DP_QUERY, sequenceN: 1}))[0]; + const encoded = parser.encode({data: payload, commandByte: CommandType.DP_QUERY, sequenceN}); + + const parsed = parser.parse(Buffer.concat([encoded, encoded])); + + t.is(parsed.length, 2); + for (const packet of parsed) { + t.is(packet.sequenceN, sequenceN); + t.is(packet.commandByte, CommandType.DP_QUERY); + t.deepEqual(packet.payload, baseline.payload); + } + }); + } +}