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