devip.c - vx32 - Local 9vx git repository for patches.
 (HTM) git clone git://r-36.net/vx32
 (DIR) Log
 (DIR) Files
 (DIR) Refs
       ---
       devip.c (16129B)
       ---
            1 /*
            2  * /net interface to host IPv4 stack.
            3  */
            4 
            5 #include "u.h"
            6 #include "lib.h"
            7 #include "mem.h"
            8 #include "dat.h"
            9 #include "fns.h"
           10 #include "error.h"
           11 #include "ip.h"
           12 #include "devip.h"
           13 
           14 void        csclose(Chan*);
           15 long        csread(Chan*, void*, long, vlong);
           16 long        cswrite(Chan*, void*, long, vlong);
           17 
           18 static int csremoved = 1;        /* was nice while it lasted... */
           19 
           20 void osipinit(void);
           21 
           22 enum
           23 {
           24         Qtopdir                = 1,        /* top level directory */
           25         Qcs,
           26         Qdns,
           27         Qprotodir,                /* directory for a protocol */
           28         Qclonus,
           29         Qconvdir,                /* directory for a conversation */
           30         Qdata,
           31         Qctl,
           32         Qstatus,
           33         Qremote,
           34         Qlocal,
           35         Qlisten,
           36 
           37         MAXPROTO        = 4
           38 };
           39 #define TYPE(x)         ((int)((x).path & 0xf))
           40 #define CONV(x)         ((int)(((x).path >> 4)&0xfff))
           41 #define PROTO(x)         ((int)(((x).path >> 16)&0xff))
           42 #define QID(p, c, y)         (((p)<<16) | ((c)<<4) | (y))
           43 
           44 typedef struct Proto        Proto;
           45 typedef struct Conv        Conv;
           46 struct Conv
           47 {
           48         int        x;
           49         Ref        r;
           50         int        sfd;
           51         int        eof;
           52         int        perm;
           53         char        owner[KNAMELEN];
           54         char*        state;
           55         ulong        laddr;
           56         ushort        lport;
           57         ulong        raddr;
           58         ushort        rport;
           59         int        restricted;
           60         char        cerr[KNAMELEN];
           61         Proto*        p;
           62 };
           63 
           64 struct Proto
           65 {
           66         Lock        l;
           67         int        x;
           68         int        stype;
           69         char        name[KNAMELEN];
           70         int        nc;
           71         int        maxconv;
           72         Conv**        conv;
           73         Qid        qid;
           74 };
           75 
           76 static        int        np;
           77 static        Proto        proto[MAXPROTO];
           78 
           79 static        Conv*        protoclone(Proto*, char*, int);
           80 static        void        setladdr(Conv*);
           81 
           82 int
           83 ipgen(Chan *c, char *nname, Dirtab *d, int nd, int s, Dir *dp)
           84 {
           85         Qid q;
           86         Conv *cv;
           87         char *p;
           88 
           89         USED(nname);
           90         q.vers = 0;
           91         q.type = 0;
           92         switch(TYPE(c->qid)) {
           93         case Qtopdir:
           94         case Qcs:
           95         case Qdns:
           96                 if(s >= 2+np)
           97                         return -1;
           98                 if(s == 0){
           99                         if(csremoved)
          100                                 return 0;
          101                         q.path = QID(s, 0, Qcs);
          102                         devdir(c, q, "cs", 0, "network", 0666, dp);
          103                 }else if(s == 1){
          104                         q.path = QID(s, 0, Qdns);
          105                         devdir(c, q, "dns", 0, "network", 0666, dp);
          106                 }else{
          107                         s-=2;
          108                         q.path = QID(s, 0, Qprotodir);
          109                         q.type = QTDIR;
          110                         devdir(c, q, proto[s].name, 0, "network", DMDIR|0555, dp);
          111                 }
          112                 return 1;
          113         case Qprotodir:
          114         case Qclonus:
          115                 if(s < proto[PROTO(c->qid)].nc) {
          116                         cv = proto[PROTO(c->qid)].conv[s];
          117                         sprint(up->genbuf, "%d", s);
          118                         q.path = QID(PROTO(c->qid), s, Qconvdir);
          119                         q.type = QTDIR;
          120                         devdir(c, q, up->genbuf, 0, cv->owner, DMDIR|0555, dp);
          121                         return 1;
          122                 }
          123                 s -= proto[PROTO(c->qid)].nc;
          124                 switch(s) {
          125                 default:
          126                         return -1;
          127                 case 0:
          128                         p = "clone";
          129                         q.path = QID(PROTO(c->qid), 0, Qclonus);
          130                         break;
          131                 }
          132                 devdir(c, q, p, 0, "network", 0555, dp);
          133                 return 1;
          134         case Qconvdir:
          135         case Qdata:
          136         case Qctl:
          137         case Qstatus:
          138         case Qremote:
          139         case Qlocal:
          140         case Qlisten:
          141                 cv = proto[PROTO(c->qid)].conv[CONV(c->qid)];
          142                 switch(s) {
          143                 default:
          144                         return -1;
          145                 case 0:
          146                         q.path = QID(PROTO(c->qid), CONV(c->qid), Qdata);
          147                         devdir(c, q, "data", 0, cv->owner, cv->perm, dp);
          148                         return 1;
          149                 case 1:
          150                         q.path = QID(PROTO(c->qid), CONV(c->qid), Qctl);
          151                         devdir(c, q, "ctl", 0, cv->owner, cv->perm, dp);
          152                         return 1;
          153                 case 2:
          154                         p = "status";
          155                         q.path = QID(PROTO(c->qid), CONV(c->qid), Qstatus);
          156                         break;
          157                 case 3:
          158                         p = "remote";
          159                         q.path = QID(PROTO(c->qid), CONV(c->qid), Qremote);
          160                         break;
          161                 case 4:
          162                         p = "local";
          163                         q.path = QID(PROTO(c->qid), CONV(c->qid), Qlocal);
          164                         break;
          165                 case 5:
          166                         p = "listen";
          167                         q.path = QID(PROTO(c->qid), CONV(c->qid), Qlisten);
          168                         break;
          169                 }
          170                 devdir(c, q, p, 0, cv->owner, 0444, dp);
          171                 return 1;
          172         }
          173         return -1;
          174 }
          175 
          176 static void
          177 newproto(char *name, int type, int maxconv)
          178 {
          179         int l;
          180         Proto *p;
          181 
          182         if(np >= MAXPROTO) {
          183                 print("no %s: increase MAXPROTO", name);
          184                 return;
          185         }
          186 
          187         p = &proto[np];
          188         strcpy(p->name, name);
          189         p->stype = type;
          190         p->qid.path = QID(np, 0, Qprotodir);
          191         p->qid.type = QTDIR;
          192         p->x = np++;
          193         p->maxconv = maxconv;
          194         l = sizeof(Conv*)*(p->maxconv+1);
          195         p->conv = mallocz(l, 1);
          196         if(p->conv == 0)
          197                 panic("no memory");
          198 }
          199 
          200 void
          201 ipinit(void)
          202 {
          203         osipinit();
          204 
          205         newproto("udp", S_UDP, 10);
          206         newproto("tcp", S_TCP, 30);
          207 
          208         fmtinstall('E', eipfmt);
          209         fmtinstall('V', eipfmt);
          210 }
          211 
          212 Chan *
          213 ipattach(char *spec)
          214 {
          215         Chan *c;
          216 
          217         c = devattach('I', spec);
          218         c->qid.path = QID(0, 0, Qtopdir);
          219         c->qid.type = QTDIR;
          220         c->qid.vers = 0;
          221         return c;
          222 }
          223 
          224 static Walkqid*
          225 ipwalk(Chan *c, Chan *nc, char **name, int nname)
          226 {
          227         return devwalk(c, nc, name, nname, 0, 0, ipgen);
          228 }
          229 
          230 int
          231 ipstat(Chan *c, uchar *dp, int n)
          232 {
          233         return devstat(c, dp, n, 0, 0, ipgen);
          234 }
          235 
          236 Chan *
          237 ipopen(Chan *c, int omode)
          238 {
          239         Proto *p;
          240         ulong raddr;
          241         ushort rport;
          242         int perm, sfd;
          243         Conv *cv, *lcv;
          244 
          245         omode &= 3;
          246         perm = 0;
          247         switch(omode) {
          248         case OREAD:
          249                 perm = 4;
          250                 break;
          251         case OWRITE:
          252                 perm = 2;
          253                 break;
          254         case ORDWR:
          255                 perm = 6;
          256                 break;
          257         }
          258 
          259         switch(TYPE(c->qid)) {
          260         default:
          261                 break;
          262         case Qtopdir:
          263         case Qprotodir:
          264         case Qconvdir:
          265         case Qstatus:
          266         case Qremote:
          267         case Qlocal:
          268                 if(omode != OREAD)
          269                         error(Eperm);
          270                 break;
          271         case Qclonus:
          272                 p = &proto[PROTO(c->qid)];
          273                 cv = protoclone(p, up->user, -1);
          274                 if(cv == 0)
          275                         error(Enodev);
          276                 c->qid.path = QID(p->x, cv->x, Qctl);
          277                 c->qid.vers = 0;
          278                 break;
          279         case Qdata:
          280         case Qctl:
          281                 p = &proto[PROTO(c->qid)];
          282                 lock(&p->l);
          283                 cv = p->conv[CONV(c->qid)];
          284                 lock(&cv->r.lk);
          285                 if((perm & (cv->perm>>6)) != perm) {
          286                         if(strcmp(up->user, cv->owner) != 0 ||
          287                            (perm & cv->perm) != perm) {
          288                                 unlock(&cv->r.lk);
          289                                 unlock(&p->l);
          290                                 error(Eperm);
          291                         }
          292                 }
          293                 cv->r.ref++;
          294                 if(cv->r.ref == 1) {
          295                         memmove(cv->owner, up->user, KNAMELEN);
          296                         cv->perm = 0660;
          297                 }
          298                 unlock(&cv->r.lk);
          299                 unlock(&p->l);
          300                 break;
          301         case Qlisten:
          302                 p = &proto[PROTO(c->qid)];
          303                 lcv = p->conv[CONV(c->qid)];
          304                 sfd = so_accept(lcv->sfd, &raddr, &rport);
          305                 cv = protoclone(p, up->user, sfd);
          306                 if(cv == 0) {
          307                         close(sfd);
          308                         error(Enodev);
          309                 }
          310                 cv->raddr = raddr;
          311                 cv->rport = rport;
          312                 setladdr(cv);
          313                 cv->state = "Established";
          314                 c->qid.path = QID(p->x, cv->x, Qctl);
          315                 break;
          316         }
          317         c->mode = openmode(omode);
          318         c->flag |= COPEN;
          319         c->offset = 0;
          320         return c;
          321 }
          322 
          323 void
          324 ipclose(Chan *c)
          325 {
          326         Conv *cc;
          327 
          328         switch(TYPE(c->qid)) {
          329         case Qcs:
          330         case Qdns:
          331                 csclose(c);
          332                 break;
          333         case Qdata:
          334         case Qctl:
          335                 if((c->flag & COPEN) == 0)
          336                         break;
          337                 cc = proto[PROTO(c->qid)].conv[CONV(c->qid)];
          338                 if(decref(&cc->r) != 0)
          339                         break;
          340                 strcpy(cc->owner, "network");
          341                 cc->perm = 0666;
          342                 cc->state = "Closed";
          343                 cc->laddr = 0;
          344                 cc->raddr = 0;
          345                 cc->lport = 0;
          346                 cc->rport = 0;
          347                 close(cc->sfd);
          348                 break;
          349         }
          350 }
          351 
          352 void
          353 ipremove(Chan *c)
          354 {
          355         if(TYPE(c->qid) == Qcs){
          356                 csremoved = 1;
          357                 csclose(c);
          358                 return;
          359         }
          360         devremove(c);
          361 }
          362 
          363 long
          364 ipread(Chan *ch, void *a, long n, vlong offset)
          365 {
          366         int r;
          367         Conv *c;
          368         Proto *x;
          369         uchar ip[4];
          370         char buf[128], *p;
          371 
          372 /*print("ipread %s %lux\n", c2name(ch), (long)ch->qid.path);*/
          373         p = a;
          374         switch(TYPE(ch->qid)) {
          375         default:
          376                 error(Eperm);
          377         case Qcs:
          378         case Qdns:
          379                 return csread(ch, a, n, offset);
          380         case Qprotodir:
          381         case Qtopdir:
          382         case Qconvdir:
          383                 return devdirread(ch, a, n, 0, 0, ipgen);
          384         case Qctl:
          385                 sprint(buf, "%d", CONV(ch->qid));
          386                 return readstr(offset, p, n, buf);
          387         case Qremote:
          388                 c = proto[PROTO(ch->qid)].conv[CONV(ch->qid)];
          389                 hnputl(ip, c->raddr);
          390                 sprint(buf, "%V!%d\n", ip, c->rport);
          391                 return readstr(offset, p, n, buf);
          392         case Qlocal:
          393                 c = proto[PROTO(ch->qid)].conv[CONV(ch->qid)];
          394                 hnputl(ip, c->laddr);
          395                 sprint(buf, "%V!%d\n", ip, c->lport);
          396                 return readstr(offset, p, n, buf);
          397         case Qstatus:
          398                 x = &proto[PROTO(ch->qid)];
          399                 c = x->conv[CONV(ch->qid)];
          400                 sprint(buf, "%s/%d %d %s \n",
          401                         c->p->name, c->x, c->r.ref, c->state);
          402                 return readstr(offset, p, n, buf);
          403         case Qdata:
          404                 c = proto[PROTO(ch->qid)].conv[CONV(ch->qid)];
          405                 r = so_recv(c->sfd, a, n, 0);
          406                 if(r < 0){
          407                         oserrstr();
          408                         nexterror();
          409                 }
          410                 if(r == 0 && ++c->eof > 3)
          411                         error(Ehungup);
          412                 return r;
          413         }
          414 }
          415 
          416 static void
          417 setladdr(Conv *c)
          418 {
          419         so_getsockname(c->sfd, &c->laddr, &c->lport);
          420 }
          421 
          422 static void
          423 setlport(Conv *c)
          424 {
          425         if(c->restricted == 0 && c->lport == 0)
          426                 return;
          427 
          428         so_bind(c->sfd, c->restricted, c->lport);
          429 }
          430 
          431 static void
          432 setladdrport(Conv *c, char *str)
          433 {
          434         char *p;
          435         uchar addr[4];
          436 
          437         p = strchr(str, '!');
          438         if(p == 0) {
          439                 p = str;
          440                 c->laddr = 0;
          441         }
          442         else {
          443                 *p++ = 0;
          444                 v4parseip(addr, str);
          445                 c->laddr = nhgetl(addr);
          446         }
          447         if(*p == '*')
          448                 c->lport = 0;
          449         else
          450                 c->lport = atoi(p);
          451 
          452         setlport(c);
          453 }
          454 
          455 static char*
          456 setraddrport(Conv *c, char *str)
          457 {
          458         char *p;
          459         uchar addr[4];
          460 
          461         p = strchr(str, '!');
          462         if(p == 0)
          463                 return "malformed address";
          464         *p++ = 0;
          465         v4parseip(addr, str);
          466         c->raddr = nhgetl(addr);
          467         c->rport = atoi(p);
          468         p = strchr(p, '!');
          469         if(p) {
          470                 if(strcmp(p, "!r") == 0)
          471                         c->restricted = 1;
          472         }
          473         return 0;
          474 }
          475 
          476 long
          477 ipwrite(Chan *ch, void *a, long n, vlong offset)
          478 {
          479         Conv *c;
          480         Proto *x;
          481         int r, nf;
          482         char *p, *fields[3], buf[128];
          483 
          484         switch(TYPE(ch->qid)) {
          485         default:
          486                 error(Eperm);
          487         case Qcs:
          488         case Qdns:
          489                 return cswrite(ch, a, n, offset);
          490         case Qctl:
          491                 x = &proto[PROTO(ch->qid)];
          492                 c = x->conv[CONV(ch->qid)];
          493                 if(n > sizeof(buf)-1)
          494                         n = sizeof(buf)-1;
          495                 memmove(buf, a, n);
          496                 buf[n] = '\0';
          497 
          498                 nf = tokenize(buf, fields, 3);
          499                 if(strcmp(fields[0], "connect") == 0){
          500                         switch(nf) {
          501                         default:
          502                                 error("bad args to connect");
          503                         case 2:
          504                                 p = setraddrport(c, fields[1]);
          505                                 if(p != 0)
          506                                         error(p);
          507                                 break;
          508                         case 3:
          509                                 p = setraddrport(c, fields[1]);
          510                                 if(p != 0)
          511                                         error(p);
          512                                 c->lport = atoi(fields[2]);
          513                                 setlport(c);
          514                                 break;
          515                         }
          516                         so_connect(c->sfd, c->raddr, c->rport);
          517                         setladdr(c);
          518                         c->state = "Established";
          519                         return n;
          520                 }
          521                 if(strcmp(fields[0], "announce") == 0) {
          522                         switch(nf){
          523                         default:
          524                                 error("bad args to announce");
          525                         case 2:
          526                                 setladdrport(c, fields[1]);
          527                                 break;
          528                         }
          529                         so_listen(c->sfd);
          530                         c->state = "Announced";
          531                         return n;
          532                 }
          533                 if(strcmp(fields[0], "bind") == 0){
          534                         switch(nf){
          535                         default:
          536                                 error("bad args to bind");
          537                         case 2:
          538                                 c->lport = atoi(fields[1]);
          539                                 break;
          540                         }
          541                         setlport(c);
          542                         return n;
          543                 }
          544                 error("bad control message");
          545         case Qdata:
          546                 x = &proto[PROTO(ch->qid)];
          547                 c = x->conv[CONV(ch->qid)];
          548                 r = so_send(c->sfd, a, n, 0);
          549                 if(r < 0){
          550                         oserrstr();
          551                         nexterror();
          552                 }
          553                 return r;
          554         }
          555         return n;
          556 }
          557 
          558 static Conv*
          559 protoclone(Proto *p, char *user, int nfd)
          560 {
          561         Conv *c, **pp, **ep;
          562 
          563         c = 0;
          564         lock(&p->l);
          565         if(waserror()) {
          566                 unlock(&p->l);
          567                 nexterror();
          568         }
          569         ep = &p->conv[p->maxconv];
          570         for(pp = p->conv; pp < ep; pp++) {
          571                 c = *pp;
          572                 if(c == 0) {
          573                         c = mallocz(sizeof(Conv), 1);
          574                         if(c == 0)
          575                                 error(Enomem);
          576                         lock(&c->r.lk);
          577                         c->r.ref = 1;
          578                         c->p = p;
          579                         c->x = pp - p->conv;
          580                         p->nc++;
          581                         *pp = c;
          582                         break;
          583                 }
          584                 lock(&c->r.lk);
          585                 if(c->r.ref == 0) {
          586                         c->r.ref++;
          587                         break;
          588                 }
          589                 unlock(&c->r.lk);
          590         }
          591         if(pp >= ep) {
          592                 unlock(&p->l);
          593                 poperror();
          594                 return 0;
          595         }
          596 
          597         strcpy(c->owner, user);
          598         c->perm = 0660;
          599         c->state = "Closed";
          600         c->restricted = 0;
          601         c->laddr = 0;
          602         c->raddr = 0;
          603         c->lport = 0;
          604         c->rport = 0;
          605         c->sfd = nfd;
          606         if(nfd == -1)
          607                 c->sfd = so_socket(p->stype);
          608         c->eof = 0;
          609 
          610         unlock(&c->r.lk);
          611         unlock(&p->l);
          612         poperror();
          613         return c;
          614 }
          615 
          616 /*
          617  * In-kernel /net/cs and /net/dns
          618  */
          619 void
          620 csclose(Chan *c)
          621 {
          622         free(c->aux);
          623 }
          624 
          625 long
          626 csread(Chan *c, void *a, long n, vlong offset)
          627 {
          628         if(c->aux == nil)
          629                 return 0;
          630         return readstr(offset, a, n, c->aux);
          631 }
          632 
          633 static struct
          634 {
          635         char *proto;
          636         char *name;
          637         uint num;
          638 } tab[] = {
          639         "tcp", "cs", 1,
          640         "tcp", "echo", 7,
          641         "tcp", "discard", 9,
          642         "tcp", "systat", 11,
          643         "tcp", "daytime", 13,
          644         "tcp", "netstat", 15,
          645         "tcp", "chargen", 19,
          646         "tcp", "ftp-data", 20,
          647         "tcp", "ftp", 21,
          648         "tcp", "ssh", 22,
          649         "tcp", "telnet", 23,
          650         "tcp", "smtp", 25,
          651         "tcp", "time", 37,
          652         "tcp", "whois", 43,
          653         "tcp", "dns", 53,
          654         "tcp", "domain", 53,
          655         "tcp", "uucp", 64,
          656         "tcp", "gopher", 70,
          657         "tcp", "rje", 77,
          658         "tcp", "finger", 79,
          659         "tcp", "http", 80,
          660         "tcp", "link", 87,
          661         "tcp", "supdup", 95,
          662         "tcp", "hostnames", 101,
          663         "tcp", "iso-tsap", 102,
          664         "tcp", "x400", 103,
          665         "tcp", "x400-snd", 104,
          666         "tcp", "csnet-ns", 105,
          667         "tcp", "pop-2", 109,
          668         "tcp", "pop3", 110,
          669         "tcp", "portmap", 111,
          670         "tcp", "uucp-path", 117,
          671         "tcp", "nntp", 119,
          672         "tcp", "netbios", 139,
          673         "tcp", "imap4", 143,
          674         "tcp", "imap", 143,
          675         "tcp", "NeWS", 144,
          676         "tcp", "print-srv", 170,
          677         "tcp", "z39.50", 210,
          678         "tcp", "fsb", 400,
          679         "tcp", "sysmon", 401,
          680         "tcp", "proxy", 402,
          681         "tcp", "proxyd", 404,
          682         "tcp", "https", 443,
          683         "tcp", "cifs", 445,
          684         "tcp", "ssmtp", 465,
          685         "tcp", "rexec", 512,
          686         "tcp", "login", 513,
          687         "tcp", "shell", 514,
          688         "tcp", "printer", 515,
          689         "tcp", "ncp", 524,
          690         "tcp", "courier", 530,
          691         "tcp", "cscan", 531,
          692         "tcp", "uucp", 540,
          693         "tcp", "snntp", 563,
          694         "tcp", "9fs", 564,
          695         "tcp", "whoami", 565,
          696         "tcp", "guard", 566,
          697         "tcp", "ticket", 567,
          698         "tcp", "fmclient", 729,
          699         "tcp", "imaps", 993,
          700         "tcp", "pop3s", 995,
          701         "tcp", "ingreslock", 1524,
          702         "tcp", "pptp", 1723,
          703         "tcp", "nfs", 2049,
          704         "tcp", "webster", 2627,
          705         "tcp", "weather", 3000,
          706         "tcp", "sip", 5060,
          707         "tcp", "sips", 5061,
          708         "tcp", "secstore", 5356,
          709         "tcp", "vnc-http", 5800,
          710         "tcp", "vnc", 5900,
          711         "tcp", "Xdisplay", 6000,
          712         "tcp", "styx", 6666,
          713         "tcp", "mpeg", 6667,
          714         "tcp", "rstyx", 6668,
          715         "tcp", "infdb", 6669,
          716         "tcp", "infsigner", 6671,
          717         "tcp", "infcsigner", 6672,
          718         "tcp", "inflogin", 6673,
          719         "tcp", "bandt", 7330,
          720         "tcp", "face", 32000,
          721         "tcp", "dhashgate", 11978,
          722         "tcp", "exportfs", 17007,
          723         "tcp", "rexexec", 17009,
          724         "tcp", "ncpu", 17010,
          725         "tcp", "cpu", 17013,
          726         "tcp", "glenglenda1", 17020,
          727         "tcp", "glenglenda2", 17021,
          728         "tcp", "glenglenda3", 17022,
          729         "tcp", "glenglenda4", 17023,
          730         "tcp", "glenglenda5", 17024,
          731         "tcp", "glenglenda6", 17025,
          732         "tcp", "glenglenda7", 17026,
          733         "tcp", "glenglenda8", 17027,
          734         "tcp", "glenglenda9", 17028,
          735         "tcp", "glenglenda10", 17029,
          736         "tcp", "flyboy", 17032,
          737         "tcp", "venti", 17034,
          738         "tcp", "wiki", 17035,
          739         "tcp", "vica", 17036,
          740 
          741 //        "il", "9fs", 17008,
          742 
          743         "udp", "echo", 7,
          744         "udp", "tacacs", 49,
          745         "udp", "tftp", 69,
          746         "udp", "bootpc", 68,
          747         "udp", "bootp", 67,
          748         "udp", "domain", 53,
          749         "udp", "dns", 53,
          750         "udp", "portmap", 111,
          751         "udp", "ntp", 123,
          752         "udp", "netbios-ns", 137,
          753         "udp", "snmp", 161,
          754         "udp", "syslog", 514,
          755         "udp", "rip", 520,
          756         "udp", "dhcp6c", 546,
          757         "udp", "dhcp6s", 547,
          758         "udp", "nfs", 2049,
          759         "udp", "bfs", 2201,
          760         "udp", "virgil", 2202,
          761         "udp", "sip", 5060,
          762         "udp", "bandt2", 7331,
          763         "udp", "oradius", 1645,
          764         "udp", "dhash", 11977,
          765         0
          766 };
          767 
          768 static int
          769 lookupport(char *s, char **pproto)
          770 {
          771         int i;
          772         char buf[10], *p, *proto;
          773 
          774         i = strtol(s, &p, 0);
          775         if(*s && *p == 0)
          776                 return i;
          777 
          778         proto = *pproto;
          779         if(strcmp(proto, "net") == 0)
          780                 proto = nil;
          781         if(proto == nil){
          782                 if(so_getservbyname(s, "tcp", buf) >= 0){
          783                         *pproto = "tcp";
          784                         return atoi(buf);
          785                 }
          786                 if(so_getservbyname(s, "udp", buf) >= 0){
          787                         *pproto = "udp";
          788                         return atoi(buf);
          789                 }
          790         }else{
          791                 if(strcmp(proto, "tcp") != 0 && strcmp(proto, "udp") != 0)
          792                         return 0;
          793                 if(so_getservbyname(s, proto, buf) >= 0){
          794                         *pproto = "tcp";
          795                         return atoi(buf);
          796                 }
          797         }
          798         for(i=0; tab[i].proto; i++){
          799                 if(proto == nil || strcmp(proto, tab[i].proto) == 0)
          800                 if(strcmp(s, tab[i].name) == 0){
          801                         if(proto == nil)
          802                                 *pproto = tab[i].proto;
          803                         return tab[i].num;
          804                 }
          805         }
          806         return 0;
          807 }
          808 
          809 static int
          810 lookuphost(char *s, uchar *to)
          811 {
          812         ulong ip;
          813         char *p;
          814 
          815         memset(to, 0, 4);
          816         p = v4parseip(to, s);
          817         if(p && *p == 0 && (ip = nhgetl(to)) != 0)
          818                 return 0;
          819         if((s = hostlookup(s)) == nil)
          820                 return -1;
          821         v4parseip(to, s);
          822         free(s);
          823         return 0;
          824 }
          825 
          826 long
          827 cswrite(Chan *c, void *a, long n, vlong offset)
          828 {
          829         char *f[4];
          830         char *s, *ns;
          831         int nf, port, bang;
          832         uchar ip[4];
          833 
          834         s = malloc(n+1);
          835         if(s == nil)
          836                 error(Enomem);
          837         ns = malloc(128);
          838         if(ns == nil){
          839                 free(s);
          840                 error(Enomem);
          841         }
          842         if(waserror()){
          843                 free(s);
          844                 free(ns);
          845                 nexterror();
          846         }
          847         memmove(s, a, n);
          848         s[n] = 0;
          849         
          850         if(TYPE(c->qid) == Qcs){
          851                 nf = getfields(s, f, nelem(f), 0, "!");
          852                 if(nf != 3)
          853                         error("bad syntax");
          854 
          855                 port = lookupport(f[2], &f[0]);
          856                 if(port <= 0)
          857                         error("no translation for port found");
          858 
          859                 if(lookuphost(f[1], ip) < 0)
          860                         error("no translation for host found");
          861                 snprint(ns, 128, "/net/%s/clone %V!%d", f[0], ip, port);
          862         }else{
          863                 /* dns */
          864                 bang = 0;
          865                 if(s[0] == '!')
          866                         bang = 1;
          867                 nf = tokenize(s+bang, f, nelem(f));
          868                 if(nf > 2)
          869                         error("bad syntax");
          870                 if(nf > 1 && strcmp(f[1], "ip") != 0)
          871                         error("can only lookup ip addresses");
          872                 if(lookuphost(f[0], ip) < 0)
          873                         error("no translation for host found");
          874                 if(bang)
          875                         snprint(ns, 128, "dom=%s ip=%V", f[0], ip);
          876                 else
          877                         snprint(ns, 128, "%s ip\t%V", f[0], ip);
          878         }
          879         free(c->aux);
          880         c->aux = ns;
          881         poperror();
          882         free(s);
          883         return n;
          884 }
          885 
          886 Dev pipdevtab = 
          887 {
          888         'I',
          889         "ip",
          890 
          891         devreset,
          892         ipinit,
          893         devshutdown,
          894         ipattach,
          895         ipwalk,
          896         ipstat,
          897         ipopen,
          898         devcreate,
          899         ipclose,
          900         ipread,
          901         devbread,
          902         ipwrite,
          903         devbwrite,
          904         ipremove,
          905         devwstat,
          906 };
          907