╭ changed ─────────────────╮ │src/serve_ssh_protocol.zig│ ╰──────────────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭──────────╮ │← previous│ ╰──────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │611 const written = try w.write(chunk); │ │612 │ │613 self.sess.consumeIncoming(written); │ │590 - self.sess.maybeRefillRecvWindowForBufferedInput() catch return error.ReadFailed; │ │614 + self.sess.maybeRefillRecvWindowForBufferedInput() catch |err| return self.sess.failRead(err);│ │615 │ │616 return written; │ │617 } │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────╮ │622 // wait until some input is buffered (or EOF) │ │623 while (self.sess.incomingBytes().len == 0) { │ │624 if (self.sess.incoming_eof) return error.EndOfStream; │ │601 - self.sess.processOneBackgroundPacket() catch return error.ReadFailed; │ │625 + self.sess.processOneBackgroundPacket() catch |err| return self.sess.failRead(err);│ │626 } │ │627 │ │628 const src = self.sess.incomingBytes(); │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │637 @memcpy(dest[0..copy], src[0..copy]); │ │638 r.end += copy; │ │639 self.sess.consumeIncoming(copy); │ │616 - self.sess.maybeRefillRecvWindowForBufferedInput() catch return error.ReadFailed; │ │640 + self.sess.maybeRefillRecvWindowForBufferedInput() catch |err| return self.sess.failRead(err);│ │641 return 0; │ │642 } │ │643 │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │645 const copy = @min(dest.len, src.len); │ │646 @memcpy(dest[0..copy], src[0..copy]); │ │647 self.sess.consumeIncoming(copy); │ │624 - self.sess.maybeRefillRecvWindowForBufferedInput() catch return error.ReadFailed; │ │648 + self.sess.maybeRefillRecvWindowForBufferedInput() catch |err| return self.sess.failRead(err);│ │649 return copy; │ │650 } │ │651 }; │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────╮ │702 }; │ │703 } │ │704 │ │681 -/// tell the peer why we're hanging up (RFC 4253 §11.1). best effort — a peer │ │682 -/// that already went away has nothing left to read it. │ │705 +/// tell the peer why we're hanging up (RFC 4253 §11.1), best effort. protocol │ │706 +/// errors are named for whoever is debugging the client. │ │707 fn disconnectOnError(conn: *Conn, err: anyerror) void { │ │684 - if (err == error.EndOfStream) return; // peer hung up first │ │685 - disconnect(conn, @errorName(err)) catch {}; │ │708 + if (conn.drained) return; │ │709 + const reason: u32, const description: []const u8 = switch (err) { │ │710 + // the peer hung up first, or the transport is broken │ │711 + error.EndOfStream, error.ReadFailed, error.WriteFailed => return, │ │712 + error.MacVerificationFailed => .{ SSH_DISCONNECT_MAC_ERROR, @errorName(err) }, │ │713 + error.OutOfMemory, error.Canceled => .{ SSH_DISCONNECT_BY_APPLICATION, "server error" },│ │714 + else => .{ SSH_DISCONNECT_PROTOCOL_ERROR, @errorName(err) }, │ │715 + }; │ │716 + │ │717 + disconnect(conn, reason, description) catch {}; │ │718 } │ │719 │ │688 -fn disconnect(conn: *Conn, description: []const u8) !void { │ │720 +fn disconnect(conn: *Conn, reason: u32, description: []const u8) !void { │ │721 var buf: std.ArrayList(u8) = .empty; │ │722 defer buf.deinit(conn.allocator); │ │723 try buf.append(conn.allocator, SSH_MSG_DISCONNECT); │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────╮ │692 - try writeU32(&buf, conn.allocator, SSH_DISCONNECT_PROTOCOL_ERROR);│ │724 + try writeU32(&buf, conn.allocator, reason); │ │725 try writeStringField(&buf, conn.allocator, description); │ │726 try writeStringField(&buf, conn.allocator, ""); // language tag │ │727 try conn.writePacket(buf.items); │ ╰───────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────╮ │1843 handler.handleSession(&sess, request) catch |session_err| {│ │1844 // try to send a non-zero exit status before closing │ │1845 sess.exit(1) catch {}; │ │1814 - return session_err; │ │1846 + return sess.underlyingError(session_err); │ │1847 }; │ │1848 if (!sess.closed) try sess.exit(0); │ │1849 return; │ ╰─────────────────────────────────────────────────────────────────────────────────────╯