╭ changed ─────────────────╮ │src/serve_ssh_protocol.zig│ ╰──────────────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭──────────────────────────────────────────────╮ │54 │ │55 // SSH message type bytes (RFC 4250 §4.1) │ │56 pub const SSH_MSG_DISCONNECT: u8 = 1; │ │57 +pub const SSH_MSG_IGNORE: u8 = 2; │ │58 +pub const SSH_MSG_UNIMPLEMENTED: u8 = 3; │ │59 +pub const SSH_MSG_DEBUG: u8 = 4; │ │60 pub const SSH_MSG_SERVICE_REQUEST: u8 = 5;│ │61 pub const SSH_MSG_SERVICE_ACCEPT: u8 = 6; │ │62 pub const SSH_MSG_KEXINIT: u8 = 20; │ ╰──────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────╮ │190 // until the consumer drains them via SessionReader. │ │191 incoming_buffer: std.ArrayList(u8) = .empty, │ │192 incoming_eof: bool = false, │ │193 + // resize seen by the background pump, returned by the next nextEvent call│ │194 + pending_resize: ?PtySize = null, │ │195 │ │196 pub fn deinit(self: *SessionCtx) void { │ │197 self.incoming_buffer.deinit(self.allocator); │ ╰───────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │202 /// handled silently. used by pumped-event consumers (e.g. the TUI path); │ │203 /// do not mix with SessionReader, which has its own packet pump. │ │204 pub fn nextEvent(self: *SessionCtx) !Event { │ │205 + if (self.pending_resize) |sz| { │ │206 + self.pending_resize = null; │ │207 + return .{ .resize = sz }; │ │208 + } │ │209 while (true) { │ │210 const packet = self.cs_cipher.readPacket(self.allocator, self.reader) catch |err| switch (err) {│ │211 error.EndOfStream => { │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────╮ │214 }, │ │215 else => return err, │ │216 }; │ │208 - errdefer self.allocator.free(packet); │ │217 + defer self.allocator.free(packet); │ │218 if (packet.len < 1) return error.EmptyPacket;│ │219 │ │220 switch (packet[0]) { │ ╰──────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │225 self.channel.local_window -= data_len; │ │226 try maybeRefillRecvWindow(self.io, self.allocator, self.sc_cipher, self.writer, self.channel);│ │227 // hand the payload bytes to the caller (caller owns) │ │219 - const owned_payload = try self.allocator.dupe(u8, payload); │ │220 - self.allocator.free(packet); │ │221 - return .{ .data = owned_payload }; │ │228 + return .{ .data = try self.allocator.dupe(u8, payload) }; │ │229 }, │ │230 SSH_MSG_CHANNEL_EXTENDED_DATA => { │ │231 // stderr from the client is unusual; discard, but adjust window │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │232 try discardChannelData(self.io, self.allocator, self.sc_cipher, self.writer, self.channel, packet, true);│ │226 - self.allocator.free(packet); │ │227 - }, │ │228 - SSH_MSG_CHANNEL_WINDOW_ADJUST => { │ │229 - try handleWindowAdjust(self.channel, packet); │ │230 - self.allocator.free(packet); │ │233 }, │ │234 + SSH_MSG_CHANNEL_WINDOW_ADJUST => try handleWindowAdjust(self.channel, packet), │ │235 SSH_MSG_KEXINIT => { │ │233 - defer self.allocator.free(packet); │ │236 try sendDisconnect(self.io, self.allocator, self.sc_cipher, self.writer, "rekey is not supported"); │ │237 return error.RekeyUnsupported; │ │238 }, │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────╮ │239 SSH_MSG_CHANNEL_REQUEST => { │ │238 - defer self.allocator.free(packet); │ │240 var r = std.Io.Reader.fixed(packet[1..]); │ │241 const recipient_channel = try r.takeInt(u32, .big); │ │242 if (recipient_channel != self.channel.local_id) return error.UnknownChannel;│ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │244 defer self.allocator.free(req_type); │ │245 const want_reply = (try r.takeByte()) != 0; │ │246 if (std.mem.eql(u8, req_type, "window-change")) { │ │246 - const w = try r.takeInt(u32, .big); │ │247 - const h = try r.takeInt(u32, .big); │ │248 - const sz = PtySize{ │ │249 - .width_cells = @intCast(@min(w, 0xFFFF)), │ │250 - .height_cells = @intCast(@min(h, 0xFFFF)), │ │251 - }; │ │247 + const sz = try takePtySize(&r); │ │248 if (self.channel.pty != null) self.channel.pty = sz; │ │249 try replyChannelRequest(self.io, self.allocator, self.sc_cipher, self.writer, self.channel, want_reply, true);│ │250 return .{ .resize = sz }; │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────╮ │254 }, │ │255 SSH_MSG_CHANNEL_EOF, SSH_MSG_CHANNEL_CLOSE => { │ │256 try parseChannelId(packet, self.channel.local_id);│ │261 - self.allocator.free(packet); │ │257 self.closed = true; │ │258 return .close; │ │259 }, │ ╰───────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │265 - SSH_MSG_GLOBAL_REQUEST => { │ │266 - try handleGlobalRequest(self.io, self.allocator, self.sc_cipher, self.writer, packet); │ │267 - self.allocator.free(packet); │ │268 - }, │ │269 - else => self.allocator.free(packet), │ │260 + SSH_MSG_GLOBAL_REQUEST => try handleGlobalRequest(self.io, self.allocator, self.sc_cipher, self.writer, packet),│ │261 + else => {}, │ │262 } │ │263 } │ │264 } │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯