URI:
       tmain.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tmain.c (16062B)
       ---
            1 #include <u.h>
            2 #include <libc.h>
            3 #include <ip.h>
            4 #include <bio.h>
            5 #include <fcall.h>
            6 #include <libsec.h>
            7 #include "dat.h"
            8 #include "protos.h"
            9 #include "y.tab.h"
           10 
           11 int Cflag;
           12 int pflag;
           13 int Nflag;
           14 int sflag;
           15 int tiflag;
           16 int toflag;
           17 
           18 char *prom = "promiscuous";
           19 
           20 enum
           21 {
           22         Pktlen=        64*1024,
           23         Blen=        16*1024
           24 };
           25 
           26 Filter *filter;
           27 Proto *root;
           28 Biobuf out;
           29 vlong starttime, pkttime;
           30 int pcap;
           31 
           32 int        filterpkt(Filter *f, uchar *ps, uchar *pe, Proto *pr, int);
           33 void        printpkt(char *p, char *e, uchar *ps, uchar *pe);
           34 void        mkprotograph(void);
           35 Proto*        findproto(char *name);
           36 Filter*        compile(Filter *f);
           37 void        printfilter(Filter *f, char *tag);
           38 void        printhelp(char*);
           39 void        tracepkt(uchar*, int);
           40 void        pcaphdr(int);
           41 
           42 struct pcap_pkthdr {
           43         u64int        ts;        /* time stamp */
           44         u32int        caplen;        /* length of portion present */
           45         u32int        len;        /* length this packet (off wire) */
           46 };
           47 
           48 
           49 void
           50 printusage(void)
           51 {
           52         fprint(2, "usage: %s [-CDdpst] [-N n] [-f filter] [-h first-header] path\n", argv0);
           53         fprint(2, "  for protocol help: %s -? [proto]\n", argv0);
           54 }
           55 
           56 void
           57 usage(void)
           58 {
           59         printusage();
           60         exits("usage");
           61 }
           62 
           63 void
           64 main(int argc, char **argv)
           65 {
           66         uchar *pkt;
           67         char *buf, *file, *p, *e;
           68         int fd;
           69         int n;
           70 
           71         Binit(&out, 1, OWRITE);
           72 
           73         fmtinstall('E', eipfmt);
           74         fmtinstall('V', eipfmt);
           75         fmtinstall('I', eipfmt);
           76         fmtinstall('H', encodefmt);
           77         fmtinstall('F', fcallfmt);
           78 
           79         pkt = malloc(Pktlen+16);
           80         pkt += 16;
           81         buf = malloc(Blen);
           82         e = buf+Blen-1;
           83 
           84         pflag = 1;
           85         Nflag = 32;
           86         sflag = 0;
           87 
           88         mkprotograph();
           89 
           90         ARGBEGIN{
           91         default:
           92                 usage();
           93         case '?':
           94                 printusage();
           95                 printhelp(ARGF());
           96                 exits(0);
           97                 break;
           98         case 'N':
           99                 p = EARGF(usage());
          100                 Nflag = atoi(p);
          101                 break;
          102         case 'f':
          103                 p = EARGF(usage());
          104                 yyinit(p);
          105                 yyparse();
          106                 break;
          107         case 's':
          108                 sflag = 1;
          109                 break;
          110         case 'h':
          111                 p = EARGF(usage());
          112                 root = findproto(p);
          113                 if(root == nil)
          114                         sysfatal("unknown protocol: %s", p);
          115                 break;
          116         case 'd':
          117                 toflag = 1;
          118                 break;
          119         case 'D':
          120                 toflag = 1;
          121                 pcap = 1;
          122                 break;
          123         case 't':
          124                 tiflag = 1;
          125                 break;
          126         case 'T':
          127                 tiflag = 1;
          128                 pcap = 1;
          129                 break;
          130         case 'C':
          131                 Cflag = 1;
          132                 break;
          133         case 'p':
          134                 pflag = 0;
          135                 break;
          136         }ARGEND;
          137 
          138         if(argc > 1)
          139                 usage();
          140 
          141         if(argc == 0)
          142                 file = nil;
          143         else
          144                 file = argv[0];
          145 
          146         if(tiflag){
          147                 if(file == nil)
          148                         sysfatal("must specify file with -t");
          149                 fd = open(file, OREAD);
          150                 if(fd < 0)
          151                         sysfatal("opening %s: %r", file);
          152         }else{
          153                 fd = opendevice(file, pflag);
          154                 if(fd < 0)
          155                         sysfatal("opening device %s: %r", file);
          156         }
          157         if(root == nil)
          158                 root = &ether;
          159 
          160         if(pcap)
          161                 pcaphdr(fd);
          162 
          163         filter = compile(filter);
          164 
          165         if(tiflag){
          166                 /* read a trace file */
          167                 for(;;){
          168                         if(pcap){
          169                                 struct pcap_pkthdr *goo;
          170                                 n = read(fd, pkt, 16);
          171                                 if(n != 16)
          172                                         break;
          173                                 goo = (struct pcap_pkthdr*)pkt;
          174                                 pkttime = goo->ts;
          175                                 n = goo->caplen;
          176                         }else{
          177                                 n = read(fd, pkt, 10);
          178                                 if(n != 10)
          179                                         break;
          180                                 pkttime = NetL(pkt+2);
          181                                 pkttime = (pkttime<<32) | NetL(pkt+6);
          182                                 if(starttime == 0LL)
          183                                         starttime = pkttime;
          184                                 n = NetS(pkt);
          185                         }
          186                         if(readn(fd, pkt, n) != n)
          187                                 break;
          188                         if(filterpkt(filter, pkt, pkt+n, root, 1))
          189                                 if(toflag)
          190                                         tracepkt(pkt, n);
          191                                 else
          192                                         printpkt(buf, e, pkt, pkt+n);
          193                 }
          194         } else {
          195                 /* read a real time stream */
          196                 starttime = nsec();
          197                 for(;;){
          198                         n = root->framer(fd, pkt, Pktlen);
          199                         if(n <= 0)
          200                                 break;
          201                         pkttime = nsec();
          202                         if(filterpkt(filter, pkt, pkt+n, root, 1))
          203                                 if(toflag)
          204                                         tracepkt(pkt, n);
          205                                 else
          206                                         printpkt(buf, e, pkt, pkt+n);
          207                 }
          208         }
          209 }
          210 
          211 /* create a new filter node */
          212 Filter*
          213 newfilter(void)
          214 {
          215         Filter *f;
          216 
          217         f = mallocz(sizeof(*f), 1);
          218         if(f == nil)
          219                 sysfatal("newfilter: %r");
          220         return f;
          221 }
          222 
          223 /*
          224  *  apply filter to packet
          225  */
          226 int
          227 _filterpkt(Filter *f, Msg *m)
          228 {
          229         Msg ma;
          230 
          231         if(f == nil)
          232                 return 1;
          233 
          234         switch(f->op){
          235         case '!':
          236                 return !_filterpkt(f->l, m);
          237         case LAND:
          238                 ma = *m;
          239                 return _filterpkt(f->l, &ma) && _filterpkt(f->r, m);
          240         case LOR:
          241                 ma = *m;
          242                 return _filterpkt(f->l, &ma) || _filterpkt(f->r, m);
          243         case WORD:
          244                 if(m->needroot){
          245                         if(m->pr != f->pr)
          246                                 return 0;
          247                         m->needroot = 0;
          248                 }else{
          249                         if(m->pr && (m->pr->filter==nil || !(m->pr->filter)(f, m)))
          250                                 return 0;
          251                 }
          252                 if(f->l == nil)
          253                         return 1;
          254                 m->pr = f->pr;
          255                 return _filterpkt(f->l, m);
          256         }
          257         sysfatal("internal error: filterpkt op: %d", f->op);
          258         return 0;
          259 }
          260 int
          261 filterpkt(Filter *f, uchar *ps, uchar *pe, Proto *pr, int needroot)
          262 {
          263         Msg m;
          264 
          265         if(f == nil)
          266                 return 1;
          267 
          268         m.needroot = needroot;
          269         m.ps = ps;
          270         m.pe = pe;
          271         m.pr = pr;
          272         return _filterpkt(f, &m);
          273 }
          274 
          275 /*
          276  *  from the Unix world
          277  */
          278 #define PCAP_VERSION_MAJOR 2
          279 #define PCAP_VERSION_MINOR 4
          280 #define TCPDUMP_MAGIC 0xa1b2c3d4
          281 
          282 struct pcap_file_header {
          283         u32int                magic;
          284         u16int                version_major;
          285         u16int                version_minor;
          286         s32int                thiszone;    /* gmt to local correction */
          287         u32int                sigfigs;    /* accuracy of timestamps */
          288         u32int                snaplen;    /* max length saved portion of each pkt */
          289         u32int                linktype;   /* data link type (DLT_*) */
          290 };
          291 
          292 /*
          293  *  pcap trace header
          294  */
          295 void
          296 pcaphdr(int fd)
          297 {
          298         if(tiflag){
          299                 struct pcap_file_header hdr;
          300 
          301                 if(readn(fd, &hdr, sizeof hdr) != sizeof hdr)
          302                         sysfatal("short header");
          303                 if(hdr.magic != TCPDUMP_MAGIC)
          304                         sysfatal("packet header %ux != %ux", hdr.magic, TCPDUMP_MAGIC);
          305                 if(hdr.version_major != PCAP_VERSION_MAJOR || hdr.version_minor != PCAP_VERSION_MINOR)
          306                         sysfatal("version %d.%d != %d.%d", hdr.version_major, hdr.version_minor, PCAP_VERSION_MAJOR, PCAP_VERSION_MINOR);
          307                 if(hdr.linktype != 1)
          308                         sysfatal("unknown linktype %d != 1 (ethernet)", hdr.linktype);
          309         }
          310         if(toflag){
          311                 struct pcap_file_header hdr;
          312 
          313                 hdr.magic = TCPDUMP_MAGIC;
          314                 hdr.version_major = PCAP_VERSION_MAJOR;
          315                 hdr.version_minor = PCAP_VERSION_MINOR;
          316 
          317                 hdr.thiszone = 0;
          318                 hdr.snaplen = 1500;
          319                 hdr.sigfigs = 0;
          320                 hdr.linktype = 1;
          321 
          322                 write(1, &hdr, sizeof(hdr));
          323         }
          324 }
          325 
          326 /*
          327  *  write out a packet trace
          328  */
          329 void
          330 tracepkt(uchar *ps, int len)
          331 {
          332         struct pcap_pkthdr *goo;
          333 
          334         if(pcap){
          335                 goo = (struct pcap_pkthdr*)(ps-16);
          336                 goo->ts = pkttime;
          337                 goo->caplen = len;
          338                 goo->len = len;
          339                 write(1, goo, len+16);
          340         } else {
          341                 hnputs(ps-10, len);
          342                 hnputl(ps-8, pkttime>>32);
          343                 hnputl(ps-4, pkttime);
          344                 write(1, ps-10, len+10);
          345         }
          346 }
          347 
          348 /*
          349  *  format and print a packet
          350  */
          351 void
          352 printpkt(char *p, char *e, uchar *ps, uchar *pe)
          353 {
          354         Msg m;
          355         ulong dt;
          356 
          357         dt = (pkttime-starttime)/1000000LL;
          358         m.p = seprint(p, e, "%6.6uld ms ", dt);
          359         m.ps = ps;
          360         m.pe = pe;
          361         m.e = e;
          362         m.pr = root;
          363         while(m.p < m.e){
          364                 if(!sflag)
          365                         m.p = seprint(m.p, m.e, "\n\t");
          366                 m.p = seprint(m.p, m.e, "%s(", m.pr->name);
          367                 if((*m.pr->seprint)(&m) < 0){
          368                         m.p = seprint(m.p, m.e, "TOO SHORT");
          369                         m.ps = m.pe;
          370                 }
          371                 m.p = seprint(m.p, m.e, ")");
          372                 if(m.pr == nil || m.ps >= m.pe)
          373                         break;
          374         }
          375         *m.p++ = '\n';
          376 
          377         if(write(1, p, m.p - p) < 0)
          378                 sysfatal("stdout: %r");
          379 }
          380 
          381 Proto **xprotos;
          382 int nprotos;
          383 
          384 /* look up a protocol by its name */
          385 Proto*
          386 findproto(char *name)
          387 {
          388         int i;
          389 
          390         for(i = 0; i < nprotos; i++)
          391                 if(strcmp(xprotos[i]->name, name) == 0)
          392                         return xprotos[i];
          393         return nil;
          394 }
          395 
          396 /*
          397  *  add an undefined protocol to protos[]
          398  */
          399 Proto*
          400 addproto(char *name)
          401 {
          402         Proto *pr;
          403 
          404         xprotos = realloc(xprotos, (nprotos+1)*sizeof(Proto*));
          405         pr = malloc(sizeof *pr);
          406         *pr = dump;
          407         pr->name = name;
          408         xprotos[nprotos++] = pr;
          409         return pr;
          410 }
          411 
          412 /*
          413  *  build a graph of protocols, this could easily be circular.  This
          414  *  links together all the multiplexing in the protocol modules.
          415  */
          416 void
          417 mkprotograph(void)
          418 {
          419         Proto **l;
          420         Proto *pr;
          421         Mux *m;
          422 
          423         /* copy protos into a reallocable area */
          424         for(nprotos = 0; protos[nprotos] != nil; nprotos++)
          425                 ;
          426         xprotos = malloc(nprotos*sizeof(Proto*));
          427         memmove(xprotos, protos, nprotos*sizeof(Proto*));
          428 
          429         for(l = protos; *l != nil; l++){
          430                 pr = *l;
          431                 for(m = pr->mux; m != nil && m->name != nil; m++){
          432                         m->pr = findproto(m->name);
          433                         if(m->pr == nil)
          434                                 m->pr = addproto(m->name);
          435                 }
          436         }
          437 }
          438 
          439 /*
          440  *  add in a protocol node
          441  */
          442 static Filter*
          443 addnode(Filter *f, Proto *pr)
          444 {
          445         Filter *nf;
          446         nf = newfilter();
          447         nf->pr = pr;
          448         nf->s = pr->name;
          449         nf->l = f;
          450         nf->op = WORD;
          451         return nf;
          452 }
          453 
          454 /*
          455  *  recurse through the protocol graph adding missing nodes
          456  *  to the filter if we reach the filter's protocol
          457  */
          458 static Filter*
          459 _fillin(Filter *f, Proto *last, int depth)
          460 {
          461         Mux *m;
          462         Filter *nf;
          463 
          464         if(depth-- <= 0)
          465                 return nil;
          466 
          467         for(m = last->mux; m != nil && m->name != nil; m++){
          468                 if(m->pr == nil)
          469                         continue;
          470                 if(f->pr == m->pr)
          471                         return f;
          472                 nf = _fillin(f, m->pr, depth);
          473                 if(nf != nil)
          474                         return addnode(nf, m->pr);
          475         }
          476         return nil;
          477 }
          478 
          479 static Filter*
          480 fillin(Filter *f, Proto *last)
          481 {
          482         int i;
          483         Filter *nf;
          484 
          485         /* hack to make sure top level node is the root */
          486         if(last == nil){
          487                 if(f->pr == root)
          488                         return f;
          489                 f = fillin(f, root);
          490                 if(f == nil)
          491                         return nil;
          492                 return addnode(f, root);
          493         }
          494 
          495         /* breadth first search though the protocol graph */
          496         nf = f;
          497         for(i = 1; i < 20; i++){
          498                 nf = _fillin(f, last, i);
          499                 if(nf != nil)
          500                         break;
          501         }
          502         return nf;
          503 }
          504 
          505 /*
          506  *  massage tree so that all paths from the root to a leaf
          507  *  contain a filter node for each header.
          508  *
          509  *  also, set f->pr where possible
          510  */
          511 Filter*
          512 complete(Filter *f, Proto *last)
          513 {
          514         Proto *pr;
          515 
          516         if(f == nil)
          517                 return f;
          518 
          519         /* do a depth first traversal of the filter tree */
          520         switch(f->op){
          521         case '!':
          522                 f->l = complete(f->l, last);
          523                 break;
          524         case LAND:
          525         case LOR:
          526                 f->l = complete(f->l, last);
          527                 f->r = complete(f->r, last);
          528                 break;
          529         case '=':
          530                 break;
          531         case WORD:
          532                 pr = findproto(f->s);
          533                 f->pr = pr;
          534                 if(pr == nil){
          535                         if(f->l != nil){
          536                                 fprint(2, "%s unknown proto, ignoring params\n",
          537                                         f->s);
          538                                 f->l = nil;
          539                         }
          540                 } else {
          541                         f->l = complete(f->l, pr);
          542                         f = fillin(f, last);
          543                         if(f == nil)
          544                                 sysfatal("internal error: can't get to %s", pr->name);
          545                 }
          546                 break;
          547         }
          548         return f;
          549 }
          550 
          551 /*
          552  *  merge common nodes under | and & moving the merged node
          553  *  above the | or &.
          554  *
          555  *  do some constant foldong, e.g. `true & x' becomes x and
          556  *  'true | x' becomes true.
          557  */
          558 static int changed;
          559 
          560 static Filter*
          561 _optimize(Filter *f)
          562 {
          563         Filter *l;
          564 
          565         if(f == nil)
          566                 return f;
          567 
          568         switch(f->op){
          569         case '!':
          570                 /* is child also a not */
          571                 if(f->l->op == '!'){
          572                         changed = 1;
          573                         return f->l->l;
          574                 }
          575                 break;
          576         case LOR:
          577                 /* are two children the same protocol? */
          578                 if(f->l->op != f->r->op || f->r->op != WORD
          579                 || f->l->pr != f->r->pr || f->l->pr == nil)
          580                         break;        /* no optimization */
          581 
          582                 changed = 1;
          583 
          584                 /* constant folding */
          585                 /* if either child is childless, just return that */
          586                 if(f->l->l == nil)
          587                         return f->l;
          588                 else if(f->r->l == nil)
          589                         return f->r;
          590 
          591                 /* move the common node up, thow away one node */
          592                 l = f->l;
          593                 f->l = l->l;
          594                 f->r = f->r->l;
          595                 l->l = f;
          596                 return l;
          597         case LAND:
          598                 /* are two children the same protocol? */
          599                 if(f->l->op != f->r->op || f->r->op != WORD
          600                 || f->l->pr != f->r->pr || f->l->pr == nil)
          601                         break;        /* no optimization */
          602 
          603                 changed = 1;
          604 
          605                 /* constant folding */
          606                 /* if either child is childless, ignore it */
          607                 if(f->l->l == nil)
          608                         return f->r;
          609                 else if(f->r->l == nil)
          610                         return f->l;
          611 
          612                 /* move the common node up, thow away one node */
          613                 l = f->l;
          614                 f->l = _optimize(l->l);
          615                 f->r = _optimize(f->r->l);
          616                 l->l = f;
          617                 return l;
          618         }
          619         f->l = _optimize(f->l);
          620         f->r = _optimize(f->r);
          621         return f;
          622 }
          623 
          624 Filter*
          625 optimize(Filter *f)
          626 {
          627         do{
          628                 changed = 0;
          629                 f = _optimize(f);
          630         }while(changed);
          631 
          632         return f;
          633 }
          634 
          635 /*
          636  *  find any top level nodes that aren't the root
          637  */
          638 int
          639 findbogus(Filter *f)
          640 {
          641         int rv;
          642 
          643         if(f->op != WORD){
          644                 rv = findbogus(f->l);
          645                 if(f->r)
          646                         rv |= findbogus(f->r);
          647                 return rv;
          648         } else if(f->pr != root){
          649                 fprint(2, "bad top-level protocol: %s\n", f->s);
          650                 return 1;
          651         }
          652         return 0;
          653 }
          654 
          655 /*
          656  *  compile the filter
          657  */
          658 static void
          659 _compile(Filter *f, Proto *last)
          660 {
          661         if(f == nil)
          662                 return;
          663 
          664         switch(f->op){
          665         case '!':
          666                 _compile(f->l, last);
          667                 break;
          668         case LOR:
          669         case LAND:
          670                 _compile(f->l, last);
          671                 _compile(f->r, last);
          672                 break;
          673         case WORD:
          674                 if(last != nil){
          675                         if(last->compile == nil)
          676                                 sysfatal("unknown %s subprotocol: %s", f->pr->name, f->s);
          677                         (*last->compile)(f);
          678                 }
          679                 if(f->l)
          680                         _compile(f->l, f->pr);
          681                 break;
          682         case '=':
          683                 if(last == nil)
          684                         sysfatal("internal error: compilewalk: badly formed tree");
          685 
          686                 if(last->compile == nil)
          687                         sysfatal("unknown %s field: %s", f->pr->name, f->s);
          688                 (*last->compile)(f);
          689                 break;
          690         default:
          691                 sysfatal("internal error: compilewalk op: %d", f->op);
          692         }
          693 }
          694 
          695 Filter*
          696 compile(Filter *f)
          697 {
          698         if(f == nil)
          699                 return f;
          700 
          701         /* fill in the missing header filters */
          702         f = complete(f, nil);
          703 
          704         /* constant folding */
          705         f = optimize(f);
          706         if(!toflag)
          707                 printfilter(f, "after optimize");
          708 
          709         /* protocol specific compilations */
          710         _compile(f, nil);
          711 
          712         /* at this point, the root had better be the root proto */
          713         if(findbogus(f)){
          714                 fprint(2, "bogus filter\n");
          715                 exits("bad filter");
          716         }
          717 
          718         return f;
          719 }
          720 
          721 /*
          722  *  parse a byte array
          723  */
          724 int
          725 parseba(uchar *to, char *from)
          726 {
          727         char nip[4];
          728         char *p;
          729         int i;
          730 
          731         p = from;
          732         for(i = 0; i < 16; i++){
          733                 if(*p == 0)
          734                         return -1;
          735                 nip[0] = *p++;
          736                 if(*p == 0)
          737                         return -1;
          738                 nip[1] = *p++;
          739                 nip[2] = 0;
          740                 to[i] = strtoul(nip, 0, 16);
          741         }
          742         return i;
          743 }
          744 
          745 /*
          746  *  compile WORD = WORD, becomes a single node with a subop
          747  */
          748 void
          749 compile_cmp(char *proto, Filter *f, Field *fld)
          750 {
          751         uchar x[IPaddrlen];
          752 
          753         if(f->op != '=')
          754                 sysfatal("internal error: compile_cmp %s: not a cmp", proto);
          755 
          756         for(; fld->name != nil; fld++){
          757                 if(strcmp(f->l->s, fld->name) == 0){
          758                         f->op = WORD;
          759                         f->subop = fld->subop;
          760                         switch(fld->ftype){
          761                         case Fnum:
          762                                 f->ulv = atoi(f->r->s);
          763                                 break;
          764                         case Fether:
          765                                 parseether(f->a, f->r->s);
          766                                 break;
          767                         case Fv4ip:
          768                                 f->ulv = parseip(x, f->r->s);
          769                                 break;
          770                         case Fv6ip:
          771                                 parseip(f->a, f->r->s);
          772                                 break;
          773                         case Fba:
          774                                 parseba(f->a, f->r->s);
          775                                 break;
          776                         default:
          777                                 sysfatal("internal error: compile_cmp %s: %d",
          778                                         proto, fld->ftype);
          779                         }
          780                         f->l = f->r = nil;
          781                         return;
          782                 }
          783         }
          784         sysfatal("unknown %s field in: %s = %s", proto, f->l->s, f->r->s);
          785 }
          786 
          787 void
          788 _pf(Filter *f)
          789 {
          790         char *s;
          791 
          792         if(f == nil)
          793                 return;
          794 
          795         s = nil;
          796         switch(f->op){
          797         case '!':
          798                 fprint(2, "!");
          799                 _pf(f->l);
          800                 break;
          801         case WORD:
          802                 fprint(2, "%s", f->s);
          803                 if(f->l != nil){
          804                         fprint(2, "(");
          805                         _pf(f->l);
          806                         fprint(2, ")");
          807                 }
          808                 break;
          809         case LAND:
          810                 s = "&&";
          811                 goto print;
          812         case LOR:
          813                 s = "||";
          814                 goto print;
          815         case '=':
          816         print:
          817                 _pf(f->l);
          818                 if(s)
          819                         fprint(2, " %s ", s);
          820                 else
          821                         fprint(2, " %c ", f->op);
          822                 _pf(f->r);
          823                 break;
          824         default:
          825                 fprint(2, "???");
          826                 break;
          827         }
          828 }
          829 
          830 void
          831 printfilter(Filter *f, char *tag)
          832 {
          833         fprint(2, "%s: ", tag);
          834         _pf(f);
          835         fprint(2, "\n");
          836 }
          837 
          838 void
          839 cat(void)
          840 {
          841         char buf[1024];
          842         int n;
          843 
          844         while((n = read(0, buf, sizeof buf)) > 0)
          845                 write(1, buf, n);
          846 }
          847 
          848 static int fd1 = -1;
          849 void
          850 startmc(void)
          851 {
          852         int p[2];
          853 
          854         if(fd1 == -1)
          855                 fd1 = dup(1, -1);
          856 
          857         if(pipe(p) < 0)
          858                 return;
          859         switch(fork()){
          860         case -1:
          861                 return;
          862         default:
          863                 close(p[0]);
          864                 dup(p[1], 1);
          865                 if(p[1] != 1)
          866                         close(p[1]);
          867                 return;
          868         case 0:
          869                 close(p[1]);
          870                 dup(p[0], 0);
          871                 if(p[0] != 0)
          872                         close(p[0]);
          873                 execl("/bin/mc", "mc", nil);
          874                 cat();
          875                 _exits(0);
          876         }
          877 }
          878 
          879 void
          880 stopmc(void)
          881 {
          882         close(1);
          883         dup(fd1, 1);
          884         waitpid();
          885 }
          886 
          887 void
          888 printhelp(char *name)
          889 {
          890         int len;
          891         Proto *pr, **l;
          892         Mux *m;
          893         Field *f;
          894         char fmt[40];
          895 
          896         if(name == nil){
          897                 print("protocols:\n");
          898                 startmc();
          899                 for(l=protos; (pr=*l) != nil; l++)
          900                         print("  %s\n", pr->name);
          901                 stopmc();
          902                 return;
          903         }
          904 
          905         pr = findproto(name);
          906         if(pr == nil){
          907                 print("unknown protocol %s\n", name);
          908                 return;
          909         }
          910 
          911         if(pr->field){
          912                 print("%s's filter attributes:\n", pr->name);
          913                 len = 0;
          914                 for(f=pr->field; f->name; f++)
          915                         if(len < strlen(f->name))
          916                                 len = strlen(f->name);
          917                 startmc();
          918                 for(f=pr->field; f->name; f++)
          919                         print("  %-*s - %s\n", len, f->name, f->help);
          920                 stopmc();
          921         }
          922         if(pr->mux){
          923                 print("%s's subprotos:\n", pr->name);
          924                 startmc();
          925                 snprint(fmt, sizeof fmt, "  %s %%s\n", pr->valfmt);
          926                 for(m=pr->mux; m->name != nil; m++)
          927                         print(fmt, m->val, m->name);
          928                 stopmc();
          929         }
          930 }
          931 
          932 /*
          933  *  demultiplex to next prototol header
          934  */
          935 void
          936 demux(Mux *mx, ulong val1, ulong val2, Msg *m, Proto *def)
          937 {
          938         m->pr = def;
          939         for(mx = mx; mx->name != nil; mx++){
          940                 if(val1 == mx->val || val2 == mx->val){
          941                         m->pr = mx->pr;
          942                         break;
          943                 }
          944         }
          945 }
          946 
          947 /*
          948  *  default framer just assumes the input packet is
          949  *  a single read
          950  */
          951 int
          952 defaultframer(int fd, uchar *pkt, int pktlen)
          953 {
          954         return read(fd, pkt, pktlen);
          955 }