1 #include <u.h> 2 #include <libc.h> 3 #include <auth.h> 4 #include <fcall.h> 5 #include <bio.h> 6 #include <ctype.h> 7 #include <ndb.h> 8 #include <ip.h> 9 10 enum 11 { 12 Nreply= 20, 13 Maxreply= 256, 14 Maxrequest= 128, 15 Maxpath= 128, 16 Maxfdata= 8192, 17 18 Qdir= 0, 19 Qcs= 1, 20 }; 21 22 typedef struct Mfile Mfile; 23 typedef struct Mlist Mlist; 24 typedef struct Network Network; 25 typedef struct Flushreq Flushreq; 26 typedef struct Job Job; 27 28 int vers; /* incremented each clone/attach */ 29 30 struct Mfile 31 { 32 int busy; 33 34 char *user; 35 Qid qid; 36 int fid; 37 38 /* 39 * current request 40 */ 41 char *net; 42 char *host; 43 char *serv; 44 char *rem; 45 46 /* 47 * result of the last lookup 48 */ 49 Network *nextnet; 50 int nreply; 51 char *reply[Nreply]; 52 int replylen[Nreply]; 53 }; 54 55 struct Mlist 56 { 57 Mlist *next; 58 Mfile mf; 59 }; 60 61 62 // 63 // active requests 64 // 65 struct Job 66 { 67 Job *next; 68 int flushed; 69 Fcall request; 70 Fcall reply; 71 }; 72 Lock joblock; 73 Job *joblist; 74 75 Mlist *mlist; 76 int mfd[2]; 77 int debug; 78 int paranoia; 79 jmp_buf masterjmp; /* return through here after a slave process has been created */ 80 int *isslave; /* *isslave non-zero means this is a slave process */ 81 char *dbfile; 82 Ndb *db, *netdb; 83 84 void rversion(Job*); 85 void rflush(Job*); 86 void rattach(Job*, Mfile*); 87 char* rwalk(Job*, Mfile*); 88 void ropen(Job*, Mfile*); 89 void rcreate(Job*, Mfile*); 90 void rread(Job*, Mfile*); 91 void rwrite(Job*, Mfile*); 92 void rclunk(Job*, Mfile*); 93 void rremove(Job*, Mfile*); 94 void rstat(Job*, Mfile*); 95 void rwstat(Job*, Mfile*); 96 void rauth(Job*); 97 void sendmsg(Job*, char*); 98 void error(char*); 99 void mountinit(char*, char*); 100 void io(void); 101 void ndbinit(void); 102 void netinit(int); 103 void netadd(char*); 104 char *genquery(Mfile*, char*); 105 char* ipinfoquery(Mfile*, char**, int); 106 int needproto(Network*, Ndbtuple*); 107 int lookup(Mfile*); 108 Ndbtuple* reorder(Ndbtuple*, Ndbtuple*); 109 void ipid(void); 110 void readipinterfaces(void); 111 void* emalloc(int); 112 char* estrdup(char*); 113 Job* newjob(void); 114 void freejob(Job*); 115 void setext(char*, int, char*); 116 void cleanmf(Mfile*); 117 118 extern void paralloc(void); 119 120 Lock dblock; /* mutex on database operations */ 121 Lock netlock; /* mutex for netinit() */ 122 123 char *logfile = "cs"; 124 char *paranoiafile = "cs.paranoia"; 125 126 char mntpt[Maxpath]; 127 char netndb[Maxpath]; 128 129 /* 130 * Network specific translators 131 */ 132 Ndbtuple* iplookup(Network*, char*, char*, int); 133 char* iptrans(Ndbtuple*, Network*, char*, char*, int); 134 Ndbtuple* telcolookup(Network*, char*, char*, int); 135 char* telcotrans(Ndbtuple*, Network*, char*, char*, int); 136 Ndbtuple* dnsiplookup(char*, Ndbs*); 137 138 struct Network 139 { 140 char *net; 141 Ndbtuple *(*lookup)(Network*, char*, char*, int); 142 char *(*trans)(Ndbtuple*, Network*, char*, char*, int); 143 int considered; 144 int fasttimeouthack; 145 Network *next; 146 }; 147 148 enum 149 { 150 Nilfast, 151 Ntcp, 152 Nil, 153 Nudp, 154 Nicmp, 155 Nrudp, 156 Ntelco, 157 }; 158 159 /* 160 * net doesn't apply to udp, icmp, or telco (for speed) 161 */ 162 Network network[] = { 163 [Nilfast] { "il", iplookup, iptrans, 0, 1 }, 164 [Ntcp] { "tcp", iplookup, iptrans, 0, 0 }, 165 [Nil] { "il", iplookup, iptrans, 0, 0 }, 166 [Nudp] { "udp", iplookup, iptrans, 1, 0 }, 167 [Nicmp] { "icmp", iplookup, iptrans, 1, 0 }, 168 [Nrudp] { "rudp", iplookup, iptrans, 1, 0 }, 169 [Ntelco] { "telco", telcolookup, telcotrans, 1, 0 }, 170 { 0 }, 171 }; 172 173 Lock ipifclock; 174 Ipifc *ipifcs; 175 176 char eaddr[Ndbvlen]; /* ascii ethernet address */ 177 char ipaddr[Ndbvlen]; /* ascii internet address */ 178 uchar ipa[IPaddrlen]; /* binary internet address */ 179 char mysysname[Ndbvlen]; 180 181 Network *netlist; /* networks ordered by preference */ 182 Network *last; 183 184 void 185 usage(void) 186 { 187 fprint(2, "usage: %s [-d] [-f ndb-file] [-x netmtpt] [-n]\n", argv0); 188 exits("usage"); 189 } 190 191 void 192 main(int argc, char *argv[]) 193 { 194 char servefile[Maxpath]; 195 int justsetname; 196 char *p; 197 char ext[Maxpath]; 198 199 justsetname = 0; 200 setnetmtpt(mntpt, sizeof(mntpt), nil); 201 ext[0] = 0; 202 ARGBEGIN{ 203 case 'd': 204 debug = 1; 205 break; 206 case 'f': 207 p = ARGF(); 208 if(p == nil) 209 usage(); 210 dbfile = p; 211 break; 212 case 'x': 213 p = ARGF(); 214 if(p == nil) 215 usage(); 216 setnetmtpt(mntpt, sizeof(mntpt), p); 217 setext(ext, sizeof(ext), mntpt); 218 break; 219 case 'n': 220 justsetname = 1; 221 break; 222 }ARGEND 223 USED(argc); 224 USED(argv); 225 226 rfork(RFREND|RFNOTEG); 227 228 snprint(servefile, sizeof(servefile), "#s/cs%s", ext); 229 snprint(netndb, sizeof(netndb), "%s/ndb", mntpt); 230 unmount(servefile, mntpt); 231 remove(servefile); 232 233 fmtinstall('E', eipfmt); 234 fmtinstall('I', eipfmt); 235 fmtinstall('M', eipfmt); 236 fmtinstall('F', fcallfmt); 237 238 ndbinit(); 239 netinit(0); 240 241 if(!justsetname){ 242 mountinit(servefile, mntpt); 243 io(); 244 } 245 exits(0); 246 } 247 248 /* 249 * if a mount point is specified, set the cs extention to be the mount point 250 * with '_'s replacing '/'s 251 */ 252 void 253 setext(char *ext, int n, char *p) 254 { 255 int i, c; 256 257 n--; 258 for(i = 0; i < n; i++){ 259 c = p[i]; 260 if(c == 0) 261 break; 262 if(c == '/') 263 c = '_'; 264 ext[i] = c; 265 } 266 ext[i] = 0; 267 } 268 269 void 270 mountinit(char *service, char *mntpt) 271 { 272 int f; 273 int p[2]; 274 char buf[32]; 275 276 if(pipe(p) < 0) 277 error("pipe failed"); 278 switch(rfork(RFFDG|RFPROC|RFNAMEG)){ 279 case 0: 280 close(p[1]); 281 break; 282 case -1: 283 error("fork failed\n"); 284 default: 285 /* 286 * make a /srv/cs 287 */ 288 f = create(service, 1, 0666); 289 if(f < 0) 290 error(service); 291 snprint(buf, sizeof(buf), "%d", p[1]); 292 if(write(f, buf, strlen(buf)) != strlen(buf)) 293 error("write /srv/cs"); 294 close(f); 295 296 /* 297 * put ourselves into the file system 298 */ 299 close(p[0]); 300 if(mount(p[1], -1, mntpt, MAFTER, "") < 0) 301 error("mount failed\n"); 302 _exits(0); 303 } 304 mfd[0] = mfd[1] = p[0]; 305 } 306 307 void 308 ndbinit(void) 309 { 310 db = ndbopen(dbfile); 311 if(db == nil) 312 error("can't open network database"); 313 314 netdb = ndbopen(netndb); 315 if(netdb != nil){ 316 netdb->nohash = 1; 317 db = ndbcat(netdb, db); 318 } 319 } 320 321 Mfile* 322 newfid(int fid) 323 { 324 Mlist *f, *ff; 325 Mfile *mf; 326 327 ff = 0; 328 for(f = mlist; f; f = f->next) 329 if(f->mf.busy && f->mf.fid == fid) 330 return &f->mf; 331 else if(!ff && !f->mf.busy) 332 ff = f; 333 if(ff == 0){ 334 ff = emalloc(sizeof *f); 335 ff->next = mlist; 336 mlist = ff; 337 } 338 mf = &ff->mf; 339 memset(mf, 0, sizeof *mf); 340 mf->fid = fid; 341 return mf; 342 } 343 344 Job* 345 newjob(void) 346 { 347 Job *job; 348 349 job = mallocz(sizeof(Job), 1); 350 lock(&joblock); 351 job->next = joblist; 352 joblist = job; 353 job->request.tag = -1; 354 unlock(&joblock); 355 return job; 356 } 357 358 void 359 freejob(Job *job) 360 { 361 Job **l; 362 363 lock(&joblock); 364 for(l = &joblist; *l; l = &(*l)->next){ 365 if((*l) == job){ 366 *l = job->next; 367 free(job); 368 break; 369 } 370 } 371 unlock(&joblock); 372 } 373 374 void 375 flushjob(int tag) 376 { 377 Job *job; 378 379 lock(&joblock); 380 for(job = joblist; job; job = job->next){ 381 if(job->request.tag == tag && job->request.type != Tflush){ 382 job->flushed = 1; 383 break; 384 } 385 } 386 unlock(&joblock); 387 } 388 389 void 390 io(void) 391 { 392 long n; 393 Mfile *mf; 394 int slaveflag; 395 uchar mdata[IOHDRSZ + Maxfdata]; 396 Job *job; 397 398 /* 399 * if we ask dns to fulfill requests, 400 * a slave process is created to wait for replies. The 401 * master process returns immediately via a longjmp 402 * through 'masterjmp'. 403 * 404 * *isslave is a pointer into the call stack to a variable 405 * that tells whether or not the current process is a slave. 406 */ 407 slaveflag = 0; /* init slave variable */ 408 isslave = &slaveflag; 409 setjmp(masterjmp); 410 411 for(;;){ 412 n = read9pmsg(mfd[0], mdata, sizeof mdata); 413 if(n<=0) 414 error("mount read"); 415 job = newjob(); 416 if(convM2S(mdata, n, &job->request) != n){ 417 syslog(1, logfile, "format error %ux %ux %ux %ux %ux", mdata[0], mdata[1], mdata[2], mdata[3], mdata[4]); 418 freejob(job); 419 continue; 420 } 421 if(job->request.fid<0) 422 error("fid out of range"); 423 lock(&dblock); 424 mf = newfid(job->request.fid); 425 if(debug) 426 syslog(0, logfile, "%F", &job->request); 427 428 429 switch(job->request.type){ 430 default: 431 syslog(1, logfile, "unknown request type %d", job->request.type); 432 break; 433 case Tversion: 434 rversion(job); 435 break; 436 case Tauth: 437 rauth(job); 438 break; 439 case Tflush: 440 rflush(job); 441 break; 442 case Tattach: 443 rattach(job, mf); 444 break; 445 case Twalk: 446 rwalk(job, mf); 447 break; 448 case Topen: 449 ropen(job, mf); 450 break; 451 case Tcreate: 452 rcreate(job, mf); 453 break; 454 case Tread: 455 rread(job, mf); 456 break; 457 case Twrite: 458 rwrite(job, mf); 459 break; 460 case Tclunk: 461 rclunk(job, mf); 462 break; 463 case Tremove: 464 rremove(job, mf); 465 break; 466 case Tstat: 467 rstat(job, mf); 468 break; 469 case Twstat: 470 rwstat(job, mf); 471 break; 472 } 473 unlock(&dblock); 474 475 freejob(job); 476 477 /* 478 * slave processes die after replying 479 */ 480 if(*isslave){ 481 if(debug) 482 syslog(0, logfile, "slave death %d", getpid()); 483 _exits(0); 484 } 485 } 486 } 487 488 void 489 rversion(Job *job) 490 { 491 if(job->request.msize > IOHDRSZ + Maxfdata) 492 job->reply.msize = IOHDRSZ + Maxfdata; 493 else 494 job->reply.msize = job->request.msize; 495 if(strncmp(job->request.version, "9P2000", 6) != 0) 496 sendmsg(job, "unknown 9P version"); 497 else{ 498 job->reply.version = "9P2000"; 499 sendmsg(job, 0); 500 } 501 } 502 503 void 504 rauth(Job *job) 505 { 506 sendmsg(job, "no authentication required"); 507 } 508 509 /* 510 * don't flush till all the slaves are done 511 */ 512 void 513 rflush(Job *job) 514 { 515 flushjob(job->request.oldtag); 516 sendmsg(job, 0); 517 } 518 519 void 520 rattach(Job *job, Mfile *mf) 521 { 522 if(mf->busy == 0){ 523 mf->busy = 1; 524 mf->user = estrdup(job->request.uname); 525 } 526 mf->qid.vers = vers++; 527 mf->qid.type = QTDIR; 528 mf->qid.path = 0LL; 529 job->reply.qid = mf->qid; 530 sendmsg(job, 0); 531 } 532 533 534 char* 535 rwalk(Job *job, Mfile *mf) 536 { 537 char *err; 538 char **elems; 539 int nelems; 540 int i; 541 Mfile *nmf; 542 Qid qid; 543 544 err = 0; 545 nmf = nil; 546 elems = job->request.wname; 547 nelems = job->request.nwname; 548 job->reply.nwqid = 0; 549 550 if(job->request.newfid != job->request.fid){ 551 /* clone fid */ 552 if(job->request.newfid<0){ 553 err = "clone newfid out of range"; 554 goto send; 555 } 556 nmf = newfid(job->request.newfid); 557 if(nmf->busy){ 558 nmf = nil; 559 err = "clone to used channel"; 560 goto send; 561 } 562 *nmf = *mf; 563 nmf->user = estrdup(mf->user); 564 nmf->fid = job->request.newfid; 565 nmf->qid.vers = vers++; 566 mf = nmf; 567 } 568 /* else nmf will be nil */ 569 570 qid = mf->qid; 571 if(nelems > 0){ 572 /* walk fid */ 573 for(i=0; i<nelems && i<MAXWELEM; i++){ 574 if((qid.type & QTDIR) == 0){ 575 err = "not a directory"; 576 break; 577 } 578 if(strcmp(elems[i], "..") == 0 || strcmp(elems[i], ".") == 0){ 579 qid.type = QTDIR; 580 qid.path = Qdir; 581 Found: 582 job->reply.wqid[i] = qid; 583 job->reply.nwqid++; 584 continue; 585 } 586 if(strcmp(elems[i], "cs") == 0){ 587 qid.type = QTFILE; 588 qid.path = Qcs; 589 goto Found; 590 } 591 err = "file does not exist"; 592 break; 593 } 594 } 595 596 send: 597 if(nmf != nil && (err!=nil || job->reply.nwqid<nelems)){ 598 nmf->busy = 0; 599 nmf->fid = 0; 600 } 601 if(err == nil) 602 mf->qid = qid; 603 sendmsg(job, err); 604 return err; 605 } 606 607 void 608 ropen(Job *job, Mfile *mf) 609 { 610 int mode; 611 char *err; 612 613 err = 0; 614 mode = job->request.mode; 615 if(mf->qid.type & QTDIR){ 616 if(mode) 617 err = "permission denied"; 618 } 619 job->reply.qid = mf->qid; 620 job->reply.iounit = 0; 621 sendmsg(job, err); 622 } 623 624 void 625 rcreate(Job *job, Mfile *mf) 626 { 627 USED(mf); 628 sendmsg(job, "creation permission denied"); 629 } 630 631 void 632 rread(Job *job, Mfile *mf) 633 { 634 int i, n, cnt; 635 long off, toff, clock; 636 Dir dir; 637 uchar buf[Maxfdata]; 638 char *err; 639 640 n = 0; 641 err = 0; 642 off = job->request.offset; 643 cnt = job->request.count; 644 if(mf->qid.type & QTDIR){ 645 clock = time(0); 646 if(off == 0){ 647 dir.name = "cs"; 648 dir.qid.type = QTFILE; 649 dir.qid.vers = vers; 650 dir.qid.path = Qcs; 651 dir.mode = 0666; 652 dir.length = 0; 653 dir.uid = mf->user; 654 dir.gid = mf->user; 655 dir.muid = mf->user; 656 dir.atime = clock; /* wrong */ 657 dir.mtime = clock; /* wrong */ 658 n = convD2M(&dir, buf, sizeof buf); 659 } 660 job->reply.data = (char*)buf; 661 } else { 662 for(;;){ 663 /* look for an answer at the right offset */ 664 toff = 0; 665 for(i = 0; mf->reply[i] && i < mf->nreply; i++){ 666 n = mf->replylen[i]; 667 if(off < toff + n) 668 break; 669 toff += n; 670 } 671 if(i < mf->nreply) 672 break; /* got something to return */ 673 674 /* try looking up more answers */ 675 if(lookup(mf) == 0){ 676 /* no more */ 677 n = 0; 678 goto send; 679 } 680 } 681 682 /* give back a single reply (or part of one) */ 683 job->reply.data = mf->reply[i] + (off - toff); 684 if(cnt > toff - off + n) 685 n = toff - off + n; 686 else 687 n = cnt; 688 } 689 send: 690 job->reply.count = n; 691 sendmsg(job, err); 692 } 693 void 694 cleanmf(Mfile *mf) 695 { 696 int i; 697 698 if(mf->net != nil){ 699 free(mf->net); 700 mf->net = nil; 701 } 702 if(mf->host != nil){ 703 free(mf->host); 704 mf->host = nil; 705 } 706 if(mf->serv != nil){ 707 free(mf->serv); 708 mf->serv = nil; 709 } 710 if(mf->rem != nil){ 711 free(mf->rem); 712 mf->rem = nil; 713 } 714 if(mf->user != nil){ 715 free(mf->user); 716 mf->user = nil; 717 } 718 for(i = 0; i < mf->nreply; i++){ 719 free(mf->reply[i]); 720 mf->reply[i] = nil; 721 mf->replylen[i] = 0; 722 } 723 mf->nreply = 0; 724 mf->nextnet = netlist; 725 } 726 727 void 728 rwrite(Job *job, Mfile *mf) 729 { 730 int cnt, n; 731 char *err; 732 char *field[4]; 733 734 err = 0; 735 cnt = job->request.count; 736 if(mf->qid.type & QTDIR){ 737 err = "can't write directory"; 738 goto send; 739 } 740 if(cnt >= Maxrequest){ 741 err = "request too long"; 742 goto send; 743 } 744 job->request.data[cnt] = 0; 745 746 /* 747 * toggle debugging 748 */ 749 if(strncmp(job->request.data, "debug", 5)==0){ 750 debug ^= 1; 751 syslog(1, logfile, "debug %d", debug); 752 goto send; 753 } 754 755 /* 756 * toggle debugging 757 */ 758 if(strncmp(job->request.data, "paranoia", 8)==0){ 759 paranoia ^= 1; 760 syslog(1, logfile, "paranoia %d", paranoia); 761 goto send; 762 } 763 764 /* 765 * add networks to the default list 766 */ 767 if(strncmp(job->request.data, "add ", 4)==0){ 768 if(job->request.data[cnt-1] == '\n') 769 job->request.data[cnt-1] = 0; 770 netadd(job->request.data+4); 771 readipinterfaces(); 772 goto send; 773 } 774 775 /* 776 * refresh all state 777 */ 778 if(strncmp(job->request.data, "refresh", 7)==0){ 779 netinit(1); 780 goto send; 781 } 782 783 /* start transaction with a clean slate */ 784 cleanmf(mf); 785 786 /* 787 * look for a general query 788 */ 789 if(*job->request.data == '!'){ 790 err = genquery(mf, job->request.data+1); 791 goto send; 792 } 793 794 if(debug) 795 syslog(0, logfile, "write %s", job->request.data); 796 if(paranoia) 797 syslog(0, paranoiafile, "write %s by %s", job->request.data, mf->user); 798 799 /* 800 * break up name 801 */ 802 n = getfields(job->request.data, field, 4, 1, "!"); 803 switch(n){ 804 case 1: 805 mf->net = strdup("net"); 806 mf->host = strdup(field[0]); 807 break; 808 case 4: 809 mf->rem = strdup(field[3]); 810 /* fall through */ 811 case 3: 812 mf->serv = strdup(field[2]); 813 /* fall through */ 814 case 2: 815 mf->host = strdup(field[1]); 816 mf->net = strdup(field[0]); 817 break; 818 } 819 820 /* 821 * do the first net worth of lookup 822 */ 823 if(lookup(mf) == 0) 824 err = "can't translate address"; 825 send: 826 job->reply.count = cnt; 827 sendmsg(job, err); 828 } 829 830 void 831 rclunk(Job *job, Mfile *mf) 832 { 833 cleanmf(mf); 834 mf->busy = 0; 835 mf->fid = 0; 836 sendmsg(job, 0); 837 } 838 839 void 840 rremove(Job *job, Mfile *mf) 841 { 842 USED(mf); 843 sendmsg(job, "remove permission denied"); 844 } 845 846 void 847 rstat(Job *job, Mfile *mf) 848 { 849 Dir dir; 850 uchar buf[IOHDRSZ+Maxfdata]; 851 852 if(mf->qid.type & QTDIR){ 853 dir.name = "."; 854 dir.mode = DMDIR|0555; 855 } else { 856 dir.name = "cs"; 857 dir.mode = 0666; 858 } 859 dir.qid = mf->qid; 860 dir.length = 0; 861 dir.uid = mf->user; 862 dir.gid = mf->user; 863 dir.muid = mf->user; 864 dir.atime = dir.mtime = time(0); 865 job->reply.nstat = convD2M(&dir, buf, sizeof buf); 866 job->reply.stat = buf; 867 sendmsg(job, 0); 868 } 869 870 void 871 rwstat(Job *job, Mfile *mf) 872 { 873 USED(mf); 874 sendmsg(job, "wstat permission denied"); 875 } 876 877 void 878 sendmsg(Job *job, char *err) 879 { 880 int n; 881 uchar mdata[IOHDRSZ + Maxfdata]; 882 char ename[ERRMAX]; 883 884 if(err){ 885 job->reply.type = Rerror; 886 snprint(ename, sizeof(ename), "cs: %s", err); 887 job->reply.ename = ename; 888 }else{ 889 job->reply.type = job->request.type+1; 890 } 891 job->reply.tag = job->request.tag; 892 n = convS2M(&job->reply, mdata, sizeof mdata); 893 if(n == 0){ 894 syslog(1, logfile, "sendmsg convS2M of %F returns 0", &job->reply); 895 abort(); 896 } 897 lock(&joblock); 898 if(job->flushed == 0) 899 if(write(mfd[1], mdata, n)!=n) 900 error("mount write"); 901 unlock(&joblock); 902 if(debug) 903 syslog(0, logfile, "%F %d", &job->reply, n); 904 } 905 906 void 907 error(char *s) 908 { 909 syslog(1, "cs", "%s: %r", s); 910 _exits(0); 911 } 912 913 static int 914 isvalidip(uchar *ip) 915 { 916 return ipcmp(ip, IPnoaddr) != 0 && ipcmp(ip, v4prefix) != 0; 917 } 918 919 void 920 readipinterfaces(void) 921 { 922 Ipifc *nifc; 923 Iplifc *lifc; 924 925 ipifcs = readipifc(mntpt, ipifcs, -1); 926 for(nifc = ipifcs; nifc; nifc = nifc->next) 927 for(lifc = nifc->lifc; lifc; lifc = lifc->next) 928 if(ipcmp(lifc->ip, IPnoaddr) != 0){ 929 ipmove(ipa, lifc->ip); 930 sprint(ipaddr, "%I", ipa); 931 if(debug) 932 syslog(0, "dns", "ipaddr is %s\n", ipaddr); 933 } 934 ipmove(ipa, IPnoaddr); 935 } 936 937 /* 938 * get the system name 939 */ 940 void 941 ipid(void) 942 { 943 uchar addr[6]; 944 Ndbtuple *t; 945 char *p, *attr; 946 Ndbs s; 947 int f; 948 char buf[Maxpath]; 949 950 951 /* use environment, ether addr, or ipaddr to get system name */ 952 if(*mysysname == 0){ 953 /* 954 * environment has priority. 955 * 956 * on the sgi power the default system name 957 * is the ip address. ignore that. 958 * 959 */ 960 p = getenv("sysname"); 961 if(p){ 962 attr = ipattr(p); 963 if(strcmp(attr, "ip") != 0) 964 strcpy(mysysname, p); 965 } 966 967 /* 968 * the /net/ndb contains what the network 969 * figured out from DHCP. use that name if 970 * there is one. 971 */ 972 if(*mysysname == 0 && netdb != nil){ 973 ndbreopen(netdb); 974 for(t = ndbparse(netdb); t != nil; t = t->entry){ 975 if(strcmp(t->attr, "sys") == 0){ 976 strcpy(mysysname, t->val); 977 break; 978 } 979 } 980 ndbfree(t); 981 } 982 983 /* next network database, ip address, and ether address to find a name */ 984 if(*mysysname == 0){ 985 t = nil; 986 if(isvalidip(ipa)) 987 t = ndbgetval(db, &s, "ip", ipaddr, "sys", mysysname); 988 else { 989 for(f = 0; f < 3; f++){ 990 snprint(buf, sizeof buf, "%s/ether%d", mntpt, f); 991 if(myetheraddr(addr, buf) >= 0){ 992 snprint(eaddr, sizeof(eaddr), "%E", addr); 993 t = ndbgetval(db, &s, "ether", eaddr, "sys", 994 mysysname); 995 if(t != nil) 996 break; 997 } 998 } 999 } 1000 ndbfree(t); 1001 } 1002 1003 /* nothing else worked, use the ip address */ 1004 if(*mysysname == 0 && isvalidip(ipa)) 1005 strcpy(mysysname, ipaddr); 1006 1007 1008 /* set /dev/mysysname if we now know it */ 1009 if(*mysysname){ 1010 f = open("/dev/sysname", OWRITE); 1011 if(f >= 0){ 1012 write(f, mysysname, strlen(mysysname)); 1013 close(f); 1014 } 1015 } 1016 } 1017 } 1018 1019 /* 1020 * Set up a list of default networks by looking for 1021 * /net/ * /clone. 1022 */ 1023 void 1024 netinit(int background) 1025 { 1026 char clone[Maxpath]; 1027 Network *np; 1028 static int working; 1029 1030 if(background){ 1031 switch(rfork(RFPROC|RFNOTEG|RFMEM|RFNOWAIT)){ 1032 case 0: 1033 break; 1034 default: 1035 return; 1036 } 1037 lock(&netlock); 1038 } 1039 1040 /* add the mounted networks to the default list */ 1041 for(np = network; np->net; np++){ 1042 if(np->considered) 1043 continue; 1044 snprint(clone, sizeof(clone), "%s/%s/clone", mntpt, np->net); 1045 if(access(clone, AEXIST) < 0) 1046 continue; 1047 if(netlist) 1048 last->next = np; 1049 else 1050 netlist = np; 1051 last = np; 1052 np->next = 0; 1053 np->considered = 1; 1054 } 1055 1056 /* find out what our ip address is */ 1057 readipinterfaces(); 1058 1059 /* set the system name if we need to, these says ip is all we have */ 1060 ipid(); 1061 1062 if(debug) 1063 syslog(0, logfile, "mysysname %s eaddr %s ipaddr %s ipa %I\n", 1064 mysysname, eaddr, ipaddr, ipa); 1065 1066 if(background){ 1067 unlock(&netlock); 1068 _exits(0); 1069 } 1070 } 1071 1072 /* 1073 * add networks to the standard list 1074 */ 1075 void 1076 netadd(char *p) 1077 { 1078 Network *np; 1079 char *field[12]; 1080 int i, n; 1081 1082 n = getfields(p, field, 12, 1, " "); 1083 for(i = 0; i < n; i++){ 1084 for(np = network; np->net; np++){ 1085 if(strcmp(field[i], np->net) != 0) 1086 continue; 1087 if(np->considered) 1088 break; 1089 if(netlist) 1090 last->next = np; 1091 else 1092 netlist = np; 1093 last = np; 1094 np->next = 0; 1095 np->considered = 1; 1096 } 1097 } 1098 } 1099 1100 /* 1101 * make a tuple 1102 */ 1103 Ndbtuple* 1104 mktuple(char *attr, char *val) 1105 { 1106 Ndbtuple *t; 1107 1108 t = emalloc(sizeof(Ndbtuple)); 1109 strcpy(t->attr, attr); 1110 strncpy(t->val, val, sizeof(t->val)); 1111 t->val[sizeof(t->val)-1] = 0; 1112 t->line = t; 1113 t->entry = 0; 1114 return t; 1115 } 1116 1117 int 1118 lookforproto(Ndbtuple *t, char *proto) 1119 { 1120 for(; t != nil; t = t->entry) 1121 if(strcmp(t->attr, "proto") == 0 && strcmp(t->val, proto) == 0) 1122 return 1; 1123 return 0; 1124 } 1125 1126 /* 1127 * lookup a request. the network "net" means we should pick the 1128 * best network to get there. 1129 */ 1130 int 1131 lookup(Mfile *mf) 1132 { 1133 Network *np; 1134 char *cp; 1135 Ndbtuple *nt, *t; 1136 char reply[Maxreply]; 1137 int i, rv; 1138 int hack; 1139 1140 /* open up the standard db files */ 1141 if(db == 0) 1142 ndbinit(); 1143 if(db == 0) 1144 error("can't open mf->network database\n"); 1145 1146 rv = 0; 1147 1148 if(mf->net == nil) 1149 return 0; /* must have been a genquery */ 1150 1151 if(strcmp(mf->net, "net") == 0){ 1152 /* 1153 * go through set of default nets 1154 */ 1155 for(np = mf->nextnet; np; np = np->next){ 1156 nt = (*np->lookup)(np, mf->host, mf->serv, 1); 1157 if(nt == nil) 1158 continue; 1159 hack = np->fasttimeouthack && !lookforproto(nt, np->net); 1160 for(t = nt; mf->nreply < Nreply && t; t = t->entry){ 1161 cp = (*np->trans)(t, np, mf->serv, mf->rem, hack); 1162 if(cp){ 1163 /* avoid duplicates */ 1164 for(i = 0; i < mf->nreply; i++) 1165 if(strcmp(mf->reply[i], cp) == 0) 1166 break; 1167 if(i == mf->nreply){ 1168 /* save the reply */ 1169 mf->replylen[mf->nreply] = strlen(cp); 1170 mf->reply[mf->nreply++] = cp; 1171 rv++; 1172 } 1173 } 1174 } 1175 ndbfree(nt); 1176 np = np->next; 1177 break; 1178 } 1179 mf->nextnet = np; 1180 return rv; 1181 } 1182 1183 /* 1184 * if not /net, we only get one lookup 1185 */ 1186 if(mf->nreply != 0) 1187 return 0; 1188 1189 /* 1190 * look for a specific network 1191 */ 1192 for(np = netlist; np->net != nil; np++){ 1193 if(np->fasttimeouthack) 1194 continue; 1195 if(strcmp(np->net, mf->net) == 0) 1196 break; 1197 } 1198 1199 if(np->net != nil){ 1200 /* 1201 * known network 1202 */ 1203 nt = (*np->lookup)(np, mf->host, mf->serv, 1); 1204 for(t = nt; mf->nreply < Nreply && t; t = t->entry){ 1205 cp = (*np->trans)(t, np, mf->serv, mf->rem, 0); 1206 if(cp){ 1207 mf->replylen[mf->nreply] = strlen(cp); 1208 mf->reply[mf->nreply++] = cp; 1209 rv++; 1210 } 1211 } 1212 ndbfree(nt); 1213 return rv; 1214 } else { 1215 /* 1216 * not a known network, don't translate host or service 1217 */ 1218 if(mf->serv) 1219 snprint(reply, sizeof(reply), "%s/%s/clone %s!%s", 1220 mntpt, mf->net, mf->host, mf->serv); 1221 else 1222 snprint(reply, sizeof(reply), "%s/%s/clone %s", 1223 mntpt, mf->net, mf->host); 1224 mf->reply[0] = strdup(reply); 1225 mf->replylen[0] = strlen(reply); 1226 mf->nreply = 1; 1227 return 1; 1228 } 1229 } 1230 1231 /* 1232 * translate an ip service name into a port number. If it's a numeric port 1233 * number, look for restricted access. 1234 * 1235 * the service '*' needs no translation. 1236 */ 1237 char* 1238 ipserv(Network *np, char *name, char *buf) 1239 { 1240 char *p; 1241 int alpha = 0; 1242 int restr = 0; 1243 char port[Ndbvlen]; 1244 Ndbtuple *t, *nt; 1245 Ndbs s; 1246 1247 /* '*' means any service */ 1248 if(strcmp(name, "*")==0){ 1249 strcpy(buf, name); 1250 return buf; 1251 } 1252 1253 /* see if it's numeric or symbolic */ 1254 port[0] = 0; 1255 for(p = name; *p; p++){ 1256 if(isdigit(*p)) 1257 {} 1258 else if(isalpha(*p) || *p == '-' || *p == '$') 1259 alpha = 1; 1260 else 1261 return 0; 1262 } 1263 if(alpha){ 1264 t = ndbgetval(db, &s, np->net, name, "port", port); 1265 if(t == 0) 1266 return 0; 1267 } else { 1268 t = ndbgetval(db, &s, "port", name, "port", port); 1269 if(t == 0){ 1270 strncpy(port, name, sizeof(port)); 1271 port[sizeof(port)-1] = 0; 1272 } 1273 } 1274 1275 if(t){ 1276 for(nt = t; nt; nt = nt->entry) 1277 if(strcmp(nt->attr, "restricted") == 0) 1278 restr = 1; 1279 ndbfree(t); 1280 } 1281 sprint(buf, "%s%s", port, restr ? "!r" : ""); 1282 return buf; 1283 } 1284 1285 /* 1286 * lookup an ip attribute 1287 */ 1288 int 1289 ipattrlookup(Ndb *db, char *ipa, char *attr, char *val) 1290 { 1291 1292 Ndbtuple *t, *nt; 1293 char *alist[2]; 1294 1295 alist[0] = attr; 1296 t = ndbipinfo(db, "ip", ipa, alist, 1); 1297 if(t == nil) 1298 return 0; 1299 for(nt = t; nt != nil; nt = nt->entry){ 1300 if(strcmp(nt->attr, attr) == 0){ 1301 strcpy(val, nt->val); 1302 ndbfree(t); 1303 return 1; 1304 } 1305 } 1306 1307 /* we shouldn't get here */ 1308 ndbfree(t); 1309 return 0; 1310 } 1311 1312 /* 1313 * lookup (and translate) an ip destination 1314 */ 1315 Ndbtuple* 1316 iplookup(Network *np, char *host, char *serv, int nolookup) 1317 { 1318 char *attr; 1319 Ndbtuple *t, *nt; 1320 Ndbs s; 1321 char ts[Ndbvlen+1]; 1322 char th[Ndbvlen+1]; 1323 char dollar[Ndbvlen+1]; 1324 uchar ip[IPaddrlen]; 1325 uchar net[IPaddrlen]; 1326 uchar tnet[IPaddrlen]; 1327 Ipifc *ifc; 1328 Iplifc *lifc; 1329 1330 USED(nolookup); 1331 1332 /* 1333 * start with the service since it's the most likely to fail 1334 * and costs the least 1335 */ 1336 if(serv==0 || ipserv(np, serv, ts) == 0){ 1337 werrstr("can't translate address"); 1338 return 0; 1339 } 1340 1341 /* for dial strings with no host */ 1342 if(strcmp(host, "*") == 0) 1343 return mktuple("ip", "*"); 1344 1345 /* 1346 * hack till we go v6 :: = 0.0.0.0 1347 */ 1348 if(strcmp("::", host) == 0) 1349 return mktuple("ip", "*"); 1350 1351 /* 1352 * '$' means the rest of the name is an attribute that we 1353 * need to search for 1354 */ 1355 if(*host == '$'){ 1356 if(ipattrlookup(db, ipaddr, host+1, dollar)) 1357 host = dollar; 1358 } 1359 1360 /* 1361 * turn '[ip address]' into just 'ip address' 1362 */ 1363 if(*host == '[' && host[strlen(host)-1] == ']'){ 1364 host++; 1365 host[strlen(host)-1] = 0; 1366 } 1367 1368 /* 1369 * just accept addresses 1370 */ 1371 attr = ipattr(host); 1372 if(strcmp(attr, "ip") == 0) 1373 return mktuple("ip", host); 1374 1375 /* 1376 * give the domain name server the first opportunity to 1377 * resolve domain names. if that fails try the database. 1378 */ 1379 t = 0; 1380 if(strcmp(attr, "dom") == 0) 1381 t = dnsiplookup(host, &s); 1382 if(t == 0) 1383 t = ndbgetval(db, &s, attr, host, "ip", th); 1384 if(t == 0) 1385 t = dnsiplookup(host, &s); 1386 if(t == 0 && strcmp(attr, "dom") != 0) 1387 t = dnsiplookup(host, &s); 1388 if(t == 0) 1389 return 0; 1390 1391 /* 1392 * reorder the tuple to have the matched line first and 1393 * save that in the request structure. 1394 */ 1395 t = reorder(t, s.t); 1396 1397 /* 1398 * reorder according to our interfaces 1399 */ 1400 lock(&ipifclock); 1401 for(ifc = ipifcs; ifc != nil; ifc = ifc->next){ 1402 for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){ 1403 maskip(lifc->ip, lifc->mask, net); 1404 for(nt = t; nt; nt = nt->entry){ 1405 if(strcmp(nt->attr, "ip") != 0) 1406 continue; 1407 parseip(ip, nt->val); 1408 maskip(ip, lifc->mask, tnet); 1409 if(memcmp(net, tnet, IPaddrlen) == 0){ 1410 t = reorder(t, nt); 1411 unlock(&ipifclock); 1412 return t; 1413 } 1414 } 1415 } 1416 } 1417 unlock(&ipifclock); 1418 1419 return t; 1420 } 1421 1422 /* 1423 * translate an ip address 1424 */ 1425 char* 1426 iptrans(Ndbtuple *t, Network *np, char *serv, char *rem, int hack) 1427 { 1428 char ts[Ndbvlen+1]; 1429 char reply[Maxreply]; 1430 char x[Ndbvlen+1]; 1431 1432 if(strcmp(t->attr, "ip") != 0) 1433 return 0; 1434 1435 if(serv == 0 || ipserv(np, serv, ts) == 0) 1436 return 0; 1437 1438 if(rem != nil) 1439 snprint(x, sizeof(x), "!%s", rem); 1440 else 1441 *x = 0; 1442 1443 if(*t->val == '*') 1444 snprint(reply, sizeof(reply), "%s/%s/clone %s%s", 1445 mntpt, np->net, ts, x); 1446 else 1447 snprint(reply, sizeof(reply), "%s/%s/clone %s!%s%s%s", 1448 mntpt, np->net, t->val, ts, x, hack?"!fasttimeout":""); 1449 1450 return strdup(reply); 1451 } 1452 1453 /* 1454 * lookup a telephone number 1455 */ 1456 Ndbtuple* 1457 telcolookup(Network *np, char *host, char *serv, int nolookup) 1458 { 1459 Ndbtuple *t; 1460 Ndbs s; 1461 char th[Ndbvlen+1]; 1462 1463 USED(np, nolookup, serv); 1464 1465 t = ndbgetval(db, &s, "sys", host, "telco", th); 1466 if(t == 0) 1467 return mktuple("telco", host); 1468 1469 return reorder(t, s.t); 1470 } 1471 1472 /* 1473 * translate a telephone address 1474 */ 1475 char* 1476 telcotrans(Ndbtuple *t, Network *np, char *serv, char *rem, int) 1477 { 1478 char reply[Maxreply]; 1479 char x[Ndbvlen+1]; 1480 1481 if(strcmp(t->attr, "telco") != 0) 1482 return 0; 1483 1484 if(rem != nil) 1485 snprint(x, sizeof(x), "!%s", rem); 1486 else 1487 *x = 0; 1488 if(serv) 1489 snprint(reply, sizeof(reply), "%s/%s/clone %s!%s%s", mntpt, np->net, 1490 t->val, serv, x); 1491 else 1492 snprint(reply, sizeof(reply), "%s/%s/clone %s%s", mntpt, np->net, 1493 t->val, x); 1494 return strdup(reply); 1495 } 1496 1497 /* 1498 * reorder the tuple to put x's line first in the entry 1499 */ 1500 Ndbtuple* 1501 reorder(Ndbtuple *t, Ndbtuple *x) 1502 { 1503 Ndbtuple *nt; 1504 Ndbtuple *line; 1505 1506 /* find start of this entry's line */ 1507 for(line = x; line->entry == line->line; line = line->line) 1508 ; 1509 line = line->line; 1510 if(line == t) 1511 return t; /* already the first line */ 1512 1513 /* remove this line and everything after it from the entry */ 1514 for(nt = t; nt->entry != line; nt = nt->entry) 1515 ; 1516 nt->entry = 0; 1517 1518 /* make that the start of the entry */ 1519 for(nt = line; nt->entry; nt = nt->entry) 1520 ; 1521 nt->entry = t; 1522 return line; 1523 } 1524 1525 /* 1526 * create a slave process to handle a request to avoid one request blocking 1527 * another. parent returns to job loop. 1528 */ 1529 void 1530 slave(void) 1531 { 1532 if(*isslave) 1533 return; /* we're already a slave process */ 1534 1535 switch(rfork(RFPROC|RFNOTEG|RFMEM|RFNOWAIT)){ 1536 case -1: 1537 break; 1538 case 0: 1539 if(debug) 1540 syslog(0, logfile, "slave %d", getpid()); 1541 *isslave = 1; 1542 break; 1543 default: 1544 longjmp(masterjmp, 1); 1545 } 1546 1547 } 1548 1549 /* 1550 * call the dns process and have it try to translate a name 1551 */ 1552 Ndbtuple* 1553 dnsiplookup(char *host, Ndbs *s) 1554 { 1555 char buf[Ndbvlen + 4]; 1556 Ndbtuple *t; 1557 1558 unlock(&dblock); 1559 1560 /* save the name before starting a slave */ 1561 snprint(buf, sizeof(buf), "%s", host); 1562 1563 slave(); 1564 1565 if(strcmp(ipattr(buf), "ip") == 0) 1566 t = dnsquery(mntpt, buf, "ptr"); 1567 else 1568 t = dnsquery(mntpt, buf, "ip"); 1569 s->t = t; 1570 1571 lock(&dblock); 1572 return t; 1573 } 1574 1575 int 1576 qmatch(Ndbtuple *t, char **attr, char **val, int n) 1577 { 1578 int i, found; 1579 Ndbtuple *nt; 1580 1581 for(i = 1; i < n; i++){ 1582 found = 0; 1583 for(nt = t; nt; nt = nt->entry) 1584 if(strcmp(attr[i], nt->attr) == 0) 1585 if(strcmp(val[i], "*") == 0 1586 || strcmp(val[i], nt->val) == 0){ 1587 found = 1; 1588 break; 1589 } 1590 if(found == 0) 1591 break; 1592 } 1593 return i == n; 1594 } 1595 1596 void 1597 qreply(Mfile *mf, Ndbtuple *t) 1598 { 1599 int i; 1600 Ndbtuple *nt; 1601 char buf[2048]; 1602 1603 buf[0] = 0; 1604 for(nt = t; mf->nreply < Nreply && nt; nt = nt->entry){ 1605 strcat(buf, nt->attr); 1606 strcat(buf, "="); 1607 strcat(buf, nt->val); 1608 i = strlen(buf); 1609 if(nt->line != nt->entry || sizeof(buf) - i < 2*Ndbvlen+2){ 1610 mf->replylen[mf->nreply] = strlen(buf); 1611 mf->reply[mf->nreply++] = strdup(buf); 1612 buf[0] = 0; 1613 } else 1614 strcat(buf, " "); 1615 } 1616 } 1617 1618 enum 1619 { 1620 Maxattr= 32, 1621 }; 1622 1623 /* 1624 * generic query lookup. The query is of one of the following 1625 * forms: 1626 * 1627 * attr1=val1 attr2=val2 attr3=val3 ... 1628 * 1629 * returns the matching tuple 1630 * 1631 * ipinfo attr=val attr1 attr2 attr3 ... 1632 * 1633 * is like ipinfo and returns the attr{1-n} 1634 * associated with the ip address. 1635 */ 1636 char* 1637 genquery(Mfile *mf, char *query) 1638 { 1639 int i, n; 1640 char *p; 1641 char *attr[Maxattr]; 1642 char *val[Maxattr]; 1643 Ndbtuple *t; 1644 Ndbs s; 1645 1646 n = getfields(query, attr, 32, 1, " "); 1647 if(n == 0) 1648 return "bad query"; 1649 1650 if(strcmp(attr[0], "ipinfo") == 0) 1651 return ipinfoquery(mf, attr, n); 1652 1653 /* parse pairs */ 1654 for(i = 0; i < n; i++){ 1655 p = strchr(attr[i], '='); 1656 if(p == 0) 1657 return "bad query"; 1658 *p++ = 0; 1659 val[i] = p; 1660 } 1661 1662 /* give dns a chance */ 1663 if((strcmp(attr[0], "dom") == 0 || strcmp(attr[0], "ip") == 0) && val[0]){ 1664 t = dnsiplookup(val[0], &s); 1665 if(t){ 1666 if(qmatch(t, attr, val, n)){ 1667 qreply(mf, t); 1668 ndbfree(t); 1669 return 0; 1670 } 1671 ndbfree(t); 1672 } 1673 } 1674 1675 /* first pair is always the key. It can't be a '*' */ 1676 t = ndbsearch(db, &s, attr[0], val[0]); 1677 1678 /* search is the and of all the pairs */ 1679 while(t){ 1680 if(qmatch(t, attr, val, n)){ 1681 qreply(mf, t); 1682 ndbfree(t); 1683 return 0; 1684 } 1685 1686 ndbfree(t); 1687 t = ndbsnext(&s, attr[0], val[0]); 1688 } 1689 1690 return "no match"; 1691 } 1692 1693 /* 1694 * resolve an ip address 1695 */ 1696 static Ndbtuple* 1697 ipresolve(char *attr, char *host) 1698 { 1699 Ndbtuple *t, *nt, **l; 1700 1701 t = iplookup(&network[Ntcp], host, "*", 0); 1702 for(l = &t; *l != nil; ){ 1703 nt = *l; 1704 if(strcmp(nt->attr, "ip") != 0){ 1705 *l = nt->entry; 1706 nt->entry = nil; 1707 ndbfree(nt); 1708 continue; 1709 } 1710 strcpy(nt->attr, attr); 1711 l = &nt->entry; 1712 } 1713 return t; 1714 } 1715 1716 char* 1717 ipinfoquery(Mfile *mf, char **list, int n) 1718 { 1719 int i, nresolve; 1720 int resolve[Maxattr]; 1721 Ndbtuple *t, *nt, **l; 1722 char *attr, *val; 1723 1724 /* skip 'ipinfo' */ 1725 list++; n--; 1726 1727 if(n < 2) 1728 return "bad query"; 1729 1730 /* get search attribute=value */ 1731 attr = *list++; n--; 1732 val = strchr(attr, '='); 1733 if(val == nil) 1734 return "bad query"; 1735 *val++ = 0; 1736 1737 /* 1738 * don't let ndbipinfo resolve the addresses, we're 1739 * better at it. 1740 */ 1741 nresolve = 0; 1742 for(i = 0; i < n; i++) 1743 if(*list[i] == '@'){ 1744 list[i]++; 1745 resolve[i] = 1; 1746 nresolve++; 1747 } else 1748 resolve[i] = 0; 1749 1750 t = ndbipinfo(db, attr, val, list, n); 1751 if(t == nil) 1752 return "no match"; 1753 1754 if(nresolve != 0){ 1755 for(l = &t; *l != nil;){ 1756 nt = *l; 1757 1758 /* already an address? */ 1759 if(strcmp(ipattr(nt->val), "ip") == 0){ 1760 l = &(*l)->entry; 1761 continue; 1762 } 1763 1764 /* user wants it resolved? */ 1765 for(i = 0; i < n; i++) 1766 if(strcmp(list[i], nt->attr) == 0) 1767 break; 1768 if(i >= n || resolve[i] == 0){ 1769 l = &(*l)->entry; 1770 continue; 1771 } 1772 1773 /* resolve address and replace entry */ 1774 *l = ipresolve(nt->attr, nt->val); 1775 while(*l != nil) 1776 l = &(*l)->entry; 1777 *l = nt->entry; 1778 1779 nt->entry = nil; 1780 ndbfree(nt); 1781 } 1782 } 1783 1784 /* make it all one line */ 1785 for(nt = t; nt != nil; nt = nt->entry){ 1786 if(nt->entry == nil) 1787 nt->line = t; 1788 else 1789 nt->line = nt->entry; 1790 } 1791 1792 qreply(mf, t); 1793 1794 return nil; 1795 } 1796 1797 void* 1798 emalloc(int size) 1799 { 1800 void *x; 1801 1802 x = malloc(size); 1803 if(x == nil) 1804 abort(); 1805 memset(x, 0, size); 1806 return x; 1807 } 1808 1809 char* 1810 estrdup(char *s) 1811 { 1812 int size; 1813 char *p; 1814 1815 size = strlen(s)+1; 1816 p = malloc(size); 1817 if(p == nil) 1818 abort(); 1819 memmove(p, s, size); 1820 return p; 1821 } 1822