• src/sbbs3/zmodem.c zmodem.h

    From Rob Swindell (on Debian Linux)@VERT to Git commit to main/sbbs/master on Thu Jul 23 23:03:46 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/ca0765f3dd2305342f23d6b5
    Modified Files:
    src/sbbs3/zmodem.c zmodem.h
    Log Message:
    zmodem: fix windowed transfers hanging at 2 GiB (GitLab #1196)

    The ZMODEM transmit-window / ACK file positions were held in signed
    int32_t (ack_file_pos, crc_request, and the pos parameters of zmodem_send_pos_header / zmodem_send_ack). Once a file offset crossed
    2^31, these went negative, so the sender's window arithmetic (current_window_size = current_file_pos - ack_file_pos) computed a bogus
    ~4 GiB window that permanently exceeded any configured max window; the
    sender then throttled forever waiting for a ZACK it could never accept,
    and the transfer stalled the instant it passed 2 GiB. The ZACK range
    check in zmodem_handle_zack (which already takes uint32_t) likewise
    rejected valid acknowledgements above 2 GiB.

    Widen those positions to uint32_t, matching rxd_header_pos, which is
    already unsigned and correct through the protocol's 4 GiB wire limit.
    The byte-packing in zmodem_send_pos_header is unchanged (it masks each
    byte), so the on-wire encoding is identical; only the local arithmetic
    and comparisons are corrected. The receive/data path was already
    64-bit-clean (int64_t offsets, fseeko/ftello), so this is purely the
    window/ACK accounting.

    Positions >= 4 GiB remain limited by the 32-bit ZMODEM header position
    field, which is inherent to the protocol and out of scope here.

    This code is shared with SyncTERM, so its windowed uploads past 2 GiB
    are fixed by the same change.

    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Deucе@VERT to Git commit to main/sbbs/master on Sun Aug 23 13:41:08 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/ea25028dae163a6c982d08e5
    Modified Files:
    src/sbbs3/zmodem.c zmodem.h
    Log Message:
    Relicense ZModem code under a 2-clause BSD licnese.

    ---
    ï¿­ Synchronet ï¿­ Vertrauen ï¿­ Home of Synchronet ï¿­ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Debian Linux)@VERT to Git commit to main/sbbs/master on Mon Aug 24 20:32:03 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/81e5e3a3a6f008a7e11ff590
    Modified Files:
    src/sbbs3/zmodem.c zmodem.h
    Log Message:
    zmodem: derive the receive fast path from one table of plain bytes

    zmodem_rx() decided per byte, through a switch, whether the byte it had just read needed handling: ZDLE introduces an escape, the four XON/XOFF forms are dropped as flow control, and when the peer negotiated ZF0_ESCCTL a control character that arrived unescaped is dropped too. Everything else is returned verbatim, which on ordinary data is the overwhelming majority of bytes.

    Precompute that predicate into a 256-entry table and test it before the
    switch. The switch is then reached only by bytes that genuinely need it, and the common case is one indexed load. A 256 MiB localhost receive goes from 126.9 to 153.0 MB/s and from 2.11 to 1.74 CPU-seconds.

    The table is built in zmodem_init() and rebuilt where escape_ctrl_chars is assigned from the peer's ZRINIT, which is the only place it changes. Building it eagerly rather than on demand avoids needing a sentinel to distinguish
    "not built yet" from "built for escape_ctrl_chars == FALSE"; zmodem_init() memsets the struct, so any such sentinel would have to be assigned anyway and is easy to forget.

    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net