╭ changed ─────────────────╮ │src/serve_ssh_protocol.zig│ ╰──────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────╮ │350 try self.maybeRefillRecvWindowForBufferedInput(); │ │351 }, │ │352 SSH_MSG_CHANNEL_EXTENDED_DATA => { │ │353 - try discardChannelData(conn, self.channel, packet, true); │ │353 + // discarded, but the window is still credited against what the │ │354 + // incoming buffer can take, since CHANNEL_DATA may be sitting │ │355 + // in it unread │ │356 + try discardChannelData(self.channel, packet, true); │ │357 + try self.maybeRefillRecvWindowForBufferedInput(); │ │358 }, │ │359 SSH_MSG_CHANNEL_WINDOW_ADJUST => try handleWindowAdjust(self.channel, packet),│ │360 SSH_MSG_CHANNEL_REQUEST => { │ ╰───────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1771 SSH_MSG_CHANNEL_DATA, SSH_MSG_CHANNEL_EXTENDED_DATA => { │ │1772 const ch = if (channel) |*c| c else continue; │ │1773 // before shell/exec the client shouldn't be sending data, but │ │1770 - // some clients are chatty; discard and refill window. │ │1771 - try discardChannelData(conn, ch, packet, msg_type == SSH_MSG_CHANNEL_EXTENDED_DATA);│ │1774 + // some clients are chatty; discard and refill window. nothing │ │1775 + // is buffered yet, so the whole window is available to credit. │ │1776 + try discardChannelData(ch, packet, msg_type == SSH_MSG_CHANNEL_EXTENDED_DATA); │ │1777 + try maybeRefillRecvWindow(conn, ch, initial_recv_window); │ │1778 }, │ │1779 │ │1780 SSH_MSG_CHANNEL_EOF => { │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────╮ │1954 ch.remote_window +|= add; │ │1955 } │ │1956 │ │1951 -fn discardChannelData(conn: *Conn, ch: *Channel, packet: []const u8, extended: bool) !void {│ │1957 +// charge a payload's bytes against the receive window without keeping them. │ │1958 +// the caller credits the window back, choosing the budget that suits it. │ │1959 +fn discardChannelData(ch: *Channel, packet: []const u8, extended: bool) !void { │ │1960 // byte SSH_MSG_CHANNEL_DATA/EXTENDED_DATA │ │1961 // uint32 recipient_channel │ │1962 // [if extended: uint32 type_code] │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1966 │ │1967 if (data_len > ch.local_window) return error.WindowExceeded; │ │1968 ch.local_window -= data_len; │ │1961 - try maybeRefillRecvWindow(conn, ch, initial_recv_window); │ │1969 } │ │1970 │ │1971 pub fn parseChannelData(packet: []const u8, extended: bool, expected_channel: u32) ![]const u8 {│ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────╯