#include "u.h" #include "lib.h" #include "dat.h" #include "fns.h" #include "error.h" #include "devip.h" void hnputl(void *p, unsigned long v); void hnputs(void *p, unsigned short v); unsigned long nhgetl(void *p); unsigned short nhgets(void *p); unsigned long parseip(char *to, char *from); void csclose(Chan*); long csread(Chan*, void*, long, vlong); long cswrite(Chan*, void*, long, vlong); void osipinit(void); enum { Qtopdir = 1, /* top level directory */ Qcs, Qprotodir, /* directory for a protocol */ Qclonus, Qconvdir, /* directory for a conversation */ Qdata, Qctl, Qstatus, Qremote, Qlocal, Qlisten, MAXPROTO = 4 }; #define TYPE(x) ((int)((x).path & 0xf)) #define CONV(x) ((int)(((x).path >> 4)&0xfff)) #define PROTO(x) ((int)(((x).path >> 16)&0xff)) #define QID(p, c, y) (((p)<<16) | ((c)<<4) | (y)) typedef struct Proto Proto; typedef struct Conv Conv; struct Conv { int x; Ref r; int sfd; int perm; char owner[KNAMELEN]; char* state; ulong laddr; ushort lport; ulong raddr; ushort rport; int restricted; char cerr[KNAMELEN]; Proto* p; }; struct Proto { Lock l; int x; int stype; char name[KNAMELEN]; int nc; int maxconv; Conv** conv; Qid qid; }; static int np; static Proto proto[MAXPROTO]; int eipfmt(Fmt*); static Conv* protoclone(Proto*, char*, int); static void setladdr(Conv*); int ipgen(Chan *c, char *nname, Dirtab *d, int nd, int s, Dir *dp) { Qid q; Conv *cv; char *p; USED(nname); q.vers = 0; q.type = 0; switch(TYPE(c->qid)) { case Qtopdir: if(s >= 1+np) return -1; if(s == 0){ q.path = QID(s, 0, Qcs); devdir(c, q, "cs", 0, "network", 0666, dp); }else{ s--; q.path = QID(s, 0, Qprotodir); q.type = QTDIR; devdir(c, q, proto[s].name, 0, "network", DMDIR|0555, dp); } return 1; case Qprotodir: if(s < proto[PROTO(c->qid)].nc) { cv = proto[PROTO(c->qid)].conv[s]; sprint(up->genbuf, "%d", s); q.path = QID(PROTO(c->qid), s, Qconvdir); q.type = QTDIR; devdir(c, q, up->genbuf, 0, cv->owner, DMDIR|0555, dp); return 1; } s -= proto[PROTO(c->qid)].nc; switch(s) { default: return -1; case 0: p = "clone"; q.path = QID(PROTO(c->qid), 0, Qclonus); break; } devdir(c, q, p, 0, "network", 0555, dp); return 1; case Qconvdir: cv = proto[PROTO(c->qid)].conv[CONV(c->qid)]; switch(s) { default: return -1; case 0: q.path = QID(PROTO(c->qid), CONV(c->qid), Qdata); devdir(c, q, "data", 0, cv->owner, cv->perm, dp); return 1; case 1: q.path = QID(PROTO(c->qid), CONV(c->qid), Qctl); devdir(c, q, "ctl", 0, cv->owner, cv->perm, dp); return 1; case 2: p = "status"; q.path = QID(PROTO(c->qid), CONV(c->qid), Qstatus); break; case 3: p = "remote"; q.path = QID(PROTO(c->qid), CONV(c->qid), Qremote); break; case 4: p = "local"; q.path = QID(PROTO(c->qid), CONV(c->qid), Qlocal); break; case 5: p = "listen"; q.path = QID(PROTO(c->qid), CONV(c->qid), Qlisten); break; } devdir(c, q, p, 0, cv->owner, 0444, dp); return 1; } return -1; } static void newproto(char *name, int type, int maxconv) { int l; Proto *p; if(np >= MAXPROTO) { print("no %s: increase MAXPROTO", name); return; } p = &proto[np]; strcpy(p->name, name); p->stype = type; p->qid.path = QID(np, 0, Qprotodir); p->qid.type = QTDIR; p->x = np++; p->maxconv = maxconv; l = sizeof(Conv*)*(p->maxconv+1); p->conv = mallocz(l, 1); if(p->conv == 0) panic("no memory"); } void ipinit(void) { osipinit(); newproto("udp", S_UDP, 10); newproto("tcp", S_TCP, 30); fmtinstall('I', eipfmt); fmtinstall('E', eipfmt); } Chan * ipattach(char *spec) { Chan *c; c = devattach('I', spec); c->qid.path = QID(0, 0, Qtopdir); c->qid.type = QTDIR; c->qid.vers = 0; return c; } static Walkqid* ipwalk(Chan *c, Chan *nc, char **name, int nname) { return devwalk(c, nc, name, nname, 0, 0, ipgen); } int ipstat(Chan *c, uchar *dp, int n) { return devstat(c, dp, n, 0, 0, ipgen); } Chan * ipopen(Chan *c, int omode) { Proto *p; ulong raddr; ushort rport; int perm, sfd; Conv *cv, *lcv; omode &= 3; switch(omode) { case OREAD: perm = 4; break; case OWRITE: perm = 2; break; case ORDWR: perm = 6; break; } switch(TYPE(c->qid)) { default: break; case Qtopdir: case Qprotodir: case Qconvdir: case Qstatus: case Qremote: case Qlocal: if(omode != OREAD) error(Eperm); break; case Qclonus: p = &proto[PROTO(c->qid)]; cv = protoclone(p, up->user, -1); if(cv == 0) error(Enodev); c->qid.path = QID(p->x, cv->x, Qctl); c->qid.vers = 0; break; case Qdata: case Qctl: p = &proto[PROTO(c->qid)]; lock(&p->l); cv = p->conv[CONV(c->qid)]; lock(&cv->r.lk); if((perm & (cv->perm>>6)) != perm) { if(strcmp(up->user, cv->owner) != 0 || (perm & cv->perm) != perm) { unlock(&cv->r.lk); unlock(&p->l); error(Eperm); } } cv->r.ref++; if(cv->r.ref == 1) { memmove(cv->owner, up->user, KNAMELEN); cv->perm = 0660; } unlock(&cv->r.lk); unlock(&p->l); break; case Qlisten: p = &proto[PROTO(c->qid)]; lcv = p->conv[CONV(c->qid)]; sfd = so_accept(lcv->sfd, &raddr, &rport); cv = protoclone(p, up->user, sfd); if(cv == 0) { close(sfd); error(Enodev); } cv->raddr = raddr; cv->rport = rport; setladdr(cv); cv->state = "Established"; c->qid.path = QID(p->x, cv->x, Qctl); break; } c->mode = openmode(omode); c->flag |= COPEN; c->offset = 0; return c; } void ipclose(Chan *c) { Conv *cc; switch(TYPE(c->qid)) { case Qcs: csclose(c); break; case Qdata: case Qctl: if((c->flag & COPEN) == 0) break; cc = proto[PROTO(c->qid)].conv[CONV(c->qid)]; if(decref(&cc->r) != 0) break; strcpy(cc->owner, "network"); cc->perm = 0666; cc->state = "Closed"; cc->laddr = 0; cc->raddr = 0; cc->lport = 0; cc->rport = 0; close(cc->sfd); break; } } long ipread(Chan *ch, void *a, long n, vlong offset) { int r; Conv *c; Proto *x; uchar ip[4]; char buf[128], *p; /*print("ipread %s %lux\n", c2name(ch), (long)ch->qid.path);*/ p = a; switch(TYPE(ch->qid)) { default: error(Eperm); case Qcs: return csread(ch, a, n, offset); case Qprotodir: case Qtopdir: case Qconvdir: return devdirread(ch, a, n, 0, 0, ipgen); case Qctl: sprint(buf, "%d", CONV(ch->qid)); return readstr(offset, p, n, buf); case Qremote: c = proto[PROTO(ch->qid)].conv[CONV(ch->qid)]; hnputl(ip, c->raddr); sprint(buf, "%I!%d\n", ip, c->rport); return readstr(offset, p, n, buf); case Qlocal: c = proto[PROTO(ch->qid)].conv[CONV(ch->qid)]; hnputl(ip, c->laddr); sprint(buf, "%I!%d\n", ip, c->lport); return readstr(offset, p, n, buf); case Qstatus: x = &proto[PROTO(ch->qid)]; c = x->conv[CONV(ch->qid)]; sprint(buf, "%s/%d %d %s \n", c->p->name, c->x, c->r.ref, c->state); return readstr(offset, p, n, buf); case Qdata: c = proto[PROTO(ch->qid)].conv[CONV(ch->qid)]; r = so_recv(c->sfd, a, n, 0); if(r < 0){ oserrstr(); nexterror(); } return r; } } static void setladdr(Conv *c) { so_getsockname(c->sfd, &c->laddr, &c->lport); } static void setlport(Conv *c) { if(c->restricted == 0 && c->lport == 0) return; so_bind(c->sfd, c->restricted, c->lport); } static void setladdrport(Conv *c, char *str) { char *p, addr[4]; p = strchr(str, '!'); if(p == 0) { p = str; c->laddr = 0; } else { *p++ = 0; parseip(addr, str); c->laddr = nhgetl((uchar*)addr); } if(*p == '*') c->lport = 0; else c->lport = atoi(p); setlport(c); } static char* setraddrport(Conv *c, char *str) { char *p, addr[4]; p = strchr(str, '!'); if(p == 0) return "malformed address"; *p++ = 0; parseip(addr, str); c->raddr = nhgetl((uchar*)addr); c->rport = atoi(p); p = strchr(p, '!'); if(p) { if(strcmp(p, "!r") == 0) c->restricted = 1; } return 0; } long ipwrite(Chan *ch, void *a, long n, vlong offset) { Conv *c; Proto *x; int r, nf; char *p, *fields[3], buf[128]; switch(TYPE(ch->qid)) { default: error(Eperm); case Qcs: return cswrite(ch, a, n, offset); case Qctl: x = &proto[PROTO(ch->qid)]; c = x->conv[CONV(ch->qid)]; if(n > sizeof(buf)-1) n = sizeof(buf)-1; memmove(buf, a, n); buf[n] = '\0'; nf = tokenize(buf, fields, 3); if(strcmp(fields[0], "connect") == 0){ switch(nf) { default: error("bad args to connect"); case 2: p = setraddrport(c, fields[1]); if(p != 0) error(p); break; case 3: p = setraddrport(c, fields[1]); if(p != 0) error(p); c->lport = atoi(fields[2]); setlport(c); break; } so_connect(c->sfd, c->raddr, c->rport); setladdr(c); c->state = "Established"; return n; } if(strcmp(fields[0], "announce") == 0) { switch(nf){ default: error("bad args to announce"); case 2: setladdrport(c, fields[1]); break; } so_listen(c->sfd); c->state = "Announced"; return n; } if(strcmp(fields[0], "bind") == 0){ switch(nf){ default: error("bad args to bind"); case 2: c->lport = atoi(fields[1]); break; } setlport(c); return n; } error("bad control message"); case Qdata: x = &proto[PROTO(ch->qid)]; c = x->conv[CONV(ch->qid)]; r = so_send(c->sfd, a, n, 0); if(r < 0){ oserrstr(); nexterror(); } return r; } return n; } static Conv* protoclone(Proto *p, char *user, int nfd) { Conv *c, **pp, **ep; c = 0; lock(&p->l); if(waserror()) { unlock(&p->l); nexterror(); } ep = &p->conv[p->maxconv]; for(pp = p->conv; pp < ep; pp++) { c = *pp; if(c == 0) { c = mallocz(sizeof(Conv), 1); if(c == 0) error(Enomem); lock(&c->r.lk); c->r.ref = 1; c->p = p; c->x = pp - p->conv; p->nc++; *pp = c; break; } lock(&c->r.lk); if(c->r.ref == 0) { c->r.ref++; break; } unlock(&c->r.lk); } if(pp >= ep) { unlock(&p->l); poperror(); return 0; } strcpy(c->owner, user); c->perm = 0660; c->state = "Closed"; c->restricted = 0; c->laddr = 0; c->raddr = 0; c->lport = 0; c->rport = 0; c->sfd = nfd; if(nfd == -1) c->sfd = so_socket(p->stype); unlock(&c->r.lk); unlock(&p->l); poperror(); return c; } enum { Isprefix= 16, }; uchar prefixvals[256] = { /*0x00*/ 0 | Isprefix, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*0x10*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*0x20*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*0x30*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*0x40*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*0x50*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*0x60*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*0x70*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*0x80*/ 1 | Isprefix, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*0x90*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*0xA0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*0xB0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*0xC0*/ 2 | Isprefix, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*0xD0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*0xE0*/ 3 | Isprefix, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*0xF0*/ 4 | Isprefix, 0, 0, 0, 0, 0, 0, 0, /*0xF8*/ 5 | Isprefix, 0, 0, 0, /*0xFC*/ 6 | Isprefix, 0, /*0xFE*/ 7 | Isprefix, /*0xFF*/ 8 | Isprefix, }; int eipfmt(Fmt *f) { char buf[5*8]; static char *efmt = "%.2lux%.2lux%.2lux%.2lux%.2lux%.2lux"; static char *ifmt = "%d.%d.%d.%d"; uchar *p, ip[16]; ulong *lp; ushort s; int i, j, n, eln, eli; ulong ul; switch(f->r) { case 'E': /* Ethernet address */ p = va_arg(f->args, uchar*); snprint(buf, sizeof buf, efmt, p[0], p[1], p[2], p[3], p[4], p[5]); return fmtstrcpy(f, buf); case 'I': ul = va_arg(f->args, ulong); hnputl(ip, ul); snprint(buf, sizeof buf, ifmt, ip[0], ip[1], ip[2], ip[3]); return fmtstrcpy(f, buf); } return fmtstrcpy(f, "(eipfmt)"); } void hnputl(void *p, unsigned long v) { unsigned char *a; a = p; a[0] = v>>24; a[1] = v>>16; a[2] = v>>8; a[3] = v; } void hnputs(void *p, unsigned short v) { unsigned char *a; a = p; a[0] = v>>8; a[1] = v; } unsigned long nhgetl(void *p) { unsigned char *a; a = p; return (a[0]<<24)|(a[1]<<16)|(a[2]<<8)|(a[3]<<0); } unsigned short nhgets(void *p) { unsigned char *a; a = p; return (a[0]<<8)|(a[1]<<0); } #define CLASS(p) ((*(unsigned char*)(p))>>6) unsigned long parseip(char *to, char *from) { int i; char *p; p = from; memset(to, 0, 4); for(i = 0; i < 4 && *p; i++){ to[i] = strtoul(p, &p, 0); if(*p == '.') p++; } switch(CLASS(to)){ case 0: /* class A - 1 byte net */ case 1: if(i == 3){ to[3] = to[2]; to[2] = to[1]; to[1] = 0; } else if (i == 2){ to[3] = to[1]; to[1] = 0; } break; case 2: /* class B - 2 byte net */ if(i == 3){ to[3] = to[2]; to[2] = 0; } break; } return nhgetl(to); } void csclose(Chan *c) { free(c->aux); } long csread(Chan *c, void *a, long n, vlong offset) { if(c->aux == nil) return 0; return readstr(offset, a, n, c->aux); } static struct { char *name; uint num; } tab[] = { "cs", 1, "echo", 7, "discard", 9, "systat", 11, "daytime", 13, "netstat", 15, "chargen", 19, "ftp-data", 20, "ftp", 21, "ssh", 22, "telnet", 23, "smtp", 25, "time", 37, "whois", 43, "dns", 53, "domain", 53, "uucp", 64, "gopher", 70, "rje", 77, "finger", 79, "http", 80, "link", 87, "supdup", 95, "hostnames", 101, "iso-tsap", 102, "x400", 103, "x400-snd", 104, "csnet-ns", 105, "pop-2", 109, "pop3", 110, "portmap", 111, "uucp-path", 117, "nntp", 119, "netbios", 139, "imap4", 143, "NeWS", 144, "print-srv", 170, "z39.50", 210, "fsb", 400, "sysmon", 401, "proxy", 402, "proxyd", 404, "https", 443, "cifs", 445, "ssmtp", 465, "rexec", 512, "login", 513, "shell", 514, "printer", 515, "courier", 530, "cscan", 531, "uucp", 540, "snntp", 563, "9fs", 564, "whoami", 565, "guard", 566, "ticket", 567, "dlsftp", 666, "fmclient", 729, "imaps", 993, "pop3s", 995, "ingreslock", 1524, "pptp", 1723, "nfs", 2049, "webster", 2627, "weather", 3000, "secstore", 5356, "Xdisplay", 6000, "styx", 6666, "mpeg", 6667, "rstyx", 6668, "infdb", 6669, "infsigner", 6671, "infcsigner", 6672, "inflogin", 6673, "bandt", 7330, "face", 32000, "dhashgate", 11978, "exportfs", 17007, "rexexec", 17009, "ncpu", 17010, "cpu", 17013, "glenglenda1", 17020, "glenglenda2", 17021, "glenglenda3", 17022, "glenglenda4", 17023, "glenglenda5", 17024, "glenglenda6", 17025, "glenglenda7", 17026, "glenglenda8", 17027, "glenglenda9", 17028, "glenglenda10", 17029, "flyboy", 17032, "dlsftp", 17033, "venti", 17034, "wiki", 17035, "vica", 17036, 0 }; static int lookupport(char *s) { int i; char buf[10], *p; i = strtol(s, &p, 0); if(*s && *p == 0) return i; i = so_getservbyname(s, "tcp", buf); if(i != -1) return atoi(buf); for(i=0; tab[i].name; i++) if(strcmp(s, tab[i].name) == 0) return tab[i].num; return 0; } static ulong lookuphost(char *s) { char to[4]; ulong ip; memset(to, 0, sizeof to); parseip(to, s); ip = nhgetl(to); if(ip != 0) return ip; if((s = hostlookup(s)) == nil) return 0; parseip(to, s); ip = nhgetl(to); free(s); return ip; } long cswrite(Chan *c, void *a, long n, vlong offset) { char *f[4]; char *s, *ns; ulong ip; int nf, port; s = malloc(n+1); if(s == nil) error(Enomem); if(waserror()){ free(s); nexterror(); } memmove(s, a, n); s[n] = 0; nf = getfields(s, f, nelem(f), 0, "!"); if(nf != 3) error("can't translate"); port = lookupport(f[2]); if(port <= 0) error("no translation for port found"); ip = lookuphost(f[1]); if(ip == 0) error("no translation for host found"); ns = smprint("/net/%s/clone %I!%d", f[0], ip, port); if(ns == nil) error(Enomem); free(c->aux); c->aux = ns; poperror(); free(s); return n; } Dev ipdevtab = { 'I', "ip", devreset, ipinit, devshutdown, ipattach, ipwalk, ipstat, ipopen, devcreate, ipclose, ipread, devbread, ipwrite, devbwrite, devremove, devwstat, };