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