xref: /plan9/sys/src/cmd/ssh1/sshnet.c (revision 63afb9a5d3f910047231762bcce0ee49fed3d07c)
1 /*
2  * SSH network file system.
3  * Presents remote TCP stack as /net-style file system.
4  */
5 
6 #include "ssh.h"
7 #include <bio.h>
8 #include <ndb.h>
9 #include <thread.h>
10 #include <fcall.h>
11 #include <9p.h>
12 
13 int rawhack = 1;
14 Conn *conn;
15 char *remoteip	= "<remote>";
16 char *mtpt;
17 
18 Cipher *allcipher[] = {
19 	&cipherrc4,
20 	&cipherblowfish,
21 	&cipher3des,
22 	&cipherdes,
23 	&ciphernone,
24 	&ciphertwiddle,
25 };
26 
27 Auth *allauth[] = {
28 	&authpassword,
29 	&authrsa,
30 	&authtis,
31 };
32 
33 char *cipherlist = "rc4 3des";
34 char *authlist = "rsa password tis";
35 
36 Cipher*
findcipher(char * name,Cipher ** list,int nlist)37 findcipher(char *name, Cipher **list, int nlist)
38 {
39 	int i;
40 
41 	for(i=0; i<nlist; i++)
42 		if(strcmp(name, list[i]->name) == 0)
43 			return list[i];
44 	error("unknown cipher %s", name);
45 	return nil;
46 }
47 
48 Auth*
findauth(char * name,Auth ** list,int nlist)49 findauth(char *name, Auth **list, int nlist)
50 {
51 	int i;
52 
53 	for(i=0; i<nlist; i++)
54 		if(strcmp(name, list[i]->name) == 0)
55 			return list[i];
56 	error("unknown auth %s", name);
57 	return nil;
58 }
59 
60 void
usage(void)61 usage(void)
62 {
63 	fprint(2, "usage: sshnet [-A authlist] [-c cipherlist] [-m mtpt] [user@]hostname\n");
64 	exits("usage");
65 }
66 
67 int
isatty(int fd)68 isatty(int fd)
69 {
70 	char buf[64];
71 
72 	buf[0] = '\0';
73 	fd2path(fd, buf, sizeof buf);
74 	if(strlen(buf)>=9 && strcmp(buf+strlen(buf)-9, "/dev/cons")==0)
75 		return 1;
76 	return 0;
77 }
78 
79 enum
80 {
81 	Qroot,
82 	Qcs,
83 	Qtcp,
84 	Qclone,
85 	Qn,
86 	Qctl,
87 	Qdata,
88 	Qlocal,
89 	Qremote,
90 	Qstatus,
91 };
92 
93 #define PATH(type, n)		((type)|((n)<<8))
94 #define TYPE(path)			((int)(path) & 0xFF)
95 #define NUM(path)			((uint)(path)>>8)
96 
97 Channel *sshmsgchan;		/* chan(Msg*) */
98 Channel *fsreqchan;			/* chan(Req*) */
99 Channel *fsreqwaitchan;		/* chan(nil) */
100 Channel *fsclunkchan;		/* chan(Fid*) */
101 Channel *fsclunkwaitchan;	/* chan(nil) */
102 ulong time0;
103 
104 enum
105 {
106 	Closed,
107 	Dialing,
108 	Established,
109 	Teardown,
110 };
111 
112 char *statestr[] = {
113 	"Closed",
114 	"Dialing",
115 	"Established",
116 	"Teardown",
117 };
118 
119 typedef struct Client Client;
120 struct Client
121 {
122 	int ref;
123 	int state;
124 	int num;
125 	int servernum;
126 	char *connect;
127 	Req *rq;
128 	Req **erq;
129 	Msg *mq;
130 	Msg **emq;
131 };
132 
133 int nclient;
134 Client **client;
135 
136 int
newclient(void)137 newclient(void)
138 {
139 	int i;
140 	Client *c;
141 
142 	for(i=0; i<nclient; i++)
143 		if(client[i]->ref==0 && client[i]->state == Closed)
144 			return i;
145 
146 	if(nclient%16 == 0)
147 		client = erealloc9p(client, (nclient+16)*sizeof(client[0]));
148 
149 	c = emalloc9p(sizeof(Client));
150 	memset(c, 0, sizeof(*c));
151 	c->num = nclient;
152 	client[nclient++] = c;
153 	return c->num;
154 }
155 
156 void
queuereq(Client * c,Req * r)157 queuereq(Client *c, Req *r)
158 {
159 	if(c->rq==nil)
160 		c->erq = &c->rq;
161 	*c->erq = r;
162 	r->aux = nil;
163 	c->erq = (Req**)&r->aux;
164 }
165 
166 void
queuemsg(Client * c,Msg * m)167 queuemsg(Client *c, Msg *m)
168 {
169 	if(c->mq==nil)
170 		c->emq = &c->mq;
171 	*c->emq = m;
172 	m->link = nil;
173 	c->emq = (Msg**)&m->link;
174 }
175 
176 void
matchmsgs(Client * c)177 matchmsgs(Client *c)
178 {
179 	Req *r;
180 	Msg *m;
181 	int n, rm;
182 
183 	while(c->rq && c->mq){
184 		r = c->rq;
185 		c->rq = r->aux;
186 
187 		rm = 0;
188 		m = c->mq;
189 		n = r->ifcall.count;
190 		if(n >= m->ep - m->rp){
191 			n = m->ep - m->rp;
192 			c->mq = m->link;
193 			rm = 1;
194 		}
195 		memmove(r->ofcall.data, m->rp, n);
196 		if(rm)
197 			free(m);
198 		else
199 			m->rp += n;
200 		r->ofcall.count = n;
201 		respond(r, nil);
202 	}
203 }
204 
205 Req*
findreq(Client * c,Req * r)206 findreq(Client *c, Req *r)
207 {
208 	Req **l;
209 
210 	for(l=&c->rq; *l; l=(Req**)&(*l)->aux){
211 		if(*l == r){
212 			*l = r->aux;
213 			if(*l == nil)
214 				c->erq = l;
215 			return r;
216 		}
217 	}
218 	return nil;
219 }
220 
221 void
dialedclient(Client * c)222 dialedclient(Client *c)
223 {
224 	Req *r;
225 
226 	if(r=c->rq){
227 		if(r->aux != nil)
228 			sysfatal("more than one outstanding dial request (BUG)");
229 		if(c->state == Established)
230 			respond(r, nil);
231 		else
232 			respond(r, "connect failed");
233 	}
234 	c->rq = nil;
235 }
236 
237 void
teardownclient(Client * c)238 teardownclient(Client *c)
239 {
240 	Msg *m;
241 
242 	c->state = Teardown;
243 	m = allocmsg(conn, SSH_MSG_CHANNEL_INPUT_EOF, 4);
244 	putlong(m, c->servernum);
245 	sendmsg(m);
246 }
247 
248 void
hangupclient(Client * c)249 hangupclient(Client *c)
250 {
251 	Req *r, *next;
252 	Msg *m, *mnext;
253 
254 	c->state = Closed;
255 	for(m=c->mq; m; m=mnext){
256 		mnext = m->link;
257 		free(m);
258 	}
259 	c->mq = nil;
260 	for(r=c->rq; r; r=next){
261 		next = r->aux;
262 		respond(r, "hangup on network connection");
263 	}
264 	c->rq = nil;
265 }
266 
267 void
closeclient(Client * c)268 closeclient(Client *c)
269 {
270 	Msg *m, *next;
271 
272 	if(--c->ref)
273 		return;
274 
275 	if(c->rq != nil)
276 		sysfatal("ref count reached zero with requests pending (BUG)");
277 
278 	for(m=c->mq; m; m=next){
279 		next = m->link;
280 		free(m);
281 	}
282 	c->mq = nil;
283 
284 	if(c->state != Closed)
285 		teardownclient(c);
286 }
287 
288 
289 void
sshreadproc(void * a)290 sshreadproc(void *a)
291 {
292 	Conn *c;
293 	Msg *m;
294 
295 	c = a;
296 	for(;;){
297 		m = recvmsg(c, -1);
298 		if(m == nil)
299 			sysfatal("eof on ssh connection");
300 		sendp(sshmsgchan, m);
301 	}
302 }
303 
304 typedef struct Tab Tab;
305 struct Tab
306 {
307 	char *name;
308 	ulong mode;
309 };
310 
311 Tab tab[] =
312 {
313 	"/",		DMDIR|0555,
314 	"cs",		0666,
315 	"tcp",	DMDIR|0555,
316 	"clone",	0666,
317 	nil,		DMDIR|0555,
318 	"ctl",		0666,
319 	"data",	0666,
320 	"local",	0444,
321 	"remote",	0444,
322 	"status",	0444,
323 };
324 
325 static void
fillstat(Dir * d,uvlong path)326 fillstat(Dir *d, uvlong path)
327 {
328 	Tab *t;
329 
330 	memset(d, 0, sizeof(*d));
331 	d->uid = estrdup9p("ssh");
332 	d->gid = estrdup9p("ssh");
333 	d->qid.path = path;
334 	d->atime = d->mtime = time0;
335 	t = &tab[TYPE(path)];
336 	if(t->name)
337 		d->name = estrdup9p(t->name);
338 	else{
339 		d->name = smprint("%ud", NUM(path));
340 		if(d->name == nil)
341 			sysfatal("out of memory");
342 	}
343 	d->qid.type = t->mode>>24;
344 	d->mode = t->mode;
345 }
346 
347 static void
fsattach(Req * r)348 fsattach(Req *r)
349 {
350 	if(r->ifcall.aname && r->ifcall.aname[0]){
351 		respond(r, "invalid attach specifier");
352 		return;
353 	}
354 	r->fid->qid.path = PATH(Qroot, 0);
355 	r->fid->qid.type = QTDIR;
356 	r->fid->qid.vers = 0;
357 	r->ofcall.qid = r->fid->qid;
358 	respond(r, nil);
359 }
360 
361 static void
fsstat(Req * r)362 fsstat(Req *r)
363 {
364 	fillstat(&r->d, r->fid->qid.path);
365 	respond(r, nil);
366 }
367 
368 static int
rootgen(int i,Dir * d,void *)369 rootgen(int i, Dir *d, void*)
370 {
371 	i += Qroot+1;
372 	if(i <= Qtcp){
373 		fillstat(d, i);
374 		return 0;
375 	}
376 	return -1;
377 }
378 
379 static int
tcpgen(int i,Dir * d,void *)380 tcpgen(int i, Dir *d, void*)
381 {
382 	i += Qtcp+1;
383 	if(i < Qn){
384 		fillstat(d, i);
385 		return 0;
386 	}
387 	i -= Qn;
388 	if(i < nclient){
389 		fillstat(d, PATH(Qn, i));
390 		return 0;
391 	}
392 	return -1;
393 }
394 
395 static int
clientgen(int i,Dir * d,void * aux)396 clientgen(int i, Dir *d, void *aux)
397 {
398 	Client *c;
399 
400 	c = aux;
401 	i += Qn+1;
402 	if(i <= Qstatus){
403 		fillstat(d, PATH(i, c->num));
404 		return 0;
405 	}
406 	return -1;
407 }
408 
409 static char*
fswalk1(Fid * fid,char * name,Qid * qid)410 fswalk1(Fid *fid, char *name, Qid *qid)
411 {
412 	int i, n;
413 	char buf[32];
414 	ulong path;
415 
416 	path = fid->qid.path;
417 	if(!(fid->qid.type&QTDIR))
418 		return "walk in non-directory";
419 
420 	if(strcmp(name, "..") == 0){
421 		switch(TYPE(path)){
422 		case Qn:
423 			qid->path = PATH(Qtcp, NUM(path));
424 			qid->type = tab[Qtcp].mode>>24;
425 			return nil;
426 		case Qtcp:
427 			qid->path = PATH(Qroot, 0);
428 			qid->type = tab[Qroot].mode>>24;
429 			return nil;
430 		case Qroot:
431 			return nil;
432 		default:
433 			return "bug in fswalk1";
434 		}
435 	}
436 
437 	i = TYPE(path)+1;
438 	for(; i<nelem(tab); i++){
439 		if(i==Qn){
440 			n = atoi(name);
441 			snprint(buf, sizeof buf, "%d", n);
442 			if(n < nclient && strcmp(buf, name) == 0){
443 				qid->path = PATH(i, n);
444 				qid->type = tab[i].mode>>24;
445 				return nil;
446 			}
447 			break;
448 		}
449 		if(strcmp(name, tab[i].name) == 0){
450 			qid->path = PATH(i, NUM(path));
451 			qid->type = tab[i].mode>>24;
452 			return nil;
453 		}
454 		if(tab[i].mode&DMDIR)
455 			break;
456 	}
457 	return "directory entry not found";
458 }
459 
460 typedef struct Cs Cs;
461 struct Cs
462 {
463 	char *resp;
464 	int isnew;
465 };
466 
467 static int
ndbfindport(char * p)468 ndbfindport(char *p)
469 {
470 	char *s, *port;
471 	int n;
472 	static Ndb *db;
473 
474 	if(*p == '\0')
475 		return -1;
476 
477 	n = strtol(p, &s, 0);
478 	if(*s == '\0')
479 		return n;
480 
481 	if(db == nil){
482 		db = ndbopen("/lib/ndb/common");
483 		if(db == nil)
484 			return -1;
485 	}
486 
487 	port = ndbgetvalue(db, nil, "tcp", p, "port", nil);
488 	if(port == nil)
489 		return -1;
490 	n = atoi(port);
491 	free(port);
492 
493 	return n;
494 }
495 
496 static void
csread(Req * r)497 csread(Req *r)
498 {
499 	Cs *cs;
500 
501 	cs = r->fid->aux;
502 	if(cs->resp==nil){
503 		respond(r, "cs read without write");
504 		return;
505 	}
506 	if(r->ifcall.offset==0){
507 		if(!cs->isnew){
508 			r->ofcall.count = 0;
509 			respond(r, nil);
510 			return;
511 		}
512 		cs->isnew = 0;
513 	}
514 	readstr(r, cs->resp);
515 	respond(r, nil);
516 }
517 
518 static void
cswrite(Req * r)519 cswrite(Req *r)
520 {
521 	int port, nf;
522 	char err[ERRMAX], *f[4], *s, *ns;
523 	Cs *cs;
524 
525 	cs = r->fid->aux;
526 	s = emalloc(r->ifcall.count+1);
527 	memmove(s, r->ifcall.data, r->ifcall.count);
528 	s[r->ifcall.count] = '\0';
529 
530 	nf = getfields(s, f, nelem(f), 0, "!");
531 	if(nf != 3){
532 		free(s);
533 		respond(r, "can't translate");
534 		return;
535 	}
536 	if(strcmp(f[0], "tcp") != 0 && strcmp(f[0], "net") != 0){
537 		free(s);
538 		respond(r, "unknown protocol");
539 		return;
540 	}
541 	port = ndbfindport(f[2]);
542 	if(port <= 0){
543 		free(s);
544 		respond(r, "no translation found");
545 		return;
546 	}
547 
548 	ns = smprint("%s/tcp/clone %s!%d", mtpt, f[1], port);
549 	if(ns == nil){
550 		free(s);
551 		rerrstr(err, sizeof err);
552 		respond(r, err);
553 		return;
554 	}
555 	free(s);
556 	free(cs->resp);
557 	cs->resp = ns;
558 	cs->isnew = 1;
559 	r->ofcall.count = r->ifcall.count;
560 	respond(r, nil);
561 }
562 
563 static void
ctlread(Req * r,Client * c)564 ctlread(Req *r, Client *c)
565 {
566 	char buf[32];
567 
568 	sprint(buf, "%d", c->num);
569 	readstr(r, buf);
570 	respond(r, nil);
571 }
572 
573 static void
ctlwrite(Req * r,Client * c)574 ctlwrite(Req *r, Client *c)
575 {
576 	char *f[3], *s;
577 	int nf;
578 	Msg *m;
579 
580 	s = emalloc(r->ifcall.count+1);
581 	memmove(s, r->ifcall.data, r->ifcall.count);
582 	s[r->ifcall.count] = '\0';
583 
584 	nf = tokenize(s, f, 3);
585 	if(nf == 0){
586 		free(s);
587 		respond(r, nil);
588 		return;
589 	}
590 
591 	if(strcmp(f[0], "hangup") == 0){
592 		if(c->state != Established)
593 			goto Badarg;
594 		if(nf != 1)
595 			goto Badarg;
596 		queuereq(c, r);
597 		teardownclient(c);
598 	}else if(strcmp(f[0], "connect") == 0){
599 		if(c->state != Closed)
600 			goto Badarg;
601 		if(nf != 2)
602 			goto Badarg;
603 		c->connect = estrdup9p(f[1]);
604 		nf = getfields(f[1], f, nelem(f), 0, "!");
605 		if(nf != 2){
606 			free(c->connect);
607 			c->connect = nil;
608 			goto Badarg;
609 		}
610 		c->state = Dialing;
611 		m = allocmsg(conn, SSH_MSG_PORT_OPEN, 4+4+strlen(f[0])+4+4+strlen("localhost"));
612 		putlong(m, c->num);
613 		putstring(m, f[0]);
614 		putlong(m, ndbfindport(f[1]));
615 		putstring(m, "localhost");
616 		queuereq(c, r);
617 		sendmsg(m);
618 	}else{
619 	Badarg:
620 		respond(r, "bad or inappropriate tcp control message");
621 	}
622 	free(s);
623 }
624 
625 static void
dataread(Req * r,Client * c)626 dataread(Req *r, Client *c)
627 {
628 	if(c->state != Established){
629 		respond(r, "not connected");
630 		return;
631 	}
632 	queuereq(c, r);
633 	matchmsgs(c);
634 }
635 
636 static void
datawrite(Req * r,Client * c)637 datawrite(Req *r, Client *c)
638 {
639 	Msg *m;
640 
641 	if(c->state != Established){
642 		respond(r, "not connected");
643 		return;
644 	}
645 	if(r->ifcall.count){
646 		m = allocmsg(conn, SSH_MSG_CHANNEL_DATA, 4+4+r->ifcall.count);
647 		putlong(m, c->servernum);
648 		putlong(m, r->ifcall.count);
649 		putbytes(m, r->ifcall.data, r->ifcall.count);
650 		sendmsg(m);
651 	}
652 	r->ofcall.count = r->ifcall.count;
653 	respond(r, nil);
654 }
655 
656 static void
localread(Req * r)657 localread(Req *r)
658 {
659 	char buf[128];
660 
661 	snprint(buf, sizeof buf, "%s!%d\n", remoteip, 0);
662 	readstr(r, buf);
663 	respond(r, nil);
664 }
665 
666 static void
remoteread(Req * r,Client * c)667 remoteread(Req *r, Client *c)
668 {
669 	char *s;
670 	char buf[128];
671 
672 	s = c->connect;
673 	if(s == nil)
674 		s = "::!0";
675 	snprint(buf, sizeof buf, "%s\n", s);
676 	readstr(r, buf);
677 	respond(r, nil);
678 }
679 
680 static void
statusread(Req * r,Client * c)681 statusread(Req *r, Client *c)
682 {
683 	char buf[64];
684 	char *s;
685 
686 	snprint(buf, sizeof buf, "%s!%d", remoteip, 0);
687 	s = statestr[c->state];
688 	readstr(r, s);
689 	respond(r, nil);
690 }
691 
692 static void
fsread(Req * r)693 fsread(Req *r)
694 {
695 	char e[ERRMAX];
696 	ulong path;
697 
698 	path = r->fid->qid.path;
699 	switch(TYPE(path)){
700 	default:
701 		snprint(e, sizeof e, "bug in fsread path=%lux", path);
702 		respond(r, e);
703 		break;
704 
705 	case Qroot:
706 		dirread9p(r, rootgen, nil);
707 		respond(r, nil);
708 		break;
709 
710 	case Qcs:
711 		csread(r);
712 		break;
713 
714 	case Qtcp:
715 		dirread9p(r, tcpgen, nil);
716 		respond(r, nil);
717 		break;
718 
719 	case Qn:
720 		dirread9p(r, clientgen, client[NUM(path)]);
721 		respond(r, nil);
722 		break;
723 
724 	case Qctl:
725 		ctlread(r, client[NUM(path)]);
726 		break;
727 
728 	case Qdata:
729 		dataread(r, client[NUM(path)]);
730 		break;
731 
732 	case Qlocal:
733 		localread(r);
734 		break;
735 
736 	case Qremote:
737 		remoteread(r, client[NUM(path)]);
738 		break;
739 
740 	case Qstatus:
741 		statusread(r, client[NUM(path)]);
742 		break;
743 	}
744 }
745 
746 static void
fswrite(Req * r)747 fswrite(Req *r)
748 {
749 	ulong path;
750 	char e[ERRMAX];
751 
752 	path = r->fid->qid.path;
753 	switch(TYPE(path)){
754 	default:
755 		snprint(e, sizeof e, "bug in fswrite path=%lux", path);
756 		respond(r, e);
757 		break;
758 
759 	case Qcs:
760 		cswrite(r);
761 		break;
762 
763 	case Qctl:
764 		ctlwrite(r, client[NUM(path)]);
765 		break;
766 
767 	case Qdata:
768 		datawrite(r, client[NUM(path)]);
769 		break;
770 	}
771 }
772 
773 static void
fsopen(Req * r)774 fsopen(Req *r)
775 {
776 	static int need[4] = { 4, 2, 6, 1 };
777 	ulong path;
778 	int n;
779 	Tab *t;
780 	Cs *cs;
781 
782 	/*
783 	 * lib9p already handles the blatantly obvious.
784 	 * we just have to enforce the permissions we have set.
785 	 */
786 	path = r->fid->qid.path;
787 	t = &tab[TYPE(path)];
788 	n = need[r->ifcall.mode&3];
789 	if((n&t->mode) != n){
790 		respond(r, "permission denied");
791 		return;
792 	}
793 
794 	switch(TYPE(path)){
795 	case Qcs:
796 		cs = emalloc(sizeof(Cs));
797 		r->fid->aux = cs;
798 		respond(r, nil);
799 		break;
800 	case Qclone:
801 		n = newclient();
802 		path = PATH(Qctl, n);
803 		r->fid->qid.path = path;
804 		r->ofcall.qid.path = path;
805 		if(chatty9p)
806 			fprint(2, "open clone => path=%lux\n", path);
807 		t = &tab[Qctl];
808 		/* fall through */
809 	default:
810 		if(t-tab >= Qn)
811 			client[NUM(path)]->ref++;
812 		respond(r, nil);
813 		break;
814 	}
815 }
816 
817 static void
fsflush(Req * r)818 fsflush(Req *r)
819 {
820 	int i;
821 
822 	for(i=0; i<nclient; i++)
823 		if(findreq(client[i], r->oldreq))
824 			respond(r->oldreq, "interrupted");
825 	respond(r, nil);
826 }
827 
828 static void
handlemsg(Msg * m)829 handlemsg(Msg *m)
830 {
831 	int chan, n;
832 	Client *c;
833 
834 	switch(m->type){
835 	case SSH_MSG_DISCONNECT:
836 	case SSH_CMSG_EXIT_CONFIRMATION:
837 		sysfatal("disconnect");
838 
839 	case SSH_CMSG_STDIN_DATA:
840 	case SSH_CMSG_EOF:
841 	case SSH_CMSG_WINDOW_SIZE:
842 		/* don't care */
843 		free(m);
844 		break;
845 
846 	case SSH_MSG_CHANNEL_DATA:
847 		chan = getlong(m);
848 		n = getlong(m);
849 		if(m->rp+n != m->ep)
850 			sysfatal("got bad channel data");
851 		if(chan<nclient && (c=client[chan])->state==Established){
852 			queuemsg(c, m);
853 			matchmsgs(c);
854 		}else
855 			free(m);
856 		break;
857 
858 	case SSH_MSG_CHANNEL_INPUT_EOF:
859 		chan = getlong(m);
860 		free(m);
861 		if(chan<nclient){
862 			c = client[chan];
863 			chan = c->servernum;
864 			hangupclient(c);
865 			m = allocmsg(conn, SSH_MSG_CHANNEL_OUTPUT_CLOSED, 4);
866 			putlong(m, chan);
867 			sendmsg(m);
868 		}
869 		break;
870 
871 	case SSH_MSG_CHANNEL_OUTPUT_CLOSED:
872 		chan = getlong(m);
873 		if(chan<nclient)
874 			hangupclient(client[chan]);
875 		free(m);
876 		break;
877 
878 	case SSH_MSG_CHANNEL_OPEN_CONFIRMATION:
879 		chan = getlong(m);
880 		c = nil;
881 		if(chan>=nclient || (c=client[chan])->state != Dialing){
882 			if(c)
883 				fprint(2, "cstate %d\n", c->state);
884 			sysfatal("got unexpected open confirmation for %d", chan);
885 		}
886 		c->servernum = getlong(m);
887 		c->state = Established;
888 		dialedclient(c);
889 		free(m);
890 		break;
891 
892 	case SSH_MSG_CHANNEL_OPEN_FAILURE:
893 		chan = getlong(m);
894 		c = nil;
895 		if(chan>=nclient || (c=client[chan])->state != Dialing)
896 			sysfatal("got unexpected open failure");
897 		if(m->rp+4 <= m->ep)
898 			c->servernum = getlong(m);
899 		c->state = Closed;
900 		dialedclient(c);
901 		free(m);
902 		break;
903 	}
904 }
905 
906 void
fsnetproc(void *)907 fsnetproc(void*)
908 {
909 	ulong path;
910 	Alt a[4];
911 	Cs *cs;
912 	Fid *fid;
913 	Req *r;
914 	Msg *m;
915 
916 	threadsetname("fsthread");
917 
918 	a[0].op = CHANRCV;
919 	a[0].c = fsclunkchan;
920 	a[0].v = &fid;
921 	a[1].op = CHANRCV;
922 	a[1].c = fsreqchan;
923 	a[1].v = &r;
924 	a[2].op = CHANRCV;
925 	a[2].c = sshmsgchan;
926 	a[2].v = &m;
927 	a[3].op = CHANEND;
928 
929 	for(;;){
930 		switch(alt(a)){
931 		case 0:
932 			path = fid->qid.path;
933 			switch(TYPE(path)){
934 			case Qcs:
935 				cs = fid->aux;
936 				if(cs){
937 					free(cs->resp);
938 					free(cs);
939 				}
940 				break;
941 			}
942 			if(fid->omode != -1 && TYPE(path) >= Qn)
943 				closeclient(client[NUM(path)]);
944 			sendp(fsclunkwaitchan, nil);
945 			break;
946 		case 1:
947 			switch(r->ifcall.type){
948 			case Tattach:
949 				fsattach(r);
950 				break;
951 			case Topen:
952 				fsopen(r);
953 				break;
954 			case Tread:
955 				fsread(r);
956 				break;
957 			case Twrite:
958 				fswrite(r);
959 				break;
960 			case Tstat:
961 				fsstat(r);
962 				break;
963 			case Tflush:
964 				fsflush(r);
965 				break;
966 			default:
967 				respond(r, "bug in fsthread");
968 				break;
969 			}
970 			sendp(fsreqwaitchan, 0);
971 			break;
972 		case 2:
973 			handlemsg(m);
974 			break;
975 		}
976 	}
977 }
978 
979 static void
fssend(Req * r)980 fssend(Req *r)
981 {
982 	sendp(fsreqchan, r);
983 	recvp(fsreqwaitchan);	/* avoids need to deal with spurious flushes */
984 }
985 
986 static void
fsdestroyfid(Fid * fid)987 fsdestroyfid(Fid *fid)
988 {
989 	sendp(fsclunkchan, fid);
990 	recvp(fsclunkwaitchan);
991 }
992 
993 void
takedown(Srv *)994 takedown(Srv*)
995 {
996 	threadexitsall("done");
997 }
998 
999 Srv fs =
1000 {
1001 .attach=		fssend,
1002 .destroyfid=	fsdestroyfid,
1003 .walk1=		fswalk1,
1004 .open=		fssend,
1005 .read=		fssend,
1006 .write=		fssend,
1007 .stat=		fssend,
1008 .flush=		fssend,
1009 .end=		takedown,
1010 };
1011 
1012 void
threadmain(int argc,char ** argv)1013 threadmain(int argc, char **argv)
1014 {
1015 	int i, fd;
1016 	char *host, *user, *p, *service;
1017 	char *f[16];
1018 	Msg *m;
1019 	static Conn c;
1020 
1021 	fmtinstall('B', mpfmt);
1022 	fmtinstall('H', encodefmt);
1023 
1024 	mtpt = "/net";
1025 	service = nil;
1026 	user = nil;
1027 	ARGBEGIN{
1028 	case 'B':	/* undocumented, debugging */
1029 		doabort = 1;
1030 		break;
1031 	case 'D':	/* undocumented, debugging */
1032 		debuglevel = strtol(EARGF(usage()), nil, 0);
1033 		break;
1034 	case '9':	/* undocumented, debugging */
1035 		chatty9p++;
1036 		break;
1037 
1038 	case 'A':
1039 		authlist = EARGF(usage());
1040 		break;
1041 	case 'c':
1042 		cipherlist = EARGF(usage());
1043 		break;
1044 	case 'm':
1045 		mtpt = EARGF(usage());
1046 		break;
1047 	case 's':
1048 		service = EARGF(usage());
1049 		break;
1050 	default:
1051 		usage();
1052 	}ARGEND
1053 
1054 	if(argc != 1)
1055 		usage();
1056 
1057 	host = argv[0];
1058 
1059 	if((p = strchr(host, '@')) != nil){
1060 		*p++ = '\0';
1061 		user = host;
1062 		host = p;
1063 	}
1064 	if(user == nil)
1065 		user = getenv("user");
1066 	if(user == nil)
1067 		sysfatal("cannot find user name");
1068 
1069 	privatefactotum();
1070 
1071 	if((fd = dial(netmkaddr(host, "tcp", "ssh"), nil, nil, nil)) < 0)
1072 		sysfatal("dialing %s: %r", host);
1073 
1074 	c.interactive = isatty(0);
1075 	c.fd[0] = c.fd[1] = fd;
1076 	c.user = user;
1077 	c.host = host;
1078 	setaliases(&c, host);
1079 
1080 	c.nokcipher = getfields(cipherlist, f, nelem(f), 1, ", ");
1081 	c.okcipher = emalloc(sizeof(Cipher*)*c.nokcipher);
1082 	for(i=0; i<c.nokcipher; i++)
1083 		c.okcipher[i] = findcipher(f[i], allcipher, nelem(allcipher));
1084 
1085 	c.nokauth = getfields(authlist, f, nelem(f), 1, ", ");
1086 	c.okauth = emalloc(sizeof(Auth*)*c.nokauth);
1087 	for(i=0; i<c.nokauth; i++)
1088 		c.okauth[i] = findauth(f[i], allauth, nelem(allauth));
1089 
1090 	sshclienthandshake(&c);
1091 
1092 	requestpty(&c);		/* turns on TCP_NODELAY on other side */
1093 	m = allocmsg(&c, SSH_CMSG_EXEC_SHELL, 0);
1094 	sendmsg(m);
1095 
1096 	time0 = time(0);
1097 	sshmsgchan = chancreate(sizeof(Msg*), 16);
1098 	fsreqchan = chancreate(sizeof(Req*), 0);
1099 	fsreqwaitchan = chancreate(sizeof(void*), 0);
1100 	fsclunkchan = chancreate(sizeof(Fid*), 0);
1101 	fsclunkwaitchan = chancreate(sizeof(void*), 0);
1102 
1103 	conn = &c;
1104 	procrfork(sshreadproc, &c, 8192, RFNAMEG|RFNOTEG);
1105 	procrfork(fsnetproc, nil, 8192, RFNAMEG|RFNOTEG);
1106 
1107 	threadpostmountsrv(&fs, service, mtpt, MREPL);
1108 	exits(0);
1109 }
1110 
1111