╭ changed ─────────────────╮ │src/serve_ssh_protocol.zig│ ╰──────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────╮ │413 var r = std.Io.Reader.fixed(packet[1..]); │ │414 const recipient_channel = try r.takeInt(u32, .big); │ │415 if (recipient_channel != self.channel.local_id) return error.UnknownChannel;│ │416 - const req_type = try takeStringField(conn.allocator, &r, 64); │ │417 - defer conn.allocator.free(req_type); │ │416 + const req_type = try takeString(&r, 64); │ │417 const want_reply = (try r.takeByte()) != 0; │ │418 if (std.mem.eql(u8, req_type, "window-change")) { │ │419 const sz = try takePtySize(&r); │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────╮ │890 try buf.appendSlice(allocator, trimmed); │ │891 } │ │892 │ │894 -pub fn takeStringField(allocator: std.mem.Allocator, reader: *std.Io.Reader, max_len: u32) ![]u8 {│ │893 +/// a string field's bytes, borrowed from the packet behind a fixed reader │ │894 +pub fn takeString(reader: *std.Io.Reader, max_len: u32) ![]const u8 { │ │895 const len = try reader.takeInt(u32, .big); │ │896 if (len > max_len) return error.FieldTooLarge; │ │897 - return try reader.readAlloc(allocator, len); │ │897 + return reader.take(len); │ │898 } │ │899 │ │900 fn nameListContainsAny(haystack: []const u8, our_options: []const []const u8) bool { │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │965 return buildKexInit(io, allocator, &(our_kex_algos ++ [_][]const u8{kex_strict_server}), &our_host_key_algos, &our_ciphers);│ │966 } │ │967 │ │968 +// the name-lists borrow from the KEXINIT payload │ │969 const ParsedKexInit = struct { │ │969 - kex_algos: []u8, │ │970 - host_key_algos: []u8, │ │971 - cs_cipher: []u8, │ │972 - sc_cipher: []u8, │ │970 + kex_algos: []const u8, │ │971 + host_key_algos: []const u8, │ │972 + cs_cipher: []const u8, │ │973 + sc_cipher: []const u8, │ │974 first_kex_packet_follows: bool, │ │974 - │ │975 - fn deinit(self: ParsedKexInit, allocator: std.mem.Allocator) void { │ │976 - allocator.free(self.kex_algos); │ │977 - allocator.free(self.host_key_algos); │ │978 - allocator.free(self.cs_cipher); │ │979 - allocator.free(self.sc_cipher); │ │980 - } │ │975 }; │ │976 │ │983 -fn parseClientKexInit(allocator: std.mem.Allocator, payload: []const u8) !ParsedKexInit { │ │977 +fn parseClientKexInit(payload: []const u8) !ParsedKexInit { │ │978 if (payload.len < 1 + 16) return error.KexInitTruncated; │ │979 if (payload[0] != SSH_MSG_KEXINIT) return error.UnexpectedMessage; │ │980 │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────╮ │ 981 var reader = std.Io.Reader.fixed(payload[1 + 16 ..]); // skip type + cookie │ │ 982 │ │ 989 - const kex_algos = try takeStringField(allocator, &reader, max_name_list_len); │ │ 990 - errdefer allocator.free(kex_algos); │ │ 991 - const host_key_algos = try takeStringField(allocator, &reader, max_name_list_len);│ │ 992 - errdefer allocator.free(host_key_algos); │ │ 993 - const cs_cipher = try takeStringField(allocator, &reader, max_name_list_len); │ │ 994 - errdefer allocator.free(cs_cipher); │ │ 995 - const sc_cipher = try takeStringField(allocator, &reader, max_name_list_len); │ │ 996 - errdefer allocator.free(sc_cipher); │ │ 983 + const kex_algos = try takeString(&reader, max_name_list_len); │ │ 984 + const host_key_algos = try takeString(&reader, max_name_list_len); │ │ 985 + const cs_cipher = try takeString(&reader, max_name_list_len); │ │ 986 + const sc_cipher = try takeString(&reader, max_name_list_len); │ │ 987 │ │ 988 // skip c->s mac, s->c mac, c->s compress, s->c compress, lang c->s, lang s->c │ │ 999 - for (0..6) |_| { │ │1000 - const len = try reader.takeInt(u32, .big); │ │1001 - if (len > max_name_list_len) return error.FieldTooLarge; │ │1002 - try reader.discardAll(len); │ │1003 - } │ │ 989 + for (0..6) |_| _ = try takeString(&reader, max_name_list_len); │ │ 990 const first_kex_packet_follows = (try reader.takeByte()) != 0; │ │ 991 _ = try reader.takeInt(u32, .big); // reserved │ │ 992 │ ╰────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────╮ │1031 const client_kex_init = try transport.readPacket(allocator, reader, false);│ │1032 defer allocator.free(client_kex_init); │ │1033 │ │1048 - var parsed = try parseClientKexInit(allocator, client_kex_init); │ │1049 - defer parsed.deinit(allocator); │ │1034 + const parsed = try parseClientKexInit(client_kex_init); │ │1035 │ │1036 // strict kex: when the client advertises it, its KEXINIT must be the │ │1037 // first packet, ignorable messages are banned until NEWKEYS, and both │ ╰─────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────╮ │1078 // the whole exchange under the current keys, then swap the new keys into│ │1079 // both ciphers in place. │ │1080 fn runRekey(conn: *Conn, client_kex_init: []const u8) !void { │ │1096 - var parsed = try parseClientKexInit(conn.allocator, client_kex_init);│ │1097 - defer parsed.deinit(conn.allocator); │ │1081 + const parsed = try parseClientKexInit(client_kex_init); │ │1082 │ │1083 // the exchange reads and writes through the ciphers directly │ │1084 conn.setWaiting(true); │ ╰───────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1174 │ │1175 var ecdh_init_reader = std.Io.Reader.fixed(ecdh_init[1..]); │ │1176 const expected_client_len: u32 = if (hybrid) hybrid_client_blob_len else X25519.public_length;│ │1193 - const client_blob = try takeStringField(allocator, &ecdh_init_reader, expected_client_len); │ │1194 - defer allocator.free(client_blob); │ │1177 + const client_blob = try takeString(&ecdh_init_reader, expected_client_len); │ │1178 if (client_blob.len != expected_client_len) return error.InvalidEphemeralKey; │ │1179 │ │1180 // derive server reply blob (S_REPLY) + shared secret K. │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────╮ │1584 if (req.len < 1 or req[0] != SSH_MSG_SERVICE_REQUEST) return error.UnexpectedMessage;│ │1585 │ │1586 var req_reader = std.Io.Reader.fixed(req[1..]); │ │1604 - const service = try takeStringField(allocator, &req_reader, 64); │ │1605 - defer allocator.free(service); │ │1587 + const service = try takeString(&req_reader, 64); │ │1588 if (!std.mem.eql(u8, service, "ssh-userauth")) return error.UnsupportedService; │ │1589 │ │1590 var accept: std.ArrayList(u8) = .empty; │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1603 if (req.len < 1 or req[0] != SSH_MSG_USERAUTH_REQUEST) return error.UnexpectedMessage;│ │1604 │ │1605 var req_reader = std.Io.Reader.fixed(req[1..]); │ │1624 - const user_name = try takeStringField(allocator, &req_reader, 256); │ │1625 - defer allocator.free(user_name); │ │1626 - const service_name = try takeStringField(allocator, &req_reader, 64); │ │1627 - defer allocator.free(service_name); │ │1628 - const method = try takeStringField(allocator, &req_reader, 64); │ │1629 - defer allocator.free(method); │ │1606 + const user_name = try takeString(&req_reader, 256); │ │1607 + const service_name = try takeString(&req_reader, 64); │ │1608 + const method = try takeString(&req_reader, 64); │ │1609 │ │1610 // RFC 4252 §5: the server must verify the requested service │ │1611 if (!std.mem.eql(u8, service_name, "ssh-connection")) { │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────╮ │1623 } │ │1624 │ │1625 const has_signature = (try req_reader.takeByte()) != 0; │ │1647 - const algo = try takeStringField(allocator, &req_reader, 64); │ │1648 - defer allocator.free(algo); │ │1649 - const pubkey_blob = try takeStringField(allocator, &req_reader, 4096);│ │1650 - defer allocator.free(pubkey_blob); │ │1626 + const algo = try takeString(&req_reader, 64); │ │1627 + const pubkey_blob = try takeString(&req_reader, 4096); │ │1628 │ │1629 if (!std.mem.eql(u8, algo, "ssh-ed25519")) { │ │1630 auth_attempts += 1; │ ╰────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯