<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv=Content-Type content="text/html; charset=utf8">
<title>/usr/web/sources/contrib/cinap_lenrek/sshnet.c - Plan 9 from Bell Labs</title>
<!-- THIS FILE IS AUTOMATICALLY GENERATED. -->
<!-- EDIT sources.tr INSTEAD. -->
</meta>
</head>
<body>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<p style="line-height: 1.2em; margin-left: 1.00in; text-indent: 0.00in; margin-right: 1.00in; margin-top: 0; margin-bottom: 0; text-align: center;">
<span style="font-size: 10pt"><a href="/plan9/">Plan 9 from Bell Labs</a>&rsquo;s /usr/web/sources/contrib/cinap_lenrek/sshnet.c</span></p>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<center><font size=-1>
Copyright © 2009 Alcatel-Lucent.<br />
Distributed under the
<a href="/plan9/license.html">Lucent Public License version 1.02</a>.
<br />
<a href="/plan9/download.html">Download the Plan 9 distribution.</a>
</font>
</center>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<table width="100%" cellspacing=0 border=0><tr><td align="center">
<table cellspacing=0 cellpadding=5 bgcolor="#eeeeff"><tr><td align="left">
<pre>
<!-- END HEADER -->
/*
* SSH network file system.
* Presents remote TCP stack as /net-style file system.
*/

#include "ssh.h"
#include &lt;bio.h&gt;
#include &lt;ndb.h&gt;
#include &lt;thread.h&gt;
#include &lt;fcall.h&gt;
#include &lt;9p.h&gt;

int rawhack = 1;
Conn *conn;
char *remoteip  = "&lt;remote&gt;";
char *mtpt;

Cipher *allcipher[] = {
       &amp;cipherrc4,
       &amp;cipherblowfish,
       &amp;cipher3des,
       &amp;cipherdes,
       &amp;ciphernone,
       &amp;ciphertwiddle,
};

Auth *allauth[] = {
       &amp;authpassword,
       &amp;authrsa,
       &amp;authtis,
};

char *cipherlist = "rc4 3des";
char *authlist = "rsa password tis";

Cipher*
findcipher(char *name, Cipher **list, int nlist)
{
       int i;

       for(i=0; i&lt;nlist; i++)
               if(strcmp(name, list[i]-&gt;name) == 0)
                       return list[i];
       error("unknown cipher %s", name);
       return nil;
}

Auth*
findauth(char *name, Auth **list, int nlist)
{
       int i;

       for(i=0; i&lt;nlist; i++)
               if(strcmp(name, list[i]-&gt;name) == 0)
                       return list[i];
       error("unknown auth %s", name);
       return nil;
}

void
usage(void)
{
       fprint(2, "usage: sshnet [-A authlist] [-c cipherlist] [-m mtpt] [user@]hostname\n");
       exits("usage");
}

int
isatty(int fd)
{
       char buf[64];

       buf[0] = '\0';
       fd2path(fd, buf, sizeof buf);
       if(strlen(buf)&gt;=9 &amp;&amp; strcmp(buf+strlen(buf)-9, "/dev/cons")==0)
               return 1;
       return 0;
}

enum
{
       Qroot,
       Qcs,
       Qtcp,
       Qclone,
       Qn,
       Qctl,
       Qdata,
       Qlocal,
       Qremote,
       Qstatus,
};

#define PATH(type, n)           ((type)|((n)&lt;&lt;8))
#define TYPE(path)                      ((int)(path) &amp; 0xFF)
#define NUM(path)                       ((uint)(path)&gt;&gt;8)

Channel *sshmsgchan;            /* chan(Msg*) */
Channel *fsreqchan;                     /* chan(Req*) */
Channel *fsreqwaitchan;         /* chan(nil) */
Channel *fsclunkchan;           /* chan(Fid*) */
Channel *fsclunkwaitchan;       /* chan(nil) */
ulong time0;

enum
{
       Closed,
       Dialing,
       Established,
       Teardown,
};

char *statestr[] = {
       "Closed",
       "Dialing",
       "Established",
       "Teardown",
};

typedef struct Client Client;
struct Client
{
       int ref;
       int state;
       int eof;
       int num;
       int servernum;
       char *connect;
       Req *rq;
       Req **erq;
       Msg *mq;
       Msg **emq;
};

int nclient;
Client **client;

int
newclient(void)
{
       int i;
       Client *c;

       for(i=0; i&lt;nclient; i++){
               if(client[i]-&gt;ref==0 &amp;&amp; client[i]-&gt;state == Closed){
                       client[i]-&gt;eof = 0;
                       return i;
               }
       }

       if(nclient%16 == 0)
               client = erealloc9p(client, (nclient+16)*sizeof(client[0]));

       c = emalloc9p(sizeof(Client));
       memset(c, 0, sizeof(*c));
       c-&gt;num = nclient;
       c-&gt;eof = 0;
       client[nclient++] = c;
       return c-&gt;num;
}

void
queuereq(Client *c, Req *r)
{
       if(c-&gt;rq==nil)
               c-&gt;erq = &amp;c-&gt;rq;
       *c-&gt;erq = r;
       r-&gt;aux = nil;
       c-&gt;erq = (Req**)&amp;r-&gt;aux;
}

void
queuemsg(Client *c, Msg *m)
{
       if(c-&gt;mq==nil)
               c-&gt;emq = &amp;c-&gt;mq;
       *c-&gt;emq = m;
       m-&gt;link = nil;
       c-&gt;emq = (Msg**)&amp;m-&gt;link;
}

void
matchmsgs(Client *c)
{
       Req *r;
       Msg *m;
       int n, rm;

       while(c-&gt;rq &amp;&amp; c-&gt;mq){
               r = c-&gt;rq;
               c-&gt;rq = r-&gt;aux;

               rm = 0;
               m = c-&gt;mq;
               n = r-&gt;ifcall.count;
               if(n &gt;= m-&gt;ep - m-&gt;rp){
                       n = m-&gt;ep - m-&gt;rp;
                       c-&gt;mq = m-&gt;link;
                       rm = 1;
               }
               memmove(r-&gt;ofcall.data, m-&gt;rp, n);
               if(rm)
                       free(m);
               else
                       m-&gt;rp += n;
               r-&gt;ofcall.count = n;
               respond(r, nil);
       }
}

Req*
findreq(Client *c, Req *r)
{
       Req **l;

       for(l=&amp;c-&gt;rq; *l; l=(Req**)&amp;(*l)-&gt;aux){
               if(*l == r){
                       *l = r-&gt;aux;
                       if(*l == nil)
                               c-&gt;erq = l;
                       return r;
               }
       }
       return nil;
}

void
dialedclient(Client *c)
{
       Req *r;

       if(r=c-&gt;rq){
               if(r-&gt;aux != nil)
                       sysfatal("more than one outstanding dial request (BUG)");
               if(c-&gt;state == Established)
                       respond(r, nil);
               else
                       respond(r, "connect failed");
       }
       c-&gt;rq = nil;
}

void
teardownclient(Client *c)
{
       Msg *m;

       c-&gt;state = Teardown;
       m = allocmsg(conn, SSH_MSG_CHANNEL_INPUT_EOF, 4);
       putlong(m, c-&gt;servernum);
       sendmsg(m);
}

void
hangupclient(Client *c)
{
       Req *r, *next;
       Msg *m, *mnext;

       c-&gt;state = Closed;
       for(m=c-&gt;mq; m; m=mnext){
               mnext = m-&gt;link;
               free(m);
       }
       c-&gt;mq = nil;
       for(r=c-&gt;rq; r; r=next){
               next = r-&gt;aux;
               respond(r, "hangup on network connection");
       }
       c-&gt;rq = nil;
}

void
closeclient(Client *c)
{
       Msg *m, *next;

       if(--c-&gt;ref)
               return;

       if(c-&gt;rq != nil)
               sysfatal("ref count reached zero with requests pending (BUG)");

       for(m=c-&gt;mq; m; m=next){
               next = m-&gt;link;
               free(m);
       }
       c-&gt;mq = nil;

       if(c-&gt;state != Closed)
               teardownclient(c);

       c-&gt;eof = 0;
}


void
sshreadproc(void *a)
{
       Conn *c;
       Msg *m;

       c = a;
       for(;;){
               m = recvmsg(c, -1);
               if(m == nil)
                       sysfatal("eof on ssh connection");
               sendp(sshmsgchan, m);
       }
}

typedef struct Tab Tab;
struct Tab
{
       char *name;
       ulong mode;
};

Tab tab[] =
{
       "/",            DMDIR|0555,
       "cs",           0666,
       "tcp",  DMDIR|0555,
       "clone",        0666,
       nil,            DMDIR|0555,
       "ctl",          0666,
       "data", 0666,
       "local",        0444,
       "remote",       0444,
       "status",       0444,
};

static void
fillstat(Dir *d, uvlong path)
{
       Tab *t;

       memset(d, 0, sizeof(*d));
       d-&gt;uid = estrdup9p("ssh");
       d-&gt;gid = estrdup9p("ssh");
       d-&gt;qid.path = path;
       d-&gt;atime = d-&gt;mtime = time0;
       t = &amp;tab[TYPE(path)];
       if(t-&gt;name)
               d-&gt;name = estrdup9p(t-&gt;name);
       else{
               d-&gt;name = smprint("%ud", NUM(path));
               if(d-&gt;name == nil)
                       sysfatal("out of memory");
       }
       d-&gt;qid.type = t-&gt;mode&gt;&gt;24;
       d-&gt;mode = t-&gt;mode;
}

static void
fsattach(Req *r)
{
       if(r-&gt;ifcall.aname &amp;&amp; r-&gt;ifcall.aname[0]){
               respond(r, "invalid attach specifier");
               return;
       }
       r-&gt;fid-&gt;qid.path = PATH(Qroot, 0);
       r-&gt;fid-&gt;qid.type = QTDIR;
       r-&gt;fid-&gt;qid.vers = 0;
       r-&gt;ofcall.qid = r-&gt;fid-&gt;qid;
       respond(r, nil);
}

static void
fsstat(Req *r)
{
       fillstat(&amp;r-&gt;d, r-&gt;fid-&gt;qid.path);
       respond(r, nil);
}

static int
rootgen(int i, Dir *d, void*)
{
       i += Qroot+1;
       if(i &lt;= Qtcp){
               fillstat(d, i);
               return 0;
       }
       return -1;
}

static int
tcpgen(int i, Dir *d, void*)
{
       i += Qtcp+1;
       if(i &lt; Qn){
               fillstat(d, i);
               return 0;
       }
       i -= Qn;
       if(i &lt; nclient){
               fillstat(d, PATH(Qn, i));
               return 0;
       }
       return -1;
}

static int
clientgen(int i, Dir *d, void *aux)
{
       Client *c;

       c = aux;
       i += Qn+1;
       if(i &lt;= Qstatus){
               fillstat(d, PATH(i, c-&gt;num));
               return 0;
       }
       return -1;
}

static char*
fswalk1(Fid *fid, char *name, Qid *qid)
{
       int i, n;
       char buf[32];
       ulong path;

       path = fid-&gt;qid.path;
       if(!(fid-&gt;qid.type&amp;QTDIR))
               return "walk in non-directory";

       if(strcmp(name, "..") == 0){
               switch(TYPE(path)){
               case Qn:
                       qid-&gt;path = PATH(Qtcp, NUM(path));
                       qid-&gt;type = tab[Qtcp].mode&gt;&gt;24;
                       return nil;
               case Qtcp:
                       qid-&gt;path = PATH(Qroot, 0);
                       qid-&gt;type = tab[Qroot].mode&gt;&gt;24;
                       return nil;
               case Qroot:
                       return nil;
               default:
                       return "bug in fswalk1";
               }
       }

       i = TYPE(path)+1;
       for(; i&lt;nelem(tab); i++){
               if(i==Qn){
                       n = atoi(name);
                       snprint(buf, sizeof buf, "%d", n);
                       if(n &lt; nclient &amp;&amp; strcmp(buf, name) == 0){
                               qid-&gt;path = PATH(i, n);
                               qid-&gt;type = tab[i].mode&gt;&gt;24;
                               return nil;
                       }
                       break;
               }
               if(strcmp(name, tab[i].name) == 0){
                       qid-&gt;path = PATH(i, NUM(path));
                       qid-&gt;type = tab[i].mode&gt;&gt;24;
                       return nil;
               }
               if(tab[i].mode&amp;DMDIR)
                       break;
       }
       return "directory entry not found";
}

typedef struct Cs Cs;
struct Cs
{
       char *resp;
       int isnew;
};

static int
ndbfindport(char *p)
{
       char *s, *port;
       int n;
       static Ndb *db;

       if(*p == '\0')
               return -1;

       n = strtol(p, &amp;s, 0);
       if(*s == '\0')
               return n;

       if(db == nil){
               db = ndbopen("/lib/ndb/common");
               if(db == nil)
                       return -1;
       }

       port = ndbgetvalue(db, nil, "tcp", p, "port", nil);
       if(port == nil)
               return -1;
       n = atoi(port);
       free(port);

       return n;
}

static void
csread(Req *r)
{
       Cs *cs;

       cs = r-&gt;fid-&gt;aux;
       if(cs-&gt;resp==nil){
               respond(r, "cs read without write");
               return;
       }
       if(r-&gt;ifcall.offset==0){
               if(!cs-&gt;isnew){
                       r-&gt;ofcall.count = 0;
                       respond(r, nil);
                       return;
               }
               cs-&gt;isnew = 0;
       }
       readstr(r, cs-&gt;resp);
       respond(r, nil);
}

static void
cswrite(Req *r)
{
       int port, nf;
       char err[ERRMAX], *f[4], *s, *ns;
       Cs *cs;

       cs = r-&gt;fid-&gt;aux;
       s = emalloc(r-&gt;ifcall.count+1);
       memmove(s, r-&gt;ifcall.data, r-&gt;ifcall.count);
       s[r-&gt;ifcall.count] = '\0';

       nf = getfields(s, f, nelem(f), 0, "!");
       if(nf != 3){
               free(s);
               respond(r, "can't translate");
               return;
       }
       if(strcmp(f[0], "tcp") != 0 &amp;&amp; strcmp(f[0], "net") != 0){
               free(s);
               respond(r, "unknown protocol");
               return;
       }
       port = ndbfindport(f[2]);
       if(port &lt;= 0){
               free(s);
               respond(r, "no translation found");
               return;
       }

       ns = smprint("%s/tcp/clone %s!%d", mtpt, f[1], port);
       if(ns == nil){
               free(s);
               rerrstr(err, sizeof err);
               respond(r, err);
               return;
       }
       free(s);
       free(cs-&gt;resp);
       cs-&gt;resp = ns;
       cs-&gt;isnew = 1;
       r-&gt;ofcall.count = r-&gt;ifcall.count;
       respond(r, nil);
}

static void
ctlread(Req *r, Client *c)
{
       char buf[32];

       sprint(buf, "%d", c-&gt;num);
       readstr(r, buf);
       respond(r, nil);
}

static void
ctlwrite(Req *r, Client *c)
{
       char *f[3], *s;
       int nf;
       Msg *m;

       s = emalloc(r-&gt;ifcall.count+1);
       memmove(s, r-&gt;ifcall.data, r-&gt;ifcall.count);
       s[r-&gt;ifcall.count] = '\0';

       nf = tokenize(s, f, 3);
       if(nf == 0){
               free(s);
               respond(r, nil);
               return;
       }

       if(strcmp(f[0], "hangup") == 0){
               if(c-&gt;state != Established)
                       goto Badarg;
               if(nf != 1)
                       goto Badarg;
               queuereq(c, r);
               teardownclient(c);
       }else if(strcmp(f[0], "connect") == 0){
               if(c-&gt;state != Closed)
                       goto Badarg;
               if(nf != 2)
                       goto Badarg;
               c-&gt;connect = estrdup9p(f[1]);
               nf = getfields(f[1], f, nelem(f), 0, "!");
               if(nf != 2){
                       free(c-&gt;connect);
                       c-&gt;connect = nil;
                       goto Badarg;
               }
               c-&gt;state = Dialing;
               m = allocmsg(conn, SSH_MSG_PORT_OPEN, 4+4+strlen(f[0])+4+4+strlen("localhost"));
               putlong(m, c-&gt;num);
               putstring(m, f[0]);
               putlong(m, ndbfindport(f[1]));
               putstring(m, "localhost");
               queuereq(c, r);
               sendmsg(m);
       }else{
       Badarg:
               respond(r, "bad or inappropriate tcp control message");
       }
       free(s);
}

static void
dataread(Req *r, Client *c)
{
       if(c-&gt;state!=Established){
               respond(r, "not connected");
               return;
       }
       if(c-&gt;eof){
               if(c-&gt;mq==nil){
                       respond(r, "eof");
                       return;
               }
       }
       queuereq(c, r);
       matchmsgs(c);
}

static void
datawrite(Req *r, Client *c)
{
       Msg *m;

       if(c-&gt;state != Established){
               respond(r, "not connected");
               return;
       }
       if(c-&gt;eof){
               respond(r, "closed");
               return;
       }
       if(r-&gt;ifcall.count){
               m = allocmsg(conn, SSH_MSG_CHANNEL_DATA, 4+4+r-&gt;ifcall.count);
               putlong(m, c-&gt;servernum);
               putlong(m, r-&gt;ifcall.count);
               putbytes(m, r-&gt;ifcall.data, r-&gt;ifcall.count);
               sendmsg(m);
       }
       r-&gt;ofcall.count = r-&gt;ifcall.count;
       respond(r, nil);
}

static void
localread(Req *r)
{
       char buf[128];

       snprint(buf, sizeof buf, "%s!%d\n", remoteip, 0);
       readstr(r, buf);
       respond(r, nil);
}

static void
remoteread(Req *r, Client *c)
{
       char *s;
       char buf[128];

       s = c-&gt;connect;
       if(s == nil)
               s = "::!0";
       snprint(buf, sizeof buf, "%s\n", s);
       readstr(r, buf);
       respond(r, nil);
}

static void
statusread(Req *r, Client *c)
{
       char buf[64];
       char *s;

       snprint(buf, sizeof buf, "%s!%d", remoteip, 0);
       s = statestr[c-&gt;state];
       readstr(r, s);
       respond(r, nil);
}

static void
fsread(Req *r)
{
       char e[ERRMAX];
       ulong path;

       path = r-&gt;fid-&gt;qid.path;
       switch(TYPE(path)){
       default:
               snprint(e, sizeof e, "bug in fsread path=%lux", path);
               respond(r, e);
               break;

       case Qroot:
               dirread9p(r, rootgen, nil);
               respond(r, nil);
               break;

       case Qcs:
               csread(r);
               break;

       case Qtcp:
               dirread9p(r, tcpgen, nil);
               respond(r, nil);
               break;

       case Qn:
               dirread9p(r, clientgen, client[NUM(path)]);
               respond(r, nil);
               break;

       case Qctl:
               ctlread(r, client[NUM(path)]);
               break;

       case Qdata:
               dataread(r, client[NUM(path)]);
               break;

       case Qlocal:
               localread(r);
               break;

       case Qremote:
               remoteread(r, client[NUM(path)]);
               break;

       case Qstatus:
               statusread(r, client[NUM(path)]);
               break;
       }
}

static void
fswrite(Req *r)
{
       ulong path;
       char e[ERRMAX];

       path = r-&gt;fid-&gt;qid.path;
       switch(TYPE(path)){
       default:
               snprint(e, sizeof e, "bug in fswrite path=%lux", path);
               respond(r, e);
               break;

       case Qcs:
               cswrite(r);
               break;

       case Qctl:
               ctlwrite(r, client[NUM(path)]);
               break;

       case Qdata:
               datawrite(r, client[NUM(path)]);
               break;
       }
}

static void
fsopen(Req *r)
{
       static int need[4] = { 4, 2, 6, 1 };
       ulong path;
       int n;
       Tab *t;
       Cs *cs;

       /*
        * lib9p already handles the blatantly obvious.
        * we just have to enforce the permissions we have set.
        */
       path = r-&gt;fid-&gt;qid.path;
       t = &amp;tab[TYPE(path)];
       n = need[r-&gt;ifcall.mode&amp;3];
       if((n&amp;t-&gt;mode) != n){
               respond(r, "permission denied");
               return;
       }

       switch(TYPE(path)){
       case Qcs:
               cs = emalloc(sizeof(Cs));
               r-&gt;fid-&gt;aux = cs;
               respond(r, nil);
               break;
       case Qclone:
               n = newclient();
               path = PATH(Qctl, n);
               r-&gt;fid-&gt;qid.path = path;
               r-&gt;ofcall.qid.path = path;
               if(chatty9p)
                       fprint(2, "open clone =&gt; path=%lux\n", path);
               t = &amp;tab[Qctl];
               /* fall through */
       default:
               if(t-tab &gt;= Qn)
                       client[NUM(path)]-&gt;ref++;
               respond(r, nil);
               break;
       }
}

static void
fsflush(Req *r)
{
       int i;

       for(i=0; i&lt;nclient; i++)
               if(findreq(client[i], r-&gt;oldreq))
                       respond(r-&gt;oldreq, "interrupted");
       respond(r, nil);
}

static void
handlemsg(Msg *m)
{
       int chan, n;
       Client *c;

       switch(m-&gt;type){
       case SSH_MSG_DISCONNECT:
       case SSH_CMSG_EXIT_CONFIRMATION:
               sysfatal("disconnect");

       case SSH_CMSG_STDIN_DATA:
       case SSH_CMSG_EOF:
       case SSH_CMSG_WINDOW_SIZE:
               /* don't care */
               free(m);
               break;

       case SSH_MSG_CHANNEL_DATA:
               chan = getlong(m);
               n = getlong(m);
               if(m-&gt;rp+n != m-&gt;ep)
                       sysfatal("got bad channel data");
               if(chan&lt;nclient &amp;&amp; (c=client[chan])-&gt;state==Established){
                       queuemsg(c, m);
                       matchmsgs(c);
               }else
                       free(m);
               break;

       case SSH_MSG_CHANNEL_INPUT_EOF:
               chan = getlong(m);
               free(m);
               if(chan&lt;nclient){
                       c = client[chan];
                       chan = c-&gt;servernum;
                       c-&gt;eof = 1;
/*
                       hangupclient(c);
                       m = allocmsg(conn, SSH_MSG_CHANNEL_INPUT_EOF, 4);
                       putlong(m, chan);
                       sendmsg(m);
*/
                       m = allocmsg(conn, SSH_MSG_CHANNEL_OUTPUT_CLOSED, 4);
                       putlong(m, chan);
                       sendmsg(m);
               }
               break;

       case SSH_MSG_CHANNEL_OUTPUT_CLOSED:
               chan = getlong(m);
               if(chan&lt;nclient)
                       hangupclient(client[chan]);
               free(m);
               break;

       case SSH_MSG_CHANNEL_OPEN_CONFIRMATION:
               chan = getlong(m);
               c = nil;
               if(chan&gt;=nclient || (c=client[chan])-&gt;state != Dialing){
                       if(c)
                               fprint(2, "cstate %d\n", c-&gt;state);
                       sysfatal("got unexpected open confirmation for %d", chan);
               }
               c-&gt;servernum = getlong(m);
               c-&gt;state = Established;
               dialedclient(c);
               free(m);
               break;

       case SSH_MSG_CHANNEL_OPEN_FAILURE:
               chan = getlong(m);
               c = nil;
               if(chan&gt;=nclient || (c=client[chan])-&gt;state != Dialing)
                       sysfatal("got unexpected open failure");
               if(m-&gt;rp+4 &lt;= m-&gt;ep)
                       c-&gt;servernum = getlong(m);
               c-&gt;state = Closed;
               dialedclient(c);
               free(m);
               break;
       }
}

void
fsnetproc(void*)
{
       ulong path;
       Alt a[4];
       Cs *cs;
       Fid *fid;
       Req *r;
       Msg *m;

       threadsetname("fsthread");

       a[0].op = CHANRCV;
       a[0].c = fsclunkchan;
       a[0].v = &amp;fid;
       a[1].op = CHANRCV;
       a[1].c = fsreqchan;
       a[1].v = &amp;r;
       a[2].op = CHANRCV;
       a[2].c = sshmsgchan;
       a[2].v = &amp;m;
       a[3].op = CHANEND;

       for(;;){
               switch(alt(a)){
               case 0:
                       path = fid-&gt;qid.path;
                       switch(TYPE(path)){
                       case Qcs:
                               cs = fid-&gt;aux;
                               if(cs){
                                       free(cs-&gt;resp);
                                       free(cs);
                               }
                               break;
                       }
                       if(fid-&gt;omode != -1 &amp;&amp; TYPE(path) &gt;= Qn)
                               closeclient(client[NUM(path)]);
                       sendp(fsclunkwaitchan, nil);
                       break;
               case 1:
                       switch(r-&gt;ifcall.type){
                       case Tattach:
                               fsattach(r);
                               break;
                       case Topen:
                               fsopen(r);
                               break;
                       case Tread:
                               fsread(r);
                               break;
                       case Twrite:
                               fswrite(r);
                               break;
                       case Tstat:
                               fsstat(r);
                               break;
                       case Tflush:
                               fsflush(r);
                               break;
                       default:
                               respond(r, "bug in fsthread");
                               break;
                       }
                       sendp(fsreqwaitchan, 0);
                       break;
               case 2:
                       handlemsg(m);
                       break;
               }
       }
}

static void
fssend(Req *r)
{
       sendp(fsreqchan, r);
       recvp(fsreqwaitchan);   /* avoids need to deal with spurious flushes */
}

static void
fsdestroyfid(Fid *fid)
{
       sendp(fsclunkchan, fid);
       recvp(fsclunkwaitchan);
}

void
takedown(Srv*)
{
       threadexitsall("done");
}

Srv fs =
{
attach=         fssend,
destroyfid=     fsdestroyfid,
walk1=          fswalk1,
open=           fssend,
read=           fssend,
write=          fssend,
stat=           fssend,
flush=          fssend,
end=            takedown,
};

void
threadmain(int argc, char **argv)
{
       int i, fd;
       char *host, *user, *p, *service;
       char *f[16];
       Msg *m;
       static Conn c;

       fmtinstall('B', mpfmt);
       fmtinstall('H', encodefmt);

       mtpt = "/net";
       service = nil;
       user = nil;
       ARGBEGIN{
       case 'B':       /* undocumented, debugging */
               doabort = 1;
               break;
       case 'D':       /* undocumented, debugging */
               debuglevel = strtol(EARGF(usage()), nil, 0);
               break;
       case '9':       /* undocumented, debugging */
               chatty9p++;
               break;

       case 'A':
               authlist = EARGF(usage());
               break;
       case 'c':
               cipherlist = EARGF(usage());
               break;
       case 'm':
               mtpt = EARGF(usage());
               break;
       case 's':
               service = EARGF(usage());
               break;
       default:
               usage();
       }ARGEND

       if(argc != 1)
               usage();

       host = argv[0];

       if((p = strchr(host, '@')) != nil){
               *p++ = '\0';
               user = host;
               host = p;
       }
       if(user == nil)
               user = getenv("user");
       if(user == nil)
               sysfatal("cannot find user name");

       privatefactotum();

       if((fd = dial(netmkaddr(host, "tcp", "ssh"), nil, nil, nil)) &lt; 0)
               sysfatal("dialing %s: %r", host);

       c.interactive = isatty(0);
       c.fd[0] = c.fd[1] = fd;
       c.user = user;
       c.host = host;
       setaliases(&amp;c, host);

       c.nokcipher = getfields(cipherlist, f, nelem(f), 1, ", ");
       c.okcipher = emalloc(sizeof(Cipher*)*c.nokcipher);
       for(i=0; i&lt;c.nokcipher; i++)
               c.okcipher[i] = findcipher(f[i], allcipher, nelem(allcipher));

       c.nokauth = getfields(authlist, f, nelem(f), 1, ", ");
       c.okauth = emalloc(sizeof(Auth*)*c.nokauth);
       for(i=0; i&lt;c.nokauth; i++)
               c.okauth[i] = findauth(f[i], allauth, nelem(allauth));

       sshclienthandshake(&amp;c);

       requestpty(&amp;c);             /* turns on TCP_NODELAY on other side */
       m = allocmsg(&amp;c, SSH_CMSG_EXEC_SHELL, 0);
       sendmsg(m);

       time0 = time(0);
       sshmsgchan = chancreate(sizeof(Msg*), 16);
       fsreqchan = chancreate(sizeof(Req*), 0);
       fsreqwaitchan = chancreate(sizeof(void*), 0);
       fsclunkchan = chancreate(sizeof(Fid*), 0);
       fsclunkwaitchan = chancreate(sizeof(void*), 0);

       conn = &amp;c;
       procrfork(sshreadproc, &amp;c, 8192, RFNAMEG|RFNOTEG);
       procrfork(fsnetproc, nil, 8192, RFNAMEG|RFNOTEG);

       threadpostmountsrv(&amp;fs, service, mtpt, MREPL);
       exits(0);
}

<!-- BEGIN TAIL -->
</pre>
</td></tr></table>
</td></tr></table>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<p style="line-height: 1.2em; margin-left: 1.00in; text-indent: 0.00in; margin-right: 1.00in; margin-top: 0; margin-bottom: 0; text-align: center;">
<span style="font-size: 10pt"></span></p>
<p style="margin-top: 0; margin-bottom: 0.50in"></p>
<p style="margin-top: 0; margin-bottom: 0.33in"></p>
<center><table border="0"><tr>
<td valign="middle"><a href="http://www.alcatel-lucent.com/"><img border="0" src="/plan9/img/logo_ft.gif" alt="Bell Labs" />
</a></td>
<td valign="middle"><a href="http://www.opensource.org"><img border="0" alt="OSI certified" src="/plan9/img/osi-certified-60x50.gif" />
</a></td>
<td><img style="padding-right: 45px;" alt="Powered by Plan 9" src="/plan9/img/power36.gif" />
</td>
</tr></table></center>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<center>
<span style="font-size: 10pt">(<a href="/plan9/">Return to Plan 9 Home Page</a>)</span>
</center>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<center><font size=-1>
<span style="font-size: 10pt"><a href="http://www.lucent.com/copyright.html">Copyright</a></span>
<span style="font-size: 10pt">© 2009 Alcatel-Lucent.</span>
<span style="font-size: 10pt">All Rights Reserved.</span>
<br />
<span style="font-size: 10pt">Comments to</span>
<span style="font-size: 10pt"><a href="mailto:[email protected]">[email protected]</a>.</span>
</font></center>
</body>
</html>