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* 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* 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 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 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 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 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 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 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* 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 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 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 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 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 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 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 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 362 fsstat(Req *r) 363 { 364 fillstat(&r->d, r->fid->qid.path); 365 respond(r, nil); 366 } 367 368 static int 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 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 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* 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 980 fssend(Req *r) 981 { 982 sendp(fsreqchan, r); 983 recvp(fsreqwaitchan); /* avoids need to deal with spurious flushes */ 984 } 985 986 static void 987 fsdestroyfid(Fid *fid) 988 { 989 sendp(fsclunkchan, fid); 990 recvp(fsclunkwaitchan); 991 } 992 993 void 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 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