2 * SSH network file system.
3 * Presents remote TCP stack as /net-style file system.
14 typedef struct Client Client;
15 typedef struct Msg Msg;
32 #define PATH(type, n) ((type)|((n)<<8))
33 #define TYPE(path) ((int)(path) & 0xFF)
34 #define NUM(path) ((uint)(path)>>8)
37 Channel *sshmsgchan; /* chan(Msg*) */
38 Channel *fsreqchan; /* chan(Req*) */
39 Channel *fsreqwaitchan; /* chan(nil) */
40 Channel *fsclunkchan; /* chan(Fid*) */
41 Channel *fsclunkwaitchan; /* chan(nil) */
92 MSG_GLOBAL_REQUEST = 80,
94 MSG_CHANNEL_OPEN = 90,
95 MSG_CHANNEL_OPEN_CONFIRMATION,
96 MSG_CHANNEL_OPEN_FAILURE,
97 MSG_CHANNEL_WINDOW_ADJUST,
99 MSG_CHANNEL_EXTENDED_DATA,
107 MaxPacket = (1<<15)-256, /* 32K is maxatomic for pipe */
120 uchar buf[MaxPacket + Overhead];
123 #define PUT4(p, u) (p)[0] = (u)>>24, (p)[1] = (u)>>16, (p)[2] = (u)>>8, (p)[3] = (u)
124 #define GET4(p) (u32int)(p)[3] | (u32int)(p)[2]<<8 | (u32int)(p)[1]<<16 | (u32int)(p)[0]<<24
128 char *mtpt, *service;
132 vpack(uchar *p, int n, char *fmt, va_list a)
134 uchar *p0 = p, *e = p+n;
144 if(++p > e) goto err;
147 *va_arg(a, void**) = p;
151 *p++ = va_arg(a, int);
155 s = va_arg(a, void*);
158 if(p+4 > e) goto err;
161 if(u > e-p) goto err;
167 if(p+4 > e) goto err;
177 vunpack(uchar *p, int n, char *fmt, va_list a)
179 uchar *p0 = p, *e = p+n;
188 if(++p > e) goto err;
191 *va_arg(a, void**) = p;
195 *va_arg(a, int*) = *p++;
198 if(p+4 > e) goto err;
200 if(u > e-p) goto err;
201 *va_arg(a, void**) = p;
202 *va_arg(a, int*) = u;
206 s = va_arg(a, void*);
208 if(u > e-p) goto err;
213 if(p+4 > e) goto err;
215 *va_arg(a, int*) = u;
229 m = emalloc9p(sizeof(Msg));
231 m->rp = m->wp = m->buf;
232 m->ep = m->rp + sizeof(m->buf);
237 pack(Msg *m, char *fmt, ...)
245 n = vpack(m->wp, m->ep - m->wp, fmt, a);
247 sysfatal("pack faild");
254 unpack(Msg *m, char *fmt, ...)
260 n = vunpack(m->rp, m->wp - m->rp, fmt, a);
276 if(write(sshfd, m->rp, n) != n)
277 sysfatal("write to ssh failed: %r");
288 for(i=0; i<nclient; i++)
289 if(client[i]->ref==0 && client[i]->state == Closed)
293 client = erealloc9p(client, (nclient+16)*sizeof(client[0]));
295 c = emalloc9p(sizeof(Client));
296 memset(c, 0, sizeof(*c));
298 client[nclient++] = c;
305 if(num < 0 || num >= nclient)
311 acceptclient(char *lhost, int lport, char *rhost, int rport)
316 for(i = 0; i < nclient; i++){
318 if(c->state == Listen && c->lport == lport && c->wq != nil){
319 nc = client[newclient()];
336 adjustwin(Client *c, int len)
339 if(c->recvacc >= MaxPacket*WinPackets/2 || c->recvwin < MaxPacket){
340 sendmsg(pack(nil, "buu", MSG_CHANNEL_WINDOW_ADJUST, c->servernum, c->recvacc));
347 senddata(Client *c, void *data, int len)
349 sendmsg(pack(nil, "bus", MSG_CHANNEL_DATA, c->servernum, (char*)data, len));
354 queuerreq(Client *c, Req *r)
360 c->erq = (Req**)&r->aux;
364 queuermsg(Client *c, Msg *m)
370 c->emq = (Msg**)&m->link;
374 matchrmsgs(Client *c)
380 while((r = c->rq) != nil && (m = c->mq) != nil){
385 if(n >= m->wp - m->rp){
390 memmove(r->ofcall.data, m->rp, n);
401 while((r = c->rq) != nil){
411 queuewreq(Client *c, Req *r)
417 c->ewq = (Req**)&r->aux;
426 while((r = c->wq) != nil && (n = c->sendwin) > 0){
429 if(r->ifcall.count > n){
430 senddata(c, r->ifcall.data, n);
431 r->ifcall.count -= n;
432 memmove(r->ifcall.data, (char*)r->ifcall.data + n, r->ifcall.count);
435 c->wq = (Req*)r->aux;
437 senddata(c, r->ifcall.data, r->ifcall.count);
438 r->ofcall.count = r->ifcall.count;
444 findreq(Client *c, Req *r)
448 for(l=&c->rq; *l; l=(Req**)&(*l)->aux){
456 for(l=&c->wq; *l; l=(Req**)&(*l)->aux){
468 hangupclient(Client *c, char *err)
475 while((r = c->wq) != nil){
484 teardownclient(Client *c)
487 hangupclient(c, "i/o on hungup channel");
488 sendmsg(pack(nil, "bu", MSG_CHANNEL_CLOSE, c->servernum));
492 closeclient(Client *c)
504 sendmsg(pack(nil, "bu", MSG_CHANNEL_CLOSE, c->servernum));
508 sendmsg(pack(nil, "bsbsu", MSG_GLOBAL_REQUEST,
509 "cancel-tcpip-forward", 20,
511 c->lhost, strlen(c->lhost),
515 while((m = c->mq) != nil){
529 n = read(sshfd, m->rp, m->ep - m->rp);
531 sysfatal("eof on ssh connection");
533 sendp(sshmsgchan, m);
537 typedef struct Tab Tab;
560 fillstat(Dir *d, uvlong path)
564 memset(d, 0, sizeof(*d));
565 d->uid = estrdup9p("ssh");
566 d->gid = estrdup9p("ssh");
568 d->atime = d->mtime = time0;
569 t = &tab[TYPE(path)];
571 d->name = estrdup9p(t->name);
573 d->name = smprint("%ud", NUM(path));
575 sysfatal("out of memory");
577 d->qid.type = t->mode>>24;
584 if(r->ifcall.aname && r->ifcall.aname[0]){
585 respond(r, "invalid attach specifier");
588 r->fid->qid.path = PATH(Qroot, 0);
589 r->fid->qid.type = QTDIR;
590 r->fid->qid.vers = 0;
591 r->ofcall.qid = r->fid->qid;
598 fillstat(&r->d, r->fid->qid.path);
603 rootgen(int i, Dir *d, void*)
614 tcpgen(int i, Dir *d, void*)
623 fillstat(d, PATH(Qn, i));
630 clientgen(int i, Dir *d, void *aux)
637 fillstat(d, PATH(i, c->num));
644 fswalk1(Fid *fid, char *name, Qid *qid)
650 path = fid->qid.path;
651 if(!(fid->qid.type&QTDIR))
652 return "walk in non-directory";
654 if(strcmp(name, "..") == 0){
657 qid->path = PATH(Qtcp, NUM(path));
658 qid->type = tab[Qtcp].mode>>24;
661 qid->path = PATH(Qroot, 0);
662 qid->type = tab[Qroot].mode>>24;
667 return "bug in fswalk1";
672 for(; i<nelem(tab); i++){
675 snprint(buf, sizeof buf, "%d", n);
676 if(n < nclient && strcmp(buf, name) == 0){
677 qid->path = PATH(i, n);
678 qid->type = tab[i].mode>>24;
683 if(strcmp(name, tab[i].name) == 0){
684 qid->path = PATH(i, NUM(path));
685 qid->type = tab[i].mode>>24;
688 if(tab[i].mode&DMDIR)
691 return "directory entry not found";
694 typedef struct Cs Cs;
711 n = strtol(p, &s, 0);
716 db = ndbopen("/lib/ndb/common");
721 port = ndbgetvalue(db, nil, "tcp", p, "port", nil);
737 respond(r, "cs read without write");
740 if(r->ifcall.offset==0){
748 readstr(r, cs->resp);
756 char err[ERRMAX], *f[4], *s, *ns;
760 s = emalloc9p(r->ifcall.count+1);
761 memmove(s, r->ifcall.data, r->ifcall.count);
762 s[r->ifcall.count] = '\0';
764 nf = getfields(s, f, nelem(f), 0, "!");
767 respond(r, "can't translate");
770 if(strcmp(f[0], "tcp") != 0 && strcmp(f[0], "net") != 0){
772 respond(r, "unknown protocol");
775 port = ndbfindport(f[2]);
778 respond(r, "no translation found");
782 ns = smprint("%s/tcp/clone %s!%d", mtpt, f[1], port);
785 rerrstr(err, sizeof err);
793 r->ofcall.count = r->ifcall.count;
798 ctlread(Req *r, Client *c)
802 sprint(buf, "%d", c->num);
808 ctlwrite(Req *r, Client *c)
813 s = emalloc9p(r->ifcall.count+1);
814 r->ofcall.count = r->ifcall.count;
815 memmove(s, r->ifcall.data, r->ifcall.count);
816 s[r->ifcall.count] = '\0';
818 nf = tokenize(s, f, 3);
825 if(strcmp(f[0], "hangup") == 0){
826 if(c->state != Established)
832 }else if(strcmp(f[0], "connect") == 0){
833 if(nf != 2 || c->state != Closed)
835 if(getfields(f[1], f, nelem(f), 0, "!") != 2)
837 if((port = ndbfindport(f[1])) < 0)
840 c->lhost = estrdup9p("::");
843 c->rhost = estrdup9p(f[0]);
845 c->recvwin = WinPackets*MaxPacket;
850 sendmsg(pack(nil, "bsuuususu", MSG_CHANNEL_OPEN,
852 c->num, c->recvwin, MaxPacket,
853 c->rhost, strlen(c->rhost), c->rport,
854 c->lhost, strlen(c->lhost), c->lport));
855 }else if(strcmp(f[0], "announce") == 0){
856 if(nf != 2 || c->state != Closed)
858 if(getfields(f[1], f, nelem(f), 0, "!") != 2)
860 if((port = ndbfindport(f[1])) < 0)
862 if(strcmp(f[0], "*") == 0)
865 c->lhost = estrdup9p(f[0]);
868 c->rhost = estrdup9p("::");
871 sendmsg(pack(nil, "bsbsu", MSG_GLOBAL_REQUEST,
872 "tcpip-forward", 13, 0,
873 c->lhost, strlen(c->lhost), c->lport));
877 respond(r, "bad or inappropriate tcp control message");
883 dataread(Req *r, Client *c)
885 if(c->state < Established){
886 respond(r, "not connected");
894 datawrite(Req *r, Client *c)
896 if(c->state != Established){
897 respond(r, "not connected");
900 if(r->ifcall.count == 0){
901 r->ofcall.count = r->ifcall.count;
910 localread(Req *r, Client *c)
919 snprint(buf, sizeof buf, "%s!%d\n", s, c->lport);
925 remoteread(Req *r, Client *c)
932 snprint(buf, sizeof buf, "%s!%d\n", s, c->rport);
938 statusread(Req *r, Client *c)
942 s = statestr[c->state];
953 path = r->fid->qid.path;
956 snprint(e, sizeof e, "bug in fsread path=%lux", path);
961 dirread9p(r, rootgen, nil);
970 dirread9p(r, tcpgen, nil);
975 dirread9p(r, clientgen, client[NUM(path)]);
980 ctlread(r, client[NUM(path)]);
984 dataread(r, client[NUM(path)]);
988 localread(r, client[NUM(path)]);
992 remoteread(r, client[NUM(path)]);
996 statusread(r, client[NUM(path)]);
1007 path = r->fid->qid.path;
1010 snprint(e, sizeof e, "bug in fswrite path=%lux", path);
1019 ctlwrite(r, client[NUM(path)]);
1023 datawrite(r, client[NUM(path)]);
1031 static int need[4] = { 4, 2, 6, 1 };
1038 * lib9p already handles the blatantly obvious.
1039 * we just have to enforce the permissions we have set.
1041 path = r->fid->qid.path;
1042 t = &tab[TYPE(path)];
1043 n = need[r->ifcall.mode&3];
1044 if((n&t->mode) != n){
1045 respond(r, "permission denied");
1051 cs = emalloc9p(sizeof(Cs));
1056 if(client[NUM(path)]->state != Listen){
1057 respond(r, "no address set");
1060 queuewreq(client[NUM(path)], r);
1064 path = PATH(Qctl, n);
1065 r->fid->qid.path = path;
1066 r->ofcall.qid.path = path;
1068 fprint(2, "open clone => path=%lux\n", path);
1073 client[NUM(path)]->ref++;
1084 for(i=0; i<nclient; i++)
1085 if(findreq(client[i], r->oldreq))
1086 respond(r->oldreq, "interrupted");
1093 int chan, win, pkt, lport, rport, n, ln, rn;
1094 char *s, *lhost, *rhost;
1098 case MSG_CHANNEL_WINDOW_ADJUST:
1099 if(unpack(m, "_uu", &chan, &n) < 0)
1101 c = getclient(chan);
1102 if(c != nil && c->state == Established){
1107 case MSG_CHANNEL_DATA:
1108 if(unpack(m, "_us", &chan, &s, &n) < 0)
1110 c = getclient(chan);
1111 if(c != nil && c->state == Established){
1121 case MSG_CHANNEL_EOF:
1122 if(unpack(m, "_u", &chan) < 0)
1124 c = getclient(chan);
1125 if(c != nil && c->state == Established){
1131 case MSG_CHANNEL_CLOSE:
1132 if(unpack(m, "_u", &chan) < 0)
1134 c = getclient(chan);
1139 c->state = Finished;
1140 hangupclient(c, "connection closed");
1147 case MSG_CHANNEL_OPEN_CONFIRMATION:
1148 if(unpack(m, "_uuuu", &chan, &n, &win, &pkt) < 0)
1150 if(chan == SESSIONCHAN){
1154 c = getclient(chan);
1155 if(c == nil || c->state != Dialing)
1157 if(pkt <= 0 || pkt > MaxPacket)
1167 respond(c->wq, nil);
1169 c->state = Established;
1171 case MSG_CHANNEL_OPEN_FAILURE:
1172 if(unpack(m, "_u____s", &chan, &s, &n) < 0)
1174 s = smprint("%.*s", utfnlen(s, n), s);
1175 if(chan == SESSIONCHAN){
1176 sysfatal("ssh failed: %s", s);
1179 c = getclient(chan);
1180 if(c != nil && c->state == Dialing){
1186 case MSG_CHANNEL_OPEN:
1187 if(unpack(m, "_suuususu", &s, &n, &chan,
1189 &lhost, &ln, &lport,
1190 &rhost, &rn, &rport) < 0)
1192 if(n != 15 || strncmp(s, "forwarded-tcpip", 15) != 0){
1193 n = 3, s = "unknown open type";
1195 sendmsg(pack(nil, "buus", MSG_CHANNEL_OPEN_FAILURE,
1196 chan, n, s, strlen(s)));
1199 lhost = smprint("%.*s", utfnlen(lhost, ln), lhost);
1200 rhost = smprint("%.*s", utfnlen(rhost, rn), rhost);
1201 c = acceptclient(lhost, lport, rhost, rport);
1205 n = 2, s = "connection refused";
1208 c->servernum = chan;
1209 c->recvwin = WinPackets*MaxPacket;
1214 c->state = Established;
1216 sendmsg(pack(nil, "buuuu", MSG_CHANNEL_OPEN_CONFIRMATION,
1217 c->servernum, c->num, c->recvwin, MaxPacket));
1220 c->wq->fid->qid.path = PATH(Qctl, c->num);
1221 c->wq->ofcall.qid.path = c->wq->fid->qid.path;
1222 respond(c->wq, nil);
1239 threadsetname("fsthread");
1242 a[0].c = fsclunkchan;
1248 a[2].c = sshmsgchan;
1255 path = fid->qid.path;
1265 if(fid->omode != -1 && TYPE(path) >= Qn)
1266 closeclient(client[NUM(path)]);
1267 sendp(fsclunkwaitchan, nil);
1270 switch(r->ifcall.type){
1290 respond(r, "bug in fsthread");
1293 sendp(fsreqwaitchan, 0);
1305 sendp(fsreqchan, r);
1306 recvp(fsreqwaitchan); /* avoids need to deal with spurious flushes */
1310 fsdestroyfid(Fid *fid)
1312 sendp(fsclunkchan, fid);
1313 recvp(fsclunkwaitchan);
1319 proccreate(fsnetproc, nil, 8*1024);
1325 threadexitsall("done");
1331 .destroyfid= fsdestroyfid,
1355 if(strncmp(sshargv[0], "./", 2) != 0)
1356 f = smprint("/bin/%s", sshargv[0]);
1359 procexec(nil, f, sshargv);
1360 sysfatal("exec: %r");
1364 ssh(int argc, char *argv[])
1371 sshargv = emalloc9p(sizeof(char *) * (sshargc + 1));
1374 memcpy(sshargv + 2, argv, argc * sizeof(char *));
1377 sysfatal("pipe: %r");
1379 procrfork(startssh, nil, 8*1024, RFFDG|RFNOTEG);
1382 procrfork(sshreadproc, nil, 8*1024, RFFDG|RFNOTEG);
1384 sendmsg(pack(nil, "bsuuu", MSG_CHANNEL_OPEN,
1391 a[0].c = threadwaitchan();
1394 a[1].c = sshmsgchan;
1398 while(!sessionopen){
1401 sysfatal("ssh failed: %s", w->msg);
1411 fprint(2, "usage: sshnet [-m mtpt] [ssh options]\n");
1416 threadmain(int argc, char **argv)
1418 fmtinstall('H', encodefmt);
1427 mtpt = EARGF(usage());
1430 service = EARGF(usage());
1440 sshmsgchan = chancreate(sizeof(Msg*), 16);
1441 fsreqchan = chancreate(sizeof(Req*), 0);
1442 fsreqwaitchan = chancreate(sizeof(void*), 0);
1443 fsclunkchan = chancreate(sizeof(Fid*), 0);
1444 fsclunkwaitchan = chancreate(sizeof(void*), 0);
1448 threadpostmountsrv(&fs, service, mtpt, MREPL);