changelog shortlog tags branches files raw gz bz2 help

Mercurial > hg > plan9front / changeset: sshnet: bring back sshnet using ssh(1) mux mode

changeset 7116: 17daf6d05444
parent 7115: ff3d2ba9517a
child 7117: 81227123e1b8
author: cinap_lenrek@felloff.net
date: Tue, 02 Apr 2019 16:23:01 +0200
files: sys/src/cmd/sshnet.c
description: sshnet: bring back sshnet using ssh(1) mux mode

this is a port of the original ssh1 sshnet to our
ssh2 client using mux mode.
     1.1new file mode 100755
     1.2--- /dev/null
     1.3+++ b/sys/src/cmd/sshnet.c
     1.4@@ -0,0 +1,1286 @@
     1.5+/*
     1.6+ * SSH network file system.
     1.7+ * Presents remote TCP stack as /net-style file system.
     1.8+ */
     1.9+
    1.10+#include <u.h>
    1.11+#include <libc.h>
    1.12+#include <bio.h>
    1.13+#include <ndb.h>
    1.14+#include <thread.h>
    1.15+#include <fcall.h>
    1.16+#include <9p.h>
    1.17+
    1.18+typedef struct Client Client;
    1.19+typedef struct Msg Msg;
    1.20+
    1.21+enum
    1.22+{
    1.23+	Qroot,
    1.24+	Qcs,
    1.25+	Qtcp,
    1.26+	Qclone,
    1.27+	Qn,
    1.28+	Qctl,
    1.29+	Qdata,
    1.30+	Qlocal,
    1.31+	Qremote,
    1.32+	Qstatus,
    1.33+};
    1.34+
    1.35+#define PATH(type, n)		((type)|((n)<<8))
    1.36+#define TYPE(path)		((int)(path) & 0xFF)
    1.37+#define NUM(path)		((uint)(path)>>8)
    1.38+
    1.39+Channel *sshmsgchan;		/* chan(Msg*) */
    1.40+Channel *fsreqchan;		/* chan(Req*) */
    1.41+Channel *fsreqwaitchan;		/* chan(nil) */
    1.42+Channel *fsclunkchan;		/* chan(Fid*) */
    1.43+Channel *fsclunkwaitchan;	/* chan(nil) */
    1.44+ulong time0;
    1.45+
    1.46+enum
    1.47+{
    1.48+	Closed,
    1.49+	Dialing,
    1.50+	Established,
    1.51+	Teardown,
    1.52+};
    1.53+
    1.54+char *statestr[] = {
    1.55+	"Closed",
    1.56+	"Dialing",
    1.57+	"Established",
    1.58+	"Teardown",
    1.59+};
    1.60+
    1.61+struct Client
    1.62+{
    1.63+	int ref;
    1.64+	int state;
    1.65+	int num;
    1.66+	int servernum;
    1.67+	char *connect;
    1.68+
    1.69+	int sendpkt;
    1.70+	int sendwin;
    1.71+	int recvwin;
    1.72+	int recvacc;
    1.73+
    1.74+	Req *wq;
    1.75+	Req **ewq;
    1.76+
    1.77+	Req *rq;
    1.78+	Req **erq;
    1.79+
    1.80+	Msg *mq;
    1.81+	Msg **emq;
    1.82+};
    1.83+
    1.84+enum {
    1.85+	MSG_CHANNEL_OPEN = 90,
    1.86+	MSG_CHANNEL_OPEN_CONFIRMATION,
    1.87+	MSG_CHANNEL_OPEN_FAILURE,
    1.88+	MSG_CHANNEL_WINDOW_ADJUST,
    1.89+	MSG_CHANNEL_DATA,
    1.90+	MSG_CHANNEL_EXTENDED_DATA,
    1.91+	MSG_CHANNEL_EOF,
    1.92+	MSG_CHANNEL_CLOSE,
    1.93+	MSG_CHANNEL_REQUEST,
    1.94+	MSG_CHANNEL_SUCCESS,
    1.95+	MSG_CHANNEL_FAILURE,
    1.96+
    1.97+	MaxPacket = 1<<15,
    1.98+	WinPackets = 8,
    1.99+};
   1.100+
   1.101+struct Msg
   1.102+{
   1.103+	Msg	*link;
   1.104+
   1.105+	uchar	*rp;
   1.106+	uchar	*wp;
   1.107+	uchar	*ep;
   1.108+	uchar	buf[MaxPacket];
   1.109+};
   1.110+
   1.111+#define PUT4(p, u) (p)[0] = (u)>>24, (p)[1] = (u)>>16, (p)[2] = (u)>>8, (p)[3] = (u)
   1.112+#define GET4(p)	(u32int)(p)[3] | (u32int)(p)[2]<<8 | (u32int)(p)[1]<<16 | (u32int)(p)[0]<<24
   1.113+
   1.114+int nclient;
   1.115+Client **client;
   1.116+char *mtpt;
   1.117+int sshfd;
   1.118+int localport;
   1.119+char localip[] = "::";
   1.120+
   1.121+int
   1.122+vpack(uchar *p, int n, char *fmt, va_list a)
   1.123+{
   1.124+	uchar *p0 = p, *e = p+n;
   1.125+	u32int u;
   1.126+	void *s;
   1.127+	int c;
   1.128+
   1.129+	for(;;){
   1.130+		switch(c = *fmt++){
   1.131+		case '\0':
   1.132+			return p - p0;
   1.133+		case '_':
   1.134+			if(++p > e) goto err;
   1.135+			break;
   1.136+		case '.':
   1.137+			*va_arg(a, void**) = p;
   1.138+			break;
   1.139+		case 'b':
   1.140+			if(p >= e) goto err;
   1.141+			*p++ = va_arg(a, int);
   1.142+			break;
   1.143+		case '[':
   1.144+		case 's':
   1.145+			s = va_arg(a, void*);
   1.146+			u = va_arg(a, int);
   1.147+			if(c == 's'){
   1.148+				if(p+4 > e) goto err;
   1.149+				PUT4(p, u), p += 4;
   1.150+			}
   1.151+			if(u > e-p) goto err;
   1.152+			memmove(p, s, u);
   1.153+			p += u;
   1.154+			break;
   1.155+		case 'u':
   1.156+			u = va_arg(a, int);
   1.157+			if(p+4 > e) goto err;
   1.158+			PUT4(p, u), p += 4;
   1.159+			break;
   1.160+		}
   1.161+	}
   1.162+err:
   1.163+	return -1;
   1.164+}
   1.165+
   1.166+int
   1.167+vunpack(uchar *p, int n, char *fmt, va_list a)
   1.168+{
   1.169+	uchar *p0 = p, *e = p+n;
   1.170+	u32int u;
   1.171+	void *s;
   1.172+
   1.173+	for(;;){
   1.174+		switch(*fmt++){
   1.175+		case '\0':
   1.176+			return p - p0;
   1.177+		case '_':
   1.178+			if(++p > e) goto err;
   1.179+			break;
   1.180+		case '.':
   1.181+			*va_arg(a, void**) = p;
   1.182+			break;
   1.183+		case 'b':
   1.184+			if(p >= e) goto err;
   1.185+			*va_arg(a, int*) = *p++;
   1.186+			break;
   1.187+		case 's':
   1.188+			if(p+4 > e) goto err;
   1.189+			u = GET4(p), p += 4;
   1.190+			if(u > e-p) goto err;
   1.191+			*va_arg(a, void**) = p;
   1.192+			*va_arg(a, int*) = u;
   1.193+			p += u;
   1.194+			break;
   1.195+		case '[':
   1.196+			s = va_arg(a, void*);
   1.197+			u = va_arg(a, int);
   1.198+			if(u > e-p) goto err;
   1.199+			memmove(s, p, u);
   1.200+			p += u;
   1.201+			break;
   1.202+		case 'u':
   1.203+			if(p+4 > e) goto err;
   1.204+			u = GET4(p);
   1.205+			*va_arg(a, int*) = u;
   1.206+			p += 4;
   1.207+			break;
   1.208+		}
   1.209+	}
   1.210+err:
   1.211+	return -1;
   1.212+}
   1.213+
   1.214+Msg*
   1.215+allocmsg(void)
   1.216+{
   1.217+	Msg *m;
   1.218+
   1.219+	m = emalloc9p(sizeof(Msg));
   1.220+	m->link = nil;
   1.221+	m->rp = m->wp = m->buf;
   1.222+	m->ep = m->rp + sizeof(m->buf);
   1.223+	return m;
   1.224+}
   1.225+
   1.226+Msg*
   1.227+pack(Msg *m, char *fmt, ...)
   1.228+{
   1.229+	va_list a;
   1.230+	int n;
   1.231+
   1.232+	if(m == nil)
   1.233+		m = allocmsg();
   1.234+	va_start(a, fmt);
   1.235+	n = vpack(m->wp, m->ep - m->wp, fmt, a);
   1.236+	if(n < 0)
   1.237+		sysfatal("pack faild");
   1.238+	m->wp += n;
   1.239+	va_end(a);
   1.240+	return m;
   1.241+}
   1.242+
   1.243+int
   1.244+unpack(Msg *m, char *fmt, ...)
   1.245+{
   1.246+	va_list a;
   1.247+	int n;
   1.248+
   1.249+	va_start(a, fmt);
   1.250+	n = vunpack(m->rp, m->wp - m->rp, fmt, a);
   1.251+	if(n > 0)
   1.252+		m->rp += n;
   1.253+	va_end(a);
   1.254+	return n;
   1.255+}
   1.256+
   1.257+void
   1.258+sendmsg(Msg *m)
   1.259+{
   1.260+	int n;
   1.261+
   1.262+	if(m == nil)
   1.263+		return;
   1.264+	n = m->wp - m->rp;
   1.265+	if(n > 0){
   1.266+		if(write(sshfd, m->rp, n) != n)
   1.267+			sysfatal("write to ssh failed: %r");
   1.268+	}
   1.269+	free(m);
   1.270+}
   1.271+
   1.272+int
   1.273+newclient(void)
   1.274+{
   1.275+	int i;
   1.276+	Client *c;
   1.277+
   1.278+	for(i=0; i<nclient; i++)
   1.279+		if(client[i]->ref==0 && client[i]->state == Closed)
   1.280+			return i;
   1.281+
   1.282+	if(nclient%16 == 0)
   1.283+		client = erealloc9p(client, (nclient+16)*sizeof(client[0]));
   1.284+
   1.285+	c = emalloc9p(sizeof(Client));
   1.286+	memset(c, 0, sizeof(*c));
   1.287+	c->num = nclient;
   1.288+	client[nclient++] = c;
   1.289+	return c->num;
   1.290+}
   1.291+
   1.292+Client*
   1.293+getclient(int num)
   1.294+{
   1.295+	if(num < 0 || num >= nclient)
   1.296+		return nil;
   1.297+	return client[num];
   1.298+}
   1.299+
   1.300+void
   1.301+adjustwin(Client *c, int len)
   1.302+{
   1.303+	c->recvacc += len;
   1.304+	if(c->recvacc >= MaxPacket*WinPackets/2 || c->recvwin < MaxPacket){
   1.305+		sendmsg(pack(nil, "buu", MSG_CHANNEL_WINDOW_ADJUST, c->servernum, c->recvacc));
   1.306+		c->recvacc = 0;
   1.307+	}
   1.308+	c->recvwin += len;
   1.309+}
   1.310+
   1.311+void
   1.312+senddata(Client *c, void *data, int len)
   1.313+{
   1.314+	sendmsg(pack(nil, "bus", MSG_CHANNEL_DATA, c->servernum, (char*)data, len));
   1.315+	c->sendwin -= len;
   1.316+}
   1.317+
   1.318+void
   1.319+queuerreq(Client *c, Req *r)
   1.320+{
   1.321+	if(c->rq==nil)
   1.322+		c->erq = &c->rq;
   1.323+	*c->erq = r;
   1.324+	r->aux = nil;
   1.325+	c->erq = (Req**)&r->aux;
   1.326+}
   1.327+
   1.328+void
   1.329+queuermsg(Client *c, Msg *m)
   1.330+{
   1.331+	if(c->mq==nil)
   1.332+		c->emq = &c->mq;
   1.333+	*c->emq = m;
   1.334+	m->link = nil;
   1.335+	c->emq = (Msg**)&m->link;
   1.336+}
   1.337+
   1.338+void
   1.339+matchrmsgs(Client *c)
   1.340+{
   1.341+	Req *r;
   1.342+	Msg *m;
   1.343+	int n, rm;
   1.344+
   1.345+	while(c->rq != nil && c->mq != nil){
   1.346+		r = c->rq;
   1.347+		c->rq = r->aux;
   1.348+
   1.349+		rm = 0;
   1.350+		m = c->mq;
   1.351+		n = r->ifcall.count;
   1.352+		if(n >= m->wp - m->rp){
   1.353+			n = m->wp - m->rp;
   1.354+			c->mq = m->link;
   1.355+			rm = 1;
   1.356+		}
   1.357+		memmove(r->ofcall.data, m->rp, n);
   1.358+		if(rm)
   1.359+			free(m);
   1.360+		else
   1.361+			m->rp += n;
   1.362+		r->ofcall.count = n;
   1.363+		respond(r, nil);
   1.364+		adjustwin(c, n);
   1.365+	}
   1.366+}
   1.367+
   1.368+void
   1.369+queuewreq(Client *c, Req *r)
   1.370+{
   1.371+	if(c->wq==nil)
   1.372+		c->ewq = &c->wq;
   1.373+	*c->ewq = r;
   1.374+	r->aux = nil;
   1.375+	c->ewq = (Req**)&r->aux;
   1.376+}
   1.377+
   1.378+void
   1.379+procwreqs(Client *c)
   1.380+{
   1.381+	Req *r;
   1.382+	int n;
   1.383+
   1.384+	while((r = c->wq) != nil && (n = c->sendwin) > 0){
   1.385+		if(n > c->sendpkt)
   1.386+			n = c->sendpkt;
   1.387+		if(r->ifcall.count > n){
   1.388+			senddata(c, r->ifcall.data, n);
   1.389+			r->ifcall.count -= n;
   1.390+			memmove(r->ifcall.data, (char*)r->ifcall.data + n, r->ifcall.count);
   1.391+			continue;
   1.392+		}
   1.393+		c->wq = (Req*)r->aux;
   1.394+		r->aux = nil;
   1.395+		senddata(c, r->ifcall.data, r->ifcall.count);
   1.396+		r->ofcall.count = r->ifcall.count;
   1.397+		respond(r, nil);
   1.398+	}
   1.399+}
   1.400+
   1.401+Req*
   1.402+findreq(Client *c, Req *r)
   1.403+{
   1.404+	Req **l;
   1.405+
   1.406+	for(l=&c->rq; *l; l=(Req**)&(*l)->aux){
   1.407+		if(*l == r){
   1.408+			*l = r->aux;
   1.409+			if(*l == nil)
   1.410+				c->erq = l;
   1.411+			return r;
   1.412+		}
   1.413+	}
   1.414+	for(l=&c->wq; *l; l=(Req**)&(*l)->aux){
   1.415+		if(*l == r){
   1.416+			*l = r->aux;
   1.417+			if(*l == nil)
   1.418+				c->ewq = l;
   1.419+			return r;
   1.420+		}
   1.421+	}
   1.422+	return nil;
   1.423+}
   1.424+
   1.425+void
   1.426+dialedclient(Client *c)
   1.427+{
   1.428+	Req *r;
   1.429+
   1.430+	if(r=c->wq){
   1.431+		if(r->aux != nil)
   1.432+			sysfatal("more than one outstanding dial request (BUG)");
   1.433+		if(c->state == Established)
   1.434+			respond(r, nil);
   1.435+		else
   1.436+			respond(r, "connect failed");
   1.437+	}
   1.438+	c->wq = nil;
   1.439+}
   1.440+
   1.441+void
   1.442+teardownclient(Client *c)
   1.443+{
   1.444+	c->state = Teardown;
   1.445+	sendmsg(pack(nil, "bu", MSG_CHANNEL_EOF, c->servernum));
   1.446+}
   1.447+
   1.448+void
   1.449+hangupclient(Client *c)
   1.450+{
   1.451+	Req *r, *next;
   1.452+	Msg *m, *mnext;
   1.453+
   1.454+	c->state = Closed;
   1.455+	for(m=c->mq; m; m=mnext){
   1.456+		mnext = m->link;
   1.457+		free(m);
   1.458+	}
   1.459+	c->mq = nil;
   1.460+	for(r=c->rq; r; r=next){
   1.461+		next = r->aux;
   1.462+		respond(r, "hangup on network connection");
   1.463+	}
   1.464+	c->rq = nil;
   1.465+	for(r=c->wq; r; r=next){
   1.466+		next = r->aux;
   1.467+		respond(r, "hangup on network connection");
   1.468+	}
   1.469+	c->wq = nil;
   1.470+}
   1.471+
   1.472+void
   1.473+closeclient(Client *c)
   1.474+{
   1.475+	Msg *m, *next;
   1.476+
   1.477+	if(--c->ref)
   1.478+		return;
   1.479+
   1.480+	if(c->rq != nil || c->wq != nil)
   1.481+		sysfatal("ref count reached zero with requests pending (BUG)");
   1.482+
   1.483+	for(m=c->mq; m; m=next){
   1.484+		next = m->link;
   1.485+		free(m);
   1.486+	}
   1.487+	c->mq = nil;
   1.488+
   1.489+	if(c->state != Closed)
   1.490+		teardownclient(c);
   1.491+}
   1.492+
   1.493+	
   1.494+void
   1.495+sshreadproc(void*)
   1.496+{
   1.497+	Msg *m;
   1.498+	int n;
   1.499+
   1.500+	for(;;){
   1.501+		m = allocmsg();
   1.502+		n = read(sshfd, m->rp, m->ep - m->rp);
   1.503+		if(n <= 0)
   1.504+			sysfatal("eof on ssh connection");
   1.505+		m->wp += n;
   1.506+		sendp(sshmsgchan, m);
   1.507+	}
   1.508+}
   1.509+
   1.510+typedef struct Tab Tab;
   1.511+struct Tab
   1.512+{
   1.513+	char *name;
   1.514+	ulong mode;
   1.515+};
   1.516+
   1.517+Tab tab[] =
   1.518+{
   1.519+	"/",		DMDIR|0555,
   1.520+	"cs",		0666,
   1.521+	"tcp",		DMDIR|0555,	
   1.522+	"clone",	0666,
   1.523+	nil,		DMDIR|0555,
   1.524+	"ctl",		0666,
   1.525+	"data",		0666,
   1.526+	"local",	0444,
   1.527+	"remote",	0444,
   1.528+	"status",	0444,
   1.529+};
   1.530+
   1.531+static void
   1.532+fillstat(Dir *d, uvlong path)
   1.533+{
   1.534+	Tab *t;
   1.535+
   1.536+	memset(d, 0, sizeof(*d));
   1.537+	d->uid = estrdup9p("ssh");
   1.538+	d->gid = estrdup9p("ssh");
   1.539+	d->qid.path = path;
   1.540+	d->atime = d->mtime = time0;
   1.541+	t = &tab[TYPE(path)];
   1.542+	if(t->name)
   1.543+		d->name = estrdup9p(t->name);
   1.544+	else{
   1.545+		d->name = smprint("%ud", NUM(path));
   1.546+		if(d->name == nil)
   1.547+			sysfatal("out of memory");
   1.548+	}
   1.549+	d->qid.type = t->mode>>24;
   1.550+	d->mode = t->mode;
   1.551+}
   1.552+
   1.553+static void
   1.554+fsattach(Req *r)
   1.555+{
   1.556+	if(r->ifcall.aname && r->ifcall.aname[0]){
   1.557+		respond(r, "invalid attach specifier");
   1.558+		return;
   1.559+	}
   1.560+	r->fid->qid.path = PATH(Qroot, 0);
   1.561+	r->fid->qid.type = QTDIR;
   1.562+	r->fid->qid.vers = 0;
   1.563+	r->ofcall.qid = r->fid->qid;
   1.564+	respond(r, nil);
   1.565+}
   1.566+
   1.567+static void
   1.568+fsstat(Req *r)
   1.569+{
   1.570+	fillstat(&r->d, r->fid->qid.path);
   1.571+	respond(r, nil);
   1.572+}
   1.573+
   1.574+static int
   1.575+rootgen(int i, Dir *d, void*)
   1.576+{
   1.577+	i += Qroot+1;
   1.578+	if(i <= Qtcp){
   1.579+		fillstat(d, i);
   1.580+		return 0;
   1.581+	}
   1.582+	return -1;
   1.583+}
   1.584+
   1.585+static int
   1.586+tcpgen(int i, Dir *d, void*)
   1.587+{
   1.588+	i += Qtcp+1;
   1.589+	if(i < Qn){
   1.590+		fillstat(d, i);
   1.591+		return 0;
   1.592+	}
   1.593+	i -= Qn;
   1.594+	if(i < nclient){
   1.595+		fillstat(d, PATH(Qn, i));
   1.596+		return 0;
   1.597+	}
   1.598+	return -1;
   1.599+}
   1.600+
   1.601+static int
   1.602+clientgen(int i, Dir *d, void *aux)
   1.603+{
   1.604+	Client *c;
   1.605+
   1.606+	c = aux;
   1.607+	i += Qn+1;
   1.608+	if(i <= Qstatus){
   1.609+		fillstat(d, PATH(i, c->num));
   1.610+		return 0;
   1.611+	}
   1.612+	return -1;
   1.613+}
   1.614+
   1.615+static char*
   1.616+fswalk1(Fid *fid, char *name, Qid *qid)
   1.617+{
   1.618+	int i, n;
   1.619+	char buf[32];
   1.620+	ulong path;
   1.621+
   1.622+	path = fid->qid.path;
   1.623+	if(!(fid->qid.type&QTDIR))
   1.624+		return "walk in non-directory";
   1.625+
   1.626+	if(strcmp(name, "..") == 0){
   1.627+		switch(TYPE(path)){
   1.628+		case Qn:
   1.629+			qid->path = PATH(Qtcp, NUM(path));
   1.630+			qid->type = tab[Qtcp].mode>>24;
   1.631+			return nil;
   1.632+		case Qtcp:
   1.633+			qid->path = PATH(Qroot, 0);
   1.634+			qid->type = tab[Qroot].mode>>24;
   1.635+			return nil;
   1.636+		case Qroot:
   1.637+			return nil;
   1.638+		default:
   1.639+			return "bug in fswalk1";
   1.640+		}
   1.641+	}
   1.642+
   1.643+	i = TYPE(path)+1;
   1.644+	for(; i<nelem(tab); i++){
   1.645+		if(i==Qn){
   1.646+			n = atoi(name);
   1.647+			snprint(buf, sizeof buf, "%d", n);
   1.648+			if(n < nclient && strcmp(buf, name) == 0){
   1.649+				qid->path = PATH(i, n);
   1.650+				qid->type = tab[i].mode>>24;
   1.651+				return nil;
   1.652+			}
   1.653+			break;
   1.654+		}
   1.655+		if(strcmp(name, tab[i].name) == 0){
   1.656+			qid->path = PATH(i, NUM(path));
   1.657+			qid->type = tab[i].mode>>24;
   1.658+			return nil;
   1.659+		}
   1.660+		if(tab[i].mode&DMDIR)
   1.661+			break;
   1.662+	}
   1.663+	return "directory entry not found";
   1.664+}
   1.665+
   1.666+typedef struct Cs Cs;
   1.667+struct Cs
   1.668+{
   1.669+	char *resp;
   1.670+	int isnew;
   1.671+};
   1.672+
   1.673+static int
   1.674+ndbfindport(char *p)
   1.675+{
   1.676+	char *s, *port;
   1.677+	int n;
   1.678+	static Ndb *db;
   1.679+
   1.680+	if(*p == '\0')
   1.681+		return -1;
   1.682+
   1.683+	n = strtol(p, &s, 0);
   1.684+	if(*s == '\0')
   1.685+		return n;
   1.686+
   1.687+	if(db == nil){
   1.688+		db = ndbopen("/lib/ndb/common");
   1.689+		if(db == nil)
   1.690+			return -1;
   1.691+	}
   1.692+
   1.693+	port = ndbgetvalue(db, nil, "tcp", p, "port", nil);
   1.694+	if(port == nil)
   1.695+		return -1;
   1.696+	n = atoi(port);
   1.697+	free(port);
   1.698+
   1.699+	return n;
   1.700+}	
   1.701+
   1.702+static void
   1.703+csread(Req *r)
   1.704+{
   1.705+	Cs *cs;
   1.706+
   1.707+	cs = r->fid->aux;
   1.708+	if(cs->resp==nil){
   1.709+		respond(r, "cs read without write");
   1.710+		return;
   1.711+	}
   1.712+	if(r->ifcall.offset==0){
   1.713+		if(!cs->isnew){
   1.714+			r->ofcall.count = 0;
   1.715+			respond(r, nil);
   1.716+			return;
   1.717+		}
   1.718+		cs->isnew = 0;
   1.719+	}
   1.720+	readstr(r, cs->resp);
   1.721+	respond(r, nil);
   1.722+}
   1.723+
   1.724+static void
   1.725+cswrite(Req *r)
   1.726+{
   1.727+	int port, nf;
   1.728+	char err[ERRMAX], *f[4], *s, *ns;
   1.729+	Cs *cs;
   1.730+
   1.731+	cs = r->fid->aux;
   1.732+	s = emalloc9p(r->ifcall.count+1);
   1.733+	memmove(s, r->ifcall.data, r->ifcall.count);
   1.734+	s[r->ifcall.count] = '\0';
   1.735+
   1.736+	nf = getfields(s, f, nelem(f), 0, "!");
   1.737+	if(nf != 3){
   1.738+		free(s);
   1.739+		respond(r, "can't translate");
   1.740+		return;
   1.741+	}
   1.742+	if(strcmp(f[0], "tcp") != 0 && strcmp(f[0], "net") != 0){
   1.743+		free(s);
   1.744+		respond(r, "unknown protocol");
   1.745+		return;
   1.746+	}
   1.747+	port = ndbfindport(f[2]);
   1.748+	if(port <= 0){
   1.749+		free(s);
   1.750+		respond(r, "no translation found");
   1.751+		return;
   1.752+	}
   1.753+
   1.754+	ns = smprint("%s/tcp/clone %s!%d", mtpt, f[1], port);
   1.755+	if(ns == nil){
   1.756+		free(s);
   1.757+		rerrstr(err, sizeof err);
   1.758+		respond(r, err);
   1.759+		return;
   1.760+	}
   1.761+	free(s);
   1.762+	free(cs->resp);
   1.763+	cs->resp = ns;
   1.764+	cs->isnew = 1;
   1.765+	r->ofcall.count = r->ifcall.count;
   1.766+	respond(r, nil);
   1.767+}
   1.768+
   1.769+static void
   1.770+ctlread(Req *r, Client *c)
   1.771+{
   1.772+	char buf[32];
   1.773+
   1.774+	sprint(buf, "%d", c->num);
   1.775+	readstr(r, buf);
   1.776+	respond(r, nil);
   1.777+}
   1.778+
   1.779+static void
   1.780+ctlwrite(Req *r, Client *c)
   1.781+{
   1.782+	char *f[3], *s;
   1.783+	int nf;
   1.784+
   1.785+	s = emalloc9p(r->ifcall.count+1);
   1.786+	memmove(s, r->ifcall.data, r->ifcall.count);
   1.787+	s[r->ifcall.count] = '\0';
   1.788+
   1.789+	nf = tokenize(s, f, 3);
   1.790+	if(nf == 0){
   1.791+		free(s);
   1.792+		r->ofcall.count = r->ifcall.count;
   1.793+		respond(r, nil);
   1.794+		return;
   1.795+	}
   1.796+
   1.797+	if(strcmp(f[0], "hangup") == 0){
   1.798+		if(c->state != Established)
   1.799+			goto Badarg;
   1.800+		if(nf != 1)
   1.801+			goto Badarg;
   1.802+		teardownclient(c);
   1.803+		r->ofcall.count = r->ifcall.count;
   1.804+		respond(r, nil);
   1.805+	}else if(strcmp(f[0], "connect") == 0){
   1.806+		if(c->state != Closed)
   1.807+			goto Badarg;
   1.808+		if(nf != 2)
   1.809+			goto Badarg;
   1.810+		c->connect = estrdup9p(f[1]);
   1.811+		nf = getfields(f[1], f, nelem(f), 0, "!");
   1.812+		if(nf != 2){
   1.813+			free(c->connect);
   1.814+			c->connect = nil;
   1.815+			goto Badarg;
   1.816+		}
   1.817+		c->sendwin = MaxPacket;
   1.818+		c->recvwin = WinPackets * MaxPacket;
   1.819+		c->recvacc = 0;
   1.820+		c->state = Dialing;
   1.821+		queuewreq(c, r);
   1.822+
   1.823+		sendmsg(pack(nil, "bsuuususu", MSG_CHANNEL_OPEN,
   1.824+			"direct-tcpip", 12,
   1.825+			c->num, c->recvwin, MaxPacket,
   1.826+			f[0], strlen(f[0]), ndbfindport(f[1]),
   1.827+			localip, strlen(localip), localport));
   1.828+	}else{
   1.829+	Badarg:
   1.830+		respond(r, "bad or inappropriate tcp control message");
   1.831+	}
   1.832+	free(s);
   1.833+}
   1.834+
   1.835+static void
   1.836+dataread(Req *r, Client *c)
   1.837+{
   1.838+	if(c->state != Established){
   1.839+		respond(r, "not connected");
   1.840+		return;
   1.841+	}
   1.842+	queuerreq(c, r);
   1.843+	matchrmsgs(c);
   1.844+}
   1.845+
   1.846+static void
   1.847+datawrite(Req *r, Client *c)
   1.848+{
   1.849+	if(c->state != Established){
   1.850+		respond(r, "not connected");
   1.851+		return;
   1.852+	}
   1.853+	if(r->ifcall.count == 0){
   1.854+		r->ofcall.count = r->ifcall.count;
   1.855+		respond(r, nil);
   1.856+		return;
   1.857+	}
   1.858+	queuewreq(c, r);
   1.859+	procwreqs(c);
   1.860+}
   1.861+
   1.862+static void
   1.863+localread(Req *r)
   1.864+{
   1.865+	char buf[128];
   1.866+
   1.867+	snprint(buf, sizeof buf, "%s!%d\n", localip, localport);
   1.868+	readstr(r, buf);
   1.869+	respond(r, nil);
   1.870+}
   1.871+
   1.872+static void
   1.873+remoteread(Req *r, Client *c)
   1.874+{
   1.875+	char *s;
   1.876+	char buf[128];
   1.877+
   1.878+	s = c->connect;
   1.879+	if(s == nil)
   1.880+		s = "::!0";
   1.881+	snprint(buf, sizeof buf, "%s\n", s);
   1.882+	readstr(r, buf);
   1.883+	respond(r, nil);
   1.884+}
   1.885+
   1.886+static void
   1.887+statusread(Req *r, Client *c)
   1.888+{
   1.889+	char *s;
   1.890+
   1.891+	s = statestr[c->state];
   1.892+	readstr(r, s);
   1.893+	respond(r, nil);
   1.894+}
   1.895+
   1.896+static void
   1.897+fsread(Req *r)
   1.898+{
   1.899+	char e[ERRMAX];
   1.900+	ulong path;
   1.901+
   1.902+	path = r->fid->qid.path;
   1.903+	switch(TYPE(path)){
   1.904+	default:
   1.905+		snprint(e, sizeof e, "bug in fsread path=%lux", path);
   1.906+		respond(r, e);
   1.907+		break;
   1.908+
   1.909+	case Qroot:
   1.910+		dirread9p(r, rootgen, nil);
   1.911+		respond(r, nil);
   1.912+		break;
   1.913+
   1.914+	case Qcs:
   1.915+		csread(r);
   1.916+		break;
   1.917+
   1.918+	case Qtcp:
   1.919+		dirread9p(r, tcpgen, nil);
   1.920+		respond(r, nil);
   1.921+		break;
   1.922+
   1.923+	case Qn:
   1.924+		dirread9p(r, clientgen, client[NUM(path)]);
   1.925+		respond(r, nil);
   1.926+		break;
   1.927+
   1.928+	case Qctl:
   1.929+		ctlread(r, client[NUM(path)]);
   1.930+		break;
   1.931+
   1.932+	case Qdata:
   1.933+		dataread(r, client[NUM(path)]);
   1.934+		break;
   1.935+
   1.936+	case Qlocal:
   1.937+		localread(r);
   1.938+		break;
   1.939+
   1.940+	case Qremote:
   1.941+		remoteread(r, client[NUM(path)]);
   1.942+		break;
   1.943+
   1.944+	case Qstatus:
   1.945+		statusread(r, client[NUM(path)]);
   1.946+		break;
   1.947+	}
   1.948+}
   1.949+
   1.950+static void
   1.951+fswrite(Req *r)
   1.952+{
   1.953+	ulong path;
   1.954+	char e[ERRMAX];
   1.955+
   1.956+	path = r->fid->qid.path;
   1.957+	switch(TYPE(path)){
   1.958+	default:
   1.959+		snprint(e, sizeof e, "bug in fswrite path=%lux", path);
   1.960+		respond(r, e);
   1.961+		break;
   1.962+
   1.963+	case Qcs:
   1.964+		cswrite(r);
   1.965+		break;
   1.966+
   1.967+	case Qctl:
   1.968+		ctlwrite(r, client[NUM(path)]);
   1.969+		break;
   1.970+
   1.971+	case Qdata:
   1.972+		datawrite(r, client[NUM(path)]);
   1.973+		break;
   1.974+	}
   1.975+}
   1.976+
   1.977+static void
   1.978+fsopen(Req *r)
   1.979+{
   1.980+	static int need[4] = { 4, 2, 6, 1 };
   1.981+	ulong path;
   1.982+	int n;
   1.983+	Tab *t;
   1.984+	Cs *cs;
   1.985+
   1.986+	/*
   1.987+	 * lib9p already handles the blatantly obvious.
   1.988+	 * we just have to enforce the permissions we have set.
   1.989+	 */
   1.990+	path = r->fid->qid.path;
   1.991+	t = &tab[TYPE(path)];
   1.992+	n = need[r->ifcall.mode&3];
   1.993+	if((n&t->mode) != n){
   1.994+		respond(r, "permission denied");
   1.995+		return;
   1.996+	}
   1.997+
   1.998+	switch(TYPE(path)){
   1.999+	case Qcs:
  1.1000+		cs = emalloc9p(sizeof(Cs));
  1.1001+		r->fid->aux = cs;
  1.1002+		respond(r, nil);
  1.1003+		break;
  1.1004+	case Qclone:
  1.1005+		n = newclient();
  1.1006+		path = PATH(Qctl, n);
  1.1007+		r->fid->qid.path = path;
  1.1008+		r->ofcall.qid.path = path;
  1.1009+		if(chatty9p)
  1.1010+			fprint(2, "open clone => path=%lux\n", path);
  1.1011+		t = &tab[Qctl];
  1.1012+		/* fall through */
  1.1013+	default:
  1.1014+		if(t-tab >= Qn)
  1.1015+			client[NUM(path)]->ref++;
  1.1016+		respond(r, nil);
  1.1017+		break;
  1.1018+	}
  1.1019+}
  1.1020+
  1.1021+static void
  1.1022+fsflush(Req *r)
  1.1023+{
  1.1024+	int i;
  1.1025+
  1.1026+	for(i=0; i<nclient; i++)
  1.1027+		if(findreq(client[i], r->oldreq))
  1.1028+			respond(r->oldreq, "interrupted");
  1.1029+	respond(r, nil);
  1.1030+}
  1.1031+
  1.1032+static void
  1.1033+handlemsg(Msg *m)
  1.1034+{
  1.1035+	int chan, win, pkt, n;
  1.1036+	Client *c;
  1.1037+	char *s;
  1.1038+
  1.1039+	switch(m->rp[0]){
  1.1040+	case MSG_CHANNEL_WINDOW_ADJUST:
  1.1041+		if(unpack(m, "_uu", &chan, &n) < 0)
  1.1042+			break;
  1.1043+		c = getclient(chan);
  1.1044+		if(c != nil && c->state==Established){
  1.1045+			c->sendwin += n;
  1.1046+			procwreqs(c);
  1.1047+		}
  1.1048+		break;
  1.1049+	case MSG_CHANNEL_DATA:
  1.1050+		if(unpack(m, "_us", &chan, &s, &n) < 0)
  1.1051+			break;
  1.1052+		c = getclient(chan);
  1.1053+		if(c != nil && c->state==Established){
  1.1054+			c->recvwin -= n;
  1.1055+			m->rp = (uchar*)s;
  1.1056+			queuermsg(c, m);
  1.1057+			matchrmsgs(c);
  1.1058+			return;
  1.1059+		}
  1.1060+		break;
  1.1061+	case MSG_CHANNEL_EOF:
  1.1062+		if(unpack(m, "_u", &chan) < 0)
  1.1063+			break;
  1.1064+		c = getclient(chan);
  1.1065+		if(c != nil){
  1.1066+			hangupclient(c);
  1.1067+			m->rp = m->wp = m->buf;
  1.1068+			sendmsg(pack(m, "bu", MSG_CHANNEL_CLOSE, c->servernum));
  1.1069+			return;
  1.1070+		}
  1.1071+		break;
  1.1072+	case MSG_CHANNEL_CLOSE:
  1.1073+		if(unpack(m, "_u", &chan) < 0)
  1.1074+			break;
  1.1075+		c = getclient(chan);
  1.1076+		if(c != nil)
  1.1077+			hangupclient(c);
  1.1078+		break;
  1.1079+	case MSG_CHANNEL_OPEN_CONFIRMATION:
  1.1080+		if(unpack(m, "_uuuu", &chan, &n, &win, &pkt) < 0)
  1.1081+			break;
  1.1082+		c = getclient(chan);
  1.1083+		if(c == nil || c->state != Dialing)
  1.1084+			break;
  1.1085+		if(pkt <= 0 || pkt > MaxPacket)
  1.1086+			pkt = MaxPacket;
  1.1087+		c->sendpkt = pkt;
  1.1088+		c->sendwin = win;
  1.1089+		c->servernum = n;
  1.1090+		c->state = Established;
  1.1091+		dialedclient(c);
  1.1092+		break;
  1.1093+	case MSG_CHANNEL_OPEN_FAILURE:
  1.1094+		if(unpack(m, "_uu", &chan, &n) < 0)
  1.1095+			break;
  1.1096+		c = getclient(chan);
  1.1097+		if(c == nil || c->state != Dialing)
  1.1098+			break;
  1.1099+		c->servernum = n;
  1.1100+		c->state = Closed;
  1.1101+		dialedclient(c);
  1.1102+		break;
  1.1103+	}
  1.1104+	free(m);
  1.1105+}
  1.1106+
  1.1107+void
  1.1108+fsnetproc(void*)
  1.1109+{
  1.1110+	ulong path;
  1.1111+	Alt a[4];
  1.1112+	Cs *cs;
  1.1113+	Fid *fid;
  1.1114+	Req *r;
  1.1115+	Msg *m;
  1.1116+
  1.1117+	threadsetname("fsthread");
  1.1118+
  1.1119+	a[0].op = CHANRCV;
  1.1120+	a[0].c = fsclunkchan;
  1.1121+	a[0].v = &fid;
  1.1122+	a[1].op = CHANRCV;
  1.1123+	a[1].c = fsreqchan;
  1.1124+	a[1].v = &r;
  1.1125+	a[2].op = CHANRCV;
  1.1126+	a[2].c = sshmsgchan;
  1.1127+	a[2].v = &m;
  1.1128+	a[3].op = CHANEND;
  1.1129+
  1.1130+	for(;;){
  1.1131+		switch(alt(a)){
  1.1132+		case 0:
  1.1133+			path = fid->qid.path;
  1.1134+			switch(TYPE(path)){
  1.1135+			case Qcs:
  1.1136+				cs = fid->aux;
  1.1137+				if(cs){
  1.1138+					free(cs->resp);
  1.1139+					free(cs);
  1.1140+				}
  1.1141+				break;
  1.1142+			}
  1.1143+			if(fid->omode != -1 && TYPE(path) >= Qn)
  1.1144+				closeclient(client[NUM(path)]);
  1.1145+			sendp(fsclunkwaitchan, nil);
  1.1146+			break;
  1.1147+		case 1:
  1.1148+			switch(r->ifcall.type){
  1.1149+			case Tattach:
  1.1150+				fsattach(r);
  1.1151+				break;
  1.1152+			case Topen:
  1.1153+				fsopen(r);
  1.1154+				break;
  1.1155+			case Tread:
  1.1156+				fsread(r);
  1.1157+				break;
  1.1158+			case Twrite:
  1.1159+				fswrite(r);
  1.1160+				break;
  1.1161+			case Tstat:
  1.1162+				fsstat(r);
  1.1163+				break;
  1.1164+			case Tflush:
  1.1165+				fsflush(r);
  1.1166+				break;
  1.1167+			default:
  1.1168+				respond(r, "bug in fsthread");
  1.1169+				break;
  1.1170+			}
  1.1171+			sendp(fsreqwaitchan, 0);
  1.1172+			break;
  1.1173+		case 2:
  1.1174+			handlemsg(m);
  1.1175+			break;
  1.1176+		}
  1.1177+	}
  1.1178+}
  1.1179+
  1.1180+static void
  1.1181+fssend(Req *r)
  1.1182+{
  1.1183+	sendp(fsreqchan, r);
  1.1184+	recvp(fsreqwaitchan);	/* avoids need to deal with spurious flushes */
  1.1185+}
  1.1186+
  1.1187+static void
  1.1188+fsdestroyfid(Fid *fid)
  1.1189+{
  1.1190+	sendp(fsclunkchan, fid);
  1.1191+	recvp(fsclunkwaitchan);
  1.1192+}
  1.1193+
  1.1194+void
  1.1195+takedown(Srv*)
  1.1196+{
  1.1197+	threadexitsall("done");
  1.1198+}
  1.1199+
  1.1200+Srv fs = 
  1.1201+{
  1.1202+.attach=		fssend,
  1.1203+.destroyfid=	fsdestroyfid,
  1.1204+.walk1=		fswalk1,
  1.1205+.open=		fssend,
  1.1206+.read=		fssend,
  1.1207+.write=		fssend,
  1.1208+.stat=		fssend,
  1.1209+.flush=		fssend,
  1.1210+.end=		takedown,
  1.1211+};
  1.1212+
  1.1213+int pfd[2];
  1.1214+int sshargc;
  1.1215+char **sshargv;
  1.1216+
  1.1217+void
  1.1218+startssh(void *)
  1.1219+{
  1.1220+	char *f;
  1.1221+
  1.1222+	close(pfd[0]);
  1.1223+	dup(pfd[1], 0);
  1.1224+	dup(pfd[1], 1);
  1.1225+	close(pfd[1]);
  1.1226+	if(strncmp(sshargv[0], "./", 2) != 0)
  1.1227+		f = smprint("/bin/%s", sshargv[0]);
  1.1228+	else
  1.1229+		f = sshargv[0];
  1.1230+	procexec(nil, f, sshargv);
  1.1231+	sysfatal("exec: %r");
  1.1232+}
  1.1233+
  1.1234+void
  1.1235+usage(void)
  1.1236+{
  1.1237+	fprint(2, "usage: sshnet [-m mtpt] [ssh options]\n");
  1.1238+	exits("usage");
  1.1239+}
  1.1240+
  1.1241+void
  1.1242+threadmain(int argc, char **argv)
  1.1243+{
  1.1244+	char *service;
  1.1245+
  1.1246+	fmtinstall('H', encodefmt);
  1.1247+
  1.1248+	mtpt = "/net";
  1.1249+	service = nil;
  1.1250+	ARGBEGIN{
  1.1251+	case 'D':
  1.1252+		chatty9p++;
  1.1253+		break;
  1.1254+	case 'm':
  1.1255+		mtpt = EARGF(usage());
  1.1256+		break;
  1.1257+	case 's':
  1.1258+		service = EARGF(usage());
  1.1259+		break;
  1.1260+	default:
  1.1261+		usage();
  1.1262+	}ARGEND
  1.1263+
  1.1264+	if(argc == 0)
  1.1265+		usage();
  1.1266+	
  1.1267+	sshargc = argc + 2;
  1.1268+	sshargv = emalloc9p(sizeof(char *) * (sshargc + 1));
  1.1269+	sshargv[0] = "ssh";
  1.1270+	sshargv[1] = "-X";
  1.1271+	memcpy(sshargv + 2, argv, argc * sizeof(char *));
  1.1272+
  1.1273+	pipe(pfd);
  1.1274+	sshfd = pfd[0];
  1.1275+	procrfork(startssh, nil, mainstacksize, RFFDG|RFNOTEG|RFNAMEG);
  1.1276+	close(pfd[1]);
  1.1277+
  1.1278+	time0 = time(0);
  1.1279+	sshmsgchan = chancreate(sizeof(Msg*), 16);
  1.1280+	fsreqchan = chancreate(sizeof(Req*), 0);
  1.1281+	fsreqwaitchan = chancreate(sizeof(void*), 0);
  1.1282+	fsclunkchan = chancreate(sizeof(Fid*), 0);
  1.1283+	fsclunkwaitchan = chancreate(sizeof(void*), 0);
  1.1284+
  1.1285+	procrfork(sshreadproc, nil, mainstacksize, RFNAMEG|RFNOTEG);
  1.1286+	procrfork(fsnetproc, nil, mainstacksize, RFNAMEG|RFNOTEG);
  1.1287+
  1.1288+	threadpostmountsrv(&fs, service, mtpt, MREPL);
  1.1289+	exits(0);
  1.1290+}