fix: s3parser now looks for bare ETX, not DLE+ETX.

This commit is contained in:
serversdwn
2026-03-31 00:10:13 -04:00
parent 8e985154a7
commit 88adcbcb81

View File

@@ -155,8 +155,16 @@ class S3FrameParser:
State machine:
IDLE — scanning for DLE (0x10)
SEEN_DLE — saw DLE, waiting for STX (0x02) to start a frame
IN_FRAME — collecting de-stuffed payload bytes
IN_FRAME_DLE — inside frame, saw DLE; ETX ends frame, DLE continues stuffing
IN_FRAME — collecting de-stuffed payload bytes; bare ETX ends frame
IN_FRAME_DLE — inside frame, saw DLE; DLE continues stuffing;
DLE+ETX is treated as literal data (NOT a frame end),
which lets inner-frame terminators pass through intact
Wire format confirmed from captures:
[DLE=0x10] [STX=0x02] [stuffed payload+chk] [bare ETX=0x03]
The ETX is NOT preceded by a DLE on the wire. DLE+ETX sequences that
appear inside the payload are inner-frame terminators and must be
treated as literal data.
ACK (0x41) bytes and arbitrary non-DLE bytes in IDLE state are silently
discarded (covers device boot string "Operating System" and keepalive ACKs).
@@ -210,6 +218,11 @@ class S3FrameParser:
elif self._state == self._IN_FRAME:
if b == DLE:
self._state = self._IN_FRAME_DLE
elif b == ETX:
# Bare ETX = real frame terminator (confirmed from captures)
frame = self._finalise()
self._state = self._IDLE
return frame
else:
self._body.append(b)
@@ -219,10 +232,11 @@ class S3FrameParser:
self._body.append(DLE)
self._state = self._IN_FRAME
elif b == ETX:
# End of frame
frame = self._finalise()
self._state = self._IDLE
return frame
# DLE+ETX inside a frame is an inner-frame terminator, NOT
# the outer frame end. Treat as literal data and continue.
self._body.append(DLE)
self._body.append(ETX)
self._state = self._IN_FRAME
else:
# Unexpected DLE + byte — treat both as literal data and continue
self._body.append(DLE)