xref: /plan9/sys/src/cmd/ssh2/netssh.c (revision e5a0fbb75eb58a1e71232e731bb6db9c54abf8b7)
163afb9a5SDavid du Colombier /*
263afb9a5SDavid du Colombier  *  /net/ssh
363afb9a5SDavid du Colombier  */
463afb9a5SDavid du Colombier #include <u.h>
563afb9a5SDavid du Colombier #include <libc.h>
663afb9a5SDavid du Colombier #include <fcall.h>
763afb9a5SDavid du Colombier #include <thread.h>
863afb9a5SDavid du Colombier #include <9p.h>
963afb9a5SDavid du Colombier #include <mp.h>
1063afb9a5SDavid du Colombier #include <auth.h>
1163afb9a5SDavid du Colombier #include <authsrv.h>
1263afb9a5SDavid du Colombier #include <libsec.h>
1363afb9a5SDavid du Colombier #include <ip.h>
1463afb9a5SDavid du Colombier #include "netssh.h"
1563afb9a5SDavid du Colombier 
1663afb9a5SDavid du Colombier extern int nokeyverify;
1763afb9a5SDavid du Colombier 
1863afb9a5SDavid du Colombier void stclunk(Fid *);
1963afb9a5SDavid du Colombier void stend(Srv *);
2063afb9a5SDavid du Colombier void stflush(Req *);
2163afb9a5SDavid du Colombier void stopen(Req *);
2263afb9a5SDavid du Colombier void stread(Req *);
2363afb9a5SDavid du Colombier void stwrite(Req *);
2463afb9a5SDavid du Colombier 
2563afb9a5SDavid du Colombier Srv netsshsrv = {
2663afb9a5SDavid du Colombier 	.open = stopen,
2763afb9a5SDavid du Colombier 	.read = stread,
2863afb9a5SDavid du Colombier 	.write = stwrite,
2963afb9a5SDavid du Colombier 	.flush = stflush,
3063afb9a5SDavid du Colombier 	.destroyfid = stclunk,
3163afb9a5SDavid du Colombier 	.end = stend,
3263afb9a5SDavid du Colombier };
3363afb9a5SDavid du Colombier 
3463afb9a5SDavid du Colombier Cipher *cryptos[] = {
3563afb9a5SDavid du Colombier 	&cipheraes128,
3663afb9a5SDavid du Colombier 	&cipheraes192,
3763afb9a5SDavid du Colombier 	&cipheraes256,
3863afb9a5SDavid du Colombier //	&cipherblowfish,
3963afb9a5SDavid du Colombier 	&cipher3des,
4063afb9a5SDavid du Colombier 	&cipherrc4,
4163afb9a5SDavid du Colombier };
4263afb9a5SDavid du Colombier 
4363afb9a5SDavid du Colombier Kex *kexes[] = {
4463afb9a5SDavid du Colombier 	&dh1sha1,
4563afb9a5SDavid du Colombier 	&dh14sha1,
4663afb9a5SDavid du Colombier };
4763afb9a5SDavid du Colombier 
4863afb9a5SDavid du Colombier PKA *pkas[3];
4963afb9a5SDavid du Colombier 
5063afb9a5SDavid du Colombier char *macnames[] = {
5163afb9a5SDavid du Colombier 	"hmac-sha1",
5263afb9a5SDavid du Colombier };
5363afb9a5SDavid du Colombier 
5463afb9a5SDavid du Colombier char *st_names[] = {
5563afb9a5SDavid du Colombier [Empty]		"Empty",
5663afb9a5SDavid du Colombier [Allocated]	"Allocated",
5763afb9a5SDavid du Colombier [Initting]	"Initting",
5863afb9a5SDavid du Colombier [Listening]	"Listening",
5963afb9a5SDavid du Colombier [Opening]	"Opening",
6063afb9a5SDavid du Colombier [Negotiating]	"Negotiating",
6163afb9a5SDavid du Colombier [Authing]	"Authing",
6263afb9a5SDavid du Colombier [Established]	"Established",
6363afb9a5SDavid du Colombier [Eof]		"Eof",
6463afb9a5SDavid du Colombier [Closing]	"Closing",
6563afb9a5SDavid du Colombier [Closed]	"Closed",
6663afb9a5SDavid du Colombier };
6763afb9a5SDavid du Colombier 
6863afb9a5SDavid du Colombier int debug;
6963afb9a5SDavid du Colombier int kflag;
7063afb9a5SDavid du Colombier char *mntpt = "/net";
7163afb9a5SDavid du Colombier char uid[32];
7263afb9a5SDavid du Colombier Conn *connections[MAXCONN];
7363afb9a5SDavid du Colombier File *rootfile, *clonefile, *ctlfile, *keysfile;
7463afb9a5SDavid du Colombier Ioproc *io9p;
7563afb9a5SDavid du Colombier MBox keymbox;
7663afb9a5SDavid du Colombier QLock availlck;
7763afb9a5SDavid du Colombier Rendez availrend;
7863afb9a5SDavid du Colombier 
7963afb9a5SDavid du Colombier SSHChan *alloc_chan(Conn *);
8063afb9a5SDavid du Colombier Conn *alloc_conn(void);
8163afb9a5SDavid du Colombier int auth_req(Packet *, Conn *);
8263afb9a5SDavid du Colombier int client_auth(Conn *, Ioproc *);
8363afb9a5SDavid du Colombier int dohandshake(Conn *, char *);
8463afb9a5SDavid du Colombier char *factlookup(int, int, char *[]);
8563afb9a5SDavid du Colombier void filedup(Req *, File *);
8663afb9a5SDavid du Colombier void readdata(void *);
8763afb9a5SDavid du Colombier void reader(void *);
8863afb9a5SDavid du Colombier void readreqrem(void *);
8963afb9a5SDavid du Colombier void send_kexinit(Conn *);
9063afb9a5SDavid du Colombier void server(char *, char *);
9163afb9a5SDavid du Colombier void shutdown(Conn *);
9263afb9a5SDavid du Colombier void stlisconn(void *);
9363afb9a5SDavid du Colombier void stlischan(void *);
9463afb9a5SDavid du Colombier int validatekex(Conn *, Packet *);
9563afb9a5SDavid du Colombier int validatekexc(Packet *);
9663afb9a5SDavid du Colombier int validatekexs(Packet *);
9763afb9a5SDavid du Colombier void writectlproc(void *);
9863afb9a5SDavid du Colombier void writedataproc(void *);
9963afb9a5SDavid du Colombier void writereqremproc(void *);
10063afb9a5SDavid du Colombier 
10163afb9a5SDavid du Colombier static int deferredinit(Conn *c);
10263afb9a5SDavid du Colombier 
10363afb9a5SDavid du Colombier static void
10463afb9a5SDavid du Colombier sshlogint(Conn *c, char *file, char *p)
10563afb9a5SDavid du Colombier {
10663afb9a5SDavid du Colombier 	char *role, *id;
10763afb9a5SDavid du Colombier 
10863afb9a5SDavid du Colombier 	if (c == nil)
10963afb9a5SDavid du Colombier 		role = "";
11063afb9a5SDavid du Colombier 	else if (c->role == Server)
11163afb9a5SDavid du Colombier 		role = "server ";
11263afb9a5SDavid du Colombier 	else
11363afb9a5SDavid du Colombier 		role = "client ";
11463afb9a5SDavid du Colombier 	if (c == nil)
11563afb9a5SDavid du Colombier 		id = strdup("");
11663afb9a5SDavid du Colombier 	else if (c->user || c->remote)
11763afb9a5SDavid du Colombier 		id = smprint("user %s@%s id %d ", c->user, c->remote, c->id);
11863afb9a5SDavid du Colombier 	else
11963afb9a5SDavid du Colombier 		id = smprint("id %d ", c->id);
12063afb9a5SDavid du Colombier 
12163afb9a5SDavid du Colombier 	syslog(0, file, "%s: %s%s%s", argv0, role, id, p);
12263afb9a5SDavid du Colombier 	free(id);
12363afb9a5SDavid du Colombier }
12463afb9a5SDavid du Colombier 
12563afb9a5SDavid du Colombier void
12663afb9a5SDavid du Colombier sshlog(Conn *c, char *fmt, ...)
12763afb9a5SDavid du Colombier {
12863afb9a5SDavid du Colombier 	va_list args;
12963afb9a5SDavid du Colombier 	char *p;
13063afb9a5SDavid du Colombier 
13163afb9a5SDavid du Colombier 	/* do this first in case fmt contains "%r" */
13263afb9a5SDavid du Colombier 	va_start(args, fmt);
13363afb9a5SDavid du Colombier 	p = vsmprint(fmt, args);
13463afb9a5SDavid du Colombier 	va_end(args);
13563afb9a5SDavid du Colombier 
13663afb9a5SDavid du Colombier 	sshlogint(c, "ssh", p);
13763afb9a5SDavid du Colombier 	sshlogint(c, "sshdebug", p);	/* log in both places */
13863afb9a5SDavid du Colombier 	free(p);
13963afb9a5SDavid du Colombier }
14063afb9a5SDavid du Colombier 
14163afb9a5SDavid du Colombier void
14263afb9a5SDavid du Colombier sshdebug(Conn *c, char *fmt, ...)
14363afb9a5SDavid du Colombier {
14463afb9a5SDavid du Colombier 	va_list args;
14563afb9a5SDavid du Colombier 	char *p;
14663afb9a5SDavid du Colombier 
14763afb9a5SDavid du Colombier 	if (!debug)
14863afb9a5SDavid du Colombier 		return;
14963afb9a5SDavid du Colombier 
15063afb9a5SDavid du Colombier 	/* do this first in case fmt contains "%r" */
15163afb9a5SDavid du Colombier 	va_start(args, fmt);
15263afb9a5SDavid du Colombier 	p = vsmprint(fmt, args);
15363afb9a5SDavid du Colombier 	va_end(args);
15463afb9a5SDavid du Colombier 
15563afb9a5SDavid du Colombier 	sshlogint(c, "sshdebug", p);
15663afb9a5SDavid du Colombier 	free(p);
15763afb9a5SDavid du Colombier }
15863afb9a5SDavid du Colombier 
15963afb9a5SDavid du Colombier void
16063afb9a5SDavid du Colombier usage(void)
16163afb9a5SDavid du Colombier {
16263afb9a5SDavid du Colombier 	fprint(2, "usage: %s [-dkv] [-m mntpt] [-s srvpt]\n", argv0);
16363afb9a5SDavid du Colombier 	exits("usage");
16463afb9a5SDavid du Colombier }
16563afb9a5SDavid du Colombier 
16663afb9a5SDavid du Colombier void
16763afb9a5SDavid du Colombier threadmain(int argc, char *argv[])
16863afb9a5SDavid du Colombier {
16963afb9a5SDavid du Colombier 	char *p, *srvpt = nil;
17063afb9a5SDavid du Colombier 
17163afb9a5SDavid du Colombier 	threadsetname("main");
17263afb9a5SDavid du Colombier 	ARGBEGIN {
17363afb9a5SDavid du Colombier 	case '9':
17463afb9a5SDavid du Colombier 		chatty9p = 1;
17563afb9a5SDavid du Colombier 		break;
17663afb9a5SDavid du Colombier 	case 'd':
17763afb9a5SDavid du Colombier 		debug++;
17863afb9a5SDavid du Colombier 		break;
17963afb9a5SDavid du Colombier 	case 'k':
18063afb9a5SDavid du Colombier 		kflag = 1;
18163afb9a5SDavid du Colombier 		break;
18263afb9a5SDavid du Colombier 	case 'm':
18363afb9a5SDavid du Colombier 		mntpt = EARGF(usage());
18463afb9a5SDavid du Colombier 		break;
18563afb9a5SDavid du Colombier 	case 's':
18663afb9a5SDavid du Colombier 		srvpt = EARGF(usage());
18763afb9a5SDavid du Colombier 		break;
18863afb9a5SDavid du Colombier 	case 'v':
18963afb9a5SDavid du Colombier 		nokeyverify = 1;
19063afb9a5SDavid du Colombier 		break;
19163afb9a5SDavid du Colombier 	default:
19263afb9a5SDavid du Colombier 		usage();
19363afb9a5SDavid du Colombier 		break;
19463afb9a5SDavid du Colombier 	} ARGEND;
19563afb9a5SDavid du Colombier 
19663afb9a5SDavid du Colombier 	p = getenv("nosshkeyverify");
19763afb9a5SDavid du Colombier 	if (p) {
19863afb9a5SDavid du Colombier 		nokeyverify = 1;
19963afb9a5SDavid du Colombier 		free(p);
20063afb9a5SDavid du Colombier 	}
20163afb9a5SDavid du Colombier 
20263afb9a5SDavid du Colombier 	if (readfile("/dev/user", uid, sizeof uid) <= 0)
20363afb9a5SDavid du Colombier 		strcpy(uid, "none");
20463afb9a5SDavid du Colombier 
20563afb9a5SDavid du Colombier 	keymbox.mchan = chancreate(4, 0);
20663afb9a5SDavid du Colombier 	availrend.l = &availlck;
20763afb9a5SDavid du Colombier 	dh_init(pkas);
20863afb9a5SDavid du Colombier 
20963afb9a5SDavid du Colombier 	/* become a daemon */
21063afb9a5SDavid du Colombier 	if (rfork(RFNOTEG) < 0)
21163afb9a5SDavid du Colombier 		fprint(2, "%s: rfork(NOTEG) failed: %r\n", argv0);
21263afb9a5SDavid du Colombier 	server(mntpt, srvpt);
213*e5a0fbb7SDavid du Colombier 	threadexits(nil);
21463afb9a5SDavid du Colombier }
21563afb9a5SDavid du Colombier 
21663afb9a5SDavid du Colombier int
21763afb9a5SDavid du Colombier readio(Ioproc *io, int fd, void *buf, int n)
21863afb9a5SDavid du Colombier {
21963afb9a5SDavid du Colombier 	if (io)
22063afb9a5SDavid du Colombier 		return ioread(io, fd, buf, n);
22163afb9a5SDavid du Colombier 	else
22263afb9a5SDavid du Colombier 		return read(fd, buf, n);
22363afb9a5SDavid du Colombier }
22463afb9a5SDavid du Colombier 
22563afb9a5SDavid du Colombier int
22663afb9a5SDavid du Colombier writeio(Ioproc *io, int fd, void *buf, int n)
22763afb9a5SDavid du Colombier {
22863afb9a5SDavid du Colombier 	if (io)
22963afb9a5SDavid du Colombier 		return iowrite(io, fd, buf, n);
23063afb9a5SDavid du Colombier 	else
23163afb9a5SDavid du Colombier 		return write(fd, buf, n);
23263afb9a5SDavid du Colombier }
23363afb9a5SDavid du Colombier 
23463afb9a5SDavid du Colombier int
23563afb9a5SDavid du Colombier read9pmsg(int fd, void *abuf, uint n)
23663afb9a5SDavid du Colombier {
23763afb9a5SDavid du Colombier 	int m, len;
23863afb9a5SDavid du Colombier 	uchar *buf;
23963afb9a5SDavid du Colombier 
24063afb9a5SDavid du Colombier 	if (io9p == nil)
24163afb9a5SDavid du Colombier 		io9p = ioproc();
24263afb9a5SDavid du Colombier 
24363afb9a5SDavid du Colombier 	buf = abuf;
24463afb9a5SDavid du Colombier 
24563afb9a5SDavid du Colombier 	/* read count */
24663afb9a5SDavid du Colombier 	m = ioreadn(io9p, fd, buf, BIT32SZ);
24763afb9a5SDavid du Colombier 	if(m != BIT32SZ){
24863afb9a5SDavid du Colombier 		if(m < 0)
24963afb9a5SDavid du Colombier 			return -1;
25063afb9a5SDavid du Colombier 		return 0;
25163afb9a5SDavid du Colombier 	}
25263afb9a5SDavid du Colombier 
25363afb9a5SDavid du Colombier 	len = GBIT32(buf);
25463afb9a5SDavid du Colombier 	if(len <= BIT32SZ || len > n){
25563afb9a5SDavid du Colombier 		werrstr("bad length in 9P2000 message header");
25663afb9a5SDavid du Colombier 		return -1;
25763afb9a5SDavid du Colombier 	}
25863afb9a5SDavid du Colombier 	len -= BIT32SZ;
25963afb9a5SDavid du Colombier 	m = ioreadn(io9p, fd, buf+BIT32SZ, len);
26063afb9a5SDavid du Colombier 	if(m < len)
26163afb9a5SDavid du Colombier 		return 0;
26263afb9a5SDavid du Colombier 	return BIT32SZ+m;
26363afb9a5SDavid du Colombier }
26463afb9a5SDavid du Colombier 
26563afb9a5SDavid du Colombier void
26663afb9a5SDavid du Colombier stend(Srv *)
26763afb9a5SDavid du Colombier {
26863afb9a5SDavid du Colombier 	closeioproc(io9p);
26963afb9a5SDavid du Colombier 	threadkillgrp(threadgetgrp());
27063afb9a5SDavid du Colombier }
27163afb9a5SDavid du Colombier 
27263afb9a5SDavid du Colombier void
27363afb9a5SDavid du Colombier server(char *mntpt, char *srvpt)
27463afb9a5SDavid du Colombier {
27563afb9a5SDavid du Colombier 	Dir d;
27663afb9a5SDavid du Colombier 	char *p;
27763afb9a5SDavid du Colombier 	int fd;
27863afb9a5SDavid du Colombier 
27963afb9a5SDavid du Colombier 	netsshsrv.tree = alloctree(uid, uid, 0777, nil);
28063afb9a5SDavid du Colombier 	rootfile = createfile(netsshsrv.tree->root, "ssh", uid, 0555|DMDIR,
28163afb9a5SDavid du Colombier 		(void*)Qroot);
28263afb9a5SDavid du Colombier 	clonefile = createfile(rootfile, "clone", uid, 0666, (void*)Qclone);
28363afb9a5SDavid du Colombier 	ctlfile = createfile(rootfile, "ctl", uid, 0666, (void*)Qctl);
28463afb9a5SDavid du Colombier 	keysfile = createfile(rootfile, "keys", uid, 0600, (void *)Qreqrem);
28563afb9a5SDavid du Colombier 
286*e5a0fbb7SDavid du Colombier 	/*
287*e5a0fbb7SDavid du Colombier 	 * needs to be MBEFORE in case there are previous, now defunct,
288*e5a0fbb7SDavid du Colombier 	 * netssh processes mounted in mntpt.
289*e5a0fbb7SDavid du Colombier 	 */
290*e5a0fbb7SDavid du Colombier 	threadpostmountsrv(&netsshsrv, srvpt, mntpt, MBEFORE);
29163afb9a5SDavid du Colombier 
29263afb9a5SDavid du Colombier 	p = esmprint("%s/cs", mntpt);
29363afb9a5SDavid du Colombier 	fd = open(p, OWRITE);
29463afb9a5SDavid du Colombier 	free(p);
29563afb9a5SDavid du Colombier 	if (fd >= 0) {
29663afb9a5SDavid du Colombier 		fprint(fd, "add ssh");
29763afb9a5SDavid du Colombier 		close(fd);
29863afb9a5SDavid du Colombier 	}
29963afb9a5SDavid du Colombier 	if (srvpt) {
30063afb9a5SDavid du Colombier 		nulldir(&d);
30163afb9a5SDavid du Colombier 		d.mode = 0666;
30263afb9a5SDavid du Colombier 		p = esmprint("/srv/%s", srvpt);
30363afb9a5SDavid du Colombier 		dirwstat(p, &d);
30463afb9a5SDavid du Colombier 		free(p);
30563afb9a5SDavid du Colombier 	}
30663afb9a5SDavid du Colombier 	sshdebug(nil, "server started for %s", getuser());
30763afb9a5SDavid du Colombier }
30863afb9a5SDavid du Colombier 
30963afb9a5SDavid du Colombier static void
31063afb9a5SDavid du Colombier respexit(Conn *c, Req *r, void *freeme, char *msg)
31163afb9a5SDavid du Colombier {
31263afb9a5SDavid du Colombier 	if (msg)
31363afb9a5SDavid du Colombier 		sshdebug(c, "%s", msg);
31463afb9a5SDavid du Colombier 	r->aux = 0;
31563afb9a5SDavid du Colombier 	respond(r, msg);
31663afb9a5SDavid du Colombier 	free(freeme);
31763afb9a5SDavid du Colombier 	threadexits(nil);	/* maybe use msg here */
31863afb9a5SDavid du Colombier }
31963afb9a5SDavid du Colombier 
32063afb9a5SDavid du Colombier void
32163afb9a5SDavid du Colombier stopen(Req *r)
32263afb9a5SDavid du Colombier {
32363afb9a5SDavid du Colombier 	int lev, xconn, fd;
32463afb9a5SDavid du Colombier 	uvlong qidpath;
32563afb9a5SDavid du Colombier 	char *p;
32663afb9a5SDavid du Colombier 	char buf[32];
32763afb9a5SDavid du Colombier 	Conn *c;
32863afb9a5SDavid du Colombier 	SSHChan *sc;
32963afb9a5SDavid du Colombier 
33063afb9a5SDavid du Colombier 	qidpath = (uvlong)r->fid->file->aux;
33163afb9a5SDavid du Colombier 	lev = qidpath >> Levshift;
33263afb9a5SDavid du Colombier 	switch ((ulong)(qidpath & Qtypemask)) {
33363afb9a5SDavid du Colombier 	default:
33463afb9a5SDavid du Colombier 		respond(r, nil);
33563afb9a5SDavid du Colombier 		break;
33663afb9a5SDavid du Colombier 	case Qlisten:
33763afb9a5SDavid du Colombier 		r->aux = (void *)threadcreate((lev == Connection?
33863afb9a5SDavid du Colombier 			stlisconn: stlischan), r, Defstk);
33963afb9a5SDavid du Colombier 		break;
34063afb9a5SDavid du Colombier 	case Qclone:
34163afb9a5SDavid du Colombier 		switch (lev) {
34263afb9a5SDavid du Colombier 		case Top:
34363afb9a5SDavid du Colombier 			/* should use dial(2) instead of diddling /net/tcp */
34463afb9a5SDavid du Colombier 			p = esmprint("%s/tcp/clone", mntpt);
34563afb9a5SDavid du Colombier 			fd = open(p, ORDWR);
34663afb9a5SDavid du Colombier 			if (fd < 0) {
34763afb9a5SDavid du Colombier 				sshdebug(nil, "stopen: open %s failed: %r", p);
34863afb9a5SDavid du Colombier 				free(p);
34963afb9a5SDavid du Colombier 				responderror(r);
35063afb9a5SDavid du Colombier 				return;
35163afb9a5SDavid du Colombier 			}
35263afb9a5SDavid du Colombier 			free(p);
35363afb9a5SDavid du Colombier 
35463afb9a5SDavid du Colombier 			c = alloc_conn();
35563afb9a5SDavid du Colombier 			if (c == nil) {
35663afb9a5SDavid du Colombier 				close(fd);
35763afb9a5SDavid du Colombier 				respond(r, "no more connections");
35863afb9a5SDavid du Colombier 				return;
35963afb9a5SDavid du Colombier 			}
36063afb9a5SDavid du Colombier 			c->ctlfd = fd;
36163afb9a5SDavid du Colombier 			c->poisoned = 0;
36263afb9a5SDavid du Colombier 			filedup(r, c->ctlfile);
36363afb9a5SDavid du Colombier 			sshlog(c, "new connection on fd %d", fd);
36463afb9a5SDavid du Colombier 			break;
36563afb9a5SDavid du Colombier 		case Connection:
36663afb9a5SDavid du Colombier 			xconn = (qidpath >> Connshift) & Connmask;
36763afb9a5SDavid du Colombier 			c = connections[xconn];
36863afb9a5SDavid du Colombier 			if (c == nil) {
36963afb9a5SDavid du Colombier 				respond(r, "bad connection");
37063afb9a5SDavid du Colombier 				return;
37163afb9a5SDavid du Colombier 			}
37263afb9a5SDavid du Colombier 			sc = alloc_chan(c);
37363afb9a5SDavid du Colombier 			if (sc == nil) {
37463afb9a5SDavid du Colombier 				respond(r, "no more channels");
37563afb9a5SDavid du Colombier 				return;
37663afb9a5SDavid du Colombier 			}
37763afb9a5SDavid du Colombier 			filedup(r, sc->ctl);
37863afb9a5SDavid du Colombier 			break;
37963afb9a5SDavid du Colombier 		default:
38063afb9a5SDavid du Colombier 			snprint(buf, sizeof buf, "bad level %d", lev);
38163afb9a5SDavid du Colombier 			readstr(r, buf);
38263afb9a5SDavid du Colombier 			break;
38363afb9a5SDavid du Colombier 		}
38463afb9a5SDavid du Colombier 		respond(r, nil);
38563afb9a5SDavid du Colombier 		break;
38663afb9a5SDavid du Colombier 	}
38763afb9a5SDavid du Colombier }
38863afb9a5SDavid du Colombier 
38963afb9a5SDavid du Colombier static void
39063afb9a5SDavid du Colombier listerrexit(Req *r, Ioproc *io, Conn *cl)
39163afb9a5SDavid du Colombier {
39263afb9a5SDavid du Colombier 	r->aux = 0;
39363afb9a5SDavid du Colombier 	responderror(r);
39463afb9a5SDavid du Colombier 	closeioproc(io);
39563afb9a5SDavid du Colombier 	shutdown(cl);
39663afb9a5SDavid du Colombier 	threadexits(nil);
39763afb9a5SDavid du Colombier }
39863afb9a5SDavid du Colombier 
39963afb9a5SDavid du Colombier void
40063afb9a5SDavid du Colombier stlisconn(void *a)
40163afb9a5SDavid du Colombier {
40263afb9a5SDavid du Colombier 	int xconn, fd, n;
40363afb9a5SDavid du Colombier 	uvlong qidpath;
40463afb9a5SDavid du Colombier 	char *msg;
40563afb9a5SDavid du Colombier 	char buf[Numbsz], path[NETPATHLEN];
40663afb9a5SDavid du Colombier 	Conn *c, *cl;
40763afb9a5SDavid du Colombier 	Ioproc *io;
40863afb9a5SDavid du Colombier 	Req *r;
40963afb9a5SDavid du Colombier 
41063afb9a5SDavid du Colombier 	threadsetname("stlisconn");
41163afb9a5SDavid du Colombier 	r = a;
41263afb9a5SDavid du Colombier 	qidpath = (uvlong)r->fid->file->aux;
41363afb9a5SDavid du Colombier 	xconn = (qidpath >> Connshift) & Connmask;
41463afb9a5SDavid du Colombier 
41563afb9a5SDavid du Colombier 	cl = connections[xconn];
41663afb9a5SDavid du Colombier 	if (cl == nil) {
41763afb9a5SDavid du Colombier 		sshlog(cl, "bad connection");
41863afb9a5SDavid du Colombier 		respond(r, "bad connection");
41963afb9a5SDavid du Colombier 		threadexits("bad connection");
42063afb9a5SDavid du Colombier 	}
42163afb9a5SDavid du Colombier 	if (cl->poisoned) {
42263afb9a5SDavid du Colombier 		sshdebug(cl, "stlisconn conn %d poisoned", xconn);
42363afb9a5SDavid du Colombier 		r->aux = 0;
42463afb9a5SDavid du Colombier 		respond(r, "top level listen conn poisoned");
42563afb9a5SDavid du Colombier 		threadexits("top level listen conn poisoned");
42663afb9a5SDavid du Colombier 	}
42763afb9a5SDavid du Colombier 	if (cl->ctlfd < 0) {
42863afb9a5SDavid du Colombier 		sshdebug(cl, "stlisconn conn %d ctlfd < 0; poisoned", xconn);
42963afb9a5SDavid du Colombier 		r->aux = 0;
43063afb9a5SDavid du Colombier 		respond(r, "top level listen with closed fd");
43163afb9a5SDavid du Colombier 		shutdown(cl);
43263afb9a5SDavid du Colombier 		cl->poisoned = 1;	/* no more use until ctlfd is set */
43363afb9a5SDavid du Colombier 		threadexits("top level listen with closed fd");
43463afb9a5SDavid du Colombier 	}
43563afb9a5SDavid du Colombier 
43663afb9a5SDavid du Colombier 	io = ioproc();
43763afb9a5SDavid du Colombier 
43863afb9a5SDavid du Colombier 	/* read xconn's tcp conn's ctl file */
43963afb9a5SDavid du Colombier 	seek(cl->ctlfd, 0, 0);
44063afb9a5SDavid du Colombier 	n = ioread(io, cl->ctlfd, buf, sizeof buf - 1);
44163afb9a5SDavid du Colombier 	if (n == 0) {
44263afb9a5SDavid du Colombier 		sshlog(cl, "stlisconn read eof on fd %d", cl->ctlfd);
44363afb9a5SDavid du Colombier 		listerrexit(r, io, cl);
44463afb9a5SDavid du Colombier 	} else if (n < 0) {
44563afb9a5SDavid du Colombier 		sshlog(cl, "stlisconn read failed on fd %d: %r", cl->ctlfd);
44663afb9a5SDavid du Colombier 		listerrexit(r, io, cl);
44763afb9a5SDavid du Colombier 	}
44863afb9a5SDavid du Colombier 	buf[n] = '\0';
44963afb9a5SDavid du Colombier 
45063afb9a5SDavid du Colombier 	cl->state = Listening;
45163afb9a5SDavid du Colombier 	/* should use dial(2) instead of diddling /net/tcp */
45263afb9a5SDavid du Colombier 	snprint(path, sizeof path, "%s/tcp/%s/listen", mntpt, buf);
45363afb9a5SDavid du Colombier 	for(;;) {
45463afb9a5SDavid du Colombier 		fd = ioopen(io, path, ORDWR);
45563afb9a5SDavid du Colombier 		if (fd < 0)
45663afb9a5SDavid du Colombier 			listerrexit(r, io, cl);
45763afb9a5SDavid du Colombier 		c = alloc_conn();
45863afb9a5SDavid du Colombier 		if (c)
45963afb9a5SDavid du Colombier 			break;
46063afb9a5SDavid du Colombier 		n = ioread(io, fd, buf, sizeof buf - 1);
46163afb9a5SDavid du Colombier 		if (n <= 0)
46263afb9a5SDavid du Colombier 			listerrexit(r, io, cl);
46363afb9a5SDavid du Colombier 		buf[n] = '\0';
46463afb9a5SDavid du Colombier 		msg = smprint("reject %s no available connections", buf);
46563afb9a5SDavid du Colombier 		iowrite(io, fd, msg, strlen(msg));
46663afb9a5SDavid du Colombier 		free(msg);
46763afb9a5SDavid du Colombier 		close(fd);			/* surely ioclose? */
46863afb9a5SDavid du Colombier 	}
46963afb9a5SDavid du Colombier 	c->ctlfd = fd;
47063afb9a5SDavid du Colombier 	if (c->ctlfd < 0) {
47163afb9a5SDavid du Colombier 		sshlog(cl, "stlisconn c->ctlfd < 0 for conn %d", xconn);
47263afb9a5SDavid du Colombier 		threadexitsall("stlisconn c->ctlfd < 0");
47363afb9a5SDavid du Colombier 	}
47463afb9a5SDavid du Colombier 	c->poisoned = 0;
47563afb9a5SDavid du Colombier 	c->stifle = 1;			/* defer server; was for coexistence */
47663afb9a5SDavid du Colombier 	filedup(r, c->ctlfile);
47763afb9a5SDavid du Colombier 	sshdebug(c, "responding to listen open");
47863afb9a5SDavid du Colombier 	r->aux = 0;
47963afb9a5SDavid du Colombier 	respond(r, nil);
48063afb9a5SDavid du Colombier 	closeioproc(io);
48163afb9a5SDavid du Colombier 	threadexits(nil);
48263afb9a5SDavid du Colombier }
48363afb9a5SDavid du Colombier 
48463afb9a5SDavid du Colombier void
48563afb9a5SDavid du Colombier stlischan(void *a)
48663afb9a5SDavid du Colombier {
48763afb9a5SDavid du Colombier 	Req *r;
48863afb9a5SDavid du Colombier 	Packet *p2;
48963afb9a5SDavid du Colombier 	Ioproc *io;
49063afb9a5SDavid du Colombier 	Conn *c;
49163afb9a5SDavid du Colombier 	SSHChan *sc;
49263afb9a5SDavid du Colombier 	int i, n, xconn;
49363afb9a5SDavid du Colombier 	uvlong qidpath;
49463afb9a5SDavid du Colombier 
49563afb9a5SDavid du Colombier 	threadsetname("stlischan");
49663afb9a5SDavid du Colombier 	r = a;
49763afb9a5SDavid du Colombier 	qidpath = (uvlong)r->fid->file->aux;
49863afb9a5SDavid du Colombier 	xconn = (qidpath >> Connshift) & Connmask;
49963afb9a5SDavid du Colombier 	c = connections[xconn];
50063afb9a5SDavid du Colombier 	if (c == nil) {
50163afb9a5SDavid du Colombier 		respond(r, "bad channel");
50263afb9a5SDavid du Colombier 		sshlog(c, "bad channel");
50363afb9a5SDavid du Colombier 		threadexits(nil);
50463afb9a5SDavid du Colombier 	}
50563afb9a5SDavid du Colombier 	if (c->state == Closed || c->state == Closing)
50663afb9a5SDavid du Colombier 		respexit(c, r, nil, "channel listen on closed connection");
50763afb9a5SDavid du Colombier 	sc = c->chans[qidpath & Chanmask];
50863afb9a5SDavid du Colombier 
50963afb9a5SDavid du Colombier 	qlock(&c->l);
51063afb9a5SDavid du Colombier 	sc->lreq = r;
51163afb9a5SDavid du Colombier 	for (i = 0; i < c->nchan; ++i)
51263afb9a5SDavid du Colombier 		if (c->chans[i] && c->chans[i]->state == Opening &&
51363afb9a5SDavid du Colombier 		    c->chans[i]->ann && strcmp(c->chans[i]->ann, sc->ann) == 0)
51463afb9a5SDavid du Colombier 			break;
51563afb9a5SDavid du Colombier 	if (i >= c->nchan) {
51663afb9a5SDavid du Colombier 		sc->state = Listening;
51763afb9a5SDavid du Colombier 		rsleep(&sc->r);
51863afb9a5SDavid du Colombier 		i = sc->waker;
51963afb9a5SDavid du Colombier 		if (i < 0) {
52063afb9a5SDavid du Colombier 			qunlock(&c->l);
52163afb9a5SDavid du Colombier 			r->aux = 0;
52263afb9a5SDavid du Colombier 			responderror(r);
52363afb9a5SDavid du Colombier 			threadexits(nil);
52463afb9a5SDavid du Colombier 		}
52563afb9a5SDavid du Colombier 	} else
52663afb9a5SDavid du Colombier 		rwakeup(&c->chans[i]->r);
52763afb9a5SDavid du Colombier 	qunlock(&c->l);
52863afb9a5SDavid du Colombier 
52963afb9a5SDavid du Colombier 	if (c->state == Closed || c->state == Closing || c->state == Eof)
53063afb9a5SDavid du Colombier 		respexit(c, r, nil, "channel listen on closed connection");
53163afb9a5SDavid du Colombier 	c->chans[i]->state = Established;
53263afb9a5SDavid du Colombier 
53363afb9a5SDavid du Colombier 	p2 = new_packet(c);
53463afb9a5SDavid du Colombier 	c->chans[i]->rwindow = Maxpayload;
53563afb9a5SDavid du Colombier 	add_byte(p2, SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
53663afb9a5SDavid du Colombier 	hnputl(p2->payload + 1, c->chans[i]->otherid);
53763afb9a5SDavid du Colombier 	hnputl(p2->payload + 5, c->chans[i]->id);
53863afb9a5SDavid du Colombier 	hnputl(p2->payload + 9, Maxpayload);
53963afb9a5SDavid du Colombier 	hnputl(p2->payload + 13, Maxrpcbuf);
54063afb9a5SDavid du Colombier 	p2->rlength = 18;
54163afb9a5SDavid du Colombier 	n = finish_packet(p2);
54263afb9a5SDavid du Colombier 	filedup(r, c->chans[i]->ctl);
54363afb9a5SDavid du Colombier 
54463afb9a5SDavid du Colombier 	io = ioproc();
54563afb9a5SDavid du Colombier 	n = iowrite(io, c->datafd, p2->nlength, n);
54663afb9a5SDavid du Colombier 	closeioproc(io);
54763afb9a5SDavid du Colombier 
54863afb9a5SDavid du Colombier 	free(p2);
54963afb9a5SDavid du Colombier 
55063afb9a5SDavid du Colombier 	sshdebug(c, "responding to chan listen open");
55163afb9a5SDavid du Colombier 	r->aux = 0;
55263afb9a5SDavid du Colombier 	if (n < 0)
55363afb9a5SDavid du Colombier 		responderror(r);
55463afb9a5SDavid du Colombier 	else
55563afb9a5SDavid du Colombier 		respond(r, nil);
55663afb9a5SDavid du Colombier 	threadexits(nil);
55763afb9a5SDavid du Colombier }
55863afb9a5SDavid du Colombier 
55963afb9a5SDavid du Colombier void
56063afb9a5SDavid du Colombier getdata(Conn *c, SSHChan *sc, Req *r)
56163afb9a5SDavid du Colombier {
56263afb9a5SDavid du Colombier 	Packet *p;
56363afb9a5SDavid du Colombier 	Plist *d;
56463afb9a5SDavid du Colombier 	int n;
56563afb9a5SDavid du Colombier 
56663afb9a5SDavid du Colombier 	n = r->ifcall.count;
56763afb9a5SDavid du Colombier 	if (sc->dataq->rem < n)
56863afb9a5SDavid du Colombier 		n = sc->dataq->rem;
56963afb9a5SDavid du Colombier 	if (n > Maxrpcbuf)
57063afb9a5SDavid du Colombier 		n = Maxrpcbuf;
57163afb9a5SDavid du Colombier 	r->ifcall.offset = 0;
57263afb9a5SDavid du Colombier 
57363afb9a5SDavid du Colombier 	readbuf(r, sc->dataq->st, n);
57463afb9a5SDavid du Colombier 	sc->dataq->st += n;
57563afb9a5SDavid du Colombier 	sc->dataq->rem -= n;
57663afb9a5SDavid du Colombier 	sc->inrqueue -= n;
57763afb9a5SDavid du Colombier 	if (sc->dataq->rem <= 0) {
57863afb9a5SDavid du Colombier 		d = sc->dataq;
57963afb9a5SDavid du Colombier 		sc->dataq = sc->dataq->next;
58063afb9a5SDavid du Colombier 		if (d->pack->tlength > sc->rwindow)
58163afb9a5SDavid du Colombier 			sc->rwindow = 0;
58263afb9a5SDavid du Colombier 		else
58363afb9a5SDavid du Colombier 			sc->rwindow -= d->pack->tlength;
58463afb9a5SDavid du Colombier 		free(d->pack);
58563afb9a5SDavid du Colombier 		free(d);
58663afb9a5SDavid du Colombier 	}
58763afb9a5SDavid du Colombier 	if (sc->rwindow < 16*1024) {		/* magic.  half-way, maybe? */
58863afb9a5SDavid du Colombier 		sc->rwindow += Maxpayload;
58963afb9a5SDavid du Colombier 		sshdebug(c, "increasing receive window to %lud, inq %lud\n",
59063afb9a5SDavid du Colombier 			argv0, sc->rwindow, sc->inrqueue);
59163afb9a5SDavid du Colombier 		p = new_packet(c);
59263afb9a5SDavid du Colombier 		add_byte(p, SSH_MSG_CHANNEL_WINDOW_ADJUST);
59363afb9a5SDavid du Colombier 		hnputl(p->payload+1, sc->otherid);
59463afb9a5SDavid du Colombier 		hnputl(p->payload+5, Maxpayload);
59563afb9a5SDavid du Colombier 		p->rlength += 8;
59663afb9a5SDavid du Colombier 		n = finish_packet(p);
59763afb9a5SDavid du Colombier 		iowrite(c->dio, c->datafd, p->nlength, n);
59863afb9a5SDavid du Colombier 		free(p);
59963afb9a5SDavid du Colombier 	}
60063afb9a5SDavid du Colombier 	r->aux = 0;
60163afb9a5SDavid du Colombier 	respond(r, nil);
60263afb9a5SDavid du Colombier }
60363afb9a5SDavid du Colombier 
60463afb9a5SDavid du Colombier void
60563afb9a5SDavid du Colombier stread(Req *r)
60663afb9a5SDavid du Colombier {
60763afb9a5SDavid du Colombier 	Conn *c;
60863afb9a5SDavid du Colombier 	SSHChan *sc;
60963afb9a5SDavid du Colombier 	int n, lev, cnum, xconn;
61063afb9a5SDavid du Colombier 	uvlong qidpath;
61163afb9a5SDavid du Colombier 	char buf[Arbbufsz], path[NETPATHLEN];
61263afb9a5SDavid du Colombier 
61363afb9a5SDavid du Colombier 	threadsetname("stread");
61463afb9a5SDavid du Colombier 	qidpath = (uvlong)r->fid->file->aux;
61563afb9a5SDavid du Colombier 	lev = qidpath >> Levshift;
61663afb9a5SDavid du Colombier 	xconn = (qidpath >> Connshift) & Connmask;
61763afb9a5SDavid du Colombier 	c = connections[xconn];
61863afb9a5SDavid du Colombier 	if (c == nil) {
61963afb9a5SDavid du Colombier 		if (lev != Top || (qidpath & Qtypemask) != Qreqrem) {
62063afb9a5SDavid du Colombier 			respond(r, "Invalid connection");
62163afb9a5SDavid du Colombier 			return;
62263afb9a5SDavid du Colombier 		}
62363afb9a5SDavid du Colombier 		cnum = 0;
62463afb9a5SDavid du Colombier 		sc = nil;
62563afb9a5SDavid du Colombier 	} else {
62663afb9a5SDavid du Colombier 		cnum = qidpath & Chanmask;
62763afb9a5SDavid du Colombier 		sc = c->chans[cnum];
62863afb9a5SDavid du Colombier 	}
62963afb9a5SDavid du Colombier 	switch ((ulong)(qidpath & Qtypemask)) {
63063afb9a5SDavid du Colombier 	case Qctl:
63163afb9a5SDavid du Colombier 	case Qlisten:
63263afb9a5SDavid du Colombier 		if (r->ifcall.offset != 0) {
63363afb9a5SDavid du Colombier 			respond(r, nil);
63463afb9a5SDavid du Colombier 			break;
63563afb9a5SDavid du Colombier 		}
63663afb9a5SDavid du Colombier 		switch (lev) {
63763afb9a5SDavid du Colombier 		case Top:
63863afb9a5SDavid du Colombier 			readstr(r, st_names[c->state]);
63963afb9a5SDavid du Colombier 			break;
64063afb9a5SDavid du Colombier 		case Connection:
64163afb9a5SDavid du Colombier 		case Subchannel:
64263afb9a5SDavid du Colombier 			snprint(buf, sizeof buf, "%d", lev == Connection?
64363afb9a5SDavid du Colombier 				xconn: cnum);
64463afb9a5SDavid du Colombier 			readstr(r, buf);
64563afb9a5SDavid du Colombier 			break;
64663afb9a5SDavid du Colombier 		default:
64763afb9a5SDavid du Colombier 			snprint(buf, sizeof buf, "stread error, level %d", lev);
64863afb9a5SDavid du Colombier 			respond(r, buf);
64963afb9a5SDavid du Colombier 			return;
65063afb9a5SDavid du Colombier 		}
65163afb9a5SDavid du Colombier 		respond(r, nil);
65263afb9a5SDavid du Colombier 		break;
65363afb9a5SDavid du Colombier 	case Qclone:
65463afb9a5SDavid du Colombier 		if (r->ifcall.offset != 0) {
65563afb9a5SDavid du Colombier 			respond(r, nil);
65663afb9a5SDavid du Colombier 			break;
65763afb9a5SDavid du Colombier 		}
65863afb9a5SDavid du Colombier 		readstr(r, "Congratulations, you've achieved the impossible\n");
65963afb9a5SDavid du Colombier 		respond(r, nil);
66063afb9a5SDavid du Colombier 		break;
66163afb9a5SDavid du Colombier 	case Qdata:
66263afb9a5SDavid du Colombier 		if (lev == Top) {
66363afb9a5SDavid du Colombier 			respond(r, nil);
66463afb9a5SDavid du Colombier 			break;
66563afb9a5SDavid du Colombier 		}
66663afb9a5SDavid du Colombier 		if (lev == Connection) {
66763afb9a5SDavid du Colombier 			if (0 && c->stifle) {	/* was for coexistence */
66863afb9a5SDavid du Colombier 				c->stifle = 0;
66963afb9a5SDavid du Colombier 				if (deferredinit(c) < 0) {
67063afb9a5SDavid du Colombier 					respond(r, "deferredinit failed");
67163afb9a5SDavid du Colombier 					break;
67263afb9a5SDavid du Colombier 				}
67363afb9a5SDavid du Colombier 			}
67463afb9a5SDavid du Colombier 			if (c->cap)			/* auth capability? */
67563afb9a5SDavid du Colombier 				readstr(r, c->cap);
67663afb9a5SDavid du Colombier 			respond(r, nil);
67763afb9a5SDavid du Colombier 			break;
67863afb9a5SDavid du Colombier 		}
67963afb9a5SDavid du Colombier 
68063afb9a5SDavid du Colombier 		r->aux = (void *)threadcreate(readdata, r, Defstk);
68163afb9a5SDavid du Colombier 		break;
68263afb9a5SDavid du Colombier 	case Qlocal:
68363afb9a5SDavid du Colombier 		if (lev == Connection)
68463afb9a5SDavid du Colombier 			if (c->ctlfd < 0)
68563afb9a5SDavid du Colombier 				readstr(r, "::!0\n");
68663afb9a5SDavid du Colombier 			else {
68763afb9a5SDavid du Colombier 				n = pread(c->ctlfd, buf, 10, 0); // magic 10
68863afb9a5SDavid du Colombier 				buf[n >= 0? n: 0] = '\0';
68963afb9a5SDavid du Colombier 				snprint(path, sizeof path, "%s/tcp/%s/local",
69063afb9a5SDavid du Colombier 					mntpt, buf);
69163afb9a5SDavid du Colombier 				readfile(path, buf, sizeof buf);
69263afb9a5SDavid du Colombier 				readstr(r, buf);
69363afb9a5SDavid du Colombier 			}
69463afb9a5SDavid du Colombier 		respond(r, nil);
69563afb9a5SDavid du Colombier 		break;
69663afb9a5SDavid du Colombier 	case Qreqrem:
69763afb9a5SDavid du Colombier 		r->aux = (void *)threadcreate(readreqrem, r, Defstk);
69863afb9a5SDavid du Colombier 		break;
69963afb9a5SDavid du Colombier 	case Qstatus:
70063afb9a5SDavid du Colombier 		switch (lev) {
70163afb9a5SDavid du Colombier 		case Top:
70263afb9a5SDavid du Colombier 			readstr(r, "Impossible");
70363afb9a5SDavid du Colombier 			break;
70463afb9a5SDavid du Colombier 		case Connection:
70563afb9a5SDavid du Colombier 			readstr(r, (uint)c->state > Closed?
70663afb9a5SDavid du Colombier 				"Unknown": st_names[c->state]);
70763afb9a5SDavid du Colombier 			break;
70863afb9a5SDavid du Colombier 		case Subchannel:
70963afb9a5SDavid du Colombier 			readstr(r, (uint)sc->state > Closed?
71063afb9a5SDavid du Colombier 				"Unknown": st_names[sc->state]);
71163afb9a5SDavid du Colombier 			break;
71263afb9a5SDavid du Colombier 		}
71363afb9a5SDavid du Colombier 		respond(r, nil);
71463afb9a5SDavid du Colombier 		break;
71563afb9a5SDavid du Colombier 	case Qtcp:
71663afb9a5SDavid du Colombier 		/* connection number of underlying tcp connection */
71763afb9a5SDavid du Colombier 		if (lev == Connection)
71863afb9a5SDavid du Colombier 			if (c->ctlfd < 0)
71963afb9a5SDavid du Colombier 				readstr(r, "-1\n");
72063afb9a5SDavid du Colombier 			else {
72163afb9a5SDavid du Colombier 				n = pread(c->ctlfd, buf, 10, 0); /* magic 10 */
72263afb9a5SDavid du Colombier 				buf[n >= 0? n: 0] = '\0';
72363afb9a5SDavid du Colombier 				readstr(r, buf);
72463afb9a5SDavid du Colombier 			}
72563afb9a5SDavid du Colombier 		respond(r, nil);
72663afb9a5SDavid du Colombier 		break;
72763afb9a5SDavid du Colombier 	default:
72863afb9a5SDavid du Colombier 		respond(r, nil);
72963afb9a5SDavid du Colombier 		break;
73063afb9a5SDavid du Colombier 	}
73163afb9a5SDavid du Colombier }
73263afb9a5SDavid du Colombier 
73363afb9a5SDavid du Colombier void
73463afb9a5SDavid du Colombier readreqrem(void *a)
73563afb9a5SDavid du Colombier {
73663afb9a5SDavid du Colombier 	Ioproc *io;
73763afb9a5SDavid du Colombier 	Req *r;
73863afb9a5SDavid du Colombier 	Conn *c;
73963afb9a5SDavid du Colombier 	SSHChan *sc;
74063afb9a5SDavid du Colombier 	int fd, n, lev, cnum, xconn;
74163afb9a5SDavid du Colombier 	uvlong qidpath;
74263afb9a5SDavid du Colombier 	char buf[Arbbufsz], path[NETPATHLEN];
74363afb9a5SDavid du Colombier 
74463afb9a5SDavid du Colombier 	threadsetname("readreqrem");
74563afb9a5SDavid du Colombier 	r = a;
74663afb9a5SDavid du Colombier 	qidpath = (uvlong)r->fid->file->aux;
74763afb9a5SDavid du Colombier 	lev = qidpath >> Levshift;
74863afb9a5SDavid du Colombier 	xconn = (qidpath >> Connshift) & Connmask;
74963afb9a5SDavid du Colombier 	c = connections[xconn];
75063afb9a5SDavid du Colombier 	if (c == nil) {
75163afb9a5SDavid du Colombier 		if (lev != Top) {
75263afb9a5SDavid du Colombier 			respond(r, "Invalid connection");
75363afb9a5SDavid du Colombier 			return;
75463afb9a5SDavid du Colombier 		}
75563afb9a5SDavid du Colombier 		sc = nil;
75663afb9a5SDavid du Colombier 	} else {
75763afb9a5SDavid du Colombier 		cnum = qidpath & Chanmask;
75863afb9a5SDavid du Colombier 		sc = c->chans[cnum];
75963afb9a5SDavid du Colombier 	}
76063afb9a5SDavid du Colombier 	switch (lev) {
76163afb9a5SDavid du Colombier 	case Top:
76263afb9a5SDavid du Colombier 		if (r->ifcall.offset == 0 && keymbox.state != Empty) {
76363afb9a5SDavid du Colombier 			r->aux = 0;
76463afb9a5SDavid du Colombier 			respond(r, "Key file collision");	/* WTF? */
76563afb9a5SDavid du Colombier 			break;
76663afb9a5SDavid du Colombier 		}
76763afb9a5SDavid du Colombier 		if (r->ifcall.offset != 0) {
76863afb9a5SDavid du Colombier 			readstr(r, keymbox.msg);
76963afb9a5SDavid du Colombier 			r->aux = 0;
77063afb9a5SDavid du Colombier 			respond(r, nil);
77163afb9a5SDavid du Colombier 			if (r->ifcall.offset + r->ifcall.count >=
77263afb9a5SDavid du Colombier 			    strlen(keymbox.msg))
77363afb9a5SDavid du Colombier 				keymbox.state = Empty;
77463afb9a5SDavid du Colombier 			else
77563afb9a5SDavid du Colombier 				keymbox.state = Allocated;
77663afb9a5SDavid du Colombier 			break;
77763afb9a5SDavid du Colombier 		}
77863afb9a5SDavid du Colombier 		keymbox.state = Allocated;
77963afb9a5SDavid du Colombier 		for(;;) {
78063afb9a5SDavid du Colombier 			if (keymbox.msg == nil)
78163afb9a5SDavid du Colombier 				if (recv(keymbox.mchan, nil) < 0) {
78263afb9a5SDavid du Colombier 					r->aux = 0;
78363afb9a5SDavid du Colombier 					responderror(r);
78463afb9a5SDavid du Colombier 					keymbox.state = Empty;
78563afb9a5SDavid du Colombier 					threadexits(nil);
78663afb9a5SDavid du Colombier 				}
78763afb9a5SDavid du Colombier 			if (keymbox.state == Empty)
78863afb9a5SDavid du Colombier 				break;
78963afb9a5SDavid du Colombier 			else if (keymbox.state == Allocated) {
79063afb9a5SDavid du Colombier 				if (keymbox.msg) {
79163afb9a5SDavid du Colombier 					readstr(r, keymbox.msg);
79263afb9a5SDavid du Colombier 					if (r->ifcall.offset + r->ifcall.count
79363afb9a5SDavid du Colombier 					    >= strlen(keymbox.msg)) {
79463afb9a5SDavid du Colombier 						free(keymbox.msg);
79563afb9a5SDavid du Colombier 						keymbox.msg = nil;
79663afb9a5SDavid du Colombier 						keymbox.state = Empty;
79763afb9a5SDavid du Colombier 					}
79863afb9a5SDavid du Colombier 				}
79963afb9a5SDavid du Colombier 				break;
80063afb9a5SDavid du Colombier 			}
80163afb9a5SDavid du Colombier 		}
80263afb9a5SDavid du Colombier 		r->aux = 0;
80363afb9a5SDavid du Colombier 		respond(r, nil);
80463afb9a5SDavid du Colombier 		break;
80563afb9a5SDavid du Colombier 	case Connection:
80663afb9a5SDavid du Colombier 		if (c->ctlfd >= 0) {
80763afb9a5SDavid du Colombier 			io = ioproc();
80863afb9a5SDavid du Colombier 			seek(c->ctlfd, 0, 0);
80963afb9a5SDavid du Colombier 			n = ioread(io, c->ctlfd, buf, 10); /* magic 10 */
81063afb9a5SDavid du Colombier 			if (n < 0) {
81163afb9a5SDavid du Colombier 				r->aux = 0;
81263afb9a5SDavid du Colombier 				responderror(r);
81363afb9a5SDavid du Colombier 				closeioproc(io);
81463afb9a5SDavid du Colombier 				break;
81563afb9a5SDavid du Colombier 			}
81663afb9a5SDavid du Colombier 			buf[n] = '\0';
81763afb9a5SDavid du Colombier 			snprint(path, NETPATHLEN, "%s/tcp/%s/remote", mntpt, buf);
81863afb9a5SDavid du Colombier 			if ((fd = ioopen(io, path, OREAD)) < 0 ||
81963afb9a5SDavid du Colombier 			    (n = ioread(io, fd, buf, Arbbufsz - 1)) < 0) {
82063afb9a5SDavid du Colombier 				r->aux = 0;
82163afb9a5SDavid du Colombier 				responderror(r);
82263afb9a5SDavid du Colombier 				if (fd >= 0)
82363afb9a5SDavid du Colombier 					ioclose(io, fd);
82463afb9a5SDavid du Colombier 				closeioproc(io);
82563afb9a5SDavid du Colombier 				break;
82663afb9a5SDavid du Colombier 			}
82763afb9a5SDavid du Colombier 			ioclose(io, fd);
82863afb9a5SDavid du Colombier 			closeioproc(io);
82963afb9a5SDavid du Colombier 			buf[n] = '\0';
83063afb9a5SDavid du Colombier 			readstr(r, buf);
83163afb9a5SDavid du Colombier 		} else
83263afb9a5SDavid du Colombier 			readstr(r, "::!0\n");
83363afb9a5SDavid du Colombier 		r->aux = 0;
83463afb9a5SDavid du Colombier 		respond(r, nil);
83563afb9a5SDavid du Colombier 		break;
83663afb9a5SDavid du Colombier 	case Subchannel:
83763afb9a5SDavid du Colombier 		if ((sc->state == Closed || sc->state == Closing ||
83863afb9a5SDavid du Colombier 		    sc->state == Eof) && sc->reqq == nil && sc->dataq == nil) {
83963afb9a5SDavid du Colombier 			sshdebug(c, "sending EOF1 to channel request listener");
84063afb9a5SDavid du Colombier 			r->aux = 0;
84163afb9a5SDavid du Colombier 			respond(r, nil);
84263afb9a5SDavid du Colombier 			break;
84363afb9a5SDavid du Colombier 		}
84463afb9a5SDavid du Colombier 		while (sc->reqq == nil) {
84563afb9a5SDavid du Colombier 			if (recv(sc->reqchan, nil) < 0) {
84663afb9a5SDavid du Colombier 				r->aux = 0;
84763afb9a5SDavid du Colombier 				responderror(r);
84863afb9a5SDavid du Colombier 				threadexits(nil);
84963afb9a5SDavid du Colombier 			}
85063afb9a5SDavid du Colombier 			if ((sc->state == Closed || sc->state == Closing ||
85163afb9a5SDavid du Colombier 			    sc->state == Eof) && sc->reqq == nil &&
85263afb9a5SDavid du Colombier 			    sc->dataq == nil) {
85363afb9a5SDavid du Colombier 				sshdebug(c, "sending EOF2 to channel request "
85463afb9a5SDavid du Colombier 					"listener");
85563afb9a5SDavid du Colombier 				respexit(c, r, nil, nil);
85663afb9a5SDavid du Colombier 			}
85763afb9a5SDavid du Colombier 		}
85863afb9a5SDavid du Colombier 		n = r->ifcall.count;
85963afb9a5SDavid du Colombier 		if (sc->reqq->rem < n)
86063afb9a5SDavid du Colombier 			n = sc->reqq->rem;
86163afb9a5SDavid du Colombier 		if (n > Maxrpcbuf)
86263afb9a5SDavid du Colombier 			n = Maxrpcbuf;
86363afb9a5SDavid du Colombier 		r->ifcall.offset = 0;
86463afb9a5SDavid du Colombier 		readbuf(r, sc->reqq->st, n);
86563afb9a5SDavid du Colombier 		sc->reqq->st += n;
86663afb9a5SDavid du Colombier 		sc->reqq->rem -= n;
86763afb9a5SDavid du Colombier 		if (sc->reqq->rem <= 0) {
86863afb9a5SDavid du Colombier 			Plist *d = sc->reqq;
86963afb9a5SDavid du Colombier 			sc->reqq = sc->reqq->next;
87063afb9a5SDavid du Colombier 			free(d->pack);
87163afb9a5SDavid du Colombier 			free(d);
87263afb9a5SDavid du Colombier 		}
87363afb9a5SDavid du Colombier 		r->aux = 0;
87463afb9a5SDavid du Colombier 		respond(r, nil);
87563afb9a5SDavid du Colombier 		break;
87663afb9a5SDavid du Colombier 	}
87763afb9a5SDavid du Colombier 	threadexits(nil);
87863afb9a5SDavid du Colombier }
87963afb9a5SDavid du Colombier 
88063afb9a5SDavid du Colombier void
88163afb9a5SDavid du Colombier readdata(void *a)
88263afb9a5SDavid du Colombier {
88363afb9a5SDavid du Colombier 	Req *r;
88463afb9a5SDavid du Colombier 	Conn *c;
88563afb9a5SDavid du Colombier 	SSHChan *sc;
88663afb9a5SDavid du Colombier 	int cnum, xconn;
88763afb9a5SDavid du Colombier 	uvlong qidpath;
88863afb9a5SDavid du Colombier 
88963afb9a5SDavid du Colombier 	threadsetname("readdata");
89063afb9a5SDavid du Colombier 	r = a;
89163afb9a5SDavid du Colombier 	qidpath = (uvlong)r->fid->file->aux;
89263afb9a5SDavid du Colombier 	xconn = (qidpath >> Connshift) & Connmask;
89363afb9a5SDavid du Colombier 	c = connections[xconn];
89463afb9a5SDavid du Colombier 	if (c == nil) {
89563afb9a5SDavid du Colombier 		respond(r, "bad connection");
89663afb9a5SDavid du Colombier 		sshlog(c, "bad connection");
89763afb9a5SDavid du Colombier 		threadexits(nil);
89863afb9a5SDavid du Colombier 	}
89963afb9a5SDavid du Colombier 	cnum = qidpath & Chanmask;
90063afb9a5SDavid du Colombier 	sc = c->chans[cnum];
90163afb9a5SDavid du Colombier 	if (sc->dataq == nil && (sc->state == Closed || sc->state == Closing ||
90263afb9a5SDavid du Colombier 	    sc->state == Eof)) {
90363afb9a5SDavid du Colombier 		sshdebug(c, "sending EOF1 to channel listener");
90463afb9a5SDavid du Colombier 		r->aux = 0;
90563afb9a5SDavid du Colombier 		respond(r, nil);
90663afb9a5SDavid du Colombier 		threadexits(nil);
90763afb9a5SDavid du Colombier 	}
90863afb9a5SDavid du Colombier 	if (sc->dataq != nil) {
90963afb9a5SDavid du Colombier 		getdata(c, sc, r);
91063afb9a5SDavid du Colombier 		threadexits(nil);
91163afb9a5SDavid du Colombier 	}
91263afb9a5SDavid du Colombier 	while (sc->dataq == nil) {
91363afb9a5SDavid du Colombier 		if (recv(sc->inchan, nil) < 0) {
91463afb9a5SDavid du Colombier 			sshdebug(c, "got interrupt/error in readdata %r");
91563afb9a5SDavid du Colombier 			r->aux = 0;
91663afb9a5SDavid du Colombier 			responderror(r);
91763afb9a5SDavid du Colombier 			threadexits(nil);
91863afb9a5SDavid du Colombier 		}
91963afb9a5SDavid du Colombier 		if (sc->dataq == nil && (sc->state == Closed ||
92063afb9a5SDavid du Colombier 		    sc->state == Closing || sc->state == Eof)) {
92163afb9a5SDavid du Colombier 			sshdebug(c, "sending EOF2 to channel listener");
92263afb9a5SDavid du Colombier 			r->aux = 0;
92363afb9a5SDavid du Colombier 			respond(r, nil);
92463afb9a5SDavid du Colombier 			threadexits(nil);
92563afb9a5SDavid du Colombier 		}
92663afb9a5SDavid du Colombier 	}
92763afb9a5SDavid du Colombier 	getdata(c, sc, r);
92863afb9a5SDavid du Colombier 	threadexits(nil);
92963afb9a5SDavid du Colombier }
93063afb9a5SDavid du Colombier 
93163afb9a5SDavid du Colombier void
93263afb9a5SDavid du Colombier stwrite(Req *r)
93363afb9a5SDavid du Colombier {
93463afb9a5SDavid du Colombier 	Conn *c;
93563afb9a5SDavid du Colombier 	SSHChan *ch;
93663afb9a5SDavid du Colombier 	int lev, xconn;
93763afb9a5SDavid du Colombier 	uvlong qidpath;
93863afb9a5SDavid du Colombier 
93963afb9a5SDavid du Colombier 	threadsetname("stwrite");
94063afb9a5SDavid du Colombier 	qidpath = (uvlong)r->fid->file->aux;
94163afb9a5SDavid du Colombier 	lev = qidpath >> Levshift;
94263afb9a5SDavid du Colombier 	xconn = (qidpath >> Connshift) & Connmask;
94363afb9a5SDavid du Colombier 	c = connections[xconn];
94463afb9a5SDavid du Colombier 	if (c == nil) {
94563afb9a5SDavid du Colombier 		respond(r, "invalid connection");
94663afb9a5SDavid du Colombier 		return;
94763afb9a5SDavid du Colombier 	}
94863afb9a5SDavid du Colombier 	ch = c->chans[qidpath & Chanmask];
94963afb9a5SDavid du Colombier 	switch ((ulong)(qidpath & Qtypemask)) {
95063afb9a5SDavid du Colombier 	case Qclone:
95163afb9a5SDavid du Colombier 	case Qctl:
95263afb9a5SDavid du Colombier 		r->aux = (void *)threadcreate(writectlproc, r, Defstk);
95363afb9a5SDavid du Colombier 		break;
95463afb9a5SDavid du Colombier 	case Qdata:
95563afb9a5SDavid du Colombier 		r->ofcall.count = r->ifcall.count;
95663afb9a5SDavid du Colombier 		if (lev == Top || lev == Connection ||
95763afb9a5SDavid du Colombier 		    c->state == Closed || c->state == Closing ||
95863afb9a5SDavid du Colombier 		    ch->state == Closed || ch->state == Closing) {
95963afb9a5SDavid du Colombier 			respond(r, nil);
96063afb9a5SDavid du Colombier 			break;
96163afb9a5SDavid du Colombier 		}
96263afb9a5SDavid du Colombier 		if (0 && c->stifle) {		/* was for coexistence */
96363afb9a5SDavid du Colombier 			c->stifle = 0;
96463afb9a5SDavid du Colombier 			if (deferredinit(c) < 0) {
96563afb9a5SDavid du Colombier 				respond(r, "deferredinit failed");
96663afb9a5SDavid du Colombier 				break;
96763afb9a5SDavid du Colombier 			}
96863afb9a5SDavid du Colombier 		}
96963afb9a5SDavid du Colombier 		r->aux = (void *)threadcreate(writedataproc, r, Defstk);
97063afb9a5SDavid du Colombier 		break;
97163afb9a5SDavid du Colombier 	case Qreqrem:
97263afb9a5SDavid du Colombier 		r->aux = (void *)threadcreate(writereqremproc, r, Defstk);
97363afb9a5SDavid du Colombier 		break;
97463afb9a5SDavid du Colombier 	default:
97563afb9a5SDavid du Colombier 		respond(r, nil);
97663afb9a5SDavid du Colombier 		break;
97763afb9a5SDavid du Colombier 	}
97863afb9a5SDavid du Colombier }
97963afb9a5SDavid du Colombier 
98063afb9a5SDavid du Colombier static int
98163afb9a5SDavid du Colombier dialbyhand(Conn *c, int ntok, char *toks[])
98263afb9a5SDavid du Colombier {
98363afb9a5SDavid du Colombier 	/*
98463afb9a5SDavid du Colombier 	 * this uses /net/tcp to connect directly.
98563afb9a5SDavid du Colombier 	 * should use dial(2) instead of doing it by hand.
98663afb9a5SDavid du Colombier 	 */
98763afb9a5SDavid du Colombier 	sshdebug(c, "tcp connect %s %s", toks[1], ntok > 3? toks[2]: "");
98863afb9a5SDavid du Colombier 	return fprint(c->ctlfd, "connect %s %s", toks[1], ntok > 3? toks[2]: "");
98963afb9a5SDavid du Colombier }
99063afb9a5SDavid du Colombier 
99163afb9a5SDavid du Colombier static void
99263afb9a5SDavid du Colombier userauth(Conn *c, Req *r, char *buf, int ntok, char *toks[])
99363afb9a5SDavid du Colombier {
99463afb9a5SDavid du Colombier 	int n;
99563afb9a5SDavid du Colombier 	char *attrs[5];
99663afb9a5SDavid du Colombier 	Packet *p;
99763afb9a5SDavid du Colombier 
99863afb9a5SDavid du Colombier 	if (ntok < 3 || ntok > 4)
99963afb9a5SDavid du Colombier 		respexit(c, r, buf, "bad connect command");
100063afb9a5SDavid du Colombier 	if (!c->service)
100163afb9a5SDavid du Colombier 		c->service = estrdup9p(toks[0]);
100263afb9a5SDavid du Colombier 	if (c->user)
100363afb9a5SDavid du Colombier 		free(c->user);
100463afb9a5SDavid du Colombier 	c->user = estrdup9p(toks[2]);
100563afb9a5SDavid du Colombier 	sshdebug(c, "userauth for user %s", c->user);
100663afb9a5SDavid du Colombier 
100763afb9a5SDavid du Colombier 	if (ntok == 4 && strcmp(toks[1], "k") == 0) {
100863afb9a5SDavid du Colombier 		if (c->authkey) {
100963afb9a5SDavid du Colombier 			free(c->authkey);
101063afb9a5SDavid du Colombier 			c->authkey = nil;
101163afb9a5SDavid du Colombier 		}
101263afb9a5SDavid du Colombier 		if (c->password)
101363afb9a5SDavid du Colombier 			free(c->password);
101463afb9a5SDavid du Colombier 		c->password = estrdup9p(toks[3]);
101563afb9a5SDavid du Colombier 		sshdebug(c, "userauth got password");
101663afb9a5SDavid du Colombier 	} else {
101763afb9a5SDavid du Colombier 		if (c->password) {
101863afb9a5SDavid du Colombier 			free(c->password);
101963afb9a5SDavid du Colombier 			c->password = nil;
102063afb9a5SDavid du Colombier 		}
102163afb9a5SDavid du Colombier 		memset(attrs, 0, sizeof attrs);
102263afb9a5SDavid du Colombier 		attrs[0] = "proto=rsa";
102363afb9a5SDavid du Colombier 		attrs[1] = "!dk?";
102463afb9a5SDavid du Colombier 		attrs[2] = smprint("user=%s", c->user);
102563afb9a5SDavid du Colombier 		attrs[3] = smprint("sys=%s", c->remote);
102663afb9a5SDavid du Colombier 		if (c->authkey)
102763afb9a5SDavid du Colombier 			free(c->authkey);
102863afb9a5SDavid du Colombier 		sshdebug(c, "userauth trying rsa");
102963afb9a5SDavid du Colombier 		if (ntok == 3)
103063afb9a5SDavid du Colombier 			c->authkey = factlookup(4, 2, attrs);
103163afb9a5SDavid du Colombier 		else {
103263afb9a5SDavid du Colombier 			attrs[4] = toks[3];
103363afb9a5SDavid du Colombier 			c->authkey = factlookup(5, 2, attrs);
103463afb9a5SDavid du Colombier 		}
103563afb9a5SDavid du Colombier 		free(attrs[2]);
103663afb9a5SDavid du Colombier 		free(attrs[3]);
103763afb9a5SDavid du Colombier 	}
103863afb9a5SDavid du Colombier 
103963afb9a5SDavid du Colombier 	if (!c->password && !c->authkey)
104063afb9a5SDavid du Colombier 		respexit(c, r, buf, "no auth info");
104163afb9a5SDavid du Colombier 	else if (c->state != Authing) {
104263afb9a5SDavid du Colombier 		p = new_packet(c);
104363afb9a5SDavid du Colombier 		add_byte(p, SSH_MSG_SERVICE_REQUEST);
104463afb9a5SDavid du Colombier 		add_string(p, c->service);
104563afb9a5SDavid du Colombier 		n = finish_packet(p);
104663afb9a5SDavid du Colombier 		sshdebug(c, "sending msg svc req for %s", c->service);
104763afb9a5SDavid du Colombier 		if (writeio(c->dio, c->datafd, p->nlength, n) != n) {
104863afb9a5SDavid du Colombier 			sshdebug(c, "authing write failed: %r");
104963afb9a5SDavid du Colombier 			free(p);
105063afb9a5SDavid du Colombier 			r->aux = 0;
105163afb9a5SDavid du Colombier 			responderror(r);
105263afb9a5SDavid du Colombier 			free(buf);
105363afb9a5SDavid du Colombier 			threadexits(nil);
105463afb9a5SDavid du Colombier 		}
105563afb9a5SDavid du Colombier 		free(p);
105663afb9a5SDavid du Colombier 	} else
105763afb9a5SDavid du Colombier 		if (client_auth(c, c->dio) < 0)
105863afb9a5SDavid du Colombier 			respexit(c, r, buf, "ssh-userauth client auth failed");
105963afb9a5SDavid du Colombier 	qlock(&c->l);
106063afb9a5SDavid du Colombier 	if (c->state != Established) {
106163afb9a5SDavid du Colombier 		sshdebug(c, "sleeping for auth");
106263afb9a5SDavid du Colombier 		rsleep(&c->r);
106363afb9a5SDavid du Colombier 	}
106463afb9a5SDavid du Colombier 	qunlock(&c->l);
106563afb9a5SDavid du Colombier 	if (c->state != Established)
106663afb9a5SDavid du Colombier 		respexit(c, r, buf, "ssh-userath auth failed (not Established)");
106763afb9a5SDavid du Colombier }
106863afb9a5SDavid du Colombier 
106963afb9a5SDavid du Colombier void
107063afb9a5SDavid du Colombier writectlproc(void *a)
107163afb9a5SDavid du Colombier {
107263afb9a5SDavid du Colombier 	Req *r;
107363afb9a5SDavid du Colombier 	Packet *p;
107463afb9a5SDavid du Colombier 	Conn *c;
107563afb9a5SDavid du Colombier 	SSHChan *ch;
107663afb9a5SDavid du Colombier 	char *tcpconn2, *buf, *toks[4];
107763afb9a5SDavid du Colombier 	int n, ntok, lev, xconn;
107863afb9a5SDavid du Colombier 	uvlong qidpath;
107963afb9a5SDavid du Colombier 	char path[NETPATHLEN], tcpconn[Numbsz];
108063afb9a5SDavid du Colombier 
108163afb9a5SDavid du Colombier 	threadsetname("writectlproc");
108263afb9a5SDavid du Colombier 	r = a;
108363afb9a5SDavid du Colombier 	qidpath = (uvlong)r->fid->file->aux;
108463afb9a5SDavid du Colombier 	lev = qidpath >> Levshift;
108563afb9a5SDavid du Colombier 	xconn = (qidpath >> Connshift) & Connmask;
108663afb9a5SDavid du Colombier 
108763afb9a5SDavid du Colombier 	c = connections[xconn];
108863afb9a5SDavid du Colombier 	if (c == nil) {
108963afb9a5SDavid du Colombier 		respond(r, "bad connection");
109063afb9a5SDavid du Colombier 		sshlog(c, "bad connection");
109163afb9a5SDavid du Colombier 		threadexits(nil);
109263afb9a5SDavid du Colombier 	}
109363afb9a5SDavid du Colombier 	ch = c->chans[qidpath & Chanmask];
109463afb9a5SDavid du Colombier 
109563afb9a5SDavid du Colombier 	if (r->ifcall.count <= Numbsz)
109663afb9a5SDavid du Colombier 		buf = emalloc9p(Numbsz + 1);
109763afb9a5SDavid du Colombier 	else
109863afb9a5SDavid du Colombier 		buf = emalloc9p(r->ifcall.count + 1);
109963afb9a5SDavid du Colombier 	memmove(buf, r->ifcall.data, r->ifcall.count);
110063afb9a5SDavid du Colombier 	buf[r->ifcall.count] = '\0';
110163afb9a5SDavid du Colombier 
110263afb9a5SDavid du Colombier 	sshdebug(c, "level %d writectl: %s", lev, buf);
110363afb9a5SDavid du Colombier 	ntok = tokenize(buf, toks, nelem(toks));
110463afb9a5SDavid du Colombier 	switch (lev) {
110563afb9a5SDavid du Colombier 	case Connection:
110663afb9a5SDavid du Colombier 		if (strcmp(toks[0], "id") == 0) {	/* was for sshswitch */
110763afb9a5SDavid du Colombier 			if (ntok < 2)
110863afb9a5SDavid du Colombier 				respexit(c, r, buf, "bad id request");
110963afb9a5SDavid du Colombier 			strncpy(c->idstring, toks[1], sizeof c->idstring);
111063afb9a5SDavid du Colombier 			sshdebug(c, "id %s", toks[1]);
111163afb9a5SDavid du Colombier 			break;
111263afb9a5SDavid du Colombier 		}
111363afb9a5SDavid du Colombier 		if (strcmp(toks[0], "connect") == 0) {
111463afb9a5SDavid du Colombier 			if (ntok < 2)
111563afb9a5SDavid du Colombier 				respexit(c, r, buf, "bad connect request");
111663afb9a5SDavid du Colombier 			/*
111763afb9a5SDavid du Colombier 			 * should use dial(2) instead of doing it by hand.
111863afb9a5SDavid du Colombier 			 */
111963afb9a5SDavid du Colombier 			memset(tcpconn, '\0', sizeof(tcpconn));
112063afb9a5SDavid du Colombier 			pread(c->ctlfd, tcpconn, sizeof tcpconn, 0);
112163afb9a5SDavid du Colombier 			dialbyhand(c, ntok, toks);
112263afb9a5SDavid du Colombier 
112363afb9a5SDavid du Colombier 			c->role = Client;
112463afb9a5SDavid du Colombier 			/* Override the PKA list; we can take any in */
112563afb9a5SDavid du Colombier 			pkas[0] = &rsa_pka;
112663afb9a5SDavid du Colombier 			pkas[1] = &dss_pka;
112763afb9a5SDavid du Colombier 			pkas[2] = nil;
112863afb9a5SDavid du Colombier 			tcpconn2 = estrdup9p(tcpconn);
112963afb9a5SDavid du Colombier 
113063afb9a5SDavid du Colombier 			/* swap id strings, negotiate crypto */
113163afb9a5SDavid du Colombier 			if (dohandshake(c, tcpconn2) < 0) {
113263afb9a5SDavid du Colombier 				sshlog(c, "connect handshake failed: "
113363afb9a5SDavid du Colombier 					"tcp conn %s", tcpconn2);
113463afb9a5SDavid du Colombier 				free(tcpconn2);
113563afb9a5SDavid du Colombier 				respexit(c, r, buf, "connect handshake failed");
113663afb9a5SDavid du Colombier 			}
113763afb9a5SDavid du Colombier 			free(tcpconn2);
113863afb9a5SDavid du Colombier 			keymbox.state = Empty;
113963afb9a5SDavid du Colombier 			nbsendul(keymbox.mchan, 1);
114063afb9a5SDavid du Colombier 			break;
114163afb9a5SDavid du Colombier 		}
114263afb9a5SDavid du Colombier 
114363afb9a5SDavid du Colombier 		if (c->state == Closed || c->state == Closing)
114463afb9a5SDavid du Colombier 			respexit(c, r, buf, "connection closed");
114563afb9a5SDavid du Colombier 		if (strcmp(toks[0], "ssh-userauth") == 0)
114663afb9a5SDavid du Colombier 			userauth(c, r, buf, ntok, toks);
114763afb9a5SDavid du Colombier 		else if (strcmp(toks[0], "ssh-connection") == 0) {
114863afb9a5SDavid du Colombier 			/* your ad here */
114963afb9a5SDavid du Colombier 		} else if (strcmp(toks[0], "hangup") == 0) {
115063afb9a5SDavid du Colombier 			if (c->rpid >= 0)
115163afb9a5SDavid du Colombier 				threadint(c->rpid);
115263afb9a5SDavid du Colombier 			shutdown(c);
115363afb9a5SDavid du Colombier 		} else if (strcmp(toks[0], "announce") == 0) {
115463afb9a5SDavid du Colombier 			sshdebug(c, "got %s argument for announce", toks[1]);
115563afb9a5SDavid du Colombier 			write(c->ctlfd, r->ifcall.data, r->ifcall.count);
115663afb9a5SDavid du Colombier 		} else if (strcmp(toks[0], "accept") == 0) {
115763afb9a5SDavid du Colombier 			/* should use dial(2) instead of diddling /net/tcp */
115863afb9a5SDavid du Colombier 			memset(tcpconn, '\0', sizeof(tcpconn));
115963afb9a5SDavid du Colombier 			pread(c->ctlfd, tcpconn, sizeof tcpconn, 0);
116063afb9a5SDavid du Colombier 			fprint(c->ctlfd, "accept %s", tcpconn);
116163afb9a5SDavid du Colombier 
116263afb9a5SDavid du Colombier 			c->role = Server;
116363afb9a5SDavid du Colombier 			tcpconn2 = estrdup9p(tcpconn);
116463afb9a5SDavid du Colombier 			/* swap id strings, negotiate crypto */
116563afb9a5SDavid du Colombier 			if (dohandshake(c, tcpconn2) < 0) {
116663afb9a5SDavid du Colombier 				sshlog(c, "accept handshake failed: "
116763afb9a5SDavid du Colombier 					"tcp conn %s", tcpconn2);
116863afb9a5SDavid du Colombier 				free(tcpconn2);
116963afb9a5SDavid du Colombier 				shutdown(c);
117063afb9a5SDavid du Colombier 				respexit(c, r, buf, "accept handshake failed");
117163afb9a5SDavid du Colombier 			}
117263afb9a5SDavid du Colombier 			free(tcpconn2);
117363afb9a5SDavid du Colombier 		} else if (strcmp(toks[0], "reject") == 0) {
117463afb9a5SDavid du Colombier 			memset(tcpconn, '\0', sizeof(tcpconn));
117563afb9a5SDavid du Colombier 			pread(c->ctlfd, tcpconn, sizeof tcpconn, 0);
117663afb9a5SDavid du Colombier 
117763afb9a5SDavid du Colombier 			snprint(path, NETPATHLEN, "%s/tcp/%s/data", mntpt, tcpconn);
117863afb9a5SDavid du Colombier 			c->datafd = open(path, ORDWR);
117963afb9a5SDavid du Colombier 
118063afb9a5SDavid du Colombier 			p = new_packet(c);
118163afb9a5SDavid du Colombier 			add_byte(p, SSH_MSG_DISCONNECT);
118263afb9a5SDavid du Colombier 			add_byte(p, SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT);
118363afb9a5SDavid du Colombier 			add_string(p, toks[2]);
118463afb9a5SDavid du Colombier 			add_string(p, "EN");
118563afb9a5SDavid du Colombier 			n = finish_packet(p);
118663afb9a5SDavid du Colombier 			if (c->dio && c->datafd >= 0)
118763afb9a5SDavid du Colombier 				iowrite(c->dio, c->datafd, p->nlength, n);
118863afb9a5SDavid du Colombier 			free(p);
118963afb9a5SDavid du Colombier 			if (c->ctlfd >= 0)
119063afb9a5SDavid du Colombier 				fprint(c->ctlfd, "reject %s %s", buf, toks[2]);
119163afb9a5SDavid du Colombier 			if (c->rpid >= 0)
119263afb9a5SDavid du Colombier 				threadint(c->rpid);
119363afb9a5SDavid du Colombier 			shutdown(c);
119463afb9a5SDavid du Colombier 		}
119563afb9a5SDavid du Colombier 		break;
119663afb9a5SDavid du Colombier 	case Subchannel:
119763afb9a5SDavid du Colombier 		if (c->state == Closed || c->state == Closing)
119863afb9a5SDavid du Colombier 			respexit(c, r, buf, "channel closed");
119963afb9a5SDavid du Colombier 		if (strcmp(toks[0], "connect") == 0) {
120063afb9a5SDavid du Colombier 			p = new_packet(c);
120163afb9a5SDavid du Colombier 			add_byte(p, SSH_MSG_CHANNEL_OPEN);
120263afb9a5SDavid du Colombier 			sshdebug(c, "chan writectl: connect %s",
120363afb9a5SDavid du Colombier 				ntok > 1? toks[1]: "session");
120463afb9a5SDavid du Colombier 			add_string(p, ntok > 1? toks[1]: "session");
120563afb9a5SDavid du Colombier 			add_uint32(p, ch->id);
120663afb9a5SDavid du Colombier 			add_uint32(p, Maxpayload);
120763afb9a5SDavid du Colombier 			add_uint32(p, Maxrpcbuf);
120863afb9a5SDavid du Colombier 			/* more stuff if it's an x11 session */
120963afb9a5SDavid du Colombier 			n = finish_packet(p);
121063afb9a5SDavid du Colombier 			iowrite(c->dio, c->datafd, p->nlength, n);
121163afb9a5SDavid du Colombier 			free(p);
121263afb9a5SDavid du Colombier 			qlock(&c->l);
121363afb9a5SDavid du Colombier 			if (ch->otherid == -1)
121463afb9a5SDavid du Colombier 				rsleep(&ch->r);
121563afb9a5SDavid du Colombier 			qunlock(&c->l);
121663afb9a5SDavid du Colombier 		} else if (strcmp(toks[0], "global") == 0) {
121763afb9a5SDavid du Colombier 			/* your ad here */
121863afb9a5SDavid du Colombier 		} else if (strcmp(toks[0], "hangup") == 0) {
121963afb9a5SDavid du Colombier 			if (ch->state != Closed && ch->state != Closing) {
122063afb9a5SDavid du Colombier 				ch->state = Closing;
122163afb9a5SDavid du Colombier 				if (ch->otherid != -1) {
122263afb9a5SDavid du Colombier 					p = new_packet(c);
122363afb9a5SDavid du Colombier 					add_byte(p, SSH_MSG_CHANNEL_CLOSE);
122463afb9a5SDavid du Colombier 					add_uint32(p, ch->otherid);
122563afb9a5SDavid du Colombier 					n = finish_packet(p);
122663afb9a5SDavid du Colombier 					iowrite(c->dio, c->datafd, p->nlength, n);
122763afb9a5SDavid du Colombier 					free(p);
122863afb9a5SDavid du Colombier 				}
122963afb9a5SDavid du Colombier 				qlock(&c->l);
123063afb9a5SDavid du Colombier 				rwakeup(&ch->r);
123163afb9a5SDavid du Colombier 				qunlock(&c->l);
123263afb9a5SDavid du Colombier 				nbsendul(ch->inchan, 1);
123363afb9a5SDavid du Colombier 				nbsendul(ch->reqchan, 1);
123463afb9a5SDavid du Colombier 			}
123563afb9a5SDavid du Colombier 			for (n = 0; n < MAXCONN && (c->chans[n] == nil ||
123663afb9a5SDavid du Colombier 			    c->chans[n]->state == Empty ||
123763afb9a5SDavid du Colombier 			    c->chans[n]->state == Closing ||
123863afb9a5SDavid du Colombier 			    c->chans[n]->state == Closed); ++n)
123963afb9a5SDavid du Colombier 				;
124063afb9a5SDavid du Colombier 			if (n >= MAXCONN) {
124163afb9a5SDavid du Colombier 				if (c->rpid >= 0)
124263afb9a5SDavid du Colombier 					threadint(c->rpid);
124363afb9a5SDavid du Colombier 				shutdown(c);
124463afb9a5SDavid du Colombier 			}
124563afb9a5SDavid du Colombier 		} else if (strcmp(toks[0], "announce") == 0) {
124663afb9a5SDavid du Colombier 			sshdebug(c, "got argument `%s' for chan announce",
124763afb9a5SDavid du Colombier 				toks[1]);
124863afb9a5SDavid du Colombier 			free(ch->ann);
124963afb9a5SDavid du Colombier 			ch->ann = estrdup9p(toks[1]);
125063afb9a5SDavid du Colombier 		}
125163afb9a5SDavid du Colombier 		break;
125263afb9a5SDavid du Colombier 	}
125363afb9a5SDavid du Colombier 	r->ofcall.count = r->ifcall.count;
125463afb9a5SDavid du Colombier 	r->aux = 0;
125563afb9a5SDavid du Colombier 	respond(r, nil);
125663afb9a5SDavid du Colombier 	free(buf);
125763afb9a5SDavid du Colombier 	threadexits(nil);
125863afb9a5SDavid du Colombier }
125963afb9a5SDavid du Colombier 
126063afb9a5SDavid du Colombier void
126163afb9a5SDavid du Colombier writereqremproc(void *a)
126263afb9a5SDavid du Colombier {
126363afb9a5SDavid du Colombier 	Req *r;
126463afb9a5SDavid du Colombier 	Packet *p;
126563afb9a5SDavid du Colombier 	Conn *c;
126663afb9a5SDavid du Colombier 	SSHChan *ch;
126763afb9a5SDavid du Colombier 	char *cmd, *q, *buf, *toks[4];
126863afb9a5SDavid du Colombier 	int n, ntok, lev, xconn;
126963afb9a5SDavid du Colombier 	uvlong qidpath;
127063afb9a5SDavid du Colombier 
127163afb9a5SDavid du Colombier 	threadsetname("writereqremproc");
127263afb9a5SDavid du Colombier 	r = a;
127363afb9a5SDavid du Colombier 	qidpath = (uvlong)r->fid->file->aux;
127463afb9a5SDavid du Colombier 	lev = qidpath >> Levshift;
127563afb9a5SDavid du Colombier 	xconn = (qidpath >> Connshift) & Connmask;
127663afb9a5SDavid du Colombier 	c = connections[xconn];
127763afb9a5SDavid du Colombier 	if (c == nil) {
127863afb9a5SDavid du Colombier 		respond(r, "Invalid connection");
127963afb9a5SDavid du Colombier 		threadexits(nil);
128063afb9a5SDavid du Colombier 	}
128163afb9a5SDavid du Colombier 	ch = c->chans[qidpath & Chanmask];
128263afb9a5SDavid du Colombier 	if (r->ifcall.count <= 10)
128363afb9a5SDavid du Colombier 		buf = emalloc9p(10 + 1);
128463afb9a5SDavid du Colombier 	else
128563afb9a5SDavid du Colombier 		buf = emalloc9p(r->ifcall.count + 1);
128663afb9a5SDavid du Colombier 	memmove(buf, r->ifcall.data, r->ifcall.count);
128763afb9a5SDavid du Colombier 	buf[r->ifcall.count] = '\0';
128863afb9a5SDavid du Colombier 	sshdebug(c, "writereqrem: %s", buf);
128963afb9a5SDavid du Colombier 	ntok = tokenize(buf, toks, nelem(toks));
129063afb9a5SDavid du Colombier 
129163afb9a5SDavid du Colombier 	if (lev == Top) {
129263afb9a5SDavid du Colombier 		free(keymbox.msg);
129363afb9a5SDavid du Colombier 		keymbox.msg = buf;
129463afb9a5SDavid du Colombier 		nbsendul(keymbox.mchan, 1);
129563afb9a5SDavid du Colombier 		r->ofcall.count = r->ifcall.count;
129663afb9a5SDavid du Colombier 		respexit(c, r, nil, nil);
129763afb9a5SDavid du Colombier 	}
129863afb9a5SDavid du Colombier 
129963afb9a5SDavid du Colombier 	r->ofcall.count = r->ifcall.count;
130063afb9a5SDavid du Colombier 	if (c->state == Closed  || c->state == Closing ||
130163afb9a5SDavid du Colombier 	    ch->state == Closed || ch->state == Closing)
130263afb9a5SDavid du Colombier 		respexit(c, r, buf, nil);
130363afb9a5SDavid du Colombier 
130463afb9a5SDavid du Colombier 	p = new_packet(c);
130563afb9a5SDavid du Colombier 	if (strcmp(toks[0], "success") == 0) {
130663afb9a5SDavid du Colombier 		add_byte(p, SSH_MSG_CHANNEL_SUCCESS);
130763afb9a5SDavid du Colombier 		add_uint32(p, ch->otherid);
130863afb9a5SDavid du Colombier 	} else if (strcmp(toks[0], "failure") == 0) {
130963afb9a5SDavid du Colombier 		add_byte(p, SSH_MSG_CHANNEL_FAILURE);
131063afb9a5SDavid du Colombier 		add_uint32(p, ch->otherid);
131163afb9a5SDavid du Colombier 	} else if (strcmp(toks[0], "close") == 0) {
131263afb9a5SDavid du Colombier 		ch->state = Closing;
131363afb9a5SDavid du Colombier 		add_byte(p, SSH_MSG_CHANNEL_CLOSE);
131463afb9a5SDavid du Colombier 		add_uint32(p, ch->otherid);
131563afb9a5SDavid du Colombier 	} else if (strcmp(toks[0], "shell") == 0) {
131663afb9a5SDavid du Colombier 		ch->state = Established;
131763afb9a5SDavid du Colombier 		/*
131863afb9a5SDavid du Colombier 		 * Some servers *cough*OpenSSH*cough* don't seem to be able
131963afb9a5SDavid du Colombier 		 * to intelligently handle a shell with no pty.
132063afb9a5SDavid du Colombier 		 */
132163afb9a5SDavid du Colombier 		add_byte(p, SSH_MSG_CHANNEL_REQUEST);
132263afb9a5SDavid du Colombier 		add_uint32(p, ch->otherid);
132363afb9a5SDavid du Colombier 		add_string(p, "pty-req");
132463afb9a5SDavid du Colombier 		add_byte(p, 0);
132563afb9a5SDavid du Colombier 		if (ntok == 1)
132663afb9a5SDavid du Colombier 			add_string(p, "dumb");
132763afb9a5SDavid du Colombier 		else
132863afb9a5SDavid du Colombier 			add_string(p, toks[1]);
132963afb9a5SDavid du Colombier 		add_uint32(p, 0);
133063afb9a5SDavid du Colombier 		add_uint32(p, 0);
133163afb9a5SDavid du Colombier 		add_uint32(p, 0);
133263afb9a5SDavid du Colombier 		add_uint32(p, 0);
133363afb9a5SDavid du Colombier 		add_string(p, "");
133463afb9a5SDavid du Colombier 		n = finish_packet(p);
133563afb9a5SDavid du Colombier 		iowrite(c->dio, c->datafd, p->nlength, n);
133663afb9a5SDavid du Colombier 		init_packet(p);
133763afb9a5SDavid du Colombier 		p->c = c;
133863afb9a5SDavid du Colombier 		add_byte(p, SSH_MSG_CHANNEL_REQUEST);
133963afb9a5SDavid du Colombier 		add_uint32(p, ch->otherid);
134063afb9a5SDavid du Colombier 		add_string(p, "shell");
134163afb9a5SDavid du Colombier 		add_byte(p, 0);
134263afb9a5SDavid du Colombier 		sshdebug(c, "sending shell request: rlength=%lud twindow=%lud",
134363afb9a5SDavid du Colombier 			p->rlength, ch->twindow);
134463afb9a5SDavid du Colombier 	} else if (strcmp(toks[0], "exec") == 0) {
134563afb9a5SDavid du Colombier 		ch->state = Established;
134663afb9a5SDavid du Colombier 		add_byte(p, SSH_MSG_CHANNEL_REQUEST);
134763afb9a5SDavid du Colombier 		add_uint32(p, ch->otherid);
134863afb9a5SDavid du Colombier 		add_string(p, "exec");
134963afb9a5SDavid du Colombier 		add_byte(p, 0);
135063afb9a5SDavid du Colombier 		cmd = emalloc9p(Bigbufsz);
135163afb9a5SDavid du Colombier 		q = seprint(cmd, cmd+Bigbufsz, "%s", toks[1]);
135263afb9a5SDavid du Colombier 		for (n = 2; n < ntok; ++n) {
135363afb9a5SDavid du Colombier 			q = seprint(q, cmd+Bigbufsz, " %s", toks[n]);
135463afb9a5SDavid du Colombier 			if (q == nil)
135563afb9a5SDavid du Colombier 				break;
135663afb9a5SDavid du Colombier 		}
135763afb9a5SDavid du Colombier 		add_string(p, cmd);
135863afb9a5SDavid du Colombier 		free(cmd);
135963afb9a5SDavid du Colombier 	} else
136063afb9a5SDavid du Colombier 		respexit(c, r, buf, "bad request command");
136163afb9a5SDavid du Colombier 	n = finish_packet(p);
136263afb9a5SDavid du Colombier 	iowrite(c->dio, c->datafd, p->nlength, n);
136363afb9a5SDavid du Colombier 	free(p);
136463afb9a5SDavid du Colombier 	respexit(c, r, buf, nil);
136563afb9a5SDavid du Colombier }
136663afb9a5SDavid du Colombier 
136763afb9a5SDavid du Colombier void
136863afb9a5SDavid du Colombier writedataproc(void *a)
136963afb9a5SDavid du Colombier {
137063afb9a5SDavid du Colombier 	Req *r;
137163afb9a5SDavid du Colombier 	Packet *p;
137263afb9a5SDavid du Colombier 	Conn *c;
137363afb9a5SDavid du Colombier 	SSHChan *ch;
137463afb9a5SDavid du Colombier 	int n, xconn;
137563afb9a5SDavid du Colombier 	uvlong qidpath;
137663afb9a5SDavid du Colombier 
137763afb9a5SDavid du Colombier 	threadsetname("writedataproc");
137863afb9a5SDavid du Colombier 	r = a;
137963afb9a5SDavid du Colombier 	qidpath = (uvlong)r->fid->file->aux;
138063afb9a5SDavid du Colombier 	xconn = (qidpath >> Connshift) & Connmask;
138163afb9a5SDavid du Colombier 	c = connections[xconn];
138263afb9a5SDavid du Colombier 	if (c == nil) {
138363afb9a5SDavid du Colombier 		respond(r, "Invalid connection");
138463afb9a5SDavid du Colombier 		threadexits(nil);
138563afb9a5SDavid du Colombier 	}
138663afb9a5SDavid du Colombier 	ch = c->chans[qidpath & Chanmask];
138763afb9a5SDavid du Colombier 
138863afb9a5SDavid du Colombier 	p = new_packet(c);
138963afb9a5SDavid du Colombier 	add_byte(p, SSH_MSG_CHANNEL_DATA);
139063afb9a5SDavid du Colombier 	hnputl(p->payload+1, ch->otherid);
139163afb9a5SDavid du Colombier 	p->rlength += 4;
139263afb9a5SDavid du Colombier 	add_block(p, r->ifcall.data, r->ifcall.count);
139363afb9a5SDavid du Colombier 	n = finish_packet(p);
139463afb9a5SDavid du Colombier 
139563afb9a5SDavid du Colombier 	if (ch->sent + p->rlength > ch->twindow) {
139663afb9a5SDavid du Colombier 		qlock(&ch->xmtlock);
139763afb9a5SDavid du Colombier 		while (ch->sent + p->rlength > ch->twindow)
139863afb9a5SDavid du Colombier 			rsleep(&ch->xmtrendez);
139963afb9a5SDavid du Colombier 		qunlock(&ch->xmtlock);
140063afb9a5SDavid du Colombier 	}
140163afb9a5SDavid du Colombier 	iowrite(c->dio, c->datafd, p->nlength, n);
140263afb9a5SDavid du Colombier 	respexit(c, r, p, nil);
140363afb9a5SDavid du Colombier }
140463afb9a5SDavid du Colombier 
140563afb9a5SDavid du Colombier /*
140663afb9a5SDavid du Colombier  * Although this is named stclunk, it's attached to the destroyfid
140763afb9a5SDavid du Colombier  * member of the Srv struct.  It turns out there's no member
140863afb9a5SDavid du Colombier  * called clunk.  But if there are no other references, a 9P Tclunk
140963afb9a5SDavid du Colombier  * will end up calling destroyfid.
141063afb9a5SDavid du Colombier  */
141163afb9a5SDavid du Colombier void
141263afb9a5SDavid du Colombier stclunk(Fid *f)
141363afb9a5SDavid du Colombier {
141463afb9a5SDavid du Colombier 	Packet *p;
141563afb9a5SDavid du Colombier 	Conn *c;
141663afb9a5SDavid du Colombier 	SSHChan *sc;
141763afb9a5SDavid du Colombier 	int n, lev, cnum, chnum;
141863afb9a5SDavid du Colombier 	uvlong qidpath;
141963afb9a5SDavid du Colombier 
142063afb9a5SDavid du Colombier 	threadsetname("stclunk");
142163afb9a5SDavid du Colombier 	if (f == nil || f->file == nil)
142263afb9a5SDavid du Colombier 		return;
142363afb9a5SDavid du Colombier 	qidpath = (uvlong)f->file->aux;
142463afb9a5SDavid du Colombier 	lev = qidpath >> Levshift;
142563afb9a5SDavid du Colombier 	cnum = (qidpath >> Connshift) & Connmask;
142663afb9a5SDavid du Colombier 	chnum = qidpath & Chanmask;
142763afb9a5SDavid du Colombier 	c = connections[cnum];
142863afb9a5SDavid du Colombier 	sshdebug(c, "got clunk on file: %#llux %d %d %d: %s",
142963afb9a5SDavid du Colombier 		qidpath, lev, cnum, chnum, f->file->name);
143063afb9a5SDavid du Colombier 	/* qidpath test implies conn 0, chan 0 */
143163afb9a5SDavid du Colombier 	if (lev == Top && qidpath == Qreqrem) {
143263afb9a5SDavid du Colombier 		if (keymbox.state != Empty) {
143363afb9a5SDavid du Colombier 			keymbox.state = Empty;
143463afb9a5SDavid du Colombier 			// nbsendul(keymbox.mchan, 1);
143563afb9a5SDavid du Colombier 		}
143663afb9a5SDavid du Colombier 		keymbox.msg = nil;
143763afb9a5SDavid du Colombier 		return;
143863afb9a5SDavid du Colombier 	}
143963afb9a5SDavid du Colombier 
144063afb9a5SDavid du Colombier 	if (c == nil)
144163afb9a5SDavid du Colombier 		return;
144263afb9a5SDavid du Colombier 	if (lev == Connection && (qidpath & Qtypemask) == Qctl &&
144363afb9a5SDavid du Colombier 	    (c->state == Opening || c->state == Negotiating ||
144463afb9a5SDavid du Colombier 	     c->state == Authing)) {
144563afb9a5SDavid du Colombier 		for (n = 0; n < MAXCONN && (!c->chans[n] ||
144663afb9a5SDavid du Colombier 		    c->chans[n]->state == Empty ||
144763afb9a5SDavid du Colombier 		    c->chans[n]->state == Closed ||
144863afb9a5SDavid du Colombier 		    c->chans[n]->state == Closing); ++n)
144963afb9a5SDavid du Colombier 			;
145063afb9a5SDavid du Colombier 		if (n >= MAXCONN) {
145163afb9a5SDavid du Colombier 			if (c->rpid >= 0)
145263afb9a5SDavid du Colombier 				threadint(c->rpid);
145363afb9a5SDavid du Colombier 			shutdown(c);
145463afb9a5SDavid du Colombier 		}
145563afb9a5SDavid du Colombier 		return;
145663afb9a5SDavid du Colombier 	}
145763afb9a5SDavid du Colombier 
145863afb9a5SDavid du Colombier 	sc = c->chans[chnum];
145963afb9a5SDavid du Colombier 	if (lev != Subchannel)
146063afb9a5SDavid du Colombier 		return;
146163afb9a5SDavid du Colombier 	if ((qidpath & Qtypemask) == Qlisten && sc->state == Listening) {
146263afb9a5SDavid du Colombier 		qlock(&c->l);
146363afb9a5SDavid du Colombier 		if (sc->state != Closed) {
146463afb9a5SDavid du Colombier 			sc->state = Closed;
146563afb9a5SDavid du Colombier 			chanclose(sc->inchan);
146663afb9a5SDavid du Colombier 			chanclose(sc->reqchan);
146763afb9a5SDavid du Colombier 		}
146863afb9a5SDavid du Colombier 		qunlock(&c->l);
146963afb9a5SDavid du Colombier 	} else if ((qidpath & Qtypemask) == Qdata && sc->state != Empty &&
147063afb9a5SDavid du Colombier 	    sc->state != Closed && sc->state != Closing) {
147163afb9a5SDavid du Colombier 		if (f->file != sc->data && f->file != sc->request) {
147263afb9a5SDavid du Colombier 			sshlog(c, "great evil is upon us; destroying a fid "
147363afb9a5SDavid du Colombier 				"we didn't create");
147463afb9a5SDavid du Colombier 			return;
147563afb9a5SDavid du Colombier 		}
147663afb9a5SDavid du Colombier 
147763afb9a5SDavid du Colombier 		p = new_packet(c);
147863afb9a5SDavid du Colombier 		add_byte(p, SSH_MSG_CHANNEL_CLOSE);
147963afb9a5SDavid du Colombier 		hnputl(p->payload+1, sc->otherid);
148063afb9a5SDavid du Colombier 		p->rlength += 4;
148163afb9a5SDavid du Colombier 		n = finish_packet(p);
148263afb9a5SDavid du Colombier 		sc->state = Closing;
148363afb9a5SDavid du Colombier 		iowrite(c->dio, c->datafd, p->nlength, n);
148463afb9a5SDavid du Colombier 		free(p);
148563afb9a5SDavid du Colombier 
148663afb9a5SDavid du Colombier 		qlock(&c->l);
148763afb9a5SDavid du Colombier 		rwakeup(&sc->r);
148863afb9a5SDavid du Colombier 		qunlock(&c->l);
148963afb9a5SDavid du Colombier 		nbsendul(sc->inchan, 1);
149063afb9a5SDavid du Colombier 		nbsendul(sc->reqchan, 1);
149163afb9a5SDavid du Colombier 	}
149263afb9a5SDavid du Colombier 	for (n = 0; n < MAXCONN && (!c->chans[n] ||
149363afb9a5SDavid du Colombier 	    c->chans[n]->state == Empty || c->chans[n]->state == Closed ||
149463afb9a5SDavid du Colombier 	    c->chans[n]->state == Closing); ++n)
149563afb9a5SDavid du Colombier 		;
149663afb9a5SDavid du Colombier 	if (n >= MAXCONN) {
149763afb9a5SDavid du Colombier 		if (c->rpid >= 0)
149863afb9a5SDavid du Colombier 			threadint(c->rpid);
149963afb9a5SDavid du Colombier 		shutdown(c);
150063afb9a5SDavid du Colombier 	}
150163afb9a5SDavid du Colombier }
150263afb9a5SDavid du Colombier 
150363afb9a5SDavid du Colombier void
150463afb9a5SDavid du Colombier stflush(Req *r)
150563afb9a5SDavid du Colombier {
150663afb9a5SDavid du Colombier 	Req *or;
150763afb9a5SDavid du Colombier 	uvlong qidpath;
150863afb9a5SDavid du Colombier 
150963afb9a5SDavid du Colombier 	threadsetname("stflush");
151063afb9a5SDavid du Colombier 	or = r->oldreq;
151163afb9a5SDavid du Colombier 	qidpath = (uvlong)or->fid->file->aux;
151263afb9a5SDavid du Colombier 	sshdebug(nil, "got flush on file %#llux %lld %lld %lld: %s %#p",
151363afb9a5SDavid du Colombier 		argv0, qidpath, qidpath >> Levshift,
151463afb9a5SDavid du Colombier 		(qidpath >> Connshift) & Connmask, qidpath & Chanmask,
151563afb9a5SDavid du Colombier 		or->fid->file->name, or->aux);
151663afb9a5SDavid du Colombier 	if (!or->aux)
151763afb9a5SDavid du Colombier 		respond(or, "interrupted");
151863afb9a5SDavid du Colombier 	else if (or->ifcall.type == Topen && (qidpath & Qtypemask) == Qlisten &&
151963afb9a5SDavid du Colombier 	    (qidpath >> Levshift) == Connection ||
152063afb9a5SDavid du Colombier 	    or->ifcall.type == Tread && (qidpath & Qtypemask) == Qdata &&
152163afb9a5SDavid du Colombier 	    (qidpath >> Levshift) == Subchannel ||
152263afb9a5SDavid du Colombier 	    or->ifcall.type == Tread && (qidpath & Qtypemask) == Qreqrem)
152363afb9a5SDavid du Colombier 		threadint((uintptr)or->aux);
152463afb9a5SDavid du Colombier 	else {
152563afb9a5SDavid du Colombier 		threadkill((uintptr)or->aux);
152663afb9a5SDavid du Colombier 		or->aux = 0;
152763afb9a5SDavid du Colombier 		respond(or, "interrupted");
152863afb9a5SDavid du Colombier 	}
152963afb9a5SDavid du Colombier 	respond(r, nil);
153063afb9a5SDavid du Colombier }
153163afb9a5SDavid du Colombier 
153263afb9a5SDavid du Colombier void
153363afb9a5SDavid du Colombier filedup(Req *r, File *src)
153463afb9a5SDavid du Colombier {
153563afb9a5SDavid du Colombier 	r->ofcall.qid = src->qid;
153663afb9a5SDavid du Colombier 	closefile(r->fid->file);
153763afb9a5SDavid du Colombier 	r->fid->file = src;
153863afb9a5SDavid du Colombier 	incref(src);
153963afb9a5SDavid du Colombier }
154063afb9a5SDavid du Colombier 
154163afb9a5SDavid du Colombier Conn *
154263afb9a5SDavid du Colombier alloc_conn(void)
154363afb9a5SDavid du Colombier {
154463afb9a5SDavid du Colombier 	int slevconn, i, s, firstnil;
154563afb9a5SDavid du Colombier 	char buf[Numbsz];
154663afb9a5SDavid du Colombier 	Conn *c;
154763afb9a5SDavid du Colombier 	static QLock aclock;
154863afb9a5SDavid du Colombier 
154963afb9a5SDavid du Colombier 	qlock(&aclock);
155063afb9a5SDavid du Colombier 	firstnil = -1;
155163afb9a5SDavid du Colombier 	for (i = 0; i < MAXCONN; ++i) {
155263afb9a5SDavid du Colombier 		if (connections[i] == nil) {
155363afb9a5SDavid du Colombier 			if (firstnil == -1)
155463afb9a5SDavid du Colombier 				firstnil = i;
155563afb9a5SDavid du Colombier 			continue;
155663afb9a5SDavid du Colombier 		}
155763afb9a5SDavid du Colombier 		s = connections[i]->state;
155863afb9a5SDavid du Colombier 		if (s == Empty || s == Closed)
155963afb9a5SDavid du Colombier 			break;
156063afb9a5SDavid du Colombier 	}
156163afb9a5SDavid du Colombier 	if (i >= MAXCONN) {
156263afb9a5SDavid du Colombier 		if (firstnil == -1) {		/* all slots in use? */
156363afb9a5SDavid du Colombier 			qunlock(&aclock);
156463afb9a5SDavid du Colombier 			return nil;
156563afb9a5SDavid du Colombier 		}
156663afb9a5SDavid du Colombier 		/* no reusable slots, allocate a new Conn */
156763afb9a5SDavid du Colombier 		connections[firstnil] = emalloc9p(sizeof(Conn));
156863afb9a5SDavid du Colombier 		memset(connections[firstnil], 0, sizeof(Conn));
156963afb9a5SDavid du Colombier 		i = firstnil;
157063afb9a5SDavid du Colombier 	}
157163afb9a5SDavid du Colombier 
157263afb9a5SDavid du Colombier 	c = connections[i];
157363afb9a5SDavid du Colombier 	memset(&c->r, '\0', sizeof(Rendez));
157463afb9a5SDavid du Colombier 	c->r.l = &c->l;
157563afb9a5SDavid du Colombier 	c->dio = ioproc();
157663afb9a5SDavid du Colombier 	c->rio = nil;
157763afb9a5SDavid du Colombier 	c->state = Allocated;
157863afb9a5SDavid du Colombier 	c->role = Server;
157963afb9a5SDavid du Colombier 	c->id = i;
158063afb9a5SDavid du Colombier 	c->stifle = c->poisoned = 0;
158163afb9a5SDavid du Colombier 	c->user = c->service = nil;
158263afb9a5SDavid du Colombier 	c->inseq = c->nchan = c->outseq = 0;
158363afb9a5SDavid du Colombier 	c->cscrypt = c->csmac = c->ctlfd = c->datafd = c->decrypt =
158463afb9a5SDavid du Colombier 		c->encrypt = c->inmac = c->ncscrypt = c->ncsmac =
158563afb9a5SDavid du Colombier 		c->nsccrypt = c->nscmac = c->outmac = c->rpid = c->sccrypt =
158663afb9a5SDavid du Colombier 		c->scmac = c->tcpconn = -1;
158763afb9a5SDavid du Colombier 	if (c->e) {
158863afb9a5SDavid du Colombier 		mpfree(c->e);
158963afb9a5SDavid du Colombier 		c->e = nil;
159063afb9a5SDavid du Colombier 	}
159163afb9a5SDavid du Colombier 	if (c->x) {
159263afb9a5SDavid du Colombier 		mpfree(c->x);
159363afb9a5SDavid du Colombier 		c->x = nil;
159463afb9a5SDavid du Colombier 	}
159563afb9a5SDavid du Colombier 
159663afb9a5SDavid du Colombier 	snprint(buf, sizeof buf, "%d", i);
159763afb9a5SDavid du Colombier 	if (c->dir == nil) {
159863afb9a5SDavid du Colombier 		slevconn = Connection << Levshift | i << Connshift;
159963afb9a5SDavid du Colombier 		c->dir = createfile(rootfile, buf, uid, 0555|DMDIR,
160063afb9a5SDavid du Colombier 			(void *)(slevconn | Qroot));
160163afb9a5SDavid du Colombier 		c->clonefile = createfile(c->dir, "clone", uid, 0666,
160263afb9a5SDavid du Colombier 			(void *)(slevconn | Qclone));
160363afb9a5SDavid du Colombier 		c->ctlfile = createfile(c->dir, "ctl", uid, 0666,
160463afb9a5SDavid du Colombier 			(void *)(slevconn | Qctl));
160563afb9a5SDavid du Colombier 		c->datafile = createfile(c->dir, "data", uid, 0666,
160663afb9a5SDavid du Colombier 			(void *)(slevconn | Qdata));
160763afb9a5SDavid du Colombier 		c->listenfile = createfile(c->dir, "listen", uid, 0666,
160863afb9a5SDavid du Colombier 			(void *)(slevconn | Qlisten));
160963afb9a5SDavid du Colombier 		c->localfile = createfile(c->dir, "local", uid, 0444,
161063afb9a5SDavid du Colombier 			(void *)(slevconn | Qlocal));
161163afb9a5SDavid du Colombier 		c->remotefile = createfile(c->dir, "remote", uid, 0444,
161263afb9a5SDavid du Colombier 			(void *)(slevconn | Qreqrem));
161363afb9a5SDavid du Colombier 		c->statusfile = createfile(c->dir, "status", uid, 0444,
161463afb9a5SDavid du Colombier 			(void *)(slevconn | Qstatus));
161563afb9a5SDavid du Colombier 		c->tcpfile = createfile(c->dir, "tcp", uid, 0444,
161663afb9a5SDavid du Colombier 			(void *)(slevconn | Qtcp));
161763afb9a5SDavid du Colombier 	}
161863afb9a5SDavid du Colombier //	c->skexinit = c->rkexinit = nil;
161963afb9a5SDavid du Colombier 	c->got_sessid = 0;
162063afb9a5SDavid du Colombier 	c->otherid = nil;
162163afb9a5SDavid du Colombier 	c->inik = c->outik = nil;
162263afb9a5SDavid du Colombier 	c->s2ccs = c->c2scs = c->enccs = c->deccs = nil;
162363afb9a5SDavid du Colombier 	qunlock(&aclock);
162463afb9a5SDavid du Colombier 	return c;
162563afb9a5SDavid du Colombier }
162663afb9a5SDavid du Colombier 
162763afb9a5SDavid du Colombier SSHChan *
162863afb9a5SDavid du Colombier alloc_chan(Conn *c)
162963afb9a5SDavid du Colombier {
163063afb9a5SDavid du Colombier 	int cnum, slcn;
163163afb9a5SDavid du Colombier 	char buf[Numbsz];
163263afb9a5SDavid du Colombier 	Plist *p, *next;
163363afb9a5SDavid du Colombier 	SSHChan *sc;
163463afb9a5SDavid du Colombier 
163563afb9a5SDavid du Colombier 	if (c->nchan >= MAXCONN)
163663afb9a5SDavid du Colombier 		return nil;
163763afb9a5SDavid du Colombier 	qlock(&c->l);
163863afb9a5SDavid du Colombier 	cnum = c->nchan;
163963afb9a5SDavid du Colombier 	if (c->chans[cnum] == nil) {
164063afb9a5SDavid du Colombier 		c->chans[cnum] = emalloc9p(sizeof(SSHChan));
164163afb9a5SDavid du Colombier 		memset(c->chans[cnum], 0, sizeof(SSHChan));
164263afb9a5SDavid du Colombier 	}
164363afb9a5SDavid du Colombier 	sc = c->chans[cnum];
164463afb9a5SDavid du Colombier 	snprint(buf, sizeof buf, "%d", cnum);
164563afb9a5SDavid du Colombier 	memset(&sc->r, '\0', sizeof(Rendez));
164663afb9a5SDavid du Colombier 	sc->r.l = &c->l;
164763afb9a5SDavid du Colombier 	sc->id = cnum;
164863afb9a5SDavid du Colombier 	sc->state = Empty;
164963afb9a5SDavid du Colombier 	sc->conn = c->id;
165063afb9a5SDavid du Colombier 	sc->otherid = sc->waker = -1;
165163afb9a5SDavid du Colombier 	sc->sent = sc->twindow = sc->rwindow = sc->inrqueue = 0;
165263afb9a5SDavid du Colombier 	sc->ann = nil;
165363afb9a5SDavid du Colombier 	sc->lreq = nil;
165463afb9a5SDavid du Colombier 
165563afb9a5SDavid du Colombier 	if (sc->dir == nil) {
165663afb9a5SDavid du Colombier 		slcn = Subchannel << Levshift | c->id << Connshift | cnum;
165763afb9a5SDavid du Colombier 		sc->dir = createfile(c->dir, buf, uid, 0555|DMDIR,
165863afb9a5SDavid du Colombier 			(void *)(slcn | Qroot));
165963afb9a5SDavid du Colombier 		sc->ctl = createfile(sc->dir, "ctl", uid, 0666,
166063afb9a5SDavid du Colombier 			(void *)(slcn | Qctl));
166163afb9a5SDavid du Colombier 		sc->data = createfile(sc->dir, "data", uid, 0666,
166263afb9a5SDavid du Colombier 			(void *)(slcn | Qdata));
166363afb9a5SDavid du Colombier 		sc->listen = createfile(sc->dir, "listen", uid, 0666,
166463afb9a5SDavid du Colombier 			(void *)(slcn | Qlisten));
166563afb9a5SDavid du Colombier 		sc->request = createfile(sc->dir, "request", uid, 0666,
166663afb9a5SDavid du Colombier 			(void *)(slcn | Qreqrem));
166763afb9a5SDavid du Colombier 		sc->status = createfile(sc->dir, "status", uid, 0444,
166863afb9a5SDavid du Colombier 			(void *)(slcn | Qstatus));
166963afb9a5SDavid du Colombier 		sc->tcp = createfile(sc->dir, "tcp", uid, 0444,
167063afb9a5SDavid du Colombier 			(void *)(slcn | Qtcp));
167163afb9a5SDavid du Colombier 	}
167263afb9a5SDavid du Colombier 	c->nchan++;
167363afb9a5SDavid du Colombier 
167463afb9a5SDavid du Colombier 	for (; sc->reqq != nil; sc->reqq = next) {
167563afb9a5SDavid du Colombier 		p = sc->reqq;
167663afb9a5SDavid du Colombier 		next = p->next;
167763afb9a5SDavid du Colombier 		free(p->pack);
167863afb9a5SDavid du Colombier 		free(p);
167963afb9a5SDavid du Colombier 	}
168063afb9a5SDavid du Colombier 	sc->dataq = sc->datatl = sc->reqtl = nil;
168163afb9a5SDavid du Colombier 
168263afb9a5SDavid du Colombier 	if (sc->inchan)
168363afb9a5SDavid du Colombier 		chanfree(sc->inchan);
168463afb9a5SDavid du Colombier 	sc->inchan = chancreate(4, 0);
168563afb9a5SDavid du Colombier 
168663afb9a5SDavid du Colombier 	if (sc->reqchan)
168763afb9a5SDavid du Colombier 		chanfree(sc->reqchan);
168863afb9a5SDavid du Colombier 	sc->reqchan = chancreate(4, 0);
168963afb9a5SDavid du Colombier 
169063afb9a5SDavid du Colombier 	memset(&sc->xmtrendez, '\0', sizeof(Rendez));
169163afb9a5SDavid du Colombier 	sc->xmtrendez.l = &sc->xmtlock;
169263afb9a5SDavid du Colombier 	qunlock(&c->l);
169363afb9a5SDavid du Colombier 	return sc;
169463afb9a5SDavid du Colombier }
169563afb9a5SDavid du Colombier 
169663afb9a5SDavid du Colombier static int
169763afb9a5SDavid du Colombier readlineio(Conn *, Ioproc *io, int fd, char *buf, int size)
169863afb9a5SDavid du Colombier {
169963afb9a5SDavid du Colombier 	int n;
170063afb9a5SDavid du Colombier 	char *p;
170163afb9a5SDavid du Colombier 
170263afb9a5SDavid du Colombier 	for (p = buf; p < buf + size - 1; p++) {
170363afb9a5SDavid du Colombier 		n = ioread(io, fd, p, 1);
170463afb9a5SDavid du Colombier 		if (n != 1 || *p == '\n') {
170563afb9a5SDavid du Colombier 			*p = '\0';
170663afb9a5SDavid du Colombier 			break;
170763afb9a5SDavid du Colombier 		}
170863afb9a5SDavid du Colombier 	}
170963afb9a5SDavid du Colombier 	return p - buf;
171063afb9a5SDavid du Colombier }
171163afb9a5SDavid du Colombier 
171263afb9a5SDavid du Colombier static char *
171363afb9a5SDavid du Colombier readremote(Conn *c, Ioproc *io, char *tcpconn)
171463afb9a5SDavid du Colombier {
171563afb9a5SDavid du Colombier 	int n, remfd;
171663afb9a5SDavid du Colombier 	char *p, *remote;
171763afb9a5SDavid du Colombier 	char path[Arbbufsz], buf[NETPATHLEN];
171863afb9a5SDavid du Colombier 
171963afb9a5SDavid du Colombier 	remote = nil;
172063afb9a5SDavid du Colombier 	snprint(path, sizeof path, "%s/tcp/%s/remote", mntpt, tcpconn);
172163afb9a5SDavid du Colombier 	remfd = ioopen(io, path, OREAD);
172263afb9a5SDavid du Colombier 	if (remfd < 0) {
172363afb9a5SDavid du Colombier 		sshlog(c, "readremote: can't open %s: %r", path);
172463afb9a5SDavid du Colombier 		return nil;
172563afb9a5SDavid du Colombier 	}
172663afb9a5SDavid du Colombier 	n = ioread(io, remfd, buf, sizeof buf - 1);
172763afb9a5SDavid du Colombier 	if (n > 0) {
172863afb9a5SDavid du Colombier 		buf[n] = 0;
172963afb9a5SDavid du Colombier 		p = strchr(buf, '!');
173063afb9a5SDavid du Colombier 		if (p)
173163afb9a5SDavid du Colombier 			*p = 0;
173263afb9a5SDavid du Colombier 		remote = estrdup9p(buf);
173363afb9a5SDavid du Colombier 	}
173463afb9a5SDavid du Colombier 	ioclose(io, remfd);
173563afb9a5SDavid du Colombier 	return remote;
173663afb9a5SDavid du Colombier }
173763afb9a5SDavid du Colombier 
173863afb9a5SDavid du Colombier static void
173963afb9a5SDavid du Colombier sendmyid(Conn *c, Ioproc *io)
174063afb9a5SDavid du Colombier {
174163afb9a5SDavid du Colombier 	char path[Arbbufsz];
174263afb9a5SDavid du Colombier 
174363afb9a5SDavid du Colombier 	snprint(path, sizeof path, "%s\r\n", MYID);
174463afb9a5SDavid du Colombier 	iowrite(io, c->datafd, path, strlen(path));
174563afb9a5SDavid du Colombier }
174663afb9a5SDavid du Colombier 
174763afb9a5SDavid du Colombier /* save and tidy up the remote id */
174863afb9a5SDavid du Colombier static void
174963afb9a5SDavid du Colombier stashremid(Conn *c, char *remid)
175063afb9a5SDavid du Colombier {
175163afb9a5SDavid du Colombier 	char *nl;
175263afb9a5SDavid du Colombier 
175363afb9a5SDavid du Colombier 	if (c->otherid)
175463afb9a5SDavid du Colombier 		free(c->otherid);
175563afb9a5SDavid du Colombier 	c->otherid = estrdup9p(remid);
175663afb9a5SDavid du Colombier 
175763afb9a5SDavid du Colombier 	nl = strchr(c->otherid, '\n');
175863afb9a5SDavid du Colombier 	if (nl)
175963afb9a5SDavid du Colombier 		*nl = '\0';
176063afb9a5SDavid du Colombier 	nl = strchr(c->otherid, '\r');
176163afb9a5SDavid du Colombier 	if (nl)
176263afb9a5SDavid du Colombier 		*nl = '\0';
176363afb9a5SDavid du Colombier }
176463afb9a5SDavid du Colombier 
176563afb9a5SDavid du Colombier static void
176663afb9a5SDavid du Colombier hangupconn(Conn *c)
176763afb9a5SDavid du Colombier {
176863afb9a5SDavid du Colombier 	hangup(c->ctlfd);
176963afb9a5SDavid du Colombier 	close(c->ctlfd);
177063afb9a5SDavid du Colombier 	close(c->datafd);
177163afb9a5SDavid du Colombier 	c->ctlfd = c->datafd = -1;
177263afb9a5SDavid du Colombier }
177363afb9a5SDavid du Colombier 
177463afb9a5SDavid du Colombier #ifdef COEXIST
177563afb9a5SDavid du Colombier static int
177663afb9a5SDavid du Colombier exchids(Conn *c, Ioproc *io, char *remid, int remsz)
177763afb9a5SDavid du Colombier {
177863afb9a5SDavid du Colombier 	int n;
177963afb9a5SDavid du Colombier 
178063afb9a5SDavid du Colombier 	/*
178163afb9a5SDavid du Colombier 	 * exchange versions.  server writes id, then reads;
178263afb9a5SDavid du Colombier 	 * client reads id then writes (in theory).
178363afb9a5SDavid du Colombier 	 */
178463afb9a5SDavid du Colombier 	if (c->role == Server) {
178563afb9a5SDavid du Colombier 		sendmyid(c, io);
178663afb9a5SDavid du Colombier 
178763afb9a5SDavid du Colombier 		n = readlineio(c, io, c->datafd, remid, remsz);
178863afb9a5SDavid du Colombier 		if (n < 5)		/* can't be a valid SSH id string */
178963afb9a5SDavid du Colombier 			return -1;
179063afb9a5SDavid du Colombier 		sshdebug(c, "dohandshake: server, got `%s', sent `%s'", remid,
179163afb9a5SDavid du Colombier 			MYID);
179263afb9a5SDavid du Colombier 	} else {
179363afb9a5SDavid du Colombier 		/* client: read server's id */
179463afb9a5SDavid du Colombier 		n = readlineio(c, io, c->datafd, remid, remsz);
179563afb9a5SDavid du Colombier 		if (n < 5)		/* can't be a valid SSH id string */
179663afb9a5SDavid du Colombier 			return -1;
179763afb9a5SDavid du Colombier 
179863afb9a5SDavid du Colombier 		sendmyid(c, io);
179963afb9a5SDavid du Colombier 		sshdebug(c, "dohandshake: client, got `%s' sent `%s'", remid, MYID);
180063afb9a5SDavid du Colombier 		if (remid[0] == '\0') {
180163afb9a5SDavid du Colombier 			sshlog(c, "dohandshake: client, empty remote id string;"
180263afb9a5SDavid du Colombier 				" out of sync");
180363afb9a5SDavid du Colombier 			return -1;
180463afb9a5SDavid du Colombier 		}
180563afb9a5SDavid du Colombier 	}
180663afb9a5SDavid du Colombier 	sshdebug(c, "remote id string `%s'", remid);
180763afb9a5SDavid du Colombier 	return 0;
180863afb9a5SDavid du Colombier }
180963afb9a5SDavid du Colombier 
181063afb9a5SDavid du Colombier /*
181163afb9a5SDavid du Colombier  * negotiate the protocols.
181263afb9a5SDavid du Colombier  * We don't do the full negotiation here, because we also have
181363afb9a5SDavid du Colombier  * to handle a re-negotiation request from the other end.
181463afb9a5SDavid du Colombier  * So we just kick it off and let the receiver process take it from there.
181563afb9a5SDavid du Colombier  */
181663afb9a5SDavid du Colombier static int
181763afb9a5SDavid du Colombier negotiate(Conn *c)
181863afb9a5SDavid du Colombier {
181963afb9a5SDavid du Colombier 	send_kexinit(c);
182063afb9a5SDavid du Colombier 
182163afb9a5SDavid du Colombier 	qlock(&c->l);
182263afb9a5SDavid du Colombier 	if ((c->role == Client && c->state != Negotiating) ||
182363afb9a5SDavid du Colombier 	    (c->role == Server && c->state != Established)) {
182463afb9a5SDavid du Colombier 		sshdebug(c, "awaiting establishment");
182563afb9a5SDavid du Colombier 		rsleep(&c->r);
182663afb9a5SDavid du Colombier 	}
182763afb9a5SDavid du Colombier 	qunlock(&c->l);
182863afb9a5SDavid du Colombier 
182963afb9a5SDavid du Colombier 	if (c->role == Server && c->state != Established ||
183063afb9a5SDavid du Colombier 	    c->role == Client && c->state != Negotiating) {
183163afb9a5SDavid du Colombier 		sshdebug(c, "failed to establish");
183263afb9a5SDavid du Colombier 		return -1;
183363afb9a5SDavid du Colombier 	}
183463afb9a5SDavid du Colombier 	sshdebug(c, "established; crypto now on");
183563afb9a5SDavid du Colombier 	return 0;
183663afb9a5SDavid du Colombier }
183763afb9a5SDavid du Colombier 
183863afb9a5SDavid du Colombier /* this was deferred when trying to make coexistence with v1 work */
183963afb9a5SDavid du Colombier static int
184063afb9a5SDavid du Colombier deferredinit(Conn *c)
184163afb9a5SDavid du Colombier {
184263afb9a5SDavid du Colombier 	char remid[Arbbufsz];
184363afb9a5SDavid du Colombier 	Ioproc *io;
184463afb9a5SDavid du Colombier 
184563afb9a5SDavid du Colombier 	io = ioproc();
184663afb9a5SDavid du Colombier 	/*
184763afb9a5SDavid du Colombier 	 * don't bother checking the remote's id string.
184863afb9a5SDavid du Colombier 	 * as a client, we can cope with v1 if we don't verify the host key.
184963afb9a5SDavid du Colombier 	 */
185063afb9a5SDavid du Colombier 	if (exchids(c, io, remid, sizeof remid) < 0 ||
185163afb9a5SDavid du Colombier 	    0 && c->role == Client && strncmp(remid, "SSH-2", 5) != 0 &&
185263afb9a5SDavid du Colombier 	    strncmp(remid, "SSH-1.99", 8) != 0) {
185363afb9a5SDavid du Colombier 		/* not a protocol version we know; give up */
185463afb9a5SDavid du Colombier 		closeioproc(io);
185563afb9a5SDavid du Colombier 		hangupconn(c);
185663afb9a5SDavid du Colombier 		return -1;
185763afb9a5SDavid du Colombier 	}
185863afb9a5SDavid du Colombier 	closeioproc(io);
185963afb9a5SDavid du Colombier 	stashremid(c, remid);
186063afb9a5SDavid du Colombier 
186163afb9a5SDavid du Colombier 	c->state = Initting;
186263afb9a5SDavid du Colombier 
186363afb9a5SDavid du Colombier 	/* start the reader thread */
186463afb9a5SDavid du Colombier 	if (c->rpid < 0)
186563afb9a5SDavid du Colombier 		c->rpid = threadcreate(reader, c, Defstk);
186663afb9a5SDavid du Colombier 
186763afb9a5SDavid du Colombier 	return negotiate(c);
186863afb9a5SDavid du Colombier }
186963afb9a5SDavid du Colombier 
187063afb9a5SDavid du Colombier int
187163afb9a5SDavid du Colombier dohandshake(Conn *c, char *tcpconn)
187263afb9a5SDavid du Colombier {
187363afb9a5SDavid du Colombier 	int tcpdfd;
187463afb9a5SDavid du Colombier 	char *remote;
187563afb9a5SDavid du Colombier 	char path[Arbbufsz];
187663afb9a5SDavid du Colombier 	Ioproc *io;
187763afb9a5SDavid du Colombier 
187863afb9a5SDavid du Colombier 	io = ioproc();
187963afb9a5SDavid du Colombier 
188063afb9a5SDavid du Colombier 	/* read tcp conn's remote address into c->remote */
188163afb9a5SDavid du Colombier 	remote = readremote(c, io, tcpconn);
188263afb9a5SDavid du Colombier 	if (remote) {
188363afb9a5SDavid du Colombier 		free(c->remote);
188463afb9a5SDavid du Colombier 		c->remote = remote;
188563afb9a5SDavid du Colombier 	}
188663afb9a5SDavid du Colombier 
188763afb9a5SDavid du Colombier 	/* open tcp conn's data file */
188863afb9a5SDavid du Colombier 	c->tcpconn = atoi(tcpconn);
188963afb9a5SDavid du Colombier 	snprint(path, sizeof path, "%s/tcp/%s/data", mntpt, tcpconn);
189063afb9a5SDavid du Colombier 	tcpdfd = ioopen(io, path, ORDWR);
189163afb9a5SDavid du Colombier 	closeioproc(io);
189263afb9a5SDavid du Colombier 	if (tcpdfd < 0) {
189363afb9a5SDavid du Colombier 		sshlog(c, "dohandshake: can't open %s: %r", path);
189463afb9a5SDavid du Colombier 		return -1;
189563afb9a5SDavid du Colombier 	}
189663afb9a5SDavid du Colombier 	c->datafd = tcpdfd;		/* underlying tcp data descriptor */
189763afb9a5SDavid du Colombier 
189863afb9a5SDavid du Colombier 	return deferredinit(c);
189963afb9a5SDavid du Colombier }
190063afb9a5SDavid du Colombier #endif					/* COEXIST */
190163afb9a5SDavid du Colombier 
190263afb9a5SDavid du Colombier int
190363afb9a5SDavid du Colombier dohandshake(Conn *c, char *tcpconn)
190463afb9a5SDavid du Colombier {
190563afb9a5SDavid du Colombier 	int fd, n;
190663afb9a5SDavid du Colombier 	char *p, *othid;
190763afb9a5SDavid du Colombier 	char path[Arbbufsz], buf[NETPATHLEN];
190863afb9a5SDavid du Colombier 	Ioproc *io;
190963afb9a5SDavid du Colombier 
191063afb9a5SDavid du Colombier 	io = ioproc();
191163afb9a5SDavid du Colombier 	snprint(path, sizeof path, "%s/tcp/%s/remote", mntpt, tcpconn);
191263afb9a5SDavid du Colombier 	fd = ioopen(io, path, OREAD);
191363afb9a5SDavid du Colombier 	n = ioread(io, fd, buf, sizeof buf - 1);
191463afb9a5SDavid du Colombier 	if (n > 0) {
191563afb9a5SDavid du Colombier 		buf[n] = 0;
191663afb9a5SDavid du Colombier 		p = strchr(buf, '!');
191763afb9a5SDavid du Colombier 		if (p)
191863afb9a5SDavid du Colombier 			*p = 0;
191963afb9a5SDavid du Colombier 		free(c->remote);
192063afb9a5SDavid du Colombier 		c->remote = estrdup9p(buf);
192163afb9a5SDavid du Colombier 	}
192263afb9a5SDavid du Colombier 	ioclose(io, fd);
192363afb9a5SDavid du Colombier 
192463afb9a5SDavid du Colombier 	snprint(path, sizeof path, "%s/tcp/%s/data", mntpt, tcpconn);
192563afb9a5SDavid du Colombier 	fd = ioopen(io, path, ORDWR);
192663afb9a5SDavid du Colombier 	if (fd < 0) {
192763afb9a5SDavid du Colombier 		closeioproc(io);
192863afb9a5SDavid du Colombier 		return -1;
192963afb9a5SDavid du Colombier 	}
193063afb9a5SDavid du Colombier 	c->datafd = fd;
193163afb9a5SDavid du Colombier 
193263afb9a5SDavid du Colombier 	/* exchange versions--we're only doing SSH2, unfortunately */
193363afb9a5SDavid du Colombier 
193463afb9a5SDavid du Colombier 	snprint(path, sizeof path, "%s\r\n", MYID);
193563afb9a5SDavid du Colombier 	if (c->idstring && c->idstring[0])
193663afb9a5SDavid du Colombier 		strncpy(path, c->idstring, sizeof path);
193763afb9a5SDavid du Colombier 	else {
193863afb9a5SDavid du Colombier 		iowrite(io, fd, path, strlen(path));
193963afb9a5SDavid du Colombier 		p = path;
194063afb9a5SDavid du Colombier 		n = 0;
194163afb9a5SDavid du Colombier 		do {
194263afb9a5SDavid du Colombier 			if (ioread(io, fd, p, 1) < 0) {
194363afb9a5SDavid du Colombier 				fprint(2, "%s: short read in ID exchange: %r\n",
194463afb9a5SDavid du Colombier 					argv0);
194563afb9a5SDavid du Colombier 				break;
194663afb9a5SDavid du Colombier 			}
194763afb9a5SDavid du Colombier 			++n;
194863afb9a5SDavid du Colombier 		} while (*p++ != '\n');
194963afb9a5SDavid du Colombier 		if (n < 5) {		/* can't be a valid SSH id string */
195063afb9a5SDavid du Colombier 			close(fd);
195163afb9a5SDavid du Colombier 			goto err;
195263afb9a5SDavid du Colombier 		}
195363afb9a5SDavid du Colombier 		*p = 0;
195463afb9a5SDavid du Colombier 	}
195563afb9a5SDavid du Colombier 	sshdebug(c, "id string `%s'", path);
195663afb9a5SDavid du Colombier 	if (c->idstring[0] == '\0' &&
195763afb9a5SDavid du Colombier 	    strncmp(path, "SSH-2", 5) != 0 &&
195863afb9a5SDavid du Colombier 	    strncmp(path, "SSH-1.99", 8) != 0) {
195963afb9a5SDavid du Colombier 		/* not a protocol version we know; give up */
196063afb9a5SDavid du Colombier 		ioclose(io, fd);
196163afb9a5SDavid du Colombier 		goto err;
196263afb9a5SDavid du Colombier 	}
196363afb9a5SDavid du Colombier 	closeioproc(io);
196463afb9a5SDavid du Colombier 
196563afb9a5SDavid du Colombier 	if (c->otherid)
196663afb9a5SDavid du Colombier 		free(c->otherid);
196763afb9a5SDavid du Colombier 	c->otherid = othid = estrdup9p(path);
196863afb9a5SDavid du Colombier 	for (n = strlen(othid) - 1; othid[n] == '\r' || othid[n] == '\n'; --n)
196963afb9a5SDavid du Colombier 		othid[n] = '\0';
197063afb9a5SDavid du Colombier 	c->state = Initting;
197163afb9a5SDavid du Colombier 
197263afb9a5SDavid du Colombier 	/* start the reader thread */
197363afb9a5SDavid du Colombier 	if (c->rpid < 0)
197463afb9a5SDavid du Colombier 		c->rpid = threadcreate(reader, c, Defstk);
197563afb9a5SDavid du Colombier 
197663afb9a5SDavid du Colombier 	/*
197763afb9a5SDavid du Colombier 	 * negotiate the protocols
197863afb9a5SDavid du Colombier 	 * We don't do the full negotiation here, because we also have
197963afb9a5SDavid du Colombier 	 * to handle a re-negotiation request from the other end.  So
198063afb9a5SDavid du Colombier 	 * we just kick it off and let the receiver process take it from there.
198163afb9a5SDavid du Colombier 	 */
198263afb9a5SDavid du Colombier 
198363afb9a5SDavid du Colombier 	send_kexinit(c);
198463afb9a5SDavid du Colombier 
198563afb9a5SDavid du Colombier 	qlock(&c->l);
198663afb9a5SDavid du Colombier 	if ((c->role == Client && c->state != Negotiating) ||
198763afb9a5SDavid du Colombier 	    (c->role == Server && c->state != Established))
198863afb9a5SDavid du Colombier 		rsleep(&c->r);
198963afb9a5SDavid du Colombier 	qunlock(&c->l);
199063afb9a5SDavid du Colombier 	if (c->role == Server && c->state != Established ||
199163afb9a5SDavid du Colombier 	    c->role == Client && c->state != Negotiating)
199263afb9a5SDavid du Colombier 		return -1;
199363afb9a5SDavid du Colombier 	return 0;
199463afb9a5SDavid du Colombier err:
199563afb9a5SDavid du Colombier 	/* should use hangup in dial(2) instead of diddling /net/tcp */
199663afb9a5SDavid du Colombier 	snprint(path, sizeof path, "%s/tcp/%s/ctl", mntpt, tcpconn);
199763afb9a5SDavid du Colombier 	fd = ioopen(io, path, OWRITE);
199863afb9a5SDavid du Colombier 	iowrite(io, fd, "hangup", 6);
199963afb9a5SDavid du Colombier 	ioclose(io, fd);
200063afb9a5SDavid du Colombier 	closeioproc(io);
200163afb9a5SDavid du Colombier 	return -1;
200263afb9a5SDavid du Colombier }
200363afb9a5SDavid du Colombier 
200463afb9a5SDavid du Colombier void
200563afb9a5SDavid du Colombier send_kexinit(Conn *c)
200663afb9a5SDavid du Colombier {
200763afb9a5SDavid du Colombier 	Packet *ptmp;
200863afb9a5SDavid du Colombier 	char *buf, *p, *e;
200963afb9a5SDavid du Colombier 	int i, msglen;
201063afb9a5SDavid du Colombier 
201163afb9a5SDavid du Colombier 	sshdebug(c, "initializing kexinit packet");
201263afb9a5SDavid du Colombier 	if (c->skexinit != nil)
201363afb9a5SDavid du Colombier 		free(c->skexinit);
201463afb9a5SDavid du Colombier 	c->skexinit = new_packet(c);
201563afb9a5SDavid du Colombier 
201663afb9a5SDavid du Colombier 	buf = emalloc9p(Bigbufsz);
201763afb9a5SDavid du Colombier 	buf[0] = (uchar)SSH_MSG_KEXINIT;
201863afb9a5SDavid du Colombier 
201963afb9a5SDavid du Colombier 	add_packet(c->skexinit, buf, 1);
202063afb9a5SDavid du Colombier 	for (i = 0; i < 16; ++i)
202163afb9a5SDavid du Colombier 		buf[i] = fastrand();
202263afb9a5SDavid du Colombier 
202363afb9a5SDavid du Colombier 	add_packet(c->skexinit, buf, 16);		/* cookie */
202463afb9a5SDavid du Colombier 	e = buf + Bigbufsz - 1;
202563afb9a5SDavid du Colombier 	p = seprint(buf, e, "%s", kexes[0]->name);
202663afb9a5SDavid du Colombier 	for (i = 1; i < nelem(kexes); ++i)
202763afb9a5SDavid du Colombier 		p = seprint(p, e, ",%s", kexes[i]->name);
202863afb9a5SDavid du Colombier 	sshdebug(c, "sent KEX algs: %s", buf);
202963afb9a5SDavid du Colombier 
203063afb9a5SDavid du Colombier 	add_string(c->skexinit, buf);		/* Key exchange */
203163afb9a5SDavid du Colombier 	if (pkas[0] == nil)
203263afb9a5SDavid du Colombier 		add_string(c->skexinit, "");
203363afb9a5SDavid du Colombier 	else{
203463afb9a5SDavid du Colombier 		p = seprint(buf, e, "%s", pkas[0]->name);
203563afb9a5SDavid du Colombier 		for (i = 1; i < nelem(pkas) && pkas[i] != nil; ++i)
203663afb9a5SDavid du Colombier 			p = seprint(p, e, ",%s", pkas[i]->name);
203763afb9a5SDavid du Colombier 		sshdebug(c, "sent host key algs: %s", buf);
203863afb9a5SDavid du Colombier 		add_string(c->skexinit, buf);		/* server's key algs */
203963afb9a5SDavid du Colombier 	}
204063afb9a5SDavid du Colombier 
204163afb9a5SDavid du Colombier 	p = seprint(buf, e, "%s", cryptos[0]->name);
204263afb9a5SDavid du Colombier 	for (i = 1; i < nelem(cryptos); ++i)
204363afb9a5SDavid du Colombier 		p = seprint(p, e, ",%s", cryptos[i]->name);
204463afb9a5SDavid du Colombier 	sshdebug(c, "sent crypto algs: %s", buf);
204563afb9a5SDavid du Colombier 
204663afb9a5SDavid du Colombier 	add_string(c->skexinit, buf);		/* c->s crypto */
204763afb9a5SDavid du Colombier 	add_string(c->skexinit, buf);		/* s->c crypto */
204863afb9a5SDavid du Colombier 	p = seprint(buf, e, "%s", macnames[0]);
204963afb9a5SDavid du Colombier 	for (i = 1; i < nelem(macnames); ++i)
205063afb9a5SDavid du Colombier 		p = seprint(p, e, ",%s", macnames[i]);
205163afb9a5SDavid du Colombier 	sshdebug(c, "sent MAC algs: %s", buf);
205263afb9a5SDavid du Colombier 
205363afb9a5SDavid du Colombier 	add_string(c->skexinit, buf);		/* c->s mac */
205463afb9a5SDavid du Colombier 	add_string(c->skexinit, buf);		/* s->c mac */
205563afb9a5SDavid du Colombier 	add_string(c->skexinit, "none");	/* c->s compression */
205663afb9a5SDavid du Colombier 	add_string(c->skexinit, "none");	/* s->c compression */
205763afb9a5SDavid du Colombier 	add_string(c->skexinit, "");		/* c->s languages */
205863afb9a5SDavid du Colombier 	add_string(c->skexinit, "");		/* s->c languages */
205963afb9a5SDavid du Colombier 	memset(buf, 0, 5);
206063afb9a5SDavid du Colombier 	add_packet(c->skexinit, buf, 5);
206163afb9a5SDavid du Colombier 
206263afb9a5SDavid du Colombier 	ptmp = new_packet(c);
206363afb9a5SDavid du Colombier 	memmove(ptmp, c->skexinit, sizeof(Packet));
206463afb9a5SDavid du Colombier 	msglen = finish_packet(ptmp);
206563afb9a5SDavid du Colombier 
206663afb9a5SDavid du Colombier 	if (c->dio && c->datafd >= 0)
206763afb9a5SDavid du Colombier 		iowrite(c->dio, c->datafd, ptmp->nlength, msglen);
206863afb9a5SDavid du Colombier 	free(ptmp);
206963afb9a5SDavid du Colombier 	free(buf);
207063afb9a5SDavid du Colombier }
207163afb9a5SDavid du Colombier 
207263afb9a5SDavid du Colombier static void
207363afb9a5SDavid du Colombier establish(Conn *c)
207463afb9a5SDavid du Colombier {
207563afb9a5SDavid du Colombier 	qlock(&c->l);
207663afb9a5SDavid du Colombier 	c->state = Established;
207763afb9a5SDavid du Colombier 	rwakeup(&c->r);
207863afb9a5SDavid du Colombier 	qunlock(&c->l);
207963afb9a5SDavid du Colombier }
208063afb9a5SDavid du Colombier 
208163afb9a5SDavid du Colombier static int
208263afb9a5SDavid du Colombier negotiating(Conn *c, Packet *p, Packet *p2, char *buf, int size)
208363afb9a5SDavid du Colombier {
208463afb9a5SDavid du Colombier 	int i, n;
208563afb9a5SDavid du Colombier 
208663afb9a5SDavid du Colombier 	USED(size);
208763afb9a5SDavid du Colombier 	switch (p->payload[0]) {
208863afb9a5SDavid du Colombier 	case SSH_MSG_DISCONNECT:
208963afb9a5SDavid du Colombier 		if (debug) {
209063afb9a5SDavid du Colombier 			get_string(p, p->payload + 5, buf, Arbbufsz, nil);
209163afb9a5SDavid du Colombier 			sshdebug(c, "got disconnect: %s", buf);
209263afb9a5SDavid du Colombier 		}
209363afb9a5SDavid du Colombier 		return -1;
209463afb9a5SDavid du Colombier 	case SSH_MSG_NEWKEYS:
209563afb9a5SDavid du Colombier 		/*
209663afb9a5SDavid du Colombier 		 * If we're just updating, go straight to
209763afb9a5SDavid du Colombier 		 * established, otherwise wait for auth'n.
209863afb9a5SDavid du Colombier 		 */
209963afb9a5SDavid du Colombier 		i = c->encrypt;
210063afb9a5SDavid du Colombier 		memmove(c->c2siv, c->nc2siv, SHA1dlen*2);
210163afb9a5SDavid du Colombier 		memmove(c->s2civ, c->ns2civ, SHA1dlen*2);
210263afb9a5SDavid du Colombier 		memmove(c->c2sek, c->nc2sek, SHA1dlen*2);
210363afb9a5SDavid du Colombier 		memmove(c->s2cek, c->ns2cek, SHA1dlen*2);
210463afb9a5SDavid du Colombier 		memmove(c->c2sik, c->nc2sik, SHA1dlen*2);
210563afb9a5SDavid du Colombier 		memmove(c->s2cik, c->ns2cik, SHA1dlen*2);
210663afb9a5SDavid du Colombier 		c->cscrypt = c->ncscrypt;
210763afb9a5SDavid du Colombier 		c->sccrypt = c->nsccrypt;
210863afb9a5SDavid du Colombier 		c->csmac = c->ncsmac;
210963afb9a5SDavid du Colombier 		c->scmac = c->nscmac;
211063afb9a5SDavid du Colombier 		c->c2scs = cryptos[c->cscrypt]->init(c, 0);
211163afb9a5SDavid du Colombier 		c->s2ccs = cryptos[c->sccrypt]->init(c, 1);
211263afb9a5SDavid du Colombier 		if (c->role == Server) {
211363afb9a5SDavid du Colombier 			c->encrypt = c->sccrypt;
211463afb9a5SDavid du Colombier 			c->decrypt = c->cscrypt;
211563afb9a5SDavid du Colombier 			c->outmac = c->scmac;
211663afb9a5SDavid du Colombier 			c->inmac = c->csmac;
211763afb9a5SDavid du Colombier 			c->enccs = c->s2ccs;
211863afb9a5SDavid du Colombier 			c->deccs = c->c2scs;
211963afb9a5SDavid du Colombier 			c->outik = c->s2cik;
212063afb9a5SDavid du Colombier 			c->inik = c->c2sik;
212163afb9a5SDavid du Colombier 		} else{
212263afb9a5SDavid du Colombier 			c->encrypt = c->cscrypt;
212363afb9a5SDavid du Colombier 			c->decrypt = c->sccrypt;
212463afb9a5SDavid du Colombier 			c->outmac = c->csmac;
212563afb9a5SDavid du Colombier 			c->inmac = c->scmac;
212663afb9a5SDavid du Colombier 			c->enccs = c->c2scs;
212763afb9a5SDavid du Colombier 			c->deccs = c->s2ccs;
212863afb9a5SDavid du Colombier 			c->outik = c->c2sik;
212963afb9a5SDavid du Colombier 			c->inik = c->s2cik;
213063afb9a5SDavid du Colombier 		}
213163afb9a5SDavid du Colombier 		sshdebug(c, "using %s for encryption and %s for decryption",
213263afb9a5SDavid du Colombier 			cryptos[c->encrypt]->name, cryptos[c->decrypt]->name);
213363afb9a5SDavid du Colombier 		qlock(&c->l);
213463afb9a5SDavid du Colombier 		if (i != -1)
213563afb9a5SDavid du Colombier 			c->state = Established;
213663afb9a5SDavid du Colombier 		if (c->role == Client)
213763afb9a5SDavid du Colombier 			rwakeup(&c->r);
213863afb9a5SDavid du Colombier 		qunlock(&c->l);
213963afb9a5SDavid du Colombier 		break;
214063afb9a5SDavid du Colombier 	case SSH_MSG_KEXDH_INIT:
214163afb9a5SDavid du Colombier 		kexes[c->kexalg]->serverkex(c, p);
214263afb9a5SDavid du Colombier 		break;
214363afb9a5SDavid du Colombier 	case SSH_MSG_KEXDH_REPLY:
214463afb9a5SDavid du Colombier 		init_packet(p2);
214563afb9a5SDavid du Colombier 		p2->c = c;
214663afb9a5SDavid du Colombier 		if (kexes[c->kexalg]->clientkex2(c, p) < 0) {
214763afb9a5SDavid du Colombier 			add_byte(p2, SSH_MSG_DISCONNECT);
214863afb9a5SDavid du Colombier 			add_byte(p2, SSH_DISCONNECT_KEY_EXCHANGE_FAILED);
214963afb9a5SDavid du Colombier 			add_string(p2, "Key exchange failure");
215063afb9a5SDavid du Colombier 			add_string(p2, "");
215163afb9a5SDavid du Colombier 			n = finish_packet(p2);
215263afb9a5SDavid du Colombier 			iowrite(c->rio, c->datafd, p2->nlength, n);
215363afb9a5SDavid du Colombier 			shutdown(c);
215463afb9a5SDavid du Colombier 			free(p);
215563afb9a5SDavid du Colombier 			free(p2);
215663afb9a5SDavid du Colombier 			closeioproc(c->rio);
215763afb9a5SDavid du Colombier 			c->rio = nil;
215863afb9a5SDavid du Colombier 			c->rpid = -1;
215963afb9a5SDavid du Colombier 
216063afb9a5SDavid du Colombier 			qlock(&c->l);
216163afb9a5SDavid du Colombier 			rwakeup(&c->r);
216263afb9a5SDavid du Colombier 			qunlock(&c->l);
216363afb9a5SDavid du Colombier 
216463afb9a5SDavid du Colombier 			sshlog(c, "key exchange failure");
216563afb9a5SDavid du Colombier 			threadexits(nil);
216663afb9a5SDavid du Colombier 		}
216763afb9a5SDavid du Colombier 		add_byte(p2, SSH_MSG_NEWKEYS);
216863afb9a5SDavid du Colombier 		n = finish_packet(p2);
216963afb9a5SDavid du Colombier 		iowrite(c->rio, c->datafd, p2->nlength, n);
217063afb9a5SDavid du Colombier 		qlock(&c->l);
217163afb9a5SDavid du Colombier 		rwakeup(&c->r);
217263afb9a5SDavid du Colombier 		qunlock(&c->l);
217363afb9a5SDavid du Colombier 		break;
217463afb9a5SDavid du Colombier 	case SSH_MSG_SERVICE_REQUEST:
217563afb9a5SDavid du Colombier 		get_string(p, p->payload + 1, buf, Arbbufsz, nil);
217663afb9a5SDavid du Colombier 		sshdebug(c, "got service request: %s", buf);
217763afb9a5SDavid du Colombier 		if (strcmp(buf, "ssh-userauth") == 0 ||
217863afb9a5SDavid du Colombier 		    strcmp(buf, "ssh-connection") == 0) {
217963afb9a5SDavid du Colombier 			init_packet(p2);
218063afb9a5SDavid du Colombier 			p2->c = c;
218163afb9a5SDavid du Colombier 			sshdebug(c, "connection");
218263afb9a5SDavid du Colombier 			add_byte(p2, SSH_MSG_SERVICE_ACCEPT);
218363afb9a5SDavid du Colombier 			add_string(p2, buf);
218463afb9a5SDavid du Colombier 			n = finish_packet(p2);
218563afb9a5SDavid du Colombier 			iowrite(c->rio, c->datafd, p2->nlength, n);
218663afb9a5SDavid du Colombier 			c->state = Authing;
218763afb9a5SDavid du Colombier 		} else{
218863afb9a5SDavid du Colombier 			init_packet(p2);
218963afb9a5SDavid du Colombier 			p2->c = c;
219063afb9a5SDavid du Colombier 			add_byte(p2, SSH_MSG_DISCONNECT);
219163afb9a5SDavid du Colombier 			add_byte(p2, SSH_DISCONNECT_SERVICE_NOT_AVAILABLE);
219263afb9a5SDavid du Colombier 			add_string(p2, "Unknown service type");
219363afb9a5SDavid du Colombier 			add_string(p2, "");
219463afb9a5SDavid du Colombier 			n = finish_packet(p2);
219563afb9a5SDavid du Colombier 			iowrite(c->rio, c->datafd, p2->nlength, n);
219663afb9a5SDavid du Colombier 			return -1;
219763afb9a5SDavid du Colombier 		}
219863afb9a5SDavid du Colombier 		break;
219963afb9a5SDavid du Colombier 	case SSH_MSG_SERVICE_ACCEPT:
220063afb9a5SDavid du Colombier 		get_string(p, p->payload + 1, buf, Arbbufsz, nil);
220163afb9a5SDavid du Colombier 		if (c->service && strcmp(c->service, "ssh-userauth") == 0) {
220263afb9a5SDavid du Colombier 			free(c->service);
220363afb9a5SDavid du Colombier 			c->service = estrdup9p("ssh-connection");
220463afb9a5SDavid du Colombier 		}
220563afb9a5SDavid du Colombier 		sshdebug(c, "got service accept: %s: responding with %s %s",
220663afb9a5SDavid du Colombier 			buf, c->user, c->service);
220763afb9a5SDavid du Colombier 		n = client_auth(c, c->rio);
220863afb9a5SDavid du Colombier 		c->state = Authing;
220963afb9a5SDavid du Colombier 		if (n < 0) {
221063afb9a5SDavid du Colombier 			qlock(&c->l);
221163afb9a5SDavid du Colombier 			rwakeup(&c->r);
221263afb9a5SDavid du Colombier 			qunlock(&c->l);
221363afb9a5SDavid du Colombier 		}
221463afb9a5SDavid du Colombier 		break;
221563afb9a5SDavid du Colombier 	}
221663afb9a5SDavid du Colombier 	return 0;
221763afb9a5SDavid du Colombier }
221863afb9a5SDavid du Colombier 
221963afb9a5SDavid du Colombier static void
222063afb9a5SDavid du Colombier nochans(Conn *c, Packet *p, Packet *p2)
222163afb9a5SDavid du Colombier {
222263afb9a5SDavid du Colombier 	int n;
222363afb9a5SDavid du Colombier 
222463afb9a5SDavid du Colombier 	init_packet(p2);
222563afb9a5SDavid du Colombier 	p2->c = c;
222663afb9a5SDavid du Colombier 	add_byte(p2, SSH_MSG_CHANNEL_OPEN_FAILURE);
222763afb9a5SDavid du Colombier 	add_block(p2, p->payload + 5, 4);
222863afb9a5SDavid du Colombier 	hnputl(p2->payload + p2->rlength - 1, 4);
222963afb9a5SDavid du Colombier 	p2->rlength += 4;
223063afb9a5SDavid du Colombier 	add_string(p2, "No available channels");
223163afb9a5SDavid du Colombier 	add_string(p2, "EN");
223263afb9a5SDavid du Colombier 	n = finish_packet(p2);
223363afb9a5SDavid du Colombier 	iowrite(c->rio, c->datafd, p2->nlength, n);
223463afb9a5SDavid du Colombier }
223563afb9a5SDavid du Colombier 
223663afb9a5SDavid du Colombier static int
223763afb9a5SDavid du Colombier established(Conn *c, Packet *p, Packet *p2, char *buf, int size)
223863afb9a5SDavid du Colombier {
223963afb9a5SDavid du Colombier 	int i, n, cnum;
224063afb9a5SDavid du Colombier 	uchar *q;
224163afb9a5SDavid du Colombier 	Plist *pl;
224263afb9a5SDavid du Colombier 	SSHChan *ch;
224363afb9a5SDavid du Colombier 
224463afb9a5SDavid du Colombier 	USED(size);
224563afb9a5SDavid du Colombier 	if (debug > 1) {
224663afb9a5SDavid du Colombier 		sshdebug(c, "in Established state, got:");
224763afb9a5SDavid du Colombier 		dump_packet(p);
224863afb9a5SDavid du Colombier 	}
224963afb9a5SDavid du Colombier 	switch (p->payload[0]) {
225063afb9a5SDavid du Colombier 	case SSH_MSG_DISCONNECT:
225163afb9a5SDavid du Colombier 		if (debug) {
225263afb9a5SDavid du Colombier 			get_string(p, p->payload + 5, buf, Arbbufsz, nil);
225363afb9a5SDavid du Colombier 			sshdebug(c, "got disconnect: %s", buf);
225463afb9a5SDavid du Colombier 		}
225563afb9a5SDavid du Colombier 		return -1;
225663afb9a5SDavid du Colombier 	case SSH_MSG_IGNORE:
225763afb9a5SDavid du Colombier 	case SSH_MSG_UNIMPLEMENTED:
225863afb9a5SDavid du Colombier 		break;
225963afb9a5SDavid du Colombier 	case SSH_MSG_DEBUG:
226063afb9a5SDavid du Colombier 		if (debug || p->payload[1]) {
226163afb9a5SDavid du Colombier 			get_string(p, p->payload + 2, buf, Arbbufsz, nil);
226263afb9a5SDavid du Colombier 			sshdebug(c, "got debug message: %s", buf);
226363afb9a5SDavid du Colombier 		}
226463afb9a5SDavid du Colombier 		break;
226563afb9a5SDavid du Colombier 	case SSH_MSG_KEXINIT:
226663afb9a5SDavid du Colombier 		send_kexinit(c);
226763afb9a5SDavid du Colombier 		if (c->rkexinit)
226863afb9a5SDavid du Colombier 			free(c->rkexinit);
226963afb9a5SDavid du Colombier 		c->rkexinit = new_packet(c);
227063afb9a5SDavid du Colombier 		memmove(c->rkexinit, p, sizeof(Packet));
227163afb9a5SDavid du Colombier 		if (validatekex(c, p) < 0) {
227263afb9a5SDavid du Colombier 			sshdebug(c, "kex crypto algorithm mismatch (Established)");
227363afb9a5SDavid du Colombier 			return -1;
227463afb9a5SDavid du Colombier 		}
227563afb9a5SDavid du Colombier 		sshdebug(c, "using %s Kex algorithm and %s PKA",
227663afb9a5SDavid du Colombier 			kexes[c->kexalg]->name, pkas[c->pkalg]->name);
227763afb9a5SDavid du Colombier 		c->state = Negotiating;
227863afb9a5SDavid du Colombier 		break;
227963afb9a5SDavid du Colombier 	case SSH_MSG_GLOBAL_REQUEST:
228063afb9a5SDavid du Colombier 	case SSH_MSG_REQUEST_SUCCESS:
228163afb9a5SDavid du Colombier 	case SSH_MSG_REQUEST_FAILURE:
228263afb9a5SDavid du Colombier 		break;
228363afb9a5SDavid du Colombier 	case SSH_MSG_CHANNEL_OPEN:
228463afb9a5SDavid du Colombier 		q = get_string(p, p->payload + 1, buf, Arbbufsz, nil);
228563afb9a5SDavid du Colombier 		sshdebug(c, "searching for a listener for channel type %s", buf);
228663afb9a5SDavid du Colombier 		ch = alloc_chan(c);
228763afb9a5SDavid du Colombier 		if (ch == nil) {
228863afb9a5SDavid du Colombier 			nochans(c, p, p2);
228963afb9a5SDavid du Colombier 			break;
229063afb9a5SDavid du Colombier 		}
229163afb9a5SDavid du Colombier 
229263afb9a5SDavid du Colombier 		sshdebug(c, "alloced channel %d for listener", ch->id);
229363afb9a5SDavid du Colombier 		qlock(&c->l);
229463afb9a5SDavid du Colombier 		ch->otherid = nhgetl(q);
229563afb9a5SDavid du Colombier 		ch->twindow = nhgetl(q+4);
229663afb9a5SDavid du Colombier 		sshdebug(c, "got lock in channel open");
229763afb9a5SDavid du Colombier 		for (i = 0; i < c->nchan; ++i)
229863afb9a5SDavid du Colombier 			if (c->chans[i] && c->chans[i]->state == Listening &&
229963afb9a5SDavid du Colombier 			    c->chans[i]->ann &&
230063afb9a5SDavid du Colombier 			    strcmp(c->chans[i]->ann, buf) == 0)
230163afb9a5SDavid du Colombier 				break;
230263afb9a5SDavid du Colombier 		if (i >= c->nchan) {
230363afb9a5SDavid du Colombier 			sshdebug(c, "no listener: sleeping");
230463afb9a5SDavid du Colombier 			ch->state = Opening;
230563afb9a5SDavid du Colombier 			if (ch->ann)
230663afb9a5SDavid du Colombier 				free(ch->ann);
230763afb9a5SDavid du Colombier 			ch->ann = estrdup9p(buf);
230863afb9a5SDavid du Colombier 			sshdebug(c, "waiting for someone to announce %s", ch->ann);
230963afb9a5SDavid du Colombier 			rsleep(&ch->r);
231063afb9a5SDavid du Colombier 		} else{
231163afb9a5SDavid du Colombier 			sshdebug(c, "found listener on channel %d", ch->id);
231263afb9a5SDavid du Colombier 			c->chans[i]->waker = ch->id;
231363afb9a5SDavid du Colombier 			rwakeup(&c->chans[i]->r);
231463afb9a5SDavid du Colombier 		}
231563afb9a5SDavid du Colombier 		qunlock(&c->l);
231663afb9a5SDavid du Colombier 		break;
231763afb9a5SDavid du Colombier 	case SSH_MSG_CHANNEL_OPEN_CONFIRMATION:
231863afb9a5SDavid du Colombier 		cnum = nhgetl(p->payload + 1);
231963afb9a5SDavid du Colombier 		ch = c->chans[cnum];
232063afb9a5SDavid du Colombier 		qlock(&c->l);
232163afb9a5SDavid du Colombier 		ch->otherid = nhgetl(p->payload+5);
232263afb9a5SDavid du Colombier 		ch->twindow = nhgetl(p->payload+9);
232363afb9a5SDavid du Colombier 		rwakeup(&ch->r);
232463afb9a5SDavid du Colombier 		qunlock(&c->l);
232563afb9a5SDavid du Colombier 		break;
232663afb9a5SDavid du Colombier 	case SSH_MSG_CHANNEL_OPEN_FAILURE:
232763afb9a5SDavid du Colombier 		cnum = nhgetl(p->payload + 1);
232863afb9a5SDavid du Colombier 		ch = c->chans[cnum];
232963afb9a5SDavid du Colombier 		qlock(&c->l);
233063afb9a5SDavid du Colombier 		rwakeup(&ch->r);
233163afb9a5SDavid du Colombier 		qunlock(&c->l);
233263afb9a5SDavid du Colombier 		return -1;
233363afb9a5SDavid du Colombier 	case SSH_MSG_CHANNEL_WINDOW_ADJUST:
233463afb9a5SDavid du Colombier 		cnum = nhgetl(p->payload + 1);
233563afb9a5SDavid du Colombier 		ch = c->chans[cnum];
233663afb9a5SDavid du Colombier 		ch->twindow += nhgetl(p->payload + 5);
233763afb9a5SDavid du Colombier 		sshdebug(c, "new twindow for channel: %d: %lud", cnum, ch->twindow);
233863afb9a5SDavid du Colombier 		qlock(&ch->xmtlock);
233963afb9a5SDavid du Colombier 		rwakeup(&ch->xmtrendez);
234063afb9a5SDavid du Colombier 		qunlock(&ch->xmtlock);
234163afb9a5SDavid du Colombier 		break;
234263afb9a5SDavid du Colombier 	case SSH_MSG_CHANNEL_DATA:
234363afb9a5SDavid du Colombier 	case SSH_MSG_CHANNEL_EXTENDED_DATA:
234463afb9a5SDavid du Colombier 		cnum = nhgetl(p->payload + 1);
234563afb9a5SDavid du Colombier 		ch = c->chans[cnum];
234663afb9a5SDavid du Colombier 		pl = emalloc9p(sizeof(Plist));
234763afb9a5SDavid du Colombier 		pl->pack = emalloc9p(sizeof(Packet));
234863afb9a5SDavid du Colombier 		memmove(pl->pack, p, sizeof(Packet));
234963afb9a5SDavid du Colombier 		if (p->payload[0] == SSH_MSG_CHANNEL_DATA) {
235063afb9a5SDavid du Colombier 			pl->rem = nhgetl(p->payload + 5);
235163afb9a5SDavid du Colombier 			pl->st = pl->pack->payload + 9;
235263afb9a5SDavid du Colombier 		} else {
235363afb9a5SDavid du Colombier 			pl->rem = nhgetl(p->payload + 9);
235463afb9a5SDavid du Colombier 			pl->st = pl->pack->payload + 13;
235563afb9a5SDavid du Colombier 		}
235663afb9a5SDavid du Colombier 		pl->next = nil;
235763afb9a5SDavid du Colombier 		if (ch->dataq == nil)
235863afb9a5SDavid du Colombier 			ch->dataq = pl;
235963afb9a5SDavid du Colombier 		else
236063afb9a5SDavid du Colombier 			ch->datatl->next = pl;
236163afb9a5SDavid du Colombier 		ch->datatl = pl;
236263afb9a5SDavid du Colombier 		ch->inrqueue += pl->rem;
236363afb9a5SDavid du Colombier 		nbsendul(ch->inchan, 1);
236463afb9a5SDavid du Colombier 		break;
236563afb9a5SDavid du Colombier 	case SSH_MSG_CHANNEL_EOF:
236663afb9a5SDavid du Colombier 		cnum = nhgetl(p->payload + 1);
236763afb9a5SDavid du Colombier 		ch = c->chans[cnum];
236863afb9a5SDavid du Colombier 		if (ch->state != Closed && ch->state != Closing) {
236963afb9a5SDavid du Colombier 			ch->state = Eof;
237063afb9a5SDavid du Colombier 			nbsendul(ch->inchan, 1);
237163afb9a5SDavid du Colombier 			nbsendul(ch->reqchan, 1);
237263afb9a5SDavid du Colombier 		}
237363afb9a5SDavid du Colombier 		break;
237463afb9a5SDavid du Colombier 	case SSH_MSG_CHANNEL_CLOSE:
237563afb9a5SDavid du Colombier 		cnum = nhgetl(p->payload + 1);
237663afb9a5SDavid du Colombier 		ch = c->chans[cnum];
237763afb9a5SDavid du Colombier 		if (ch->state != Closed && ch->state != Closing) {
237863afb9a5SDavid du Colombier 			init_packet(p2);
237963afb9a5SDavid du Colombier 			p2->c = c;
238063afb9a5SDavid du Colombier 			add_byte(p2, SSH_MSG_CHANNEL_CLOSE);
238163afb9a5SDavid du Colombier 			hnputl(p2->payload + 1, ch->otherid);
238263afb9a5SDavid du Colombier 			p2->rlength += 4;
238363afb9a5SDavid du Colombier 			n = finish_packet(p2);
238463afb9a5SDavid du Colombier 			iowrite(c->rio, c->datafd, p2->nlength, n);
238563afb9a5SDavid du Colombier 		}
238663afb9a5SDavid du Colombier 		qlock(&c->l);
238763afb9a5SDavid du Colombier 		if (ch->state != Closed) {
238863afb9a5SDavid du Colombier 			ch->state = Closed;
238963afb9a5SDavid du Colombier 			rwakeup(&ch->r);
239063afb9a5SDavid du Colombier 			nbsendul(ch->inchan, 1);
239163afb9a5SDavid du Colombier 			nbsendul(ch->reqchan, 1);
239263afb9a5SDavid du Colombier 			chanclose(ch->inchan);
239363afb9a5SDavid du Colombier 			chanclose(ch->reqchan);
239463afb9a5SDavid du Colombier 		}
239563afb9a5SDavid du Colombier 		qunlock(&c->l);
239663afb9a5SDavid du Colombier 		for (i = 0; i < MAXCONN && (!c->chans[i] ||
239763afb9a5SDavid du Colombier 		    c->chans[i]->state == Empty || c->chans[i]->state == Closed);
239863afb9a5SDavid du Colombier 		    ++i)
239963afb9a5SDavid du Colombier 			;
240063afb9a5SDavid du Colombier 		if (i >= MAXCONN)
240163afb9a5SDavid du Colombier 			return -1;
240263afb9a5SDavid du Colombier 		break;
240363afb9a5SDavid du Colombier 	case SSH_MSG_CHANNEL_REQUEST:
240463afb9a5SDavid du Colombier 		cnum = nhgetl(p->payload + 1);
240563afb9a5SDavid du Colombier 		ch = c->chans[cnum];
240663afb9a5SDavid du Colombier 		sshdebug(c, "queueing channel request for channel: %d", cnum);
240763afb9a5SDavid du Colombier 		q = get_string(p, p->payload+5, buf, Arbbufsz, nil);
240863afb9a5SDavid du Colombier 		pl = emalloc9p(sizeof(Plist));
240963afb9a5SDavid du Colombier 		pl->pack = emalloc9p(sizeof(Packet));
241063afb9a5SDavid du Colombier 		n = snprint((char *)pl->pack->payload,
241163afb9a5SDavid du Colombier 			Maxpayload, "%s %c", buf, *q? 't': 'f');
241263afb9a5SDavid du Colombier 		sshdebug(c, "request message begins: %s",
241363afb9a5SDavid du Colombier 			(char *)pl->pack->payload);
241463afb9a5SDavid du Colombier 		memmove(pl->pack->payload + n, q + 1, p->rlength - (11 + (n-2)));
241563afb9a5SDavid du Colombier 		pl->rem = p->rlength - 11 + 2;
241663afb9a5SDavid du Colombier 		pl->st = pl->pack->payload;
241763afb9a5SDavid du Colombier 		pl->next = nil;
241863afb9a5SDavid du Colombier 		if (ch->reqq == nil)
241963afb9a5SDavid du Colombier 			ch->reqq = pl;
242063afb9a5SDavid du Colombier 		else
242163afb9a5SDavid du Colombier 			ch->reqtl->next = pl;
242263afb9a5SDavid du Colombier 		ch->reqtl = pl;
242363afb9a5SDavid du Colombier 		nbsendul(ch->reqchan, 1);
242463afb9a5SDavid du Colombier 		break;
242563afb9a5SDavid du Colombier 	case SSH_MSG_CHANNEL_SUCCESS:
242663afb9a5SDavid du Colombier 	case SSH_MSG_CHANNEL_FAILURE:
242763afb9a5SDavid du Colombier 	default:
242863afb9a5SDavid du Colombier 		break;
242963afb9a5SDavid du Colombier 	}
243063afb9a5SDavid du Colombier 	return 0;
243163afb9a5SDavid du Colombier }
243263afb9a5SDavid du Colombier 
243363afb9a5SDavid du Colombier static void
243463afb9a5SDavid du Colombier bail(Conn *c, Packet *p, Packet *p2, char *sts)
243563afb9a5SDavid du Colombier {
243663afb9a5SDavid du Colombier 	shutdown(c);
243763afb9a5SDavid du Colombier 	free(p);
243863afb9a5SDavid du Colombier 	free(p2);
243963afb9a5SDavid du Colombier 	if (c->rio) {
244063afb9a5SDavid du Colombier 		closeioproc(c->rio);
244163afb9a5SDavid du Colombier 		c->rio = nil;
244263afb9a5SDavid du Colombier 	}
244363afb9a5SDavid du Colombier 	c->rpid = -1;
244463afb9a5SDavid du Colombier 	threadexits(sts);
244563afb9a5SDavid du Colombier }
244663afb9a5SDavid du Colombier 
244763afb9a5SDavid du Colombier static void
244863afb9a5SDavid du Colombier reader0(Conn *c, Packet *p, Packet *p2)
244963afb9a5SDavid du Colombier {
245063afb9a5SDavid du Colombier 	int i, n, nl, np, nm, nb;
245163afb9a5SDavid du Colombier 	char buf[Arbbufsz];
245263afb9a5SDavid du Colombier 
245363afb9a5SDavid du Colombier 	nm = 0;
245463afb9a5SDavid du Colombier 	nb = 4;
245563afb9a5SDavid du Colombier 	if (c->decrypt != -1)
245663afb9a5SDavid du Colombier 		nb = cryptos[c->decrypt]->blklen;
245763afb9a5SDavid du Colombier 	sshdebug(c, "calling read for connection %d, state %d, nb %d, dc %d",
245863afb9a5SDavid du Colombier 		c->id, c->state, nb, c->decrypt);
245963afb9a5SDavid du Colombier 	if ((nl = ioreadn(c->rio, c->datafd, p->nlength, nb)) != nb) {
246063afb9a5SDavid du Colombier 		sshdebug(c, "reader for connection %d exiting, got %d: %r",
246163afb9a5SDavid du Colombier 			c->id, nl);
246263afb9a5SDavid du Colombier 		bail(c, p, p2, "reader exiting");
246363afb9a5SDavid du Colombier 	}
246463afb9a5SDavid du Colombier 	if (c->decrypt != -1)
246563afb9a5SDavid du Colombier 		cryptos[c->decrypt]->decrypt(c->deccs, p->nlength, nb);
246663afb9a5SDavid du Colombier 	p->rlength = nhgetl(p->nlength);
246763afb9a5SDavid du Colombier 	sshdebug(c, "got message length: %ld", p->rlength);
246863afb9a5SDavid du Colombier 	if (p->rlength > Maxpktpay) {
246963afb9a5SDavid du Colombier 		sshdebug(c, "absurd packet length: %ld, unrecoverable decrypt failure",
247063afb9a5SDavid du Colombier 			p->rlength);
247163afb9a5SDavid du Colombier 		bail(c, p, p2, "absurd packet length");
247263afb9a5SDavid du Colombier 	}
247363afb9a5SDavid du Colombier 	np = ioreadn(c->rio, c->datafd, p->nlength + nb, p->rlength + 4 - nb);
247463afb9a5SDavid du Colombier 	if (c->inmac != -1)
247563afb9a5SDavid du Colombier 		nm = ioreadn(c->rio, c->datafd, p->nlength + p->rlength + 4,
247663afb9a5SDavid du Colombier 			SHA1dlen);		/* SHA1dlen was magic 20 */
247763afb9a5SDavid du Colombier 	n = nl + np + nm;
247863afb9a5SDavid du Colombier 	if (debug) {
247963afb9a5SDavid du Colombier 		sshdebug(c, "got message of %d bytes %d padding", n, p->pad_len);
248063afb9a5SDavid du Colombier 		if (p->payload[0] > SSH_MSG_CHANNEL_OPEN) {
248163afb9a5SDavid du Colombier 			i = nhgetl(p->payload+1);
248263afb9a5SDavid du Colombier 			if (c->chans[i])
248363afb9a5SDavid du Colombier 				sshdebug(c, " for channel %d win %lud",
248463afb9a5SDavid du Colombier 					i, c->chans[i]->rwindow);
248563afb9a5SDavid du Colombier 			else
248663afb9a5SDavid du Colombier 				sshdebug(c, " for invalid channel %d", i);
248763afb9a5SDavid du Colombier 		}
248863afb9a5SDavid du Colombier 		sshdebug(c, " first byte: %d", p->payload[0]);
248963afb9a5SDavid du Colombier 	}
249063afb9a5SDavid du Colombier 	/* SHA1dlen was magic 20 */
249163afb9a5SDavid du Colombier 	if (np != p->rlength + 4 - nb || c->inmac != -1 && nm != SHA1dlen) {
249263afb9a5SDavid du Colombier 		sshdebug(c, "got EOF/error on connection read: %d %d %r", np, nm);
249363afb9a5SDavid du Colombier 		bail(c, p, p2, "error or eof");
249463afb9a5SDavid du Colombier 	}
249563afb9a5SDavid du Colombier 	p->tlength = n;
249663afb9a5SDavid du Colombier 	p->rlength = n - 4;
249763afb9a5SDavid du Colombier 	if (undo_packet(p) < 0) {
249863afb9a5SDavid du Colombier 		sshdebug(c, "bad packet in connection %d: exiting", c->id);
249963afb9a5SDavid du Colombier 		bail(c, p, p2, "bad packet");
250063afb9a5SDavid du Colombier 	}
250163afb9a5SDavid du Colombier 
250263afb9a5SDavid du Colombier 	if (c->state == Initting) {
250363afb9a5SDavid du Colombier 		if (p->payload[0] != SSH_MSG_KEXINIT) {
250463afb9a5SDavid du Colombier 			sshdebug(c, "missing KEX init packet: %d", p->payload[0]);
250563afb9a5SDavid du Colombier 			bail(c, p, p2, "bad kex");
250663afb9a5SDavid du Colombier 		}
250763afb9a5SDavid du Colombier 		if (c->rkexinit)
250863afb9a5SDavid du Colombier 			free(c->rkexinit);
250963afb9a5SDavid du Colombier 		c->rkexinit = new_packet(c);
251063afb9a5SDavid du Colombier 		memmove(c->rkexinit, p, sizeof(Packet));
251163afb9a5SDavid du Colombier 		if (validatekex(c, p) < 0) {
251263afb9a5SDavid du Colombier 			sshdebug(c, "kex crypto algorithm mismatch (Initting)");
251363afb9a5SDavid du Colombier 			bail(c, p, p2, "bad kex");
251463afb9a5SDavid du Colombier 		}
251563afb9a5SDavid du Colombier 		sshdebug(c, "using %s Kex algorithm and %s PKA",
251663afb9a5SDavid du Colombier 			kexes[c->kexalg]->name, pkas[c->pkalg]->name);
251763afb9a5SDavid du Colombier 		if (c->role == Client)
251863afb9a5SDavid du Colombier 			kexes[c->kexalg]->clientkex1(c, p);
251963afb9a5SDavid du Colombier 		c->state = Negotiating;
252063afb9a5SDavid du Colombier 	} else if (c->state == Negotiating) {
252163afb9a5SDavid du Colombier 		if (negotiating(c, p, p2, buf, sizeof buf) < 0)
252263afb9a5SDavid du Colombier 			bail(c, p, p2, "negotiating");
252363afb9a5SDavid du Colombier 	} else if (c->state == Authing) {
252463afb9a5SDavid du Colombier 		switch (p->payload[0]) {
252563afb9a5SDavid du Colombier 		case SSH_MSG_DISCONNECT:
252663afb9a5SDavid du Colombier 			if (debug) {
252763afb9a5SDavid du Colombier 				get_string(p, p->payload + 5, buf, Arbbufsz, nil);
252863afb9a5SDavid du Colombier 				sshdebug(c, "got disconnect: %s", buf);
252963afb9a5SDavid du Colombier 			}
253063afb9a5SDavid du Colombier 			bail(c, p, p2, "msg disconnect");
253163afb9a5SDavid du Colombier 		case SSH_MSG_USERAUTH_REQUEST:
253263afb9a5SDavid du Colombier 			switch (auth_req(p, c)) {
253363afb9a5SDavid du Colombier 			case 0:
253463afb9a5SDavid du Colombier 				establish(c);
253563afb9a5SDavid du Colombier 				break;
253663afb9a5SDavid du Colombier 			case -1:
253763afb9a5SDavid du Colombier 				break;
253863afb9a5SDavid du Colombier 			case -2:
253963afb9a5SDavid du Colombier 				bail(c, p, p2, "in userauth request");
254063afb9a5SDavid du Colombier 			}
254163afb9a5SDavid du Colombier 			break;
254263afb9a5SDavid du Colombier 		case SSH_MSG_USERAUTH_FAILURE:
254363afb9a5SDavid du Colombier 			qlock(&c->l);
254463afb9a5SDavid du Colombier 			rwakeup(&c->r);
254563afb9a5SDavid du Colombier 			qunlock(&c->l);
254663afb9a5SDavid du Colombier 			break;
254763afb9a5SDavid du Colombier 		case SSH_MSG_USERAUTH_SUCCESS:
254863afb9a5SDavid du Colombier 			establish(c);
254963afb9a5SDavid du Colombier 			break;
255063afb9a5SDavid du Colombier 		case SSH_MSG_USERAUTH_BANNER:
255163afb9a5SDavid du Colombier 			break;
255263afb9a5SDavid du Colombier 		}
255363afb9a5SDavid du Colombier 	} else if (c->state == Established) {
255463afb9a5SDavid du Colombier 		if (established(c, p, p2, buf, sizeof buf) < 0)
255563afb9a5SDavid du Colombier 			bail(c, p, p2, "from established state");
255663afb9a5SDavid du Colombier 	} else {
255763afb9a5SDavid du Colombier 		sshdebug(c, "connection %d in bad state, reader exiting", c->id);
255863afb9a5SDavid du Colombier 		bail(c, p, p2, "bad conn state");
255963afb9a5SDavid du Colombier 	}
256063afb9a5SDavid du Colombier }
256163afb9a5SDavid du Colombier 
256263afb9a5SDavid du Colombier void
256363afb9a5SDavid du Colombier reader(void *a)
256463afb9a5SDavid du Colombier {
256563afb9a5SDavid du Colombier 	Conn *c;
256663afb9a5SDavid du Colombier 	Packet *p, *p2;
256763afb9a5SDavid du Colombier 
256863afb9a5SDavid du Colombier 	threadsetname("reader");
256963afb9a5SDavid du Colombier 	c = a;
257063afb9a5SDavid du Colombier 	c->rpid = threadid();
257163afb9a5SDavid du Colombier 	sshdebug(c, "starting reader for connection %d, pid %d", c->id, c->rpid);
257263afb9a5SDavid du Colombier 	threadsetname("reader");
257363afb9a5SDavid du Colombier 	p = new_packet(c);
257463afb9a5SDavid du Colombier 	p2 = new_packet(c);
257563afb9a5SDavid du Colombier 	c->rio = ioproc();
257663afb9a5SDavid du Colombier 	for(;;)
257763afb9a5SDavid du Colombier 		reader0(c, p, p2);
257863afb9a5SDavid du Colombier }
257963afb9a5SDavid du Colombier 
258063afb9a5SDavid du Colombier int
258163afb9a5SDavid du Colombier validatekex(Conn *c, Packet *p)
258263afb9a5SDavid du Colombier {
258363afb9a5SDavid du Colombier 	if (c->role == Server)
258463afb9a5SDavid du Colombier 		return validatekexs(p);
258563afb9a5SDavid du Colombier 	else
258663afb9a5SDavid du Colombier 		return validatekexc(p);
258763afb9a5SDavid du Colombier }
258863afb9a5SDavid du Colombier 
258963afb9a5SDavid du Colombier int
259063afb9a5SDavid du Colombier validatekexs(Packet *p)
259163afb9a5SDavid du Colombier {
259263afb9a5SDavid du Colombier 	uchar *q;
259363afb9a5SDavid du Colombier 	char *toks[Maxtoks];
259463afb9a5SDavid du Colombier 	int i, j, n;
259563afb9a5SDavid du Colombier 	char *buf;
259663afb9a5SDavid du Colombier 
259763afb9a5SDavid du Colombier 	buf = emalloc9p(Bigbufsz);
259863afb9a5SDavid du Colombier 	q = p->payload + 17;
259963afb9a5SDavid du Colombier 
260063afb9a5SDavid du Colombier 	q = get_string(p, q, buf, Bigbufsz, nil);
260163afb9a5SDavid du Colombier 	sshdebug(nil, "received KEX algs: %s", buf);
260263afb9a5SDavid du Colombier 	n = gettokens(buf, toks, nelem(toks), ",");
260363afb9a5SDavid du Colombier 	for (i = 0; i < n; ++i)
260463afb9a5SDavid du Colombier 		for (j = 0; j < nelem(kexes); ++j)
260563afb9a5SDavid du Colombier 			if (strcmp(toks[i], kexes[j]->name) == 0)
260663afb9a5SDavid du Colombier 				goto foundk;
260763afb9a5SDavid du Colombier 	sshdebug(nil, "kex algs not in kexes");
260863afb9a5SDavid du Colombier 	free(buf);
260963afb9a5SDavid du Colombier 	return -1;
261063afb9a5SDavid du Colombier foundk:
261163afb9a5SDavid du Colombier 	p->c->kexalg = j;
261263afb9a5SDavid du Colombier 
261363afb9a5SDavid du Colombier 	q = get_string(p, q, buf, Bigbufsz, nil);
261463afb9a5SDavid du Colombier 	sshdebug(nil, "received host key algs: %s", buf);
261563afb9a5SDavid du Colombier 	n = gettokens(buf, toks, nelem(toks), ",");
261663afb9a5SDavid du Colombier 	for (i = 0; i < n; ++i)
261763afb9a5SDavid du Colombier 		for (j = 0; j < nelem(pkas) && pkas[j] != nil; ++j)
261863afb9a5SDavid du Colombier 			if (strcmp(toks[i], pkas[j]->name) == 0)
261963afb9a5SDavid du Colombier 				goto foundpka;
262063afb9a5SDavid du Colombier 	sshdebug(nil, "host key algs not in pkas");
262163afb9a5SDavid du Colombier 	free(buf);
262263afb9a5SDavid du Colombier 	return -1;
262363afb9a5SDavid du Colombier foundpka:
262463afb9a5SDavid du Colombier 	p->c->pkalg = j;
262563afb9a5SDavid du Colombier 
262663afb9a5SDavid du Colombier 	q = get_string(p, q, buf, Bigbufsz, nil);
262763afb9a5SDavid du Colombier 	sshdebug(nil, "received C2S crypto algs: %s", buf);
262863afb9a5SDavid du Colombier 	n = gettokens(buf, toks, nelem(toks), ",");
262963afb9a5SDavid du Colombier 	for (i = 0; i < n; ++i)
263063afb9a5SDavid du Colombier 		for (j = 0; j < nelem(cryptos); ++j)
263163afb9a5SDavid du Colombier 			if (strcmp(toks[i], cryptos[j]->name) == 0)
263263afb9a5SDavid du Colombier 				goto foundc1;
263363afb9a5SDavid du Colombier 	sshdebug(nil, "c2s crypto algs not in cryptos");
263463afb9a5SDavid du Colombier 	free(buf);
263563afb9a5SDavid du Colombier 	return -1;
263663afb9a5SDavid du Colombier foundc1:
263763afb9a5SDavid du Colombier 	p->c->ncscrypt = j;
263863afb9a5SDavid du Colombier 
263963afb9a5SDavid du Colombier 	q = get_string(p, q, buf, Bigbufsz, nil);
264063afb9a5SDavid du Colombier 	sshdebug(nil, "received S2C crypto algs: %s", buf);
264163afb9a5SDavid du Colombier 	n = gettokens(buf, toks, nelem(toks), ",");
264263afb9a5SDavid du Colombier 	for (i = 0; i < n; ++i)
264363afb9a5SDavid du Colombier 		for (j = 0; j < nelem(cryptos); ++j)
264463afb9a5SDavid du Colombier 			if (strcmp(toks[i], cryptos[j]->name) == 0)
264563afb9a5SDavid du Colombier 				goto foundc2;
264663afb9a5SDavid du Colombier 	sshdebug(nil, "s2c crypto algs not in cryptos");
264763afb9a5SDavid du Colombier 	free(buf);
264863afb9a5SDavid du Colombier 	return -1;
264963afb9a5SDavid du Colombier foundc2:
265063afb9a5SDavid du Colombier 	p->c->nsccrypt = j;
265163afb9a5SDavid du Colombier 
265263afb9a5SDavid du Colombier 	q = get_string(p, q, buf, Bigbufsz, nil);
265363afb9a5SDavid du Colombier 	sshdebug(nil, "received C2S MAC algs: %s", buf);
265463afb9a5SDavid du Colombier 	n = gettokens(buf, toks, nelem(toks), ",");
265563afb9a5SDavid du Colombier 	for (i = 0; i < n; ++i)
265663afb9a5SDavid du Colombier 		for (j = 0; j < nelem(macnames); ++j)
265763afb9a5SDavid du Colombier 			if (strcmp(toks[i], macnames[j]) == 0)
265863afb9a5SDavid du Colombier 				goto foundm1;
265963afb9a5SDavid du Colombier 	sshdebug(nil, "c2s mac algs not in cryptos");
266063afb9a5SDavid du Colombier 	free(buf);
266163afb9a5SDavid du Colombier 	return -1;
266263afb9a5SDavid du Colombier foundm1:
266363afb9a5SDavid du Colombier 	p->c->ncsmac = j;
266463afb9a5SDavid du Colombier 
266563afb9a5SDavid du Colombier 	q = get_string(p, q, buf, Bigbufsz, nil);
266663afb9a5SDavid du Colombier 	sshdebug(nil, "received S2C MAC algs: %s", buf);
266763afb9a5SDavid du Colombier 	n = gettokens(buf, toks, nelem(toks), ",");
266863afb9a5SDavid du Colombier 	for (i = 0; i < n; ++i)
266963afb9a5SDavid du Colombier 		for (j = 0; j < nelem(macnames); ++j)
267063afb9a5SDavid du Colombier 			if (strcmp(toks[i], macnames[j]) == 0)
267163afb9a5SDavid du Colombier 				goto foundm2;
267263afb9a5SDavid du Colombier 	sshdebug(nil, "s2c mac algs not in cryptos");
267363afb9a5SDavid du Colombier 	free(buf);
267463afb9a5SDavid du Colombier 	return -1;
267563afb9a5SDavid du Colombier foundm2:
267663afb9a5SDavid du Colombier 	p->c->nscmac = j;
267763afb9a5SDavid du Colombier 
267863afb9a5SDavid du Colombier 	q = get_string(p, q, buf, Bigbufsz, nil);
267963afb9a5SDavid du Colombier 	q = get_string(p, q, buf, Bigbufsz, nil);
268063afb9a5SDavid du Colombier 	q = get_string(p, q, buf, Bigbufsz, nil);
268163afb9a5SDavid du Colombier 	q = get_string(p, q, buf, Bigbufsz, nil);
268263afb9a5SDavid du Colombier 	free(buf);
268363afb9a5SDavid du Colombier 	if (*q)
268463afb9a5SDavid du Colombier 		return 1;
268563afb9a5SDavid du Colombier 	return 0;
268663afb9a5SDavid du Colombier }
268763afb9a5SDavid du Colombier 
268863afb9a5SDavid du Colombier int
268963afb9a5SDavid du Colombier validatekexc(Packet *p)
269063afb9a5SDavid du Colombier {
269163afb9a5SDavid du Colombier 	uchar *q;
269263afb9a5SDavid du Colombier 	char *toks[Maxtoks];
269363afb9a5SDavid du Colombier 	int i, j, n;
269463afb9a5SDavid du Colombier 	char *buf;
269563afb9a5SDavid du Colombier 
269663afb9a5SDavid du Colombier 	buf = emalloc9p(Bigbufsz);
269763afb9a5SDavid du Colombier 	q = p->payload + 17;
269863afb9a5SDavid du Colombier 	q = get_string(p, q, buf, Bigbufsz, nil);
269963afb9a5SDavid du Colombier 	n = gettokens(buf, toks, nelem(toks), ",");
270063afb9a5SDavid du Colombier 	for (j = 0; j < nelem(kexes); ++j)
270163afb9a5SDavid du Colombier 		for (i = 0; i < n; ++i)
270263afb9a5SDavid du Colombier 			if (strcmp(toks[i], kexes[j]->name) == 0)
270363afb9a5SDavid du Colombier 				goto foundk;
270463afb9a5SDavid du Colombier 	free(buf);
270563afb9a5SDavid du Colombier 	return -1;
270663afb9a5SDavid du Colombier foundk:
270763afb9a5SDavid du Colombier 	p->c->kexalg = j;
270863afb9a5SDavid du Colombier 
270963afb9a5SDavid du Colombier 	q = get_string(p, q, buf, Bigbufsz, nil);
271063afb9a5SDavid du Colombier 	n = gettokens(buf, toks, nelem(toks), ",");
271163afb9a5SDavid du Colombier 	for (j = 0; j < nelem(pkas) && pkas[j] != nil; ++j)
271263afb9a5SDavid du Colombier 		for (i = 0; i < n; ++i)
271363afb9a5SDavid du Colombier 			if (strcmp(toks[i], pkas[j]->name) == 0)
271463afb9a5SDavid du Colombier 				goto foundpka;
271563afb9a5SDavid du Colombier 	free(buf);
271663afb9a5SDavid du Colombier 	return -1;
271763afb9a5SDavid du Colombier foundpka:
271863afb9a5SDavid du Colombier 	p->c->pkalg = j;
271963afb9a5SDavid du Colombier 
272063afb9a5SDavid du Colombier 	q = get_string(p, q, buf, Bigbufsz, nil);
272163afb9a5SDavid du Colombier 	n = gettokens(buf, toks, nelem(toks), ",");
272263afb9a5SDavid du Colombier 	for (j = 0; j < nelem(cryptos); ++j)
272363afb9a5SDavid du Colombier 		for (i = 0; i < n; ++i)
272463afb9a5SDavid du Colombier 			if (strcmp(toks[i], cryptos[j]->name) == 0)
272563afb9a5SDavid du Colombier 				goto foundc1;
272663afb9a5SDavid du Colombier 	free(buf);
272763afb9a5SDavid du Colombier 	return -1;
272863afb9a5SDavid du Colombier foundc1:
272963afb9a5SDavid du Colombier 	p->c->ncscrypt = j;
273063afb9a5SDavid du Colombier 	q = get_string(p, q, buf, Bigbufsz, nil);
273163afb9a5SDavid du Colombier 	n = gettokens(buf, toks, nelem(toks), ",");
273263afb9a5SDavid du Colombier 	for (j = 0; j < nelem(cryptos); ++j)
273363afb9a5SDavid du Colombier 		for (i = 0; i < n; ++i)
273463afb9a5SDavid du Colombier 			if (strcmp(toks[i], cryptos[j]->name) == 0)
273563afb9a5SDavid du Colombier 				goto foundc2;
273663afb9a5SDavid du Colombier 	free(buf);
273763afb9a5SDavid du Colombier 	return -1;
273863afb9a5SDavid du Colombier foundc2:
273963afb9a5SDavid du Colombier 	p->c->nsccrypt = j;
274063afb9a5SDavid du Colombier 
274163afb9a5SDavid du Colombier 	q = get_string(p, q, buf, Bigbufsz, nil);
274263afb9a5SDavid du Colombier 	n = gettokens(buf, toks, nelem(toks), ",");
274363afb9a5SDavid du Colombier 	for (j = 0; j < nelem(macnames); ++j)
274463afb9a5SDavid du Colombier 		for (i = 0; i < n; ++i)
274563afb9a5SDavid du Colombier 			if (strcmp(toks[i], macnames[j]) == 0)
274663afb9a5SDavid du Colombier 				goto foundm1;
274763afb9a5SDavid du Colombier 	free(buf);
274863afb9a5SDavid du Colombier 	return -1;
274963afb9a5SDavid du Colombier foundm1:
275063afb9a5SDavid du Colombier 	p->c->ncsmac = j;
275163afb9a5SDavid du Colombier 
275263afb9a5SDavid du Colombier 	q = get_string(p, q, buf, Bigbufsz, nil);
275363afb9a5SDavid du Colombier 	n = gettokens(buf, toks, nelem(toks), ",");
275463afb9a5SDavid du Colombier 	for (j = 0; j < nelem(macnames); ++j)
275563afb9a5SDavid du Colombier 		for (i = 0; i < n; ++i)
275663afb9a5SDavid du Colombier 			if (strcmp(toks[i], macnames[j]) == 0)
275763afb9a5SDavid du Colombier 				goto foundm2;
275863afb9a5SDavid du Colombier 	free(buf);
275963afb9a5SDavid du Colombier 	return -1;
276063afb9a5SDavid du Colombier foundm2:
276163afb9a5SDavid du Colombier 	p->c->nscmac = j;
276263afb9a5SDavid du Colombier 
276363afb9a5SDavid du Colombier 	q = get_string(p, q, buf, Bigbufsz, nil);
276463afb9a5SDavid du Colombier 	q = get_string(p, q, buf, Bigbufsz, nil);
276563afb9a5SDavid du Colombier 	q = get_string(p, q, buf, Bigbufsz, nil);
276663afb9a5SDavid du Colombier 	q = get_string(p, q, buf, Bigbufsz, nil);
276763afb9a5SDavid du Colombier 	free(buf);
276863afb9a5SDavid du Colombier 	return *q != 0;
276963afb9a5SDavid du Colombier }
277063afb9a5SDavid du Colombier 
277163afb9a5SDavid du Colombier int
277263afb9a5SDavid du Colombier memrandom(void *p, int n)
277363afb9a5SDavid du Colombier {
277463afb9a5SDavid du Colombier 	uchar *cp;
277563afb9a5SDavid du Colombier 
277663afb9a5SDavid du Colombier 	for (cp = (uchar*)p; n > 0; n--)
277763afb9a5SDavid du Colombier 		*cp++ = fastrand();
277863afb9a5SDavid du Colombier 	return 0;
277963afb9a5SDavid du Colombier }
278063afb9a5SDavid du Colombier 
278163afb9a5SDavid du Colombier /*
278263afb9a5SDavid du Colombier  *  create a change uid capability
278363afb9a5SDavid du Colombier  */
278463afb9a5SDavid du Colombier char*
278563afb9a5SDavid du Colombier mkcap(char *from, char *to)
278663afb9a5SDavid du Colombier {
278763afb9a5SDavid du Colombier 	int fd, fromtosz;
278863afb9a5SDavid du Colombier 	char *cap, *key;
278963afb9a5SDavid du Colombier 	uchar rand[SHA1dlen], hash[SHA1dlen];
279063afb9a5SDavid du Colombier 
279163afb9a5SDavid du Colombier 	fd = open("#¤/caphash", OWRITE);
279263afb9a5SDavid du Colombier 	if (fd < 0)
279363afb9a5SDavid du Colombier 		sshlog(nil, "can't open #¤/caphash: %r");
279463afb9a5SDavid du Colombier 
279563afb9a5SDavid du Colombier 	/* create the capability */
279663afb9a5SDavid du Colombier 	fromtosz = strlen(from) + 1 + strlen(to) + 1;
279763afb9a5SDavid du Colombier 	cap = emalloc9p(fromtosz + sizeof(rand)*3 + 1);
279863afb9a5SDavid du Colombier 	snprint(cap, fromtosz + sizeof(rand)*3 + 1, "%s@%s", from, to);
279963afb9a5SDavid du Colombier 	memrandom(rand, sizeof(rand));
280063afb9a5SDavid du Colombier 	key = cap + fromtosz;
280163afb9a5SDavid du Colombier 	enc64(key, sizeof(rand)*3, rand, sizeof(rand));
280263afb9a5SDavid du Colombier 
280363afb9a5SDavid du Colombier 	/* hash the capability */
280463afb9a5SDavid du Colombier 	hmac_sha1((uchar*)cap, strlen(cap), (uchar*)key, strlen(key), hash, nil);
280563afb9a5SDavid du Colombier 
280663afb9a5SDavid du Colombier 	/* give the kernel the hash */
280763afb9a5SDavid du Colombier 	key[-1] = '@';
280863afb9a5SDavid du Colombier 	sshdebug(nil, "writing `%.*s' to caphash", SHA1dlen, hash);
280963afb9a5SDavid du Colombier 	if (write(fd, hash, SHA1dlen) != SHA1dlen) {
281063afb9a5SDavid du Colombier 		close(fd);
281163afb9a5SDavid du Colombier 		free(cap);
281263afb9a5SDavid du Colombier 		return nil;
281363afb9a5SDavid du Colombier 	}
281463afb9a5SDavid du Colombier 	close(fd);
281563afb9a5SDavid du Colombier 	return cap;
281663afb9a5SDavid du Colombier }
281763afb9a5SDavid du Colombier 
281863afb9a5SDavid du Colombier /*
281963afb9a5SDavid du Colombier  * ask keyfs (assumes we are on an auth server)
282063afb9a5SDavid du Colombier  */
282163afb9a5SDavid du Colombier static AuthInfo *
282263afb9a5SDavid du Colombier keyfsauth(char *me, char *user, char *pw, char *key1, char *key2)
282363afb9a5SDavid du Colombier {
282463afb9a5SDavid du Colombier 	int fd;
282563afb9a5SDavid du Colombier 	char path[Arbpathlen];
282663afb9a5SDavid du Colombier 	AuthInfo *ai;
282763afb9a5SDavid du Colombier 
282863afb9a5SDavid du Colombier 	if (passtokey(key1, pw) == 0)
282963afb9a5SDavid du Colombier 		return nil;
283063afb9a5SDavid du Colombier 
283163afb9a5SDavid du Colombier 	snprint(path, Arbpathlen, "/mnt/keys/%s/key", user);
283263afb9a5SDavid du Colombier 	if ((fd = open(path, OREAD)) < 0) {
283363afb9a5SDavid du Colombier 		werrstr("Invalid user %s", user);
283463afb9a5SDavid du Colombier 		return nil;
283563afb9a5SDavid du Colombier 	}
283663afb9a5SDavid du Colombier 	if (read(fd, key2, DESKEYLEN) != DESKEYLEN) {
283763afb9a5SDavid du Colombier 		close(fd);
283863afb9a5SDavid du Colombier 		werrstr("Password mismatch 1");
283963afb9a5SDavid du Colombier 		return nil;
284063afb9a5SDavid du Colombier 	}
284163afb9a5SDavid du Colombier 	close(fd);
284263afb9a5SDavid du Colombier 
284363afb9a5SDavid du Colombier 	if (memcmp(key1, key2, DESKEYLEN) != 0) {
284463afb9a5SDavid du Colombier 		werrstr("Password mismatch 2");
284563afb9a5SDavid du Colombier 		return nil;
284663afb9a5SDavid du Colombier 	}
284763afb9a5SDavid du Colombier 
284863afb9a5SDavid du Colombier 	ai = emalloc9p(sizeof(AuthInfo));
284963afb9a5SDavid du Colombier 	ai->cuid = estrdup9p(user);
285063afb9a5SDavid du Colombier 	ai->suid = estrdup9p(me);
285163afb9a5SDavid du Colombier 	ai->cap = mkcap(me, user);
285263afb9a5SDavid du Colombier 	ai->nsecret = 0;
285363afb9a5SDavid du Colombier 	ai->secret = (uchar *)estrdup9p("");
285463afb9a5SDavid du Colombier 	return ai;
285563afb9a5SDavid du Colombier }
285663afb9a5SDavid du Colombier 
285763afb9a5SDavid du Colombier static void
285863afb9a5SDavid du Colombier userauthfailed(Packet *p2)
285963afb9a5SDavid du Colombier {
286063afb9a5SDavid du Colombier 	add_byte(p2, SSH_MSG_USERAUTH_FAILURE);
286163afb9a5SDavid du Colombier 	add_string(p2, "password,publickey");
286263afb9a5SDavid du Colombier 	add_byte(p2, 0);
286363afb9a5SDavid du Colombier }
286463afb9a5SDavid du Colombier 
286563afb9a5SDavid du Colombier static int
286663afb9a5SDavid du Colombier authreqpk(Packet *p, Packet *p2, Conn *c, char *user, uchar *q,
286763afb9a5SDavid du Colombier 	char *alg, char *blob, char *sig, char *service, char *me)
286863afb9a5SDavid du Colombier {
286963afb9a5SDavid du Colombier 	int n, thisway, nblob, nsig;
287063afb9a5SDavid du Colombier 	char method[32];
287163afb9a5SDavid du Colombier 
287263afb9a5SDavid du Colombier 	sshdebug(c, "auth_req publickey for user %s", user);
287363afb9a5SDavid du Colombier 	thisway = *q == '\0';
287463afb9a5SDavid du Colombier 	q = get_string(p, q+1, alg, Arbpathlen, nil);
287563afb9a5SDavid du Colombier 	q = get_string(p, q, blob, Blobsz, &nblob);
287663afb9a5SDavid du Colombier 	if (thisway) {
287763afb9a5SDavid du Colombier 		/*
287863afb9a5SDavid du Colombier 		 * Should really check to see if this user can
287963afb9a5SDavid du Colombier 		 * be authed this way.
288063afb9a5SDavid du Colombier 		 */
288163afb9a5SDavid du Colombier 		for (n = 0; n < nelem(pkas) && pkas[n] != nil &&
288263afb9a5SDavid du Colombier 		    strcmp(pkas[n]->name, alg) != 0; ++n)
288363afb9a5SDavid du Colombier 			;
288463afb9a5SDavid du Colombier 		if (n >= nelem(pkas) || pkas[n] == nil) {
288563afb9a5SDavid du Colombier 			userauthfailed(p2);
288663afb9a5SDavid du Colombier 			return -1;
288763afb9a5SDavid du Colombier 		}
288863afb9a5SDavid du Colombier 		add_byte(p2, SSH_MSG_USERAUTH_PK_OK);
288963afb9a5SDavid du Colombier 		add_string(p2, alg);
289063afb9a5SDavid du Colombier 		add_block(p2, blob, nblob);
289163afb9a5SDavid du Colombier 		return 0;
289263afb9a5SDavid du Colombier 	}
289363afb9a5SDavid du Colombier 
289463afb9a5SDavid du Colombier 	get_string(p, q, sig, Blobsz, &nsig);
289563afb9a5SDavid du Colombier 	for (n = 0; n < nelem(pkas) && pkas[n] != nil &&
289663afb9a5SDavid du Colombier 	    strcmp(pkas[n]->name, alg) != 0; ++n)
289763afb9a5SDavid du Colombier 		;
289863afb9a5SDavid du Colombier 	if (n >= nelem(pkas) || pkas[n] == nil) {
289963afb9a5SDavid du Colombier 		userauthfailed(p2);
290063afb9a5SDavid du Colombier 		return -1;
290163afb9a5SDavid du Colombier 	}
290263afb9a5SDavid du Colombier 
290363afb9a5SDavid du Colombier 	add_block(p2, c->sessid, SHA1dlen);
290463afb9a5SDavid du Colombier 	add_byte(p2, SSH_MSG_USERAUTH_REQUEST);
290563afb9a5SDavid du Colombier 	add_string(p2, user);
290663afb9a5SDavid du Colombier 	add_string(p2, service);
290763afb9a5SDavid du Colombier 	add_string(p2, method);
290863afb9a5SDavid du Colombier 	add_byte(p2, 1);
290963afb9a5SDavid du Colombier 	add_string(p2, alg);
291063afb9a5SDavid du Colombier 	add_block(p2, blob, nblob);
291163afb9a5SDavid du Colombier 	if (pkas[n]->verify(c, p2->payload, p2->rlength - 1, user, sig, nsig)
291263afb9a5SDavid du Colombier 	    == 0) {
291363afb9a5SDavid du Colombier 		init_packet(p2);
291463afb9a5SDavid du Colombier 		p2->c = c;
291563afb9a5SDavid du Colombier 		sshlog(c, "public key login failed");
291663afb9a5SDavid du Colombier 		userauthfailed(p2);
291763afb9a5SDavid du Colombier 		return -1;
291863afb9a5SDavid du Colombier 	}
291963afb9a5SDavid du Colombier 	free(c->cap);
292063afb9a5SDavid du Colombier 	c->cap = mkcap(me, user);
292163afb9a5SDavid du Colombier 	init_packet(p2);
292263afb9a5SDavid du Colombier 	p2->c = c;
292363afb9a5SDavid du Colombier 	sshlog(c, "logged in by public key");
292463afb9a5SDavid du Colombier 	add_byte(p2, SSH_MSG_USERAUTH_SUCCESS);
292563afb9a5SDavid du Colombier 	return 0;
292663afb9a5SDavid du Colombier }
292763afb9a5SDavid du Colombier 
292863afb9a5SDavid du Colombier int
292963afb9a5SDavid du Colombier auth_req(Packet *p, Conn *c)
293063afb9a5SDavid du Colombier {
293163afb9a5SDavid du Colombier 	int n, ret;
293263afb9a5SDavid du Colombier 	char *alg, *blob, *sig, *service, *me, *user, *pw, *path;
293363afb9a5SDavid du Colombier 	char key1[DESKEYLEN], key2[DESKEYLEN], method[32];
293463afb9a5SDavid du Colombier 	uchar *q;
293563afb9a5SDavid du Colombier 	AuthInfo *ai;
293663afb9a5SDavid du Colombier 	Packet *p2;
293763afb9a5SDavid du Colombier 
293863afb9a5SDavid du Colombier 	service = emalloc9p(Arbpathlen);
293963afb9a5SDavid du Colombier 	me = emalloc9p(Arbpathlen);
294063afb9a5SDavid du Colombier 	user = emalloc9p(Arbpathlen);
294163afb9a5SDavid du Colombier 	pw = emalloc9p(Arbpathlen);
294263afb9a5SDavid du Colombier 	alg = emalloc9p(Arbpathlen);
294363afb9a5SDavid du Colombier 	path = emalloc9p(Arbpathlen);
294463afb9a5SDavid du Colombier 	blob = emalloc9p(Blobsz);
294563afb9a5SDavid du Colombier 	sig = emalloc9p(Blobsz);
294663afb9a5SDavid du Colombier 	ret = -1;				/* failure is default */
294763afb9a5SDavid du Colombier 
294863afb9a5SDavid du Colombier 	q = get_string(p, p->payload + 1, user, Arbpathlen, nil);
294963afb9a5SDavid du Colombier 	free(c->user);
295063afb9a5SDavid du Colombier 	c->user = estrdup9p(user);
295163afb9a5SDavid du Colombier 	q = get_string(p, q, service, Arbpathlen, nil);
295263afb9a5SDavid du Colombier 	q = get_string(p, q, method, sizeof method, nil);
295363afb9a5SDavid du Colombier 	sshdebug(c, "got userauth request: %s %s %s", user, service, method);
295463afb9a5SDavid du Colombier 
295563afb9a5SDavid du Colombier 	readfile("/dev/user", me, Arbpathlen);
295663afb9a5SDavid du Colombier 
295763afb9a5SDavid du Colombier 	p2 = new_packet(c);
295863afb9a5SDavid du Colombier 	if (strcmp(method, "publickey") == 0)
295963afb9a5SDavid du Colombier 		ret = authreqpk(p, p2, c, user, q, alg, blob, sig, service, me);
296063afb9a5SDavid du Colombier 	else if (strcmp(method, "password") == 0) {
296163afb9a5SDavid du Colombier 		get_string(p, q + 1, pw, Arbpathlen, nil);
296263afb9a5SDavid du Colombier 		// sshdebug(c, "%s", pw);	/* bad idea to log passwords */
296363afb9a5SDavid du Colombier 		sshdebug(c, "auth_req password");
296463afb9a5SDavid du Colombier 		if (kflag)
296563afb9a5SDavid du Colombier 			ai = keyfsauth(me, user, pw, key1, key2);
296663afb9a5SDavid du Colombier 		else
296763afb9a5SDavid du Colombier 			ai = auth_userpasswd(user, pw);
296863afb9a5SDavid du Colombier 		if (ai == nil) {
296963afb9a5SDavid du Colombier 			sshlog(c, "login failed: %r");
297063afb9a5SDavid du Colombier 			userauthfailed(p2);
297163afb9a5SDavid du Colombier 		} else {
297263afb9a5SDavid du Colombier 			sshdebug(c, "auth successful: cuid %s suid %s cap %s",
297363afb9a5SDavid du Colombier 				ai->cuid, ai->suid, ai->cap);
297463afb9a5SDavid du Colombier 			free(c->cap);
297563afb9a5SDavid du Colombier 			if (strcmp(user, me) == 0)
297663afb9a5SDavid du Colombier 				c->cap = estrdup9p("n/a");
297763afb9a5SDavid du Colombier 			else
297863afb9a5SDavid du Colombier 				c->cap = estrdup9p(ai->cap);
297963afb9a5SDavid du Colombier 			sshlog(c, "logged in by password");
298063afb9a5SDavid du Colombier 			add_byte(p2, SSH_MSG_USERAUTH_SUCCESS);
298163afb9a5SDavid du Colombier 			auth_freeAI(ai);
298263afb9a5SDavid du Colombier 			ret = 0;
298363afb9a5SDavid du Colombier 		}
298463afb9a5SDavid du Colombier 	} else
298563afb9a5SDavid du Colombier 		userauthfailed(p2);
298663afb9a5SDavid du Colombier 
298763afb9a5SDavid du Colombier 	n = finish_packet(p2);
298863afb9a5SDavid du Colombier 	iowrite(c->dio, c->datafd, p2->nlength, n);
298963afb9a5SDavid du Colombier 
299063afb9a5SDavid du Colombier 	free(service);
299163afb9a5SDavid du Colombier 	free(me);
299263afb9a5SDavid du Colombier 	free(user);
299363afb9a5SDavid du Colombier 	free(pw);
299463afb9a5SDavid du Colombier 	free(alg);
299563afb9a5SDavid du Colombier 	free(blob);
299663afb9a5SDavid du Colombier 	free(sig);
299763afb9a5SDavid du Colombier 	free(path);
299863afb9a5SDavid du Colombier 	free(p2);
299963afb9a5SDavid du Colombier 	return ret;
300063afb9a5SDavid du Colombier }
300163afb9a5SDavid du Colombier 
300263afb9a5SDavid du Colombier int
300363afb9a5SDavid du Colombier client_auth(Conn *c, Ioproc *io)
300463afb9a5SDavid du Colombier {
300563afb9a5SDavid du Colombier 	Packet *p2, *p3, *p4;
300663afb9a5SDavid du Colombier 	char *r, *s;
300763afb9a5SDavid du Colombier 	mpint *ek, *nk;
300863afb9a5SDavid du Colombier 	int i, n;
300963afb9a5SDavid du Colombier 
301063afb9a5SDavid du Colombier 	sshdebug(c, "client_auth");
301163afb9a5SDavid du Colombier 	if (!c->password && !c->authkey)
301263afb9a5SDavid du Colombier 		return -1;
301363afb9a5SDavid du Colombier 
301463afb9a5SDavid du Colombier 	p2 = new_packet(c);
301563afb9a5SDavid du Colombier 	add_byte(p2, SSH_MSG_USERAUTH_REQUEST);
301663afb9a5SDavid du Colombier 	add_string(p2, c->user);
301763afb9a5SDavid du Colombier 	add_string(p2, c->service);
301863afb9a5SDavid du Colombier 	if (c->password) {
301963afb9a5SDavid du Colombier 		add_string(p2, "password");
302063afb9a5SDavid du Colombier 		add_byte(p2, 0);
302163afb9a5SDavid du Colombier 		add_string(p2, c->password);
302263afb9a5SDavid du Colombier 		sshdebug(c, "client_auth using password for svc %s", c->service);
302363afb9a5SDavid du Colombier 	} else {
302463afb9a5SDavid du Colombier 		sshdebug(c, "client_auth trying rsa public key");
302563afb9a5SDavid du Colombier 		add_string(p2, "publickey");
302663afb9a5SDavid du Colombier 		add_byte(p2, 1);
302763afb9a5SDavid du Colombier 		add_string(p2, "ssh-rsa");
302863afb9a5SDavid du Colombier 
302963afb9a5SDavid du Colombier 		r = strstr(c->authkey, " ek=");
303063afb9a5SDavid du Colombier 		s = strstr(c->authkey, " n=");
303163afb9a5SDavid du Colombier 		if (!r || !s) {
303263afb9a5SDavid du Colombier 			shutdown(c);
303363afb9a5SDavid du Colombier 			free(p2);
303463afb9a5SDavid du Colombier 			sshdebug(c, "client_auth no rsa key");
303563afb9a5SDavid du Colombier 			return -1;
303663afb9a5SDavid du Colombier 		}
303763afb9a5SDavid du Colombier 		ek = strtomp(r+4, nil, 16, nil);
303863afb9a5SDavid du Colombier 		nk = strtomp(s+3, nil, 16, nil);
303963afb9a5SDavid du Colombier 
304063afb9a5SDavid du Colombier 		p3 = new_packet(c);
304163afb9a5SDavid du Colombier 		add_string(p3, "ssh-rsa");
304263afb9a5SDavid du Colombier 		add_mp(p3, ek);
304363afb9a5SDavid du Colombier 		add_mp(p3, nk);
304463afb9a5SDavid du Colombier 		add_block(p2, p3->payload, p3->rlength-1);
304563afb9a5SDavid du Colombier 
304663afb9a5SDavid du Colombier 		p4 = new_packet(c);
304763afb9a5SDavid du Colombier 		add_block(p4, c->sessid, SHA1dlen);
304863afb9a5SDavid du Colombier 		add_byte(p4, SSH_MSG_USERAUTH_REQUEST);
304963afb9a5SDavid du Colombier 		add_string(p4, c->user);
305063afb9a5SDavid du Colombier 		add_string(p4, c->service);
305163afb9a5SDavid du Colombier 		add_string(p4, "publickey");
305263afb9a5SDavid du Colombier 		add_byte(p4, 1);
305363afb9a5SDavid du Colombier 		add_string(p4, "ssh-rsa");
305463afb9a5SDavid du Colombier 		add_block(p4, p3->payload, p3->rlength-1);
305563afb9a5SDavid du Colombier 		mpfree(ek);
305663afb9a5SDavid du Colombier 		mpfree(nk);
305763afb9a5SDavid du Colombier 		free(p3);
305863afb9a5SDavid du Colombier 
305963afb9a5SDavid du Colombier 		for (i = 0; pkas[i] && strcmp("ssh-rsa", pkas[i]->name) != 0;
306063afb9a5SDavid du Colombier 		    ++i)
306163afb9a5SDavid du Colombier 			;
306263afb9a5SDavid du Colombier 		sshdebug(c, "client_auth rsa signing alg %d: %r",  i);
306363afb9a5SDavid du Colombier 		if ((p3 = pkas[i]->sign(c, p4->payload, p4->rlength-1)) == nil) {
306463afb9a5SDavid du Colombier 			sshdebug(c, "client_auth rsa signing failed: %r");
306563afb9a5SDavid du Colombier 			free(p4);
306663afb9a5SDavid du Colombier 			free(p2);
306763afb9a5SDavid du Colombier 			return -1;
306863afb9a5SDavid du Colombier 		}
306963afb9a5SDavid du Colombier 		add_block(p2, p3->payload, p3->rlength-1);
307063afb9a5SDavid du Colombier 		free(p3);
307163afb9a5SDavid du Colombier 		free(p4);
307263afb9a5SDavid du Colombier 	}
307363afb9a5SDavid du Colombier 
307463afb9a5SDavid du Colombier 	n = finish_packet(p2);
307563afb9a5SDavid du Colombier 	if (writeio(io, c->datafd, p2->nlength, n) != n)
307663afb9a5SDavid du Colombier 		sshdebug(c, "client_auth write failed: %r");
307763afb9a5SDavid du Colombier 	free(p2);
307863afb9a5SDavid du Colombier 	return 0;
307963afb9a5SDavid du Colombier }
308063afb9a5SDavid du Colombier 
308163afb9a5SDavid du Colombier /* should use auth_getkey or something similar */
308263afb9a5SDavid du Colombier char *
308363afb9a5SDavid du Colombier factlookup(int nattr, int nreq, char *attrs[])
308463afb9a5SDavid du Colombier {
308563afb9a5SDavid du Colombier 	Biobuf *bp;
308663afb9a5SDavid du Colombier 	char *buf, *toks[Maxtoks], *res, *q;
308763afb9a5SDavid du Colombier 	int ntok, nmatch, maxmatch;
308863afb9a5SDavid du Colombier 	int i, j;
308963afb9a5SDavid du Colombier 
309063afb9a5SDavid du Colombier 	res = nil;
309163afb9a5SDavid du Colombier 	bp = Bopen("/mnt/factotum/ctl", OREAD);
309263afb9a5SDavid du Colombier 	if (bp == nil)
309363afb9a5SDavid du Colombier 		return nil;
309463afb9a5SDavid du Colombier 	maxmatch = 0;
309563afb9a5SDavid du Colombier 	while (buf = Brdstr(bp, '\n', 1)) {
309663afb9a5SDavid du Colombier 		q = estrdup9p(buf);
309763afb9a5SDavid du Colombier 		ntok = gettokens(buf, toks, nelem(toks), " ");
309863afb9a5SDavid du Colombier 		nmatch = 0;
309963afb9a5SDavid du Colombier 		for (i = 0; i < nattr; ++i) {
310063afb9a5SDavid du Colombier 			for (j = 0; j < ntok; ++j)
310163afb9a5SDavid du Colombier 				if (strcmp(attrs[i], toks[j]) == 0) {
310263afb9a5SDavid du Colombier 					++nmatch;
310363afb9a5SDavid du Colombier 					break;
310463afb9a5SDavid du Colombier 				}
310563afb9a5SDavid du Colombier 			if (i < nreq && j >= ntok)
310663afb9a5SDavid du Colombier 				break;
310763afb9a5SDavid du Colombier 		}
310863afb9a5SDavid du Colombier 		if (i >= nattr && nmatch > maxmatch) {
310963afb9a5SDavid du Colombier 			free(res);
311063afb9a5SDavid du Colombier 			res = q;
311163afb9a5SDavid du Colombier 			maxmatch = nmatch;
311263afb9a5SDavid du Colombier 		} else
311363afb9a5SDavid du Colombier 			free(q);
311463afb9a5SDavid du Colombier 		free(buf);
311563afb9a5SDavid du Colombier 	}
311663afb9a5SDavid du Colombier 	Bterm(bp);
311763afb9a5SDavid du Colombier 	return res;
311863afb9a5SDavid du Colombier }
311963afb9a5SDavid du Colombier 
312063afb9a5SDavid du Colombier void
312163afb9a5SDavid du Colombier shutdown(Conn *c)
312263afb9a5SDavid du Colombier {
312363afb9a5SDavid du Colombier 	Plist *p;
312463afb9a5SDavid du Colombier 	SSHChan *sc;
312563afb9a5SDavid du Colombier 	int i, ostate;
312663afb9a5SDavid du Colombier 
312763afb9a5SDavid du Colombier 	sshdebug(c, "shutting down connection %d", c->id);
312863afb9a5SDavid du Colombier 	ostate = c->state;
312963afb9a5SDavid du Colombier 	if (c->clonefile->ref <= 2 && c->ctlfile->ref <= 2 &&
313063afb9a5SDavid du Colombier 	    c->datafile->ref <= 2 && c->listenfile->ref <= 2 &&
313163afb9a5SDavid du Colombier 	    c->localfile->ref <= 2 && c->remotefile->ref <= 2 &&
313263afb9a5SDavid du Colombier 	    c->statusfile->ref <= 2)
313363afb9a5SDavid du Colombier 		c->state = Closed;
313463afb9a5SDavid du Colombier 	else {
313563afb9a5SDavid du Colombier 		if (c->state != Closed)
313663afb9a5SDavid du Colombier 			c->state = Closing;
313763afb9a5SDavid du Colombier 		sshdebug(c, "clone %ld ctl %ld data %ld listen %ld "
313863afb9a5SDavid du Colombier 			"local %ld remote %ld status %ld",
313963afb9a5SDavid du Colombier 			c->clonefile->ref, c->ctlfile->ref, c->datafile->ref,
314063afb9a5SDavid du Colombier 			c->listenfile->ref, c->localfile->ref, c->remotefile->ref,
314163afb9a5SDavid du Colombier 			c->statusfile->ref);
314263afb9a5SDavid du Colombier 	}
314363afb9a5SDavid du Colombier 	if (ostate == Closed || ostate == Closing) {
314463afb9a5SDavid du Colombier 		c->state = Closed;
314563afb9a5SDavid du Colombier 		return;
314663afb9a5SDavid du Colombier 	}
314763afb9a5SDavid du Colombier 	if (c->role == Server && c->remote)
314863afb9a5SDavid du Colombier 		sshlog(c, "closing connection");
314963afb9a5SDavid du Colombier 	hangupconn(c);
315063afb9a5SDavid du Colombier 	if (c->dio) {
315163afb9a5SDavid du Colombier 		closeioproc(c->dio);
315263afb9a5SDavid du Colombier 		c->dio = nil;
315363afb9a5SDavid du Colombier 	}
315463afb9a5SDavid du Colombier 
315563afb9a5SDavid du Colombier 	c->decrypt = -1;
315663afb9a5SDavid du Colombier 	c->inmac = -1;
315763afb9a5SDavid du Colombier 	c->nchan = 0;
315863afb9a5SDavid du Colombier 	free(c->otherid);
315963afb9a5SDavid du Colombier 	free(c->s2ccs);
316063afb9a5SDavid du Colombier 	c->s2ccs = nil;
316163afb9a5SDavid du Colombier 	free(c->c2scs);
316263afb9a5SDavid du Colombier 	c->c2scs = nil;
316363afb9a5SDavid du Colombier 	free(c->remote);
316463afb9a5SDavid du Colombier 	c->remote = nil;
316563afb9a5SDavid du Colombier 	if (c->x) {
316663afb9a5SDavid du Colombier 		mpfree(c->x);
316763afb9a5SDavid du Colombier 		c->x = nil;
316863afb9a5SDavid du Colombier 	}
316963afb9a5SDavid du Colombier 	if (c->e) {
317063afb9a5SDavid du Colombier 		mpfree(c->e);
317163afb9a5SDavid du Colombier 		c->e = nil;
317263afb9a5SDavid du Colombier 	}
317363afb9a5SDavid du Colombier 	free(c->user);
317463afb9a5SDavid du Colombier 	c->user = nil;
317563afb9a5SDavid du Colombier 	free(c->service);
317663afb9a5SDavid du Colombier 	c->service = nil;
317763afb9a5SDavid du Colombier 	c->otherid = nil;
317863afb9a5SDavid du Colombier 	qlock(&c->l);
317963afb9a5SDavid du Colombier 	rwakeupall(&c->r);
318063afb9a5SDavid du Colombier 	qunlock(&c->l);
318163afb9a5SDavid du Colombier 	for (i = 0; i < MAXCONN; ++i) {
318263afb9a5SDavid du Colombier 		sc = c->chans[i];
318363afb9a5SDavid du Colombier 		if (sc == nil)
318463afb9a5SDavid du Colombier 			continue;
318563afb9a5SDavid du Colombier 		free(sc->ann);
318663afb9a5SDavid du Colombier 		sc->ann = nil;
318763afb9a5SDavid du Colombier 		if (sc->state != Empty && sc->state != Closed) {
318863afb9a5SDavid du Colombier 			sc->state = Closed;
318963afb9a5SDavid du Colombier 			sc->lreq = nil;
319063afb9a5SDavid du Colombier 			while (sc->dataq != nil) {
319163afb9a5SDavid du Colombier 				p = sc->dataq;
319263afb9a5SDavid du Colombier 				sc->dataq = p->next;
319363afb9a5SDavid du Colombier 				free(p->pack);
319463afb9a5SDavid du Colombier 				free(p);
319563afb9a5SDavid du Colombier 			}
319663afb9a5SDavid du Colombier 			while (sc->reqq != nil) {
319763afb9a5SDavid du Colombier 				p = sc->reqq;
319863afb9a5SDavid du Colombier 				sc->reqq = p->next;
319963afb9a5SDavid du Colombier 				free(p->pack);
320063afb9a5SDavid du Colombier 				free(p);
320163afb9a5SDavid du Colombier 			}
320263afb9a5SDavid du Colombier 			qlock(&c->l);
320363afb9a5SDavid du Colombier 			rwakeupall(&sc->r);
320463afb9a5SDavid du Colombier 			nbsendul(sc->inchan, 1);
320563afb9a5SDavid du Colombier 			nbsendul(sc->reqchan, 1);
320663afb9a5SDavid du Colombier 			chanclose(sc->inchan);
320763afb9a5SDavid du Colombier 			chanclose(sc->reqchan);
320863afb9a5SDavid du Colombier 			qunlock(&c->l);
320963afb9a5SDavid du Colombier 		}
321063afb9a5SDavid du Colombier 	}
321163afb9a5SDavid du Colombier 	qlock(&availlck);
321263afb9a5SDavid du Colombier 	rwakeup(&availrend);
321363afb9a5SDavid du Colombier 	qunlock(&availlck);
321463afb9a5SDavid du Colombier 	sshdebug(c, "done processing shutdown of connection %d", c->id);
321563afb9a5SDavid du Colombier }
3216