https://gitlab.synchro.net/main/sbbs/-/commit/fd038d9dd3cba68b4d95d026
Modified Files:
docs/v322_new.md src/sbbs3/zmodem.c
Log Message:
zmodem: escape CR unconditionally when ESCCTL is negotiated
A sender with ESCCTL negotiated escaped every control character
except carriage return, which it routed to a conditional rule --
escape only when the previous byte sent was '@' -- and otherwise put
on the wire raw. A receiver that requested ESCCTL discards unescaped
control characters by definition, so it discarded those CRs, every
subpacket carrying one failed its CRC, and the transfer never
advanced: the sender ZRPOSed to zero until it spent its error budget
and gave up. With random data that is any subpacket at all. A
download to any receiver requesting ESCCTL was therefore impossible,
not merely slow.
lrzsz settles what the rule should be. In zsendline_init() (zm.c) the
table entry for 015/0215 is:
if (Zctlesc) tab = 1; /* always escape */
else if (!turbo_escape) tab = 2; /* the conditional '@' rule */
else tab = 0;
So the conditional rule is the NON-ESCCTL case, and ESCCTL escapes CR
like any other control character. This engine had the two swapped:
the conditional rule was reachable only with ESCCTL on, where it is
wrong, and never reachable with ESCCTL off, where it would be right.
Since it could not fire correctly in either state, drop it -- fold CR
into the control class and let the escape mask decide, which is
exactly lrzsz's behaviour for both states. The '@' rule can be added
properly later if wanted; the last_sent field it read is left in
place, being public.
The unescaped path is provably untouched: without ESCCTL neither the
old CR class nor the new one is in the active mask, so CR is a normal
byte either way. Confirmed byte-for-byte -- a 4 MB send still puts
4,311,816 bytes on the wire, 256 MB still receives at 484.99 MB/s and
sends at 206.24 MB/s, and the 3e-6 corruption gate still passes 5 of 5 receiving and 3 of 3 sending.
With the fix, ESCCTL interoperates in every direction: sexyz to lrz,
lrz to sexyz, and sexyz to itself all verify byte-identical. The
strongest evidence it is now correct rather than merely accepted is
the wire size -- sexyz puts 5,245,034 bytes on the wire where lsz puts 5,245,030 for the same file, so the two escape the same byte set.
Random data exercises every one of the 256 values, so nothing is
escaped by luck.
Under injected errors the escaped path is a little less robust than
the plain one: 4 of 5 at 3e-6 and 5 of 5 at 1e-6, against 5 of 5 for
the unescaped path at 3e-6. It carries 25 % more bytes and so takes
25 % more corruption at a given per-byte rate. No lrzsz baseline
exists for that comparison -- lsz -e to lrz -e does not survive the
harness's error injection at all, dying inside 0.04 s on every
attempt.
---
þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net