~sircmpwn/hare-ssh

hare-ssh/net/ssh/agent/agent.ha -rw-r--r-- 1.7 KiB
c6a39e37Armin Preiml harden against "compromise via lattices" 29 days ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
use memio;
use io;
use os;

// All error types which the SSH agent may return.
export type error = !(invalid | io::error);

// Converts an [[error]] to its human-friendly representation.
export fn strerror(err: error) const str = {
	match (err) {
	case invalid =>
		return "Invalid SSH agent protocol message";
	case let err: io::error =>
		return io::strerror(err);
	};
};

export type agent = struct {
	fd: io::handle,
	buf: memio::stream,
};

// Creates a new SSH agent for the given socket. The user must call
// [[agent_finish]] to free resources associated with the agent after use.
export fn new(fd: io::handle) agent = {
	return agent {
		fd = fd,
		buf = memio::dynamic(),
	};
};

// Frees resources associated with an [[agent]]. Closes the underlying socket.
export fn agent_finish(agent: *agent) void = {
	io::close(agent.fd)!;
	io::close(&agent.buf)!;
};

// Reads the next message from an SSH agent socket, returning either a message,
// an error, or void if more data is required to read the full message.
export fn readmsg(agent: *agent) (message | io::EOF | void | error) = {
	let buf: [os::BUFSZ]u8 = [0...];
	const z = match (io::read(agent.fd, buf)?) {
	case let sz: size =>
		yield sz;
	case io::EOF =>
		return io::EOF;
	};
	io::write(&agent.buf, buf[..z])!;
	const msg = match (parse(memio::buffer(&agent.buf))?) {
	case size =>
		return;
	case let msg: message =>
		memio::reset(&agent.buf);
		yield msg;
	};
	return msg;
};

// Writes a message to the SSH agent socket. Blocks until the message is sent.
// For non-blocking operation, use [[serialize]] to obtain a buffer and write it
// yourself.
export fn writemsg(agent: *agent, msg: *message) (void | error) = {
	const buf = serialize(msg);
	defer free(buf);
	io::writeall(agent.fd, buf)?;
};