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