diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/9nfs/server.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/9nfs/server.c')
-rwxr-xr-x | sys/src/cmd/9nfs/server.c | 618 |
1 files changed, 618 insertions, 0 deletions
diff --git a/sys/src/cmd/9nfs/server.c b/sys/src/cmd/9nfs/server.c new file mode 100755 index 000000000..fe70f1036 --- /dev/null +++ b/sys/src/cmd/9nfs/server.c @@ -0,0 +1,618 @@ +#include "all.h" +#include <ndb.h> + +static int alarmflag; + +static int Iconv(Fmt*); +static void openudp(int); +static void cachereply(Rpccall*, void*, int); +static int replycache(int, Rpccall*, long (*)(int, void*, long)); +static void udpserver(int, Progmap*); +static void tcpserver(int, Progmap*); +static void getendpoints(Udphdr*, char*); +static long readtcp(int, void*, long); +static long writetcp(int, void*, long); +static int servemsg(int, long (*)(int, void*, long), long (*)(int, void*, long), + int, Progmap*); +void (*rpcalarm)(void); +int rpcdebug; +int rejectall; +int p9debug; + +int nocache; + +uchar buf[9000]; +uchar rbuf[9000]; +uchar resultbuf[9000]; + +static int tcp; + +char *commonopts = "[-9CDrtv]"; /* for usage() messages */ + +/* + * this recognises common, nominally rcp-related options. + * they may not take arguments. + */ +int +argopt(int c) +{ + switch(c){ + case '9': + ++p9debug; + return 0; + case 'C': + ++nocache; + return 0; + case 'D': + ++rpcdebug; + return 0; + case 'r': + ++rejectall; + return 0; + case 't': + tcp = 1; + return 0; + case 'v': + ++chatty; + return 0; + default: + return -1; + } +} + +/* + * all option parsing is now done in (*pg->init)(), which can call back + * here to argopt for common options. + */ +void +server(int argc, char **argv, int myport, Progmap *progmap) +{ + Progmap *pg; + + fmtinstall('I', Iconv); + fmtinstall('F', fcallfmt); + fmtinstall('D', dirfmt); + + switch(rfork(RFNOWAIT|RFENVG|RFNAMEG|RFNOTEG|RFFDG|RFPROC)){ + case -1: + panic("fork"); + default: + _exits(0); + case 0: + break; + } + + switch(rfork(RFMEM|RFPROC)){ + case 0: + for(;;){ + sleep(30*1000); + alarmflag = 1; + } + case -1: + sysfatal("rfork: %r"); + } + + for(pg=progmap; pg->init; pg++) + (*pg->init)(argc, argv); + if(tcp) + tcpserver(myport, progmap); + else + udpserver(myport, progmap); +} + +static void +udpserver(int myport, Progmap *progmap) +{ + char service[128]; + char data[128]; + char devdir[40]; + int ctlfd, datafd; + + snprint(service, sizeof service, "udp!*!%d", myport); + ctlfd = announce(service, devdir); + if(ctlfd < 0) + panic("can't announce %s: %r\n", service); + if(fprint(ctlfd, "headers") < 0) + panic("can't set header mode: %r\n"); + + snprint(data, sizeof data, "%s/data", devdir); + datafd = open(data, ORDWR); + if(datafd < 0) + panic("can't open udp data: %r\n"); + close(ctlfd); + + chatsrv(0); + clog("%s: listening to port %d\n", argv0, myport); + while (servemsg(datafd, read, write, myport, progmap) >= 0) + continue; + exits(0); +} + +static void +tcpserver(int myport, Progmap *progmap) +{ + char adir[40]; + char ldir[40]; + char ds[40]; + int actl, lctl, data; + + snprint(ds, sizeof ds, "tcp!*!%d", myport); + chatsrv(0); + actl = -1; + for(;;){ + if(actl < 0){ + actl = announce(ds, adir); + if(actl < 0){ + clog("%s: listening to tcp port %d\n", + argv0, myport); + clog("announcing: %r"); + break; + } + } + lctl = listen(adir, ldir); + if(lctl < 0){ + close(actl); + actl = -1; + continue; + } + switch(fork()){ + case -1: + clog("%s!%d: %r\n", argv0, myport); + /* fall through */ + default: + close(lctl); + continue; + case 0: + close(actl); + data = accept(lctl, ldir); + close(lctl); + if(data < 0) + exits(0); + + /* pretend it's udp; fill in Udphdr */ + getendpoints((Udphdr*)buf, ldir); + + while (servemsg(data, readtcp, writetcp, myport, + progmap) >= 0) + continue; + close(data); + exits(0); + } + } + exits(0); +} + +static int +servemsg(int fd, long (*readmsg)(int, void*, long), long (*writemsg)(int, void*, long), + int myport, Progmap * progmap) +{ + int i, n, nreply; + Rpccall rcall, rreply; + int vlo, vhi; + Progmap *pg; + Procmap *pp; + char errbuf[ERRMAX]; + + if(alarmflag){ + alarmflag = 0; + if(rpcalarm) + (*rpcalarm)(); + } + n = (*readmsg)(fd, buf, sizeof buf); + if(n < 0){ + errstr(errbuf, sizeof errbuf); + if(strcmp(errbuf, "interrupted") == 0) + return 0; + clog("port %d: error: %s\n", myport, errbuf); + return -1; + } + if(n == 0){ + clog("port %d: EOF\n", myport); + return -1; + } + if(rpcdebug == 1) + fprint(2, "%s: rpc from %d.%d.%d.%d/%d\n", + argv0, buf[12], buf[13], buf[14], buf[15], + (buf[32]<<8)|buf[33]); + i = rpcM2S(buf, &rcall, n); + if(i != 0){ + clog("udp port %d: message format error %d\n", + myport, i); + return 0; + } + if(rpcdebug > 1) + rpcprint(2, &rcall); + if(rcall.mtype != CALL) + return 0; + if(replycache(fd, &rcall, writemsg)) + return 0; + nreply = 0; + rreply.host = rcall.host; + rreply.port = rcall.port; + rreply.lhost = rcall.lhost; + rreply.lport = rcall.lport; + rreply.xid = rcall.xid; + rreply.mtype = REPLY; + if(rcall.rpcvers != 2){ + rreply.stat = MSG_DENIED; + rreply.rstat = RPC_MISMATCH; + rreply.rlow = 2; + rreply.rhigh = 2; + goto send_reply; + } + if(rejectall){ + rreply.stat = MSG_DENIED; + rreply.rstat = AUTH_ERROR; + rreply.authstat = AUTH_TOOWEAK; + goto send_reply; + } + i = n - (((uchar *)rcall.args) - buf); + if(rpcdebug > 1) + fprint(2, "arg size = %d\n", i); + rreply.stat = MSG_ACCEPTED; + rreply.averf.flavor = 0; + rreply.averf.count = 0; + rreply.results = resultbuf; + vlo = 0x7fffffff; + vhi = -1; + for(pg=progmap; pg->pmap; pg++){ + if(pg->progno != rcall.prog) + continue; + if(pg->vers == rcall.vers) + break; + if(pg->vers < vlo) + vlo = pg->vers; + if(pg->vers > vhi) + vhi = pg->vers; + } + if(pg->pmap == 0){ + if(vhi < 0) + rreply.astat = PROG_UNAVAIL; + else{ + rreply.astat = PROG_MISMATCH; + rreply.plow = vlo; + rreply.phigh = vhi; + } + goto send_reply; + } + for(pp = pg->pmap; pp->procp; pp++) + if(rcall.proc == pp->procno){ + if(rpcdebug > 1) + fprint(2, "process %d\n", pp->procno); + rreply.astat = SUCCESS; + nreply = (*pp->procp)(i, &rcall, &rreply); + goto send_reply; + } + rreply.astat = PROC_UNAVAIL; +send_reply: + if(nreply >= 0){ + i = rpcS2M(&rreply, nreply, rbuf); + if(rpcdebug > 1) + rpcprint(2, &rreply); + (*writemsg)(fd, rbuf, i); + cachereply(&rreply, rbuf, i); + } + return 0; +} + +static void +getendpoint(char *dir, char *file, uchar *addr, uchar *port) +{ + int fd, n; + char buf[128]; + char *sys, *serv; + + sys = serv = 0; + + snprint(buf, sizeof buf, "%s/%s", dir, file); + fd = open(buf, OREAD); + if(fd >= 0){ + n = read(fd, buf, sizeof(buf)-1); + if(n>0){ + buf[n-1] = 0; + serv = strchr(buf, '!'); + if(serv){ + *serv++ = 0; + serv = strdup(serv); + } + sys = strdup(buf); + } + close(fd); + } + if(serv == 0) + serv = strdup("unknown"); + if(sys == 0) + sys = strdup("unknown"); + parseip(addr, sys); + n = atoi(serv); + hnputs(port, n); +} + +/* set Udphdr values from protocol dir local & remote files */ +static void +getendpoints(Udphdr *ep, char *dir) +{ + getendpoint(dir, "local", ep->laddr, ep->lport); + getendpoint(dir, "remote", ep->raddr, ep->rport); +} + +static long +readtcp(int fd, void *vbuf, long blen) +{ + uchar mk[4]; + int n, m, sofar; + ulong done; + char *buf; + + buf = vbuf; + buf += Udphdrsize; + blen -= Udphdrsize; + + done = 0; + for(sofar = 0; !done; sofar += n){ + m = readn(fd, mk, 4); + if(m < 4) + return 0; + done = (mk[0]<<24)|(mk[1]<<16)|(mk[2]<<8)|mk[3]; + m = done & 0x7fffffff; + done &= 0x80000000; + if(m > blen-sofar) + return -1; + n = readn(fd, buf+sofar, m); + if(m != n) + return 0; + } + return sofar + Udphdrsize; +} + +static long +writetcp(int fd, void *vbuf, long len) +{ + char *buf; + + buf = vbuf; + buf += Udphdrsize; + len -= Udphdrsize; + + buf -= 4; + buf[0] = 0x80 | (len>>24); + buf[1] = len>>16; + buf[2] = len>>8; + buf[3] = len; + len += 4; + return write(fd, buf, len); +} +/* + *long + *niwrite(int fd, void *buf, long count) + *{ + * char errbuf[ERRLEN]; + * long n; + * + * for(;;){ + * n = write(fd, buf, count); + * if(n < 0){ + * errstr(errbuf); + * if(strcmp(errbuf, "interrupted") == 0) + * continue; + * clog("niwrite error: %s\n", errbuf); + * werrstr(errbuf); + * } + * break; + * } + * return n; + *} + */ +long +niwrite(int fd, void *buf, long n) +{ +// int savalarm; + +// savalarm = alarm(0); + n = write(fd, buf, n); +// if(savalarm > 0) +// alarm(savalarm); + return n; +} + +typedef struct Namecache Namecache; +struct Namecache { + char dom[256]; + ulong ipaddr; + Namecache *next; +}; + +Namecache *dnscache; + +static Namecache* +domlookupl(void *name, int len) +{ + Namecache *n, **ln; + + if(len >= sizeof(n->dom)) + return nil; + + for(ln=&dnscache, n=*ln; n; ln=&(*ln)->next, n=*ln) { + if(strncmp(n->dom, name, len) == 0 && n->dom[len] == 0) { + *ln = n->next; + n->next = dnscache; + dnscache = n; + return n; + } + } + return nil; +} + +static Namecache* +domlookup(void *name) +{ + return domlookupl(name, strlen(name)); +} + +static Namecache* +iplookup(ulong ip) +{ + Namecache *n, **ln; + + for(ln=&dnscache, n=*ln; n; ln=&(*ln)->next, n=*ln) { + if(n->ipaddr == ip) { + *ln = n->next; + n->next = dnscache; + dnscache = n; + return n; + } + } + return nil; +} + +static Namecache* +addcacheentry(void *name, int len, ulong ip) +{ + Namecache *n; + + if(len >= sizeof(n->dom)) + return nil; + + n = malloc(sizeof(*n)); + if(n == nil) + return nil; + strncpy(n->dom, name, len); + n->dom[len] = 0; + n->ipaddr = ip; + n->next = dnscache; + dnscache = n; + return nil; +} + +int +getdnsdom(ulong ip, char *name, int len) +{ + char buf[128]; + Namecache *nc; + char *p; + + if(nc=iplookup(ip)) { + strncpy(name, nc->dom, len); + name[len-1] = 0; + return 0; + } + clog("getdnsdom: %I\n", ip); + snprint(buf, sizeof buf, "%I", ip); + p = csgetvalue("/net", "ip", buf, "dom", nil); + if(p == nil) + return -1; + strncpy(name, p, len-1); + name[len] = 0; + free(p); + addcacheentry(name, strlen(name), ip); + return 0; +} + +int +getdom(ulong ip, char *dom, int len) +{ + int i; + static char *prefix[] = { "", "gate-", "fddi-", "u-", 0 }; + char **pr; + + if(getdnsdom(ip, dom, len)<0) + return -1; + + for(pr=prefix; *pr; pr++){ + i = strlen(*pr); + if(strncmp(dom, *pr, i) == 0) { + memmove(dom, dom+i, len-i); + break; + } + } + return 0; +} + +#define MAXCACHE 64 + +static Rpccache *head, *tail; +static int ncache; + +static void +cachereply(Rpccall *rp, void *buf, int len) +{ + Rpccache *cp; + + if(nocache) + return; + + if(ncache >= MAXCACHE){ + if(rpcdebug) + fprint(2, "%s: drop %I/%ld, xid %uld, len %d\n", + argv0, tail->host, + tail->port, tail->xid, tail->n); + tail = tail->prev; + free(tail->next); + tail->next = 0; + --ncache; + } + cp = malloc(sizeof(Rpccache)+len-4); + if(cp == 0){ + clog("cachereply: malloc %d failed\n", len); + return; + } + ++ncache; + cp->prev = 0; + cp->next = head; + if(head) + head->prev = cp; + else + tail = cp; + head = cp; + cp->host = rp->host; + cp->port = rp->port; + cp->xid = rp->xid; + cp->n = len; + memmove(cp->data, buf, len); + if(rpcdebug) + fprint(2, "%s: cache %I/%ld, xid %uld, len %d\n", + argv0, cp->host, cp->port, cp->xid, cp->n); +} + +static int +replycache(int fd, Rpccall *rp, long (*writemsg)(int, void*, long)) +{ + Rpccache *cp; + + for(cp=head; cp; cp=cp->next) + if(cp->host == rp->host && + cp->port == rp->port && + cp->xid == rp->xid) + break; + if(cp == 0) + return 0; + if(cp->prev){ /* move to front */ + cp->prev->next = cp->next; + if(cp->next) + cp->next->prev = cp->prev; + else + tail = cp->prev; + cp->prev = 0; + cp->next = head; + head->prev = cp; + head = cp; + } + (*writemsg)(fd, cp->data, cp->n); + if(rpcdebug) + fprint(2, "%s: reply %I/%ld, xid %uld, len %d\n", + argv0, cp->host, cp->port, cp->xid, cp->n); + return 1; +} + +static int +Iconv(Fmt *f) +{ + char buf[16]; + ulong h; + + h = va_arg(f->args, ulong); + snprint(buf, sizeof buf, "%ld.%ld.%ld.%ld", + (h>>24)&0xff, (h>>16)&0xff, + (h>>8)&0xff, h&0xff); + return fmtstrcpy(f, buf); +} |