╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ ssh.zig ──────────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │http│ ssh │ │ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
762 } 763 return w.consume(buffered.len + extra); 764 } 765 }; 766 767 // runs proto.handleConnection in a concurrent task (spawned via std.Io.concurrent). 768 // also acts as the session handler — captures the authenticated key's 769 // fingerprint and returns immediately, leaving runChannelLayer's wrap-up to 770 // send exit-status / EOF / CLOSE. records any error so the test can assert 771 // on it after awaiting the future. 772 const ServerTask = struct { 773 io: std.Io, 774 allocator: std.mem.Allocator, 775 host_key: *const proto.HostKey, 776 reader: *std.Io.Reader, 777 writer: *std.Io.Writer, 778 outgoing: *Pipe, // closed on exit so the client's reads return EOF 779 captured_fp: [proto.fingerprint_len]u8 = undefined, 780 captured_exec_buf: [256]u8 = undefined, 781 captured_exec_len: usize = 0, 782 result: ?anyerror = null, 783 784 pub fn handleSession(self: *ServerTask, sess: *proto.SessionCtx, request: proto.Request) !void { 785 @memcpy(&self.captured_fp, &sess.fingerprint); 786 switch (request) { 787 .exec => |cmd| { 788 // copy out of the protocol's stack-scoped buffer so the 789 // test can assert on it after the handler returns. 790 @memcpy(self.captured_exec_buf[0..cmd.len], cmd); 791 self.captured_exec_len = cmd.len; 792 }, 793 .shell => {}, 794 } 795 } 796 797 fn run(self: *ServerTask) void { 798 defer self.outgoing.close(self.io); 799 proto.handleConnection( 800 self.io, 801 self.allocator, 802 self.reader, 803 self.writer, 804 self.host_key, 805 self, 806 ) catch |e| { 807 self.result = e; 808 }; 809 } 810 }; 811 812 // deterministic keys so failures don't depend on RNG state. 813 814 fn newHostKey() !proto.HostKey { 815 const seed = [_]u8{0xAA} ** Ed25519.KeyPair.seed_length; 816 return .{ .keypair = try Ed25519.KeyPair.generateDeterministic(seed) }; 817 } 818 819 fn newUserKeypair() !Ed25519.KeyPair { 820 const seed = [_]u8{0xBB} ** Ed25519.KeyPair.seed_length; 821 return try Ed25519.KeyPair.generateDeterministic(seed); 822 } 823 824 fn newClientEphemeral() !X25519.KeyPair { 825 const seed = [_]u8{0xCC} ** X25519.seed_length; 826 return try X25519.KeyPair.generateDeterministic(seed); 827 } 828