╭ changed ─────────────────╮ │src/serve_ssh_protocol.zig│ ╰──────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────╮ │414 if (self.closed or self.remote_closed) return true; │ │415 self.conn.read_timeout = probe_timeout.toDeadline(self.conn.io); │ │416 defer self.conn.read_timeout = .none; │ │417 - self.processOneBackgroundPacket() catch |err| return err != error.Timeout;│ │417 + self.processOneBackgroundPacket() catch |err| switch (err) { │ │418 + error.Timeout => return false, │ │419 + else => { │ │420 + // the session reports this cause rather than the cancellation │ │421 + self.read_err = err; │ │422 + return true; │ │423 + }, │ │424 + }; │ │425 return self.remote_closed; │ │426 } │ │427 │ ╰───────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────╮ │1932 }, │ │1933 │ │1934 SSH_MSG_CHANNEL_REQUEST => { │ │1928 - const ch = if (channel) |*c| c else continue; │ │1935 + const ch = if (channel) |*c| c else return error.UnknownChannel; │ │1936 const start_request = try handleChannelRequest(conn, ch, packet); │ │1937 // the request borrows from the packet, which outlives the session│ │1938 if (start_request) |request| { │ ╰────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────╮ │1945 }; │ │1946 defer sess.deinit(); │ │1947 handler.handleSession(&sess, request) catch |session_err| {│ │1948 + // a failed read leaves the protocol unusable, so the │ │1949 + // disconnect names it instead of a clean close │ │1950 + if (sess.read_err) |read_err| return read_err; │ │1951 // try to send a non-zero exit status before closing │ │1952 sess.exit(1) catch {}; │ │1953 return sess.underlyingError(session_err); │ ╰─────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────╮ │1958 }, │ │1959 │ │1960 SSH_MSG_CHANNEL_WINDOW_ADJUST => { │ │1951 - const ch = if (channel) |*c| c else continue; │ │1961 + const ch = if (channel) |*c| c else return error.UnknownChannel;│ │1962 try handleWindowAdjust(ch, packet); │ │1963 }, │ │1964 │ ╰──────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────╮ │1965 SSH_MSG_CHANNEL_DATA, SSH_MSG_CHANNEL_EXTENDED_DATA => { │ │1956 - const ch = if (channel) |*c| c else continue; │ │1966 + const ch = if (channel) |*c| c else return error.UnknownChannel;│ │1967 // before shell/exec the client shouldn't be sending data, but │ │1968 // some clients are chatty; discard and refill window. nothing │ │1969 // is buffered yet, so the whole window is available to credit. │ ╰──────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────╮ │1972 }, │ │1973 │ │1974 SSH_MSG_CHANNEL_EOF => { │ │1965 - if (channel) |*c| try parseChannelId(packet, c.local_id); │ │1975 + const ch = if (channel) |*c| c else return error.UnknownChannel;│ │1976 + try parseChannelId(packet, ch.local_id); │ │1977 }, │ │1978 │ │1979 SSH_MSG_CHANNEL_CLOSE => { │ ╰──────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────╮ │1969 - if (channel) |*c| { │ │1970 - try parseChannelId(packet, c.local_id); │ │1971 - try sendChannelMessage(conn, c, SSH_MSG_CHANNEL_CLOSE); │ │1972 - drainConnection(conn); │ │1973 - } │ │1980 + const ch = if (channel) |*c| c else return error.UnknownChannel;│ │1981 + try parseChannelId(packet, ch.local_id); │ │1982 + try sendChannelMessage(conn, ch, SSH_MSG_CHANNEL_CLOSE); │ │1983 + drainConnection(conn); │ │1984 return; │ │1985 }, │ │1986 │ ╰──────────────────────────────────────────────────────────────────────────────────────╯