╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ─────────────────╮ │src/serve_ssh_protocol.zig│ ╰──────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────╮ │1635 { │ │1636 const req = try readSessionPacket(conn); │ │1637 defer allocator.free(req); │ │1747 - if (req.len < 1 or req[0] != SSH_MSG_SERVICE_REQUEST) return error.UnexpectedMessage;│ │1638 + if (req[0] != SSH_MSG_SERVICE_REQUEST) return error.UnexpectedMessage; │ │1639 │ │1640 var req_reader = std.Io.Reader.fixed(req[1..]); │ │1641 const service = try takeString(&req_reader, 64); │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1654 while (true) { │ │1655 const req = try readSessionPacket(conn); │ │1656 defer allocator.free(req); │ │1766 - if (req.len < 1 or req[0] != SSH_MSG_USERAUTH_REQUEST) return error.UnexpectedMessage;│ │1657 + if (req[0] != SSH_MSG_USERAUTH_REQUEST) return error.UnexpectedMessage; │ │1658 │ │1659 // every request counts, probes included, so a client can't loop here │ │1660 // forever │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────╮ │1836 while (true) { │ │1837 const packet = try readSessionPacket(conn); │ │1838 defer allocator.free(packet); │ │1948 - if (packet.len < 1) return error.EmptyPacket;│ │1839 const msg_type = packet[0]; │ │1840 │ │1841 switch (msg_type) { │ ╰───────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1842 SSH_MSG_GLOBAL_REQUEST => try handleGlobalRequest(conn, packet), │ │1843 │ │1844 SSH_MSG_CHANNEL_OPEN => { │ │1955 - if (channel != null) { │ │1956 - try sendChannelOpenFailure(conn, packet, SSH_OPEN_RESOURCE_SHORTAGE, "only one channel per connection");│ │1957 - continue; │ │1958 - } │ │1959 - channel = try handleChannelOpen(conn, packet) orelse continue; │ │1845 + channel = try handleChannelOpen(conn, packet, channel != null) orelse continue; │ │1846 }, │ │1847 │ │1848 SSH_MSG_CHANNEL_REQUEST => { │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────╮ │1851 // the request borrows from the packet, which outlives the session │ │1852 if (start_request) |request| { │ │1853 // any key authenticates, so the deadline holds until here │ │1968 - if (conn.idle) |idle| idle.session_started.store(true, .release);│ │1854 + conn.idle.session_started.store(true, .release); │ │1855 var sess = SessionCtx{ │ │1856 .conn = conn, │ │1857 .channel = ch, │ ╰───────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────╮ │1918 /// parse a CHANNEL_OPEN. on a session channel, send CHANNEL_OPEN_CONFIRMATION │ │1919 /// and return the new Channel. on other types, send CHANNEL_OPEN_FAILURE and │ │1920 /// return null. │ │2035 -fn handleChannelOpen(conn: *Conn, packet: []const u8) !?Channel { │ │2036 - const allocator = conn.allocator; │ │2037 - │ │1921 +fn handleChannelOpen(conn: *Conn, packet: []const u8, has_channel: bool) !?Channel {│ │1922 // byte SSH_MSG_CHANNEL_OPEN │ │1923 // string channel_type │ │1924 // uint32 sender_channel (peer's id) │ ╰──────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1930 const initial_window = try r.takeInt(u32, .big); │ │1931 const max_packet = try r.takeInt(u32, .big); │ │1932 │ │1933 + if (has_channel) { │ │1934 + try sendChannelOpenFailure(conn, remote_id, SSH_OPEN_RESOURCE_SHORTAGE, "only one channel per connection"); │ │1935 + return null; │ │1936 + } │ │1937 + │ │1938 if (!std.mem.eql(u8, ch_type, "session")) { │ │2050 - try sendChannelOpenFailure(conn, packet, SSH_OPEN_UNKNOWN_CHANNEL_TYPE, "only session channels are supported"); │ │1939 + try sendChannelOpenFailure(conn, remote_id, SSH_OPEN_UNKNOWN_CHANNEL_TYPE, "only session channels are supported");│ │1940 return null; │ │1941 } │ │1942 │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │2054 - // a zero max_packet would leave writeChannel looping without progress │ │1943 + // a zero max_packet would leave writeBytes looping without progress │ │1944 if (max_packet == 0) { │ │2056 - try sendChannelOpenFailure(conn, packet, SSH_OPEN_RESOURCE_SHORTAGE, "max packet size must be non-zero"); │ │1945 + try sendChannelOpenFailure(conn, remote_id, SSH_OPEN_RESOURCE_SHORTAGE, "max packet size must be non-zero");│ │1946 return null; │ │1947 } │ │1948 │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────╮ │1949 const local_id: u32 = 0; // single-channel connection — id is always 0 from our side│ │2061 - var reply: std.ArrayList(u8) = .empty; │ │2062 - defer reply.deinit(allocator); │ │2063 - try reply.append(allocator, SSH_MSG_CHANNEL_OPEN_CONFIRMATION); │ │2064 - try writeU32(&reply, allocator, remote_id); │ │2065 - try writeU32(&reply, allocator, local_id); │ │2066 - try writeU32(&reply, allocator, initial_recv_window); │ │2067 - try writeU32(&reply, allocator, max_packet_size); │ │2068 - try conn.writePacket(&.{reply.items}); │ │1950 + var reply: [17]u8 = undefined; │ │1951 + reply[0] = SSH_MSG_CHANNEL_OPEN_CONFIRMATION; │ │1952 + std.mem.writeInt(u32, reply[1..5], remote_id, .big); │ │1953 + std.mem.writeInt(u32, reply[5..9], local_id, .big); │ │1954 + std.mem.writeInt(u32, reply[9..13], initial_recv_window, .big); │ │1955 + std.mem.writeInt(u32, reply[13..17], max_packet_size, .big); │ │1956 + try conn.writePacket(&.{&reply}); │ │1957 │ │1958 return .{ │ │1959 .local_id = local_id, │ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1964 }; │ │1965 } │ │1966 │ │2079 -fn sendChannelOpenFailure(conn: *Conn, open_packet: []const u8, reason: u32, description: []const u8) !void {│ │1967 +fn sendChannelOpenFailure(conn: *Conn, remote_id: u32, reason: u32, description: []const u8) !void { │ │1968 const allocator = conn.allocator; │ │2081 - │ │2082 - // recover the sender_channel field from the CHANNEL_OPEN packet so we │ │2083 - // address the failure to the correct id │ │2084 - var r = std.Io.Reader.fixed(open_packet[1..]); │ │2085 - const ch_type_len = try r.takeInt(u32, .big); │ │2086 - try r.discardAll(ch_type_len); │ │2087 - const remote_id = try r.takeInt(u32, .big); │ │1969 │ │1970 var reply: std.ArrayList(u8) = .empty; │ │1971 defer reply.deinit(allocator); │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯