╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ─────────────────╮ │src/serve_ssh_protocol.zig│ ╰──────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────╮ │395 var eof: std.ArrayList(u8) = .empty; │ │386 - defer eof.deinit(self.allocator); │ │387 - try eof.append(self.allocator, SSH_MSG_CHANNEL_EOF); │ │388 - try writeU32(&eof, self.allocator, self.channel.remote_id); │ │389 - try self.sc_cipher.writePacket(self.io, self.writer, eof.items); │ │396 + defer eof.deinit(conn.allocator); │ │397 + try eof.append(conn.allocator, SSH_MSG_CHANNEL_EOF); │ │398 + try writeU32(&eof, conn.allocator, self.channel.remote_id); │ │399 + try conn.sc_cipher.writePacket(conn.io, conn.writer, eof.items); │ │400 │ │401 var close: std.ArrayList(u8) = .empty; │ │392 - defer close.deinit(self.allocator); │ │393 - try close.append(self.allocator, SSH_MSG_CHANNEL_CLOSE); │ │394 - try writeU32(&close, self.allocator, self.channel.remote_id); │ │395 - try self.sc_cipher.writePacket(self.io, self.writer, close.items); │ │402 + defer close.deinit(conn.allocator); │ │403 + try close.append(conn.allocator, SSH_MSG_CHANNEL_CLOSE); │ │404 + try writeU32(&close, conn.allocator, self.channel.remote_id); │ │405 + try conn.sc_cipher.writePacket(conn.io, conn.writer, close.items); │ │406 │ │407 // read and discard until the peer closes (TCP FIN) before letting the │ │408 // caller close the socket. a git client, after our CHANNEL_CLOSE, still│ ╰─────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────╮ │414 // contents don't matter — just get them off the socket. │ │415 var drain_packets: u32 = 0; │ │416 while (drain_packets < max_exit_drain_packets) : (drain_packets += 1) { │ │407 - const packet = self.cs_cipher.readPacket(self.allocator, self.reader) catch break;│ │408 - self.allocator.free(packet); │ │417 + const packet = conn.cs_cipher.readPacket(conn.allocator, conn.reader) catch break;│ │418 + conn.allocator.free(packet); │ │419 } │ │420 } │ │421 }; │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │576 const kex = try runKex(io, allocator, reader, writer, host_key, client_version); │ │577 │ │578 // post-KEX everything is encrypted with chacha20-poly1305@openssh.com │ │569 - var cs_cipher = Cipher.init(&kex.cs_key, kex.next_cs_seq); │ │570 - var sc_cipher = Cipher.init(&kex.sc_key, kex.next_sc_seq); │ │571 - cs_cipher.idle = idle; │ │572 - │ │573 - const rekey = RekeyState{ │ │574 - .host_key = host_key, │ │575 - .client_version = client_version, │ │576 - .session_id = kex.session_id, │ │577 - .strict = kex.strict, │ │579 + var conn = Conn{ │ │580 + .io = io, │ │581 + .allocator = allocator, │ │582 + .reader = reader, │ │583 + .writer = writer, │ │584 + .cs_cipher = Cipher.init(&kex.cs_key, kex.next_cs_seq), │ │585 + .sc_cipher = Cipher.init(&kex.sc_key, kex.next_sc_seq), │ │586 + .rekey = .{ │ │587 + .host_key = host_key, │ │588 + .client_version = client_version, │ │589 + .session_id = kex.session_id, │ │590 + .strict = kex.strict, │ │591 + }, │ │592 }; │ │593 + conn.cs_cipher.idle = idle; │ │594 │ │580 - const fingerprint = try runAuth(io, allocator, reader, writer, &cs_cipher, &sc_cipher, &rekey); │ │595 + const fingerprint = try runAuth(&conn); │ │596 │ │582 - try runChannelLayer(io, allocator, reader, writer, &cs_cipher, &sc_cipher, &rekey, &fingerprint, handler);│ │597 + try runChannelLayer(&conn, &fingerprint, handler); │ │598 } │ │599 │ │600 // --------------------------------------------------------------------------- │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────╮ │699 │ │700 // read one encrypted packet for the auth and channel layers, transparently │ │701 // skipping ignorable messages and servicing client-initiated rekeys. │ │687 -fn readSessionPacket( │ │688 - io: std.Io, │ │689 - allocator: std.mem.Allocator, │ │690 - reader: *std.Io.Reader, │ │691 - writer: *std.Io.Writer, │ │692 - cs_cipher: *Cipher, │ │693 - sc_cipher: *Cipher, │ │694 - rekey: *const RekeyState, │ │695 -) ![]u8 { │ │702 +fn readSessionPacket(conn: *Conn) ![]u8 { │ │703 while (true) { │ │697 - const packet = try cs_cipher.readPacket(allocator, reader); │ │704 + const packet = try conn.cs_cipher.readPacket(conn.allocator, conn.reader);│ │705 if (packet.len >= 1 and isIgnorableMsg(packet[0])) { │ │699 - allocator.free(packet); │ │706 + conn.allocator.free(packet); │ │707 continue; │ │708 } │ │709 if (packet.len >= 1 and packet[0] == SSH_MSG_KEXINIT) { │ ╰───────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────╮ │703 - defer allocator.free(packet); │ │704 - try runRekey(io, allocator, reader, writer, cs_cipher, sc_cipher, rekey, packet);│ │710 + defer conn.allocator.free(packet); │ │711 + try runRekey(conn, packet); │ │712 continue; │ │713 } │ │714 return packet; │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────╮ │951 } │ │952 │ │953 /// everything needed to service a client-initiated rekey mid-session.│ │947 -pub const RekeyState = struct { │ │954 +const RekeyState = struct { │ │955 host_key: *const HostKey, │ │956 client_version: []const u8, │ │957 session_id: [Sha256.digest_length]u8, │ ╰───────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────╮ │961 // service a rekey whose triggering KEXINIT payload has just been read: run │ │962 // the whole exchange under the current keys, then swap the new keys into │ │963 // both ciphers in place. │ │957 -fn runRekey( │ │958 - io: std.Io, │ │959 - allocator: std.mem.Allocator, │ │960 - reader: *std.Io.Reader, │ │961 - writer: *std.Io.Writer, │ │962 - cs_cipher: *Cipher, │ │963 - sc_cipher: *Cipher, │ │964 - rekey: *const RekeyState, │ │965 - client_kex_init: []const u8, │ │966 -) !void { │ │967 - const server_kex_init = try buildServerKexInit(io, allocator); │ │968 - defer allocator.free(server_kex_init); │ │969 - try sc_cipher.writePacket(io, writer, server_kex_init); │ │964 +fn runRekey(conn: *Conn, client_kex_init: []const u8) !void { │ │965 + const server_kex_init = try buildServerKexInit(conn.io, conn.allocator);│ │966 + defer conn.allocator.free(server_kex_init); │ │967 + try conn.sc_cipher.writePacket(conn.io, conn.writer, server_kex_init); │ │968 │ │969 const negotiated = try exchangeKeys( │ │972 - io, │ │973 - allocator, │ │974 - reader, │ │975 - writer, │ │976 - .{ .encrypted = .{ .cs = cs_cipher, .sc = sc_cipher } }, │ │970 + conn.io, │ │971 + conn.allocator, │ │972 + conn.reader, │ │973 + conn.writer, │ │974 + .{ .encrypted = .{ .cs = &conn.cs_cipher, .sc = &conn.sc_cipher } },│ │975 false, │ │978 - rekey.host_key, │ │979 - rekey.client_version, │ │976 + conn.rekey.host_key, │ │977 + conn.rekey.client_version, │ │978 client_kex_init, │ │979 server_kex_init, │ │982 - &rekey.session_id, │ │980 + &conn.rekey.session_id, │ │981 ); │ │982 │ │983 // seqnos continue across a rekey unless strict kex was negotiated, │ ╰─────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │984 // which resets them after every NEWKEYS │ │987 - cs_cipher.setKeys(&negotiated.keys.cs_enc, if (rekey.strict) 0 else cs_cipher.seq); │ │988 - sc_cipher.setKeys(&negotiated.keys.sc_enc, if (rekey.strict) 0 else sc_cipher.seq); │ │985 + conn.cs_cipher.setKeys(&negotiated.keys.cs_enc, if (conn.rekey.strict) 0 else conn.cs_cipher.seq);│ │986 + conn.sc_cipher.setKeys(&negotiated.keys.sc_enc, if (conn.rekey.strict) 0 else conn.sc_cipher.seq);│ │987 } │ │988 │ │989 const NegotiatedKeys = struct { │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1402 return out; │ │1403 } │ │1404 │ │1407 -fn runAuth( │ │1408 - io: std.Io, │ │1409 - allocator: std.mem.Allocator, │ │1410 - reader: *std.Io.Reader, │ │1411 - writer: *std.Io.Writer, │ │1412 - cs_cipher: *Cipher, │ │1413 - sc_cipher: *Cipher, │ │1414 - rekey: *const RekeyState, │ │1415 -) ![fingerprint_len]u8 { │ │1405 +fn runAuth(conn: *Conn) ![fingerprint_len]u8 { │ │1406 + const allocator = conn.allocator; │ │1407 + │ │1408 // step 1: service request for ssh-userauth → service accept │ │1409 { │ │1418 - const req = try readSessionPacket(io, allocator, reader, writer, cs_cipher, sc_cipher, rekey);│ │1410 + const req = try readSessionPacket(conn); │ │1411 defer allocator.free(req); │ │1412 if (req.len < 1 or req[0] != SSH_MSG_SERVICE_REQUEST) return error.UnexpectedMessage; │ │1413 │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────╮ │1420 defer accept.deinit(allocator); │ │1421 try accept.append(allocator, SSH_MSG_SERVICE_ACCEPT); │ │1422 try writeStringField(&accept, allocator, "ssh-userauth"); │ │1431 - try sc_cipher.writePacket(io, writer, accept.items); │ │1423 + try conn.sc_cipher.writePacket(conn.io, conn.writer, accept.items);│ │1424 } │ │1425 │ │1426 // step 2: USERAUTH loop until SUCCESS. accept any ed25519 key whose │ ╰─────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯