╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ─────────────────╮ │src/serve_ssh_protocol.zig│ ╰──────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────╮ │1264 ) ![fingerprint_len]u8 { │ │1265 // step 1: service request for ssh-userauth → service accept │ │1266 { │ │1229 - const req = try cs_cipher.readPacket(allocator, reader); │ │1267 + const req = try readNonIgnoredPacket(cs_cipher, allocator, reader); │ │1268 defer allocator.free(req); │ │1269 if (req.len >= 1 and req[0] == SSH_MSG_KEXINIT) { │ │1270 try sendDisconnect(io, allocator, sc_cipher, writer, "rekey is not supported");│ ╰─────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────╮ │1288 // signature verifies against the offered pubkey. │ │1289 var auth_attempts: u32 = 0; │ │1290 while (true) { │ │1253 - const req = try cs_cipher.readPacket(allocator, reader); │ │1291 + const req = try readNonIgnoredPacket(cs_cipher, allocator, reader); │ │1292 defer allocator.free(req); │ │1293 if (req.len >= 1 and req[0] == SSH_MSG_KEXINIT) { │ │1294 try sendDisconnect(io, allocator, sc_cipher, writer, "rekey is not supported");│ ╰─────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────╮ │1325 } │ │1326 │ │1327 if (!has_signature) { │ │1328 + // probes count as attempts so a client can't loop here forever │ │1329 + auth_attempts += 1; │ │1330 + if (auth_attempts >= max_auth_attempts) return error.TooManyAuthAttempts;│ │1331 // probe — tell the client this key is acceptable so it'll send │ │1332 // the signed version next. │ │1333 var pk_ok: std.ArrayList(u8) = .empty; │ ╰───────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────╮ │1474 }; │ │1475 │ │1476 pub const PtySize = struct { width_cells: u16, height_cells: u16 }; │ │1477 + │ │1478 +// take the uint32 width/height cell counts of a pty-req or window-change,│ │1479 +// clamped to u16 │ │1480 +fn takePtySize(r: *std.Io.Reader) !PtySize { │ │1481 + const width = try r.takeInt(u32, .big); │ │1482 + const height = try r.takeInt(u32, .big); │ │1483 + return .{ │ │1484 + .width_cells = @intCast(@min(width, 0xFFFF)), │ │1485 + .height_cells = @intCast(@min(height, 0xFFFF)), │ │1486 + }; │ │1487 +} │ │1488 │ │1489 fn runChannelLayer( │ │1490 io: std.Io, │ ╰────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1624 return null; │ │1625 } │ │1626 │ │1627 + // a zero max_packet would leave writeChannel looping without progress │ │1628 + if (max_packet == 0) { │ │1629 + try sendChannelOpenFailure(io, allocator, sc_cipher, writer, packet, SSH_OPEN_RESOURCE_SHORTAGE, "max packet size must be non-zero");│ │1630 + return null; │ │1631 + } │ │1632 + │ │1633 const local_id: u32 = 0; // single-channel connection — id is always 0 from our side │ │1634 var reply: std.ArrayList(u8) = .empty; │ │1635 defer reply.deinit(allocator); │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1704 // uint32 height px, string modes │ │1705 const term = try takeStringField(allocator, &r, 64); │ │1706 defer allocator.free(term); │ │1649 - const width = try r.takeInt(u32, .big); │ │1650 - const height = try r.takeInt(u32, .big); │ │1651 - ch.pty = .{ .width_cells = @intCast(@min(width, 0xFFFF)), .height_cells = @intCast(@min(height, 0xFFFF)) };│ │1707 + ch.pty = try takePtySize(&r); │ │1708 try replyChannelRequest(io, allocator, sc_cipher, writer, ch, want_reply, true); │ │1709 return null; │ │1710 } │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1717 │ │1718 if (std.mem.eql(u8, req_type, "window-change")) { │ │1719 // uint32 width cells, uint32 height rows, uint32 width px, uint32 height px │ │1664 - const width = try r.takeInt(u32, .big); │ │1665 - const height = try r.takeInt(u32, .big); │ │1666 - if (ch.pty != null) { │ │1667 - ch.pty = .{ .width_cells = @intCast(@min(width, 0xFFFF)), .height_cells = @intCast(@min(height, 0xFFFF)) };│ │1668 - } │ │1720 + const sz = try takePtySize(&r); │ │1721 + if (ch.pty != null) ch.pty = sz; │ │1722 // spec: window-change MUST NOT request a reply, but accept either │ │1723 try replyChannelRequest(io, allocator, sc_cipher, writer, ch, want_reply, true); │ │1724 return null; │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯