/*
** @(#) isofs.c - (for RDP) ISO protocol
** @(#) $Id: isofs.c,v 1.1.1.1 2003/11/10 10:34:00 lucio Exp $
*/
/*
** ==================================================================
**
** $Logfile:$
** $RCSfile: isofs.c,v $
** $Revision: 1.1.1.1 $
** $Date: 2003/11/10 10:34:00 $
** $Author: lucio $
**
** ==================================================================
**
** $Log: isofs.c,v $
** Revision 1.1.1.1 2003/11/10 10:34:00 lucio
** ASN.1 developments.
**
** ==================================================================
*/
#include <u.h>
#include <libc.h>
#include <auth.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
#define NAMELEN (40)
#define MSGLEN (80)
typedef unsigned char uint8;
enum ISO_PDU_CODE
{
ISO_PDU_CR = 0xE0, /* Connection Request */
ISO_PDU_CC = 0xD0, /* Connection Confirm */
ISO_PDU_DR = 0x80, /* Disconnect Request */
ISO_PDU_DT = 0xF0, /* Data */
ISO_PDU_ER = 0x70 /* Error */
};
static char *ident = "@(#) $Id: isofs.c,v 1.1.1.1 2003/11/10 10:34:00 lucio Exp $";
static void
copyright (void)
{
print ("isofs: ISO network protocol for RDP\n");
print ("Copyright (C) 2003 Lucio De Re.\n");
}
static char *use[] = {
"usage: %s [-h|H] [-N] [-m mtpt] [-p srv]\n",
"\n",
"opts: -h|H: this message\n",
" -N: show copyright notice\n",
"\n",
" -m mtpt: mountpoint (default /net/iso)\n",
" -p srv: service name\n",
nil
};
static void
usage (char *argv0, char *use) {
fprint (2, use, argv0);
exits ("usage");
}
static void
help (char *argv0, char **use) {
print (*use++, argv0);
while (*use) {
print (*use++);
}
}
static void isoopen (Req *r);
static void isoread (Req *r);
static void isowrite (Req *r);
static void isoclose (Req *r);
static void isoterm (Srv *s);
static Tree *iso;
static char *EAgain = "Not available, may be transient";
static Srv isosrv = {
.open = isoopen,
.read = isoread,
.write = isowrite,
.remove = isoclose,
.end = isoterm,
};
enum qtype {
Qctl,
Qdata,
Qstats,
Qroot,
Qrclone,
Qrctl,
Qrstats,
Qdir,
};
typedef struct root root;
struct root {
int count;
char *owner;
File *root, *clone, *ctl, *stats;
};
typedef struct select select;
struct select {
int type; // file type identifier
select *dir; // pointer to directory
union {
struct { // stats, ctl
int ndata;
char *data;
};
struct { // dir
long inbytes;
long outbytes;
};
struct { // data
int fd; // channel
long len, lsz;
char *lbuf;
};
};
};
typedef struct pkthdr pkthdr;
struct pkthdr {
uint8 version;
uint8 reserved;
uint8 length[2];
uint8 hdrlen;
uint8 code;
};
#define HDRSZ (6)
typedef struct cmdpkt cmdpkt;
struct cmdpkt {
uint8 dst[2];
uint8 src[2];
uint8 class;
};
#define CMDSZ (5)
typedef struct dtapkt dtapkt;
struct dtapkt {
uint8 eot;
char data[1];
};
#define DATSZ (1)
static long
isocmd (int fd, uint8 op) {
pkthdr hdr;
cmdpkt cmd;
long count;
int len = HDRSZ + CMDSZ;
char buf[HDRSZ + CMDSZ];
hdr.version = 3;
hdr.reserved = 0;
hdr.length[0] = (len >> 8) & 0xFF;
hdr.length[1] = len & 0xFF;
hdr.hdrlen = 6;
hdr.code = op;
memcpy (buf, &hdr, HDRSZ);
cmd.dst[0] = cmd.dst[1] = 0;
cmd.src[0] = cmd.src[1] = 0;
cmd.class = 0;
memcpy (buf + HDRSZ, &cmd, CMDSZ);
count = write (fd, buf, len);
print ("isofs: %d of %ld bytes copied (%r)\n", len, count);
if (count == len)
return count;
else
return -1;
}
static long
isorsp (int fd, int exp)
{
char buf[HDRSZ];
pkthdr *hp = (pkthdr *) buf;
long len;
if ((len = read (fd, buf, HDRSZ)) != HDRSZ) {
print ("wrong read length: %ld (%r)\n", len);
return -1;
}
if (hp->version != 3) {
print ("wrong protocol version: %d\n", hp->version);
return -1;
}
len = (((hp->length[0] & 0xFF) << 8) | (hp->length[1] & 0xFF)) - HDRSZ;
if (len <= 0) {
print ("too little data: %ld", len);
return -1;
}
if (hp->code != exp || hp->hdrlen != HDRSZ) {
print ("wrong code or header length\n");
return -1;
}
return len;
}
static long
isoout (int fd, char *data, long count) {
pkthdr hdr;
dtapkt pkt;
long len = HDRSZ + DATSZ + count;
char *buf = emalloc9p (len);
hdr.version = 3;
hdr.reserved = 0;
hdr.length[0] = (len >> 8) & 0xFF;
hdr.length[1] = len & 0xFF;
hdr.hdrlen = 2;
hdr.code = ISO_PDU_DT;
memcpy (buf, &hdr, HDRSZ);
pkt.eot = 0x80;
memcpy (buf + HDRSZ, &pkt, DATSZ);
memcpy (buf + HDRSZ + DATSZ, data, count);
if (write (fd, buf, len) != len)
count = -1;
free (buf);
return count;
}
/* r->fid points to the pertinent Fid structure */
static void
isoopen (Req *r)
{
char ch[8], msg[MSGLEN];
root *rt = (root *)(isosrv.aux);
File *d, *f;
select *dd, *ff = (select *)(r->fid->file->aux);
switch (ff->type) {
case Qrclone: // root clone
snprint (ch, 8, "%d", rt->count++);
d = createfile (rt->root, ch, rt->owner, DMDIR|0775, nil);
if (!d) {
--rt->count;
respond (r, "Can't create new channel");
return;
}
d->aux = emalloc9p (sizeof (select));
ff = (select *)(d->aux);
ff->type = Qdir;
ff->dir = nil;
ff->inbytes = ff->outbytes = 0;
f = createfile (d, "ctl", rt->owner, 0664, nil);
f->aux = emalloc9p (sizeof (select));
ff = (select *)(f->aux);
ff->type = Qctl;
ff->dir = (select *)(d->aux);
ff->data = estrdup9p (ch);
ff->ndata = strlen (ff->data);
r->fid->file = f;
r->fid->file->length = ff->ndata;
r->ofcall.qid = f->qid;
f = createfile (d, "data", rt->owner, 0664, nil);
f->aux = emalloc9p (sizeof (select));
((select *)(f->aux))->type = Qdata;
((select *)(f->aux))->dir = (select *)(d->aux);
f = createfile (d, "stats", rt->owner, 0444, nil);
f->aux = emalloc9p (sizeof (select));
ff->dir = (select *)(d->aux);
ff = (select *)(f->aux);
ff->type = Qstats;
ff->dir = (select *)(d->aux);
break;
case Qrctl: // root control
case Qrstats: // root stats
case Qctl: // channel control
case Qdata: // channel data
break;
case Qstats: // channel stats
dd = ff->dir;
if (ff->data)
free (ff->data);
snprint (msg, MSGLEN, "Bytes in: %ld\tout: %ld\n", dd->inbytes, dd->outbytes);
ff->data = estrdup9p (msg);
r->fid->file->length = ff->ndata = strlen (ff->data);
break;
case Qroot: // root node
case Qdir: // channel dir node
break;
default:
snprint (msg, MSGLEN, "File of undefined type: %d", ff->type);
respond (r, msg);
return;
}
respond (r, nil);
return;
}
static void
isoread (Req *r)
{
vlong offset = r->ifcall.offset;
long count = r->ifcall.count;
char msg[MSGLEN];
select *ff = (select *)(r->fid->file->aux);
select *df = ff->dir;
int fd = df->fd;
print ("isofs: read %ld %lld\n", count, offset);
switch (ff->type) {
case Qrclone: // root clone
break;
case Qrctl: // root control
case Qrstats: // root stats
break;
case Qctl: // channel control
case Qstats: // channel stats
if (offset >= ff->ndata) {
r->ofcall.count = 0;
respond (r, nil);
return;
}
if (offset + count >= ff->ndata)
count = ff->ndata - offset;
memmove (r->ofcall.data, ff->data + offset, count);
r->ofcall.count = count;
break;
case Qdata: // channel data
print ("FF(1): len = %ld - lsz = %ld\n", df->len, df->lsz);
if (df->len == 0) {
if ((df->len = isorsp (fd, ISO_PDU_DT)) <= 0) {
print ("out of sync\n");
df->len = 0;
respond (r, "read out of sync");
return;
}
while (df->len > df->lsz) {
print ("buffer too small\n");
df->lsz *= 2;
df->lbuf = erealloc9p (df->lbuf, df->lsz);
}
if (read (fd, df->lbuf, df->len) != df->len) {
print ("wrong length on read\n");
respond (r, "read error");
return;
}
}
print ("FF(2): len = %ld - lsz = %ld\n", df->len, df->lsz);
if (df->len < count)
count = df->len;
memcpy (r->ofcall.data, df->lbuf, count);
if ((df->len -= count) > 0)
memmove (df->lbuf, df->lbuf + count, df->len);
r->ofcall.count = count;
ff->inbytes += count;
break;
default:
snprint (msg, MSGLEN, "File of undefined type: %d", ff->type);
respond (r, msg);
return;
}
respond (r, nil);
return;
}
static void
isowrite (Req *r)
{
vlong offset = r->ifcall.offset;
long count = r->ifcall.count;
select *df, *ff = (select *)(r->fid->file->aux);
int fd, n;
char *argv[2];
char msg[MSGLEN];
print ("isofs: write %ld %lld\n", count, offset);
switch (ff->type) {
case Qctl:
/*
use /srv/svc
close
*/
n = tokenize (r->ifcall.data, argv, 2);
if (strncmp (argv[0], "use", 3) == 0) {
print ("isofs: cmd = '%s', arg = '%s'\n", argv[0], argv[1]);
if (n != 2 || (fd = open (argv[1], ORDWR)) < 0) {
snprint (msg, MSGLEN, "Can't use service: %r");
respond (r, msg);
return;
} else {
df = ff->dir;
ff->fd = df->fd = fd;
if (fd < 0 || isocmd (fd, ISO_PDU_CR) < 0) {
respond (r, EAgain);
return;
}
if (isorsp (fd, ISO_PDU_CC) < 0) {
respond (r, "Connection not established");
return;
}
df->lbuf = emalloc9p (df->lsz = 256);
read (fd, df->lbuf, df->lsz);
df->len = 0;
}
} else if (strncmp (argv[0], "close", 5) == 0) {
print ("isofs: cmd = '%s'\n", argv[0]);
df = ff->dir;
fd = df->fd;
if (fd >= 0 && isocmd (fd, ISO_PDU_DR) < 0) {
snprint (msg, MSGLEN, "Problem - %d", fd);
respond (r, msg);
return;
}
df->fd = -1;
} else {
respond (r, "Undefined control");
return;
}
r->ofcall.count = count;
// r->ofcall.offset = offset + count;
// print ("isofs: written %ld %lld\n", count, offset);
break;
case Qdata:
df = ff->dir;
fd = df->fd;
if ((count = isoout (fd, r->ifcall.data, count)) < 0) {
respond (r, EAgain);
return;
}
r->ofcall.count = count;
ff->outbytes += count;
print ("isofs: written %ld %lld\n", count, offset);
break;
case Qrctl:
break;
case Qrclone:
case Qroot:
case Qstats:
case Qrstats:
case Qdir:
respond (r, "not writable");
return;
default:
respond (r, "write not yet implemented");
return;
}
respond (r, nil);
return;
}
static void
isoclose (Req *r)
{
select *df, *ff = (select *)(r->fid->file->aux);
int fd;
switch (ff->type) {
case Qdata:
df = ff->dir;
fd = df->fd;
if (fd < 0) {
respond (r, nil);
return;
} else if (isocmd (fd, ISO_PDU_DR) < 0) {
df->fd = -1;
respond (r, EAgain);
return;
}
break;
default:
respond (r, "close not implemented");
return;
}
respond (r, nil);
return;
}
static void
isoterm (Srv *srv)
{
return;
}
static void isodest (File *f)
{
return;
}
void
main(int argc, char **argv)
{
char *u, user[NAMELEN];
char *srvpt = nil;
char *mtpt = "/net";
root *r;
u = getuser ();
if(u == nil)
u = "none";
strncpy (user, u, NAMELEN - 1);
user[NAMELEN - 1] = 0;
ARGBEGIN {
case 'm':
mtpt = EARGF (usage (argv0, use[0]));
break;
case 'p':
srvpt = EARGF (usage (argv0, use[0]));
break;
case 'v':
chatty9p++;
break;
case 'h':
case 'H':
case '?':
help (argv0, use);
exits (0);
default:
usage (argv0, use[0]);
} ARGEND;
if (argc != 0)
usage (argv0, use[0]);
iso = alloctree (user, user, DMDIR|0775, isodest);
iso->root->aux = emalloc9p (sizeof (select));
((select *)(iso->root->aux))->type = Qroot;
isosrv.tree = iso;
isosrv.aux = emalloc9p (sizeof (root));
r = (root *)(isosrv.aux);
r->owner = estrdup9p (user);
r->count = 0;
r->root = createfile (iso->root, "iso", user, DMDIR|0775, nil);
r->root->aux = emalloc9p (sizeof (select));
((select *)(r->root->aux))->type = Qdir;
r->clone = createfile (r->root, "clone", user, 0666, nil);
r->clone->aux = emalloc9p (sizeof (select));
((select *)(r->clone->aux))->type = Qrclone;
r->ctl = createfile (r->root, "ctl", user, 0666, nil);
r->ctl->aux = emalloc9p (sizeof (select));
((select *)(r->ctl->aux))->type = Qrctl;
r->stats = createfile (r->root, "stats", user, 0444, nil);
r->stats->aux = emalloc9p (sizeof (select));
((select *)(r->stats->aux))->type = Qrstats;
postmountsrv (&isosrv, srvpt, mtpt, MAFTER);
exits (0);
}
|