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", 418 mdata[0], mdata[1], mdata[2], mdata[3], mdata[4]); 419 freejob(job); 420 continue; 421 } 422 if(job->request.fid<0) 423 error("fid out of range"); 424 lock(&dblock); 425 mf = newfid(job->request.fid); 426 if(debug) 427 syslog(0, logfile, "%F", &job->request); 428 429 430 switch(job->request.type){ 431 default: 432 syslog(1, logfile, "unknown request type %d", job->request.type); 433 break; 434 case Tversion: 435 rversion(job); 436 break; 437 case Tauth: 438 rauth(job); 439 break; 440 case Tflush: 441 rflush(job); 442 break; 443 case Tattach: 444 rattach(job, mf); 445 break; 446 case Twalk: 447 rwalk(job, mf); 448 break; 449 case Topen: 450 ropen(job, mf); 451 break; 452 case Tcreate: 453 rcreate(job, mf); 454 break; 455 case Tread: 456 rread(job, mf); 457 break; 458 case Twrite: 459 rwrite(job, mf); 460 break; 461 case Tclunk: 462 rclunk(job, mf); 463 break; 464 case Tremove: 465 rremove(job, mf); 466 break; 467 case Tstat: 468 rstat(job, mf); 469 break; 470 case Twstat: 471 rwstat(job, mf); 472 break; 473 } 474 unlock(&dblock); 475 476 freejob(job); 477 478 /* 479 * slave processes die after replying 480 */ 481 if(*isslave){ 482 if(debug) 483 syslog(0, logfile, "slave death %d", getpid()); 484 _exits(0); 485 } 486 } 487 } 488 489 void 490 rversion(Job *job) 491 { 492 if(job->request.msize > IOHDRSZ + Maxfdata) 493 job->reply.msize = IOHDRSZ + Maxfdata; 494 else 495 job->reply.msize = job->request.msize; 496 if(strncmp(job->request.version, "9P2000", 6) != 0) 497 sendmsg(job, "unknown 9P version"); 498 else{ 499 job->reply.version = "9P2000"; 500 sendmsg(job, 0); 501 } 502 } 503 504 void 505 rauth(Job *job) 506 { 507 sendmsg(job, "cs: authentication not required"); 508 } 509 510 /* 511 * don't flush till all the slaves are done 512 */ 513 void 514 rflush(Job *job) 515 { 516 flushjob(job->request.oldtag); 517 sendmsg(job, 0); 518 } 519 520 void 521 rattach(Job *job, Mfile *mf) 522 { 523 if(mf->busy == 0){ 524 mf->busy = 1; 525 mf->user = estrdup(job->request.uname); 526 } 527 mf->qid.vers = vers++; 528 mf->qid.type = QTDIR; 529 mf->qid.path = 0LL; 530 job->reply.qid = mf->qid; 531 sendmsg(job, 0); 532 } 533 534 535 char* 536 rwalk(Job *job, Mfile *mf) 537 { 538 char *err; 539 char **elems; 540 int nelems; 541 int i; 542 Mfile *nmf; 543 Qid qid; 544 545 err = 0; 546 nmf = nil; 547 elems = job->request.wname; 548 nelems = job->request.nwname; 549 job->reply.nwqid = 0; 550 551 if(job->request.newfid != job->request.fid){ 552 /* clone fid */ 553 if(job->request.newfid<0){ 554 err = "clone newfid out of range"; 555 goto send; 556 } 557 nmf = newfid(job->request.newfid); 558 if(nmf->busy){ 559 nmf = nil; 560 err = "clone to used channel"; 561 goto send; 562 } 563 *nmf = *mf; 564 nmf->user = estrdup(mf->user); 565 nmf->fid = job->request.newfid; 566 nmf->qid.vers = vers++; 567 mf = nmf; 568 } 569 /* else nmf will be nil */ 570 571 qid = mf->qid; 572 if(nelems > 0){ 573 /* walk fid */ 574 for(i=0; i<nelems && i<MAXWELEM; i++){ 575 if((qid.type & QTDIR) == 0){ 576 err = "not a directory"; 577 break; 578 } 579 if(strcmp(elems[i], "..") == 0 || strcmp(elems[i], ".") == 0){ 580 qid.type = QTDIR; 581 qid.path = Qdir; 582 Found: 583 job->reply.wqid[i] = qid; 584 job->reply.nwqid++; 585 continue; 586 } 587 if(strcmp(elems[i], "cs") == 0){ 588 qid.type = QTFILE; 589 qid.path = Qcs; 590 goto Found; 591 } 592 err = "file does not exist"; 593 break; 594 } 595 } 596 597 send: 598 if(nmf != nil && (err!=nil || job->reply.nwqid<nelems)){ 599 nmf->busy = 0; 600 nmf->fid = 0; 601 } 602 if(err == nil) 603 mf->qid = qid; 604 sendmsg(job, err); 605 return err; 606 } 607 608 void 609 ropen(Job *job, Mfile *mf) 610 { 611 int mode; 612 char *err; 613 614 err = 0; 615 mode = job->request.mode; 616 if(mf->qid.type & QTDIR){ 617 if(mode) 618 err = "permission denied"; 619 } 620 job->reply.qid = mf->qid; 621 job->reply.iounit = 0; 622 sendmsg(job, err); 623 } 624 625 void 626 rcreate(Job *job, Mfile *mf) 627 { 628 USED(mf); 629 sendmsg(job, "creation permission denied"); 630 } 631 632 void 633 rread(Job *job, Mfile *mf) 634 { 635 int i, n, cnt; 636 long off, toff, clock; 637 Dir dir; 638 uchar buf[Maxfdata]; 639 char *err; 640 641 n = 0; 642 err = 0; 643 off = job->request.offset; 644 cnt = job->request.count; 645 if(mf->qid.type & QTDIR){ 646 clock = time(0); 647 if(off == 0){ 648 dir.name = "cs"; 649 dir.qid.type = QTFILE; 650 dir.qid.vers = vers; 651 dir.qid.path = Qcs; 652 dir.mode = 0666; 653 dir.length = 0; 654 dir.uid = mf->user; 655 dir.gid = mf->user; 656 dir.muid = mf->user; 657 dir.atime = clock; /* wrong */ 658 dir.mtime = clock; /* wrong */ 659 n = convD2M(&dir, buf, sizeof buf); 660 } 661 job->reply.data = (char*)buf; 662 } else { 663 for(;;){ 664 /* look for an answer at the right offset */ 665 toff = 0; 666 for(i = 0; mf->reply[i] && i < mf->nreply; i++){ 667 n = mf->replylen[i]; 668 if(off < toff + n) 669 break; 670 toff += n; 671 } 672 if(i < mf->nreply) 673 break; /* got something to return */ 674 675 /* try looking up more answers */ 676 if(lookup(mf) == 0){ 677 /* no more */ 678 n = 0; 679 goto send; 680 } 681 } 682 683 /* give back a single reply (or part of one) */ 684 job->reply.data = mf->reply[i] + (off - toff); 685 if(cnt > toff - off + n) 686 n = toff - off + n; 687 else 688 n = cnt; 689 } 690 send: 691 job->reply.count = n; 692 sendmsg(job, err); 693 } 694 void 695 cleanmf(Mfile *mf) 696 { 697 int i; 698 699 if(mf->net != nil){ 700 free(mf->net); 701 mf->net = nil; 702 } 703 if(mf->host != nil){ 704 free(mf->host); 705 mf->host = nil; 706 } 707 if(mf->serv != nil){ 708 free(mf->serv); 709 mf->serv = nil; 710 } 711 if(mf->rem != nil){ 712 free(mf->rem); 713 mf->rem = nil; 714 } 715 if(mf->user != nil){ 716 free(mf->user); 717 mf->user = nil; 718 } 719 for(i = 0; i < mf->nreply; i++){ 720 free(mf->reply[i]); 721 mf->reply[i] = nil; 722 mf->replylen[i] = 0; 723 } 724 mf->nreply = 0; 725 mf->nextnet = netlist; 726 } 727 728 void 729 rwrite(Job *job, Mfile *mf) 730 { 731 int cnt, n; 732 char *err; 733 char *field[4]; 734 char curerr[64]; 735 736 err = 0; 737 cnt = job->request.count; 738 if(mf->qid.type & QTDIR){ 739 err = "can't write directory"; 740 goto send; 741 } 742 if(cnt >= Maxrequest){ 743 err = "request too long"; 744 goto send; 745 } 746 job->request.data[cnt] = 0; 747 748 /* 749 * toggle debugging 750 */ 751 if(strncmp(job->request.data, "debug", 5)==0){ 752 debug ^= 1; 753 syslog(1, logfile, "debug %d", debug); 754 goto send; 755 } 756 757 /* 758 * toggle debugging 759 */ 760 if(strncmp(job->request.data, "paranoia", 8)==0){ 761 paranoia ^= 1; 762 syslog(1, logfile, "paranoia %d", paranoia); 763 goto send; 764 } 765 766 /* 767 * add networks to the default list 768 */ 769 if(strncmp(job->request.data, "add ", 4)==0){ 770 if(job->request.data[cnt-1] == '\n') 771 job->request.data[cnt-1] = 0; 772 netadd(job->request.data+4); 773 readipinterfaces(); 774 goto send; 775 } 776 777 /* 778 * refresh all state 779 */ 780 if(strncmp(job->request.data, "refresh", 7)==0){ 781 netinit(1); 782 goto send; 783 } 784 785 /* start transaction with a clean slate */ 786 cleanmf(mf); 787 788 /* 789 * look for a general query 790 */ 791 if(*job->request.data == '!'){ 792 err = genquery(mf, job->request.data+1); 793 goto send; 794 } 795 796 if(debug) 797 syslog(0, logfile, "write %s", job->request.data); 798 if(paranoia) 799 syslog(0, paranoiafile, "write %s by %s", job->request.data, mf->user); 800 801 /* 802 * break up name 803 */ 804 n = getfields(job->request.data, field, 4, 1, "!"); 805 switch(n){ 806 case 1: 807 mf->net = strdup("net"); 808 mf->host = strdup(field[0]); 809 break; 810 case 4: 811 mf->rem = strdup(field[3]); 812 /* fall through */ 813 case 3: 814 mf->serv = strdup(field[2]); 815 /* fall through */ 816 case 2: 817 mf->host = strdup(field[1]); 818 mf->net = strdup(field[0]); 819 break; 820 } 821 822 /* 823 * do the first net worth of lookup 824 */ 825 if(lookup(mf) == 0){ 826 rerrstr(curerr, sizeof curerr); 827 err = curerr; 828 } 829 send: 830 job->reply.count = cnt; 831 sendmsg(job, err); 832 } 833 834 void 835 rclunk(Job *job, Mfile *mf) 836 { 837 cleanmf(mf); 838 mf->busy = 0; 839 mf->fid = 0; 840 sendmsg(job, 0); 841 } 842 843 void 844 rremove(Job *job, Mfile *mf) 845 { 846 USED(mf); 847 sendmsg(job, "remove permission denied"); 848 } 849 850 void 851 rstat(Job *job, Mfile *mf) 852 { 853 Dir dir; 854 uchar buf[IOHDRSZ+Maxfdata]; 855 856 if(mf->qid.type & QTDIR){ 857 dir.name = "."; 858 dir.mode = DMDIR|0555; 859 } else { 860 dir.name = "cs"; 861 dir.mode = 0666; 862 } 863 dir.qid = mf->qid; 864 dir.length = 0; 865 dir.uid = mf->user; 866 dir.gid = mf->user; 867 dir.muid = mf->user; 868 dir.atime = dir.mtime = time(0); 869 job->reply.nstat = convD2M(&dir, buf, sizeof buf); 870 job->reply.stat = buf; 871 sendmsg(job, 0); 872 } 873 874 void 875 rwstat(Job *job, Mfile *mf) 876 { 877 USED(mf); 878 sendmsg(job, "wstat permission denied"); 879 } 880 881 void 882 sendmsg(Job *job, char *err) 883 { 884 int n; 885 uchar mdata[IOHDRSZ + Maxfdata]; 886 char ename[ERRMAX]; 887 888 if(err){ 889 job->reply.type = Rerror; 890 snprint(ename, sizeof(ename), "cs: %s", err); 891 job->reply.ename = ename; 892 }else{ 893 job->reply.type = job->request.type+1; 894 } 895 job->reply.tag = job->request.tag; 896 n = convS2M(&job->reply, mdata, sizeof mdata); 897 if(n == 0){ 898 syslog(1, logfile, "sendmsg convS2M of %F returns 0", &job->reply); 899 abort(); 900 } 901 lock(&joblock); 902 if(job->flushed == 0) 903 if(write(mfd[1], mdata, n)!=n) 904 error("mount write"); 905 unlock(&joblock); 906 if(debug) 907 syslog(0, logfile, "%F %d", &job->reply, n); 908 } 909 910 void 911 error(char *s) 912 { 913 syslog(1, "cs", "%s: %r", s); 914 _exits(0); 915 } 916 917 static int 918 isvalidip(uchar *ip) 919 { 920 return ipcmp(ip, IPnoaddr) != 0 && ipcmp(ip, v4prefix) != 0; 921 } 922 923 static uchar loopbacknet[IPaddrlen] = { 924 0, 0, 0, 0, 925 0, 0, 0, 0, 926 0, 0, 0xff, 0xff, 927 127, 0, 0, 0 928 }; 929 static uchar loopbackmask[IPaddrlen] = { 930 0xff, 0xff, 0xff, 0xff, 931 0xff, 0xff, 0xff, 0xff, 932 0xff, 0xff, 0xff, 0xff, 933 0xff, 0, 0, 0 934 }; 935 936 void 937 readipinterfaces(void) 938 { 939 if(myipaddr(ipa, mntpt) != 0) 940 ipmove(ipa, IPnoaddr); 941 sprint(ipaddr, "%I", ipa); 942 if (debug) 943 syslog(0, "dns", "ipaddr is %s\n", ipaddr); 944 } 945 946 /* 947 * get the system name 948 */ 949 void 950 ipid(void) 951 { 952 uchar addr[6]; 953 Ndbtuple *t; 954 char *p, *attr; 955 Ndbs s; 956 int f; 957 char buf[Maxpath]; 958 959 960 /* use environment, ether addr, or ipaddr to get system name */ 961 if(*mysysname == 0){ 962 /* 963 * environment has priority. 964 * 965 * on the sgi power the default system name 966 * is the ip address. ignore that. 967 * 968 */ 969 p = getenv("sysname"); 970 if(p){ 971 attr = ipattr(p); 972 if(strcmp(attr, "ip") != 0) 973 strcpy(mysysname, p); 974 } 975 976 /* 977 * the /net/ndb contains what the network 978 * figured out from DHCP. use that name if 979 * there is one. 980 */ 981 if(*mysysname == 0 && netdb != nil){ 982 ndbreopen(netdb); 983 for(t = ndbparse(netdb); t != nil; t = t->entry){ 984 if(strcmp(t->attr, "sys") == 0){ 985 strcpy(mysysname, t->val); 986 break; 987 } 988 } 989 ndbfree(t); 990 } 991 992 /* next network database, ip address, and ether address to find a name */ 993 if(*mysysname == 0){ 994 t = nil; 995 if(isvalidip(ipa)) 996 t = ndbgetval(db, &s, "ip", ipaddr, "sys", mysysname); 997 else { 998 for(f = 0; f < 3; f++){ 999 snprint(buf, sizeof buf, "%s/ether%d", mntpt, f); 1000 if(myetheraddr(addr, buf) >= 0){ 1001 snprint(eaddr, sizeof(eaddr), "%E", addr); 1002 t = ndbgetval(db, &s, "ether", eaddr, "sys", 1003 mysysname); 1004 if(t != nil) 1005 break; 1006 } 1007 } 1008 } 1009 ndbfree(t); 1010 } 1011 1012 /* nothing else worked, use the ip address */ 1013 if(*mysysname == 0 && isvalidip(ipa)) 1014 strcpy(mysysname, ipaddr); 1015 1016 1017 /* set /dev/sysname if we now know it */ 1018 if(*mysysname){ 1019 f = open("/dev/sysname", OWRITE); 1020 if(f >= 0){ 1021 write(f, mysysname, strlen(mysysname)); 1022 close(f); 1023 } 1024 } 1025 } 1026 } 1027 1028 /* 1029 * Set up a list of default networks by looking for 1030 * /net/ * /clone. 1031 */ 1032 void 1033 netinit(int background) 1034 { 1035 char clone[Maxpath]; 1036 Network *np; 1037 static int working; 1038 1039 if(background){ 1040 switch(rfork(RFPROC|RFNOTEG|RFMEM|RFNOWAIT)){ 1041 case 0: 1042 break; 1043 default: 1044 return; 1045 } 1046 lock(&netlock); 1047 } 1048 1049 /* add the mounted networks to the default list */ 1050 for(np = network; np->net; np++){ 1051 if(np->considered) 1052 continue; 1053 snprint(clone, sizeof(clone), "%s/%s/clone", mntpt, np->net); 1054 if(access(clone, AEXIST) < 0) 1055 continue; 1056 if(netlist) 1057 last->next = np; 1058 else 1059 netlist = np; 1060 last = np; 1061 np->next = 0; 1062 np->considered = 1; 1063 } 1064 1065 /* find out what our ip address is */ 1066 readipinterfaces(); 1067 1068 /* set the system name if we need to, these says ip is all we have */ 1069 ipid(); 1070 1071 if(debug) 1072 syslog(0, logfile, "mysysname %s eaddr %s ipaddr %s ipa %I\n", 1073 mysysname, eaddr, ipaddr, ipa); 1074 1075 if(background){ 1076 unlock(&netlock); 1077 _exits(0); 1078 } 1079 } 1080 1081 /* 1082 * add networks to the standard list 1083 */ 1084 void 1085 netadd(char *p) 1086 { 1087 Network *np; 1088 char *field[12]; 1089 int i, n; 1090 1091 n = getfields(p, field, 12, 1, " "); 1092 for(i = 0; i < n; i++){ 1093 for(np = network; np->net; np++){ 1094 if(strcmp(field[i], np->net) != 0) 1095 continue; 1096 if(np->considered) 1097 break; 1098 if(netlist) 1099 last->next = np; 1100 else 1101 netlist = np; 1102 last = np; 1103 np->next = 0; 1104 np->considered = 1; 1105 } 1106 } 1107 } 1108 1109 /* 1110 * make a tuple 1111 */ 1112 Ndbtuple* 1113 mktuple(char *attr, char *val) 1114 { 1115 Ndbtuple *t; 1116 1117 t = emalloc(sizeof(Ndbtuple)); 1118 strcpy(t->attr, attr); 1119 strncpy(t->val, val, sizeof(t->val)); 1120 t->val[sizeof(t->val)-1] = 0; 1121 t->line = t; 1122 t->entry = 0; 1123 return t; 1124 } 1125 1126 int 1127 lookforproto(Ndbtuple *t, char *proto) 1128 { 1129 for(; t != nil; t = t->entry) 1130 if(strcmp(t->attr, "proto") == 0 && strcmp(t->val, proto) == 0) 1131 return 1; 1132 return 0; 1133 } 1134 1135 /* 1136 * lookup a request. the network "net" means we should pick the 1137 * best network to get there. 1138 */ 1139 int 1140 lookup(Mfile *mf) 1141 { 1142 Network *np; 1143 char *cp; 1144 Ndbtuple *nt, *t; 1145 char reply[Maxreply]; 1146 int i, rv; 1147 int hack; 1148 1149 /* open up the standard db files */ 1150 if(db == 0) 1151 ndbinit(); 1152 if(db == 0) 1153 error("can't open mf->network database\n"); 1154 1155 rv = 0; 1156 1157 if(mf->net == nil) 1158 return 0; /* must have been a genquery */ 1159 1160 if(strcmp(mf->net, "net") == 0){ 1161 /* 1162 * go through set of default nets 1163 */ 1164 for(np = mf->nextnet; np; np = np->next){ 1165 nt = (*np->lookup)(np, mf->host, mf->serv, 1); 1166 if(nt == nil) 1167 continue; 1168 hack = np->fasttimeouthack && !lookforproto(nt, np->net); 1169 for(t = nt; mf->nreply < Nreply && t; t = t->entry){ 1170 cp = (*np->trans)(t, np, mf->serv, mf->rem, hack); 1171 if(cp){ 1172 /* avoid duplicates */ 1173 for(i = 0; i < mf->nreply; i++) 1174 if(strcmp(mf->reply[i], cp) == 0) 1175 break; 1176 if(i == mf->nreply){ 1177 /* save the reply */ 1178 mf->replylen[mf->nreply] = strlen(cp); 1179 mf->reply[mf->nreply++] = cp; 1180 rv++; 1181 } 1182 } 1183 } 1184 ndbfree(nt); 1185 np = np->next; 1186 break; 1187 } 1188 mf->nextnet = np; 1189 return rv; 1190 } 1191 1192 /* 1193 * if not /net, we only get one lookup 1194 */ 1195 if(mf->nreply != 0) 1196 return 0; 1197 1198 /* 1199 * look for a specific network 1200 */ 1201 for(np = netlist; 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->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) 1248 { 1249 char *p; 1250 int alpha = 0; 1251 int restr = 0; 1252 char port[Ndbvlen]; 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 if(alpha){ 1273 t = ndbgetval(db, &s, np->net, name, "port", port); 1274 if(t == 0) 1275 return 0; 1276 } else { 1277 /* look up only for tcp ports < 1024 to get the restricted 1278 * attribute 1279 */ 1280 t = nil; 1281 if(atoi(name) < 1024 && strcmp(np->net, "tcp") == 0) 1282 t = ndbgetval(db, &s, "port", name, "port", port); 1283 if(t == nil){ 1284 strncpy(port, name, sizeof(port)); 1285 port[sizeof(port)-1] = 0; 1286 } 1287 } 1288 1289 if(t){ 1290 for(nt = t; nt; nt = nt->entry) 1291 if(strcmp(nt->attr, "restricted") == 0) 1292 restr = 1; 1293 ndbfree(t); 1294 } 1295 sprint(buf, "%s%s", port, restr ? "!r" : ""); 1296 return buf; 1297 } 1298 1299 /* 1300 * lookup an ip attribute 1301 */ 1302 int 1303 ipattrlookup(Ndb *db, char *ipa, char *attr, char *val) 1304 { 1305 1306 Ndbtuple *t, *nt; 1307 char *alist[2]; 1308 1309 alist[0] = attr; 1310 t = ndbipinfo(db, "ip", ipa, alist, 1); 1311 if(t == nil) 1312 return 0; 1313 for(nt = t; nt != nil; nt = nt->entry){ 1314 if(strcmp(nt->attr, attr) == 0){ 1315 strcpy(val, nt->val); 1316 ndbfree(t); 1317 return 1; 1318 } 1319 } 1320 1321 /* we shouldn't get here */ 1322 ndbfree(t); 1323 return 0; 1324 } 1325 1326 /* 1327 * lookup (and translate) an ip destination 1328 */ 1329 Ndbtuple* 1330 iplookup(Network *np, char *host, char *serv, int nolookup) 1331 { 1332 char *attr; 1333 Ndbtuple *t, *nt; 1334 Ndbs s; 1335 char ts[Ndbvlen+1]; 1336 char th[Ndbvlen+1]; 1337 char dollar[Ndbvlen+1]; 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) == 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 mktuple("ip", "*"); 1359 1360 /* 1361 * hack till we go v6 :: = 0.0.0.0 1362 */ 1363 if(strcmp("::", host) == 0) 1364 return mktuple("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)) 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 mktuple("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 t = ndbgetval(db, &s, attr, host, "ip", th); 1400 if(t == 0) 1401 t = dnsiplookup(host, &s); 1402 if(t == 0 && strcmp(attr, "dom") != 0) 1403 t = dnsiplookup(host, &s); 1404 if(t == 0) 1405 return 0; 1406 1407 /* 1408 * reorder the tuple to have the matched line first and 1409 * save that in the request structure. 1410 */ 1411 t = reorder(t, s.t); 1412 1413 /* 1414 * reorder according to our interfaces 1415 */ 1416 lock(&ipifclock); 1417 for(ifc = ipifcs; ifc != nil; ifc = ifc->next){ 1418 for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){ 1419 maskip(lifc->ip, lifc->mask, net); 1420 for(nt = t; nt; nt = nt->entry){ 1421 if(strcmp(nt->attr, "ip") != 0) 1422 continue; 1423 parseip(ip, nt->val); 1424 maskip(ip, lifc->mask, tnet); 1425 if(memcmp(net, tnet, IPaddrlen) == 0){ 1426 t = reorder(t, nt); 1427 unlock(&ipifclock); 1428 return t; 1429 } 1430 } 1431 } 1432 } 1433 unlock(&ipifclock); 1434 1435 return t; 1436 } 1437 1438 /* 1439 * translate an ip address 1440 */ 1441 char* 1442 iptrans(Ndbtuple *t, Network *np, char *serv, char *rem, int hack) 1443 { 1444 char ts[Ndbvlen+1]; 1445 char reply[Maxreply]; 1446 char x[Ndbvlen+1]; 1447 1448 if(strcmp(t->attr, "ip") != 0) 1449 return 0; 1450 1451 if(serv == 0 || ipserv(np, serv, ts) == 0){ 1452 werrstr("can't translate service"); 1453 return 0; 1454 } 1455 if(rem != nil) 1456 snprint(x, sizeof(x), "!%s", rem); 1457 else 1458 *x = 0; 1459 1460 if(*t->val == '*') 1461 snprint(reply, sizeof(reply), "%s/%s/clone %s%s", 1462 mntpt, np->net, ts, x); 1463 else 1464 snprint(reply, sizeof(reply), "%s/%s/clone %s!%s%s%s", 1465 mntpt, np->net, t->val, ts, x, hack?"!fasttimeout":""); 1466 1467 return strdup(reply); 1468 } 1469 1470 /* 1471 * lookup a telephone number 1472 */ 1473 Ndbtuple* 1474 telcolookup(Network *np, char *host, char *serv, int nolookup) 1475 { 1476 Ndbtuple *t; 1477 Ndbs s; 1478 char th[Ndbvlen+1]; 1479 1480 USED(np, nolookup, serv); 1481 1482 werrstr("can't translate address"); 1483 t = ndbgetval(db, &s, "sys", host, "telco", th); 1484 if(t == 0) 1485 return mktuple("telco", host); 1486 1487 return reorder(t, s.t); 1488 } 1489 1490 /* 1491 * translate a telephone address 1492 */ 1493 char* 1494 telcotrans(Ndbtuple *t, Network *np, char *serv, char *rem, int) 1495 { 1496 char reply[Maxreply]; 1497 char x[Ndbvlen+1]; 1498 1499 if(strcmp(t->attr, "telco") != 0) 1500 return 0; 1501 1502 if(rem != nil) 1503 snprint(x, sizeof(x), "!%s", rem); 1504 else 1505 *x = 0; 1506 if(serv) 1507 snprint(reply, sizeof(reply), "%s/%s/clone %s!%s%s", mntpt, np->net, 1508 t->val, serv, x); 1509 else 1510 snprint(reply, sizeof(reply), "%s/%s/clone %s%s", mntpt, np->net, 1511 t->val, x); 1512 return strdup(reply); 1513 } 1514 1515 /* 1516 * reorder the tuple to put x's line first in the entry 1517 */ 1518 Ndbtuple* 1519 reorder(Ndbtuple *t, Ndbtuple *x) 1520 { 1521 Ndbtuple *nt; 1522 Ndbtuple *line; 1523 1524 /* find start of this entry's line */ 1525 for(line = x; line->entry == line->line; line = line->line) 1526 ; 1527 line = line->line; 1528 if(line == t) 1529 return t; /* already the first line */ 1530 1531 /* remove this line and everything after it from the entry */ 1532 for(nt = t; nt->entry != line; nt = nt->entry) 1533 ; 1534 nt->entry = 0; 1535 1536 /* make that the start of the entry */ 1537 for(nt = line; nt->entry; nt = nt->entry) 1538 ; 1539 nt->entry = t; 1540 return line; 1541 } 1542 1543 /* 1544 * create a slave process to handle a request to avoid one request blocking 1545 * another. parent returns to job loop. 1546 */ 1547 void 1548 slave(void) 1549 { 1550 if(*isslave) 1551 return; /* we're already a slave process */ 1552 1553 switch(rfork(RFPROC|RFNOTEG|RFMEM|RFNOWAIT)){ 1554 case -1: 1555 break; 1556 case 0: 1557 if(debug) 1558 syslog(0, logfile, "slave %d", getpid()); 1559 *isslave = 1; 1560 break; 1561 default: 1562 longjmp(masterjmp, 1); 1563 } 1564 1565 } 1566 1567 /* 1568 * call the dns process and have it try to translate a name 1569 */ 1570 Ndbtuple* 1571 dnsiplookup(char *host, Ndbs *s) 1572 { 1573 char buf[Ndbvlen + 4]; 1574 Ndbtuple *t; 1575 1576 unlock(&dblock); 1577 1578 /* save the name before starting a slave */ 1579 snprint(buf, sizeof(buf), "%s", host); 1580 1581 slave(); 1582 1583 if(strcmp(ipattr(buf), "ip") == 0) 1584 t = dnsquery(mntpt, buf, "ptr"); 1585 else 1586 t = dnsquery(mntpt, buf, "ip"); 1587 s->t = t; 1588 1589 if(t == nil){ 1590 rerrstr(buf, sizeof buf); 1591 if(strstr(buf, "exist")) 1592 werrstr("can't translate address: %s", buf); 1593 else if(strstr(buf, "dns failure")) 1594 werrstr("temporary problem: %s", buf); 1595 } 1596 1597 lock(&dblock); 1598 return t; 1599 } 1600 1601 int 1602 qmatch(Ndbtuple *t, char **attr, char **val, int n) 1603 { 1604 int i, found; 1605 Ndbtuple *nt; 1606 1607 for(i = 1; i < n; i++){ 1608 found = 0; 1609 for(nt = t; nt; nt = nt->entry) 1610 if(strcmp(attr[i], nt->attr) == 0) 1611 if(strcmp(val[i], "*") == 0 1612 || strcmp(val[i], nt->val) == 0){ 1613 found = 1; 1614 break; 1615 } 1616 if(found == 0) 1617 break; 1618 } 1619 return i == n; 1620 } 1621 1622 void 1623 qreply(Mfile *mf, Ndbtuple *t) 1624 { 1625 int i; 1626 Ndbtuple *nt; 1627 char buf[2048]; 1628 1629 buf[0] = 0; 1630 for(nt = t; mf->nreply < Nreply && nt; nt = nt->entry){ 1631 strcat(buf, nt->attr); 1632 strcat(buf, "="); 1633 strcat(buf, nt->val); 1634 i = strlen(buf); 1635 if(nt->line != nt->entry || sizeof(buf) - i < 2*Ndbvlen+2){ 1636 mf->replylen[mf->nreply] = strlen(buf); 1637 mf->reply[mf->nreply++] = strdup(buf); 1638 buf[0] = 0; 1639 } else 1640 strcat(buf, " "); 1641 } 1642 } 1643 1644 enum 1645 { 1646 Maxattr= 32, 1647 }; 1648 1649 /* 1650 * generic query lookup. The query is of one of the following 1651 * forms: 1652 * 1653 * attr1=val1 attr2=val2 attr3=val3 ... 1654 * 1655 * returns the matching tuple 1656 * 1657 * ipinfo attr=val attr1 attr2 attr3 ... 1658 * 1659 * is like ipinfo and returns the attr{1-n} 1660 * associated with the ip address. 1661 */ 1662 char* 1663 genquery(Mfile *mf, char *query) 1664 { 1665 int i, n; 1666 char *p; 1667 char *attr[Maxattr]; 1668 char *val[Maxattr]; 1669 Ndbtuple *t; 1670 Ndbs s; 1671 1672 n = getfields(query, attr, 32, 1, " "); 1673 if(n == 0) 1674 return "bad query"; 1675 1676 if(strcmp(attr[0], "ipinfo") == 0) 1677 return ipinfoquery(mf, attr, n); 1678 1679 /* parse pairs */ 1680 for(i = 0; i < n; i++){ 1681 p = strchr(attr[i], '='); 1682 if(p == 0) 1683 return "bad query"; 1684 *p++ = 0; 1685 val[i] = p; 1686 } 1687 1688 /* give dns a chance */ 1689 if((strcmp(attr[0], "dom") == 0 || strcmp(attr[0], "ip") == 0) && val[0]){ 1690 t = dnsiplookup(val[0], &s); 1691 if(t){ 1692 if(qmatch(t, attr, val, n)){ 1693 qreply(mf, t); 1694 ndbfree(t); 1695 return 0; 1696 } 1697 ndbfree(t); 1698 } 1699 } 1700 1701 /* first pair is always the key. It can't be a '*' */ 1702 t = ndbsearch(db, &s, attr[0], val[0]); 1703 1704 /* search is the and of all the pairs */ 1705 while(t){ 1706 if(qmatch(t, attr, val, n)){ 1707 qreply(mf, t); 1708 ndbfree(t); 1709 return 0; 1710 } 1711 1712 ndbfree(t); 1713 t = ndbsnext(&s, attr[0], val[0]); 1714 } 1715 1716 return "no match"; 1717 } 1718 1719 /* 1720 * resolve an ip address 1721 */ 1722 static Ndbtuple* 1723 ipresolve(char *attr, char *host) 1724 { 1725 Ndbtuple *t, *nt, **l; 1726 1727 t = iplookup(&network[Ntcp], host, "*", 0); 1728 for(l = &t; *l != nil; ){ 1729 nt = *l; 1730 if(strcmp(nt->attr, "ip") != 0){ 1731 *l = nt->entry; 1732 nt->entry = nil; 1733 ndbfree(nt); 1734 continue; 1735 } 1736 strcpy(nt->attr, attr); 1737 l = &nt->entry; 1738 } 1739 return t; 1740 } 1741 1742 char* 1743 ipinfoquery(Mfile *mf, char **list, int n) 1744 { 1745 int i, nresolve; 1746 int resolve[Maxattr]; 1747 Ndbtuple *t, *nt, **l; 1748 char *attr, *val; 1749 1750 /* skip 'ipinfo' */ 1751 list++; n--; 1752 1753 if(n < 2) 1754 return "bad query"; 1755 1756 /* get search attribute=value */ 1757 attr = *list++; n--; 1758 val = strchr(attr, '='); 1759 if(val == nil) 1760 return "bad query"; 1761 *val++ = 0; 1762 1763 /* 1764 * don't let ndbipinfo resolve the addresses, we're 1765 * better at it. 1766 */ 1767 nresolve = 0; 1768 for(i = 0; i < n; i++) 1769 if(*list[i] == '@'){ 1770 list[i]++; 1771 resolve[i] = 1; 1772 nresolve++; 1773 } else 1774 resolve[i] = 0; 1775 1776 t = ndbipinfo(db, attr, val, list, n); 1777 if(t == nil) 1778 return "no match"; 1779 1780 if(nresolve != 0){ 1781 for(l = &t; *l != nil;){ 1782 nt = *l; 1783 1784 /* already an address? */ 1785 if(strcmp(ipattr(nt->val), "ip") == 0){ 1786 l = &(*l)->entry; 1787 continue; 1788 } 1789 1790 /* user wants it resolved? */ 1791 for(i = 0; i < n; i++) 1792 if(strcmp(list[i], nt->attr) == 0) 1793 break; 1794 if(i >= n || resolve[i] == 0){ 1795 l = &(*l)->entry; 1796 continue; 1797 } 1798 1799 /* resolve address and replace entry */ 1800 *l = ipresolve(nt->attr, nt->val); 1801 while(*l != nil) 1802 l = &(*l)->entry; 1803 *l = nt->entry; 1804 1805 nt->entry = nil; 1806 ndbfree(nt); 1807 } 1808 } 1809 1810 /* make it all one line */ 1811 for(nt = t; nt != nil; nt = nt->entry){ 1812 if(nt->entry == nil) 1813 nt->line = t; 1814 else 1815 nt->line = nt->entry; 1816 } 1817 1818 qreply(mf, t); 1819 1820 return nil; 1821 } 1822 1823 void* 1824 emalloc(int size) 1825 { 1826 void *x; 1827 1828 x = malloc(size); 1829 if(x == nil) 1830 abort(); 1831 memset(x, 0, size); 1832 return x; 1833 } 1834 1835 char* 1836 estrdup(char *s) 1837 { 1838 int size; 1839 char *p; 1840 1841 size = strlen(s)+1; 1842 p = malloc(size); 1843 if(p == nil) 1844 abort(); 1845 memmove(p, s, size); 1846 return p; 1847 } 1848