• src/ssh/README.md deucessh-conn.h ssh-conn.c ssh-internal.h src/ssh/te

    From Deuc¿@VERT to Git commit to main/sbbs/master on Tue May 5 08:02:38 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/facce791262fb43be1302556
    Modified Files:
    src/ssh/README.md deucessh-conn.h ssh-conn.c ssh-internal.h src/ssh/test/test_chan.c test_conn.c
    Log Message:
    DeuceSSH: rx-window enforcement + DSSH_PARAM_ACCEPT_EARLY_DATA

    Two related changes that together let DeuceSSH clients tolerate
    servers that begin sending CHANNEL_DATA before the terminal-request
    response, while otherwise tightening rx-side window handling.

    1. Universal rx-window truncation.

    handle_channel_data and handle_channel_extended_data now clip the
    inbound payload length to the locally-advertised window before
    invoking the ZC callback. A peer that ignores the window can no
    longer drive the library into unbounded buffering -- bytes past
    the window are silently dropped and never reach the bytebuf. The
    ZC callback contract is widened (and the typedef comment updated)
    to allow len == 0 or len < wire-payload; in-tree consumers
    (stream_zc_cb) already handled both safely.

    2. Per-channel DSSH_PARAM_ACCEPT_EARLY_DATA opt-in (flag 0x02 + new
    dssh_chan_params_set_accept_early_data setter).

    Cryptlib-based servers that don't wait for
    CRYPT_SESSINFO_SSH_CHANNEL_TYPE to be set before sending (such as
    Mystic BBS) emit banner data immediately after CHANNEL_OPEN_
    CONFIRMATION, while local_window is still 0. The flag asks the
    library to deliver pre-setup data anyway: while
    ch->setup_complete is still false, handle_channel_data /
    handle_channel_extended_data take a bypass branch that delivers
    the full dlen, leaves local_window untouched (no credit returned
    to the peer), and skips the ZC WINDOW_ADJUST. Once
    send_window_adjust at the end of dssh_chan_open / dssh_chan_zc_
    open succeeds, setup_complete latches true and the bypass
    disengages permanently.

    Type-locked at chan_open entry: the flag is rejected (NULL
    return) for DSSH_CHAN_SUBSYSTEM since subsystem channels use a
    message queue and have no coherent destination for pre-setup
    bytes.

    init_channel_buffers is hoisted unconditionally to before
    open_session_channel so the bytebufs exist when the first DATA
    arrives. Universal rx-truncation makes this a behavioural no-op
    for non-flagged channels (early data is clipped to len == 0
    before stream_zc_cb runs, so the freshly-allocated bytebuf is
    harmless). The flag is the only thing that punches a hole in
    that, and only while !setup_complete.

    7 new tests:

    - params_set_accept_early_data: setter toggles the bit, NULL guard
    - rx_truncation/default: closed window drops everything
    - rx_truncation/clips_to_window: dlen > local_window clipped to 4
    - rx_bypass/pre_setup: flagged + !setup_complete delivers full
    payload, local_window untouched
    - rx_bypass/disengages_post_setup: flagged + setup_complete drops
    again
    - early_data/type_lock_subsystem: dssh_chan_open returns NULL for
    SUBSYSTEM + ACCEPT_EARLY_DATA

    OpenSSL: 3410/3410 pass. Botan: 3411/3411 pass.

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

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