From d98523cddb5c558d2108f090c83eb2d2b7560c17 Mon Sep 17 00:00:00 2001 From: Matthew Wolter Date: Sat, 11 Apr 2026 18:33:20 -0700 Subject: [PATCH] =?UTF-8?q?G1:=20N08=20+=20F12=20+=20N10=20=E2=80=94=20cos?= =?UTF-8?q?metic=20cleanup=20in=20reader.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three small cleanup fixes bundled per proposal §4.1 commit order. N08 — CONTROL_DATA empty payload guard. The handler reads `payload = dbuf.read()` then immediately dereferences `payload[0]` without checking length. A zero-length payload (firmware truncation or garbled frame) raises IndexError. Pre-F06 the IndexError would escape; post-F06 it would log and skip the dispatch via the umbrella. Adding an explicit `if len(payload) == 0: return` after the read short-circuits the empty case before it touches `payload[0]`, with a debug log noting the empty payload. The `return` exits handle_rx cleanly without engaging the F06 umbrella's parse-error path, which is the correct behavior — an empty CONTROL_DATA frame is not a parse error, it's an unusable frame. F12 — print(res) leftover debug. The RAW_DATA handler had a stray `print(res)` polluting stdout. Replaced with `logger.debug(res)` to match the surrounding `logger.debug("Received raw data")` line. N10 — magic numbers 16 and 17. Two `elif packet_type_value == 16/17` branches hardcoded the integer values for CONTACT_MSG_RECV_V3 and CHANNEL_MSG_RECV_V3, both already declared in packets.py:94-95. Replaced with `PacketType.CONTACT_MSG_RECV_V3.value` and `PacketType.CHANNEL_MSG_RECV_V3.value` to eliminate drift risk if the enum is ever renumbered. Findings: N08 (Info), F12 (Info), N10 (Info) File: src/meshcore/reader.py --- src/meshcore/reader.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/meshcore/reader.py b/src/meshcore/reader.py index 6aa5131..ead8b0a 100644 --- a/src/meshcore/reader.py +++ b/src/meshcore/reader.py @@ -222,7 +222,7 @@ class MessageReader: await self.dispatcher.dispatch(Event(evt_type, res, attributes)) - elif packet_type_value == 16: # A reply to CMD_SYNC_NEXT_MESSAGE (ver >= 3) + elif packet_type_value == PacketType.CONTACT_MSG_RECV_V3.value: # A reply to CMD_SYNC_NEXT_MESSAGE (ver >= 3) res = {} res["type"] = "PRIV" res["SNR"] = int.from_bytes(dbuf.read(1), byteorder="little", signed=True) / 4 @@ -287,7 +287,7 @@ class MessageReader: Event(EventType.CHANNEL_MSG_RECV, res, attributes) ) - elif packet_type_value == 17: # A reply to CMD_SYNC_NEXT_MESSAGE (ver >= 3) + elif packet_type_value == PacketType.CHANNEL_MSG_RECV_V3.value: # A reply to CMD_SYNC_NEXT_MESSAGE (ver >= 3) res = {} res["type"] = "CHAN" res["SNR"] = int.from_bytes(dbuf.read(1), byteorder="little", signed=True) / 4 @@ -534,7 +534,7 @@ class MessageReader: res["RSSI"] = int.from_bytes(dbuf.read(1), byteorder="little", signed=True) res["payload"] = dbuf.read(4).hex() logger.debug("Received raw data") - print(res) + logger.debug(res) await self.dispatcher.dispatch(Event(EventType.RAW_DATA, res)) elif packet_type_value == PacketType.LOGIN_SUCCESS.value: @@ -895,6 +895,9 @@ class MessageReader: res["RSSI"] = int.from_bytes(dbuf.read(1), byteorder="little", signed=True) res["path_len"] = dbuf.read(1)[0] payload = dbuf.read() + if len(payload) == 0: + logger.debug("CONTROL_DATA frame has empty payload, skipping") + return payload_type = payload[0] res["payload_type"] = payload_type res["payload"] = payload