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