https://gitlab.synchro.net/main/sbbs/-/commit/d3e4f8094d0255fafaf436f2
Modified Files:
src/sbbs3/zmodem.c
Log Message:
zmodem: don't drop the CR/LF that terminates a hex header when ESCCTL is on
zmodem_recv_hex_header() read the CR/LF ending a hex header through zmodem_rx(), which drops unescaped control characters once
escape_ctrl_chars is set. CR and LF are control characters, so with
ESCCTL negotiated the terminator was dropped, the header never
terminated, and the receiver timed out on it:
!Dropping unescaped ctrl char ... (x20)
!zmodem_recv_hex_header HEX header not terminated with LF: TIMEOUT
!zmodem_recv_header detected an INVALID HEADER
The data itself transfers, since data subpackets use binary headers; it
is the session teardown that fails. Measured on an 8 MB receive with EscapeCtrlChars=true: 100.17 seconds and exit -1, against 0.07 seconds
once fixed. The bytes are all there and verify byte-identical -- the
receiver simply cannot read the ZFIN and sits re-sending ZRINIT until it
gives up.
A hex header's terminator is framing, not data. A hex header is built
from printable characters plus CR/LF precisely so it survives a link
that mangles control characters, and every implementation sends that
terminator unescaped whether or not ESCCTL was negotiated -- so it must
not go through the ESCCTL filter at all. Read it with a small helper
that calls zmodem_recv_raw() directly and skips flow-control bytes the
way zmodem_rx() does.
This is long-standing, not a regression: a sexyz built from the commit
that moved the sexyz change log out of the packaging directory --
before the plain-byte table and before the bulk receive callback --
reproduces it identically, 100.14 seconds and the same 20 dropped
characters. It went unnoticed because EscapeCtrlChars defaults to
false and had no command-line equivalent to reach it with.
Verified unchanged on the normal path, where every hex header also goes
through the new helper: 256 MB receive 482.7 MB/s and send 206.3 MB/s,
the 3e-6 corruption gate 5 of 5 receiving and 3 of 3 sending, plus a
YMODEM round trip.
NOT fixed here, and separate: sending WITH ESCCTL is also broken --
a receiver that requests it (lrz -e) rejects sexyz's escaped data and
ZRPOSes until the error budget is spent. That path is driven by the
peer's ZRINIT rather than by any local option, reproduces on the same pre-change binary, and needs its own investigation.
---
þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net