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 279 /* 280 * make a /srv/cs 281 */ 282 f = create(service, OWRITE|ORCLOSE, 0666); 283 if(f < 0) 284 error(service); 285 snprint(buf, sizeof(buf), "%d", p[1]); 286 if(write(f, buf, strlen(buf)) != strlen(buf)) 287 error("write /srv/cs"); 288 289 switch(rfork(RFFDG|RFPROC|RFNAMEG)){ 290 case 0: 291 close(p[1]); 292 break; 293 case -1: 294 error("fork failed\n"); 295 default: 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 cleanmf(nmf); 600 free(nmf->user); 601 nmf->user = 0; 602 nmf->busy = 0; 603 nmf->fid = 0; 604 } 605 if(err == nil) 606 mf->qid = qid; 607 sendmsg(job, err); 608 return err; 609 } 610 611 void 612 ropen(Job *job, Mfile *mf) 613 { 614 int mode; 615 char *err; 616 617 err = 0; 618 mode = job->request.mode; 619 if(mf->qid.type & QTDIR){ 620 if(mode) 621 err = "permission denied"; 622 } 623 job->reply.qid = mf->qid; 624 job->reply.iounit = 0; 625 sendmsg(job, err); 626 } 627 628 void 629 rcreate(Job *job, Mfile *mf) 630 { 631 USED(mf); 632 sendmsg(job, "creation permission denied"); 633 } 634 635 void 636 rread(Job *job, Mfile *mf) 637 { 638 int i, n, cnt; 639 long off, toff, clock; 640 Dir dir; 641 uchar buf[Maxfdata]; 642 char *err; 643 644 n = 0; 645 err = 0; 646 off = job->request.offset; 647 cnt = job->request.count; 648 if(mf->qid.type & QTDIR){ 649 clock = time(0); 650 if(off == 0){ 651 dir.name = "cs"; 652 dir.qid.type = QTFILE; 653 dir.qid.vers = vers; 654 dir.qid.path = Qcs; 655 dir.mode = 0666; 656 dir.length = 0; 657 dir.uid = mf->user; 658 dir.gid = mf->user; 659 dir.muid = mf->user; 660 dir.atime = clock; /* wrong */ 661 dir.mtime = clock; /* wrong */ 662 n = convD2M(&dir, buf, sizeof buf); 663 } 664 job->reply.data = (char*)buf; 665 } else { 666 for(;;){ 667 /* look for an answer at the right offset */ 668 toff = 0; 669 for(i = 0; mf->reply[i] && i < mf->nreply; i++){ 670 n = mf->replylen[i]; 671 if(off < toff + n) 672 break; 673 toff += n; 674 } 675 if(i < mf->nreply) 676 break; /* got something to return */ 677 678 /* try looking up more answers */ 679 if(lookup(mf) == 0){ 680 /* no more */ 681 n = 0; 682 goto send; 683 } 684 } 685 686 /* give back a single reply (or part of one) */ 687 job->reply.data = mf->reply[i] + (off - toff); 688 if(cnt > toff - off + n) 689 n = toff - off + n; 690 else 691 n = cnt; 692 } 693 send: 694 job->reply.count = n; 695 sendmsg(job, err); 696 } 697 void 698 cleanmf(Mfile *mf) 699 { 700 int i; 701 702 if(mf->net != nil){ 703 free(mf->net); 704 mf->net = nil; 705 } 706 if(mf->host != nil){ 707 free(mf->host); 708 mf->host = nil; 709 } 710 if(mf->serv != nil){ 711 free(mf->serv); 712 mf->serv = nil; 713 } 714 if(mf->rem != nil){ 715 free(mf->rem); 716 mf->rem = nil; 717 } 718 for(i = 0; i < mf->nreply; i++){ 719 free(mf->reply[i]); 720 mf->reply[i] = nil; 721 mf->replylen[i] = 0; 722 } 723 mf->nreply = 0; 724 mf->nextnet = netlist; 725 } 726 727 void 728 rwrite(Job *job, Mfile *mf) 729 { 730 int cnt, n; 731 char *err; 732 char *field[4]; 733 char curerr[64]; 734 735 err = 0; 736 cnt = job->request.count; 737 if(mf->qid.type & QTDIR){ 738 err = "can't write directory"; 739 goto send; 740 } 741 if(cnt >= Maxrequest){ 742 err = "request too long"; 743 goto send; 744 } 745 job->request.data[cnt] = 0; 746 747 /* 748 * toggle debugging 749 */ 750 if(strncmp(job->request.data, "debug", 5)==0){ 751 debug ^= 1; 752 syslog(1, logfile, "debug %d", debug); 753 goto send; 754 } 755 756 /* 757 * toggle debugging 758 */ 759 if(strncmp(job->request.data, "paranoia", 8)==0){ 760 paranoia ^= 1; 761 syslog(1, logfile, "paranoia %d", paranoia); 762 goto send; 763 } 764 765 /* 766 * add networks to the default list 767 */ 768 if(strncmp(job->request.data, "add ", 4)==0){ 769 if(job->request.data[cnt-1] == '\n') 770 job->request.data[cnt-1] = 0; 771 netadd(job->request.data+4); 772 readipinterfaces(); 773 goto send; 774 } 775 776 /* 777 * refresh all state 778 */ 779 if(strncmp(job->request.data, "refresh", 7)==0){ 780 netinit(1); 781 goto send; 782 } 783 784 /* start transaction with a clean slate */ 785 cleanmf(mf); 786 787 /* 788 * look for a general query 789 */ 790 if(*job->request.data == '!'){ 791 err = genquery(mf, job->request.data+1); 792 goto send; 793 } 794 795 if(debug) 796 syslog(0, logfile, "write %s", job->request.data); 797 if(paranoia) 798 syslog(0, paranoiafile, "write %s by %s", job->request.data, mf->user); 799 800 /* 801 * break up name 802 */ 803 n = getfields(job->request.data, field, 4, 1, "!"); 804 switch(n){ 805 case 1: 806 mf->net = strdup("net"); 807 mf->host = strdup(field[0]); 808 break; 809 case 4: 810 mf->rem = strdup(field[3]); 811 /* fall through */ 812 case 3: 813 mf->serv = strdup(field[2]); 814 /* fall through */ 815 case 2: 816 mf->host = strdup(field[1]); 817 mf->net = strdup(field[0]); 818 break; 819 } 820 821 /* 822 * do the first net worth of lookup 823 */ 824 if(lookup(mf) == 0){ 825 rerrstr(curerr, sizeof curerr); 826 err = curerr; 827 } 828 send: 829 job->reply.count = cnt; 830 sendmsg(job, err); 831 } 832 833 void 834 rclunk(Job *job, Mfile *mf) 835 { 836 cleanmf(mf); 837 free(mf->user); 838 mf->user = 0; 839 mf->busy = 0; 840 mf->fid = 0; 841 sendmsg(job, 0); 842 } 843 844 void 845 rremove(Job *job, Mfile *mf) 846 { 847 USED(mf); 848 sendmsg(job, "remove permission denied"); 849 } 850 851 void 852 rstat(Job *job, Mfile *mf) 853 { 854 Dir dir; 855 uchar buf[IOHDRSZ+Maxfdata]; 856 857 if(mf->qid.type & QTDIR){ 858 dir.name = "."; 859 dir.mode = DMDIR|0555; 860 } else { 861 dir.name = "cs"; 862 dir.mode = 0666; 863 } 864 dir.qid = mf->qid; 865 dir.length = 0; 866 dir.uid = mf->user; 867 dir.gid = mf->user; 868 dir.muid = mf->user; 869 dir.atime = dir.mtime = time(0); 870 job->reply.nstat = convD2M(&dir, buf, sizeof buf); 871 job->reply.stat = buf; 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 uchar mdata[IOHDRSZ + Maxfdata]; 887 char ename[ERRMAX]; 888 889 if(err){ 890 job->reply.type = Rerror; 891 snprint(ename, sizeof(ename), "cs: %s", err); 892 job->reply.ename = ename; 893 }else{ 894 job->reply.type = job->request.type+1; 895 } 896 job->reply.tag = job->request.tag; 897 n = convS2M(&job->reply, mdata, sizeof mdata); 898 if(n == 0){ 899 syslog(1, logfile, "sendmsg convS2M of %F returns 0", &job->reply); 900 abort(); 901 } 902 lock(&joblock); 903 if(job->flushed == 0) 904 if(write(mfd[1], mdata, n)!=n) 905 error("mount write"); 906 unlock(&joblock); 907 if(debug) 908 syslog(0, logfile, "%F %d", &job->reply, n); 909 } 910 911 void 912 error(char *s) 913 { 914 syslog(1, "cs", "%s: %r", s); 915 _exits(0); 916 } 917 918 static int 919 isvalidip(uchar *ip) 920 { 921 return ipcmp(ip, IPnoaddr) != 0 && ipcmp(ip, v4prefix) != 0; 922 } 923 924 static uchar loopbacknet[IPaddrlen] = { 925 0, 0, 0, 0, 926 0, 0, 0, 0, 927 0, 0, 0xff, 0xff, 928 127, 0, 0, 0 929 }; 930 static uchar loopbackmask[IPaddrlen] = { 931 0xff, 0xff, 0xff, 0xff, 932 0xff, 0xff, 0xff, 0xff, 933 0xff, 0xff, 0xff, 0xff, 934 0xff, 0, 0, 0 935 }; 936 937 void 938 readipinterfaces(void) 939 { 940 if(myipaddr(ipa, mntpt) != 0) 941 ipmove(ipa, IPnoaddr); 942 sprint(ipaddr, "%I", ipa); 943 if (debug) 944 syslog(0, "dns", "ipaddr is %s\n", ipaddr); 945 } 946 947 /* 948 * get the system name 949 */ 950 void 951 ipid(void) 952 { 953 uchar addr[6]; 954 Ndbtuple *t, *tt; 955 char *p, *attr; 956 Ndbs s; 957 int f; 958 char buf[Maxpath]; 959 960 961 /* use environment, ether addr, or ipaddr to get system name */ 962 if(*mysysname == 0){ 963 /* 964 * environment has priority. 965 * 966 * on the sgi power the default system name 967 * is the ip address. ignore that. 968 * 969 */ 970 p = getenv("sysname"); 971 if(p){ 972 attr = ipattr(p); 973 if(strcmp(attr, "ip") != 0) 974 strcpy(mysysname, p); 975 } 976 977 /* 978 * the /net/ndb contains what the network 979 * figured out from DHCP. use that name if 980 * there is one. 981 */ 982 if(*mysysname == 0 && netdb != nil){ 983 ndbreopen(netdb); 984 for(tt = t = ndbparse(netdb); t != nil; t = t->entry){ 985 if(strcmp(t->attr, "sys") == 0){ 986 strcpy(mysysname, t->val); 987 break; 988 } 989 } 990 ndbfree(tt); 991 } 992 993 /* next network database, ip address, and ether address to find a name */ 994 if(*mysysname == 0){ 995 t = nil; 996 if(isvalidip(ipa)) 997 t = ndbgetval(db, &s, "ip", ipaddr, "sys", mysysname); 998 else { 999 for(f = 0; f < 3; f++){ 1000 snprint(buf, sizeof buf, "%s/ether%d", mntpt, f); 1001 if(myetheraddr(addr, buf) >= 0){ 1002 snprint(eaddr, sizeof(eaddr), "%E", addr); 1003 t = ndbgetval(db, &s, "ether", eaddr, "sys", 1004 mysysname); 1005 if(t != nil) 1006 break; 1007 } 1008 } 1009 } 1010 ndbfree(t); 1011 } 1012 1013 /* nothing else worked, use the ip address */ 1014 if(*mysysname == 0 && isvalidip(ipa)) 1015 strcpy(mysysname, ipaddr); 1016 1017 1018 /* set /dev/sysname if we now know it */ 1019 if(*mysysname){ 1020 f = open("/dev/sysname", OWRITE); 1021 if(f >= 0){ 1022 write(f, mysysname, strlen(mysysname)); 1023 close(f); 1024 } 1025 } 1026 } 1027 } 1028 1029 /* 1030 * Set up a list of default networks by looking for 1031 * /net/ * /clone. 1032 */ 1033 void 1034 netinit(int background) 1035 { 1036 char clone[Maxpath]; 1037 Network *np; 1038 static int working; 1039 1040 if(background){ 1041 switch(rfork(RFPROC|RFNOTEG|RFMEM|RFNOWAIT)){ 1042 case 0: 1043 break; 1044 default: 1045 return; 1046 } 1047 lock(&netlock); 1048 } 1049 1050 /* add the mounted networks to the default list */ 1051 for(np = network; np->net; np++){ 1052 if(np->considered) 1053 continue; 1054 snprint(clone, sizeof(clone), "%s/%s/clone", mntpt, np->net); 1055 if(access(clone, AEXIST) < 0) 1056 continue; 1057 if(netlist) 1058 last->next = np; 1059 else 1060 netlist = np; 1061 last = np; 1062 np->next = 0; 1063 np->considered = 1; 1064 } 1065 1066 /* find out what our ip address is */ 1067 readipinterfaces(); 1068 1069 /* set the system name if we need to, these says ip is all we have */ 1070 ipid(); 1071 1072 if(debug) 1073 syslog(0, logfile, "mysysname %s eaddr %s ipaddr %s ipa %I\n", 1074 mysysname, eaddr, ipaddr, ipa); 1075 1076 if(background){ 1077 unlock(&netlock); 1078 _exits(0); 1079 } 1080 } 1081 1082 /* 1083 * add networks to the standard list 1084 */ 1085 void 1086 netadd(char *p) 1087 { 1088 Network *np; 1089 char *field[12]; 1090 int i, n; 1091 1092 n = getfields(p, field, 12, 1, " "); 1093 for(i = 0; i < n; i++){ 1094 for(np = network; np->net; np++){ 1095 if(strcmp(field[i], np->net) != 0) 1096 continue; 1097 if(np->considered) 1098 break; 1099 if(netlist) 1100 last->next = np; 1101 else 1102 netlist = np; 1103 last = np; 1104 np->next = 0; 1105 np->considered = 1; 1106 } 1107 } 1108 } 1109 1110 /* 1111 * make a tuple 1112 */ 1113 Ndbtuple* 1114 mktuple(char *attr, char *val) 1115 { 1116 Ndbtuple *t; 1117 1118 t = emalloc(sizeof(Ndbtuple)); 1119 strcpy(t->attr, attr); 1120 strncpy(t->val, val, sizeof(t->val)); 1121 t->val[sizeof(t->val)-1] = 0; 1122 t->line = t; 1123 t->entry = 0; 1124 return t; 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 /* 1200 * look for a specific network 1201 */ 1202 for(np = netlist; np->net != nil; np++){ 1203 if(np->fasttimeouthack) 1204 continue; 1205 if(strcmp(np->net, mf->net) == 0) 1206 break; 1207 } 1208 1209 if(np->net != nil){ 1210 /* 1211 * known network 1212 */ 1213 nt = (*np->lookup)(np, mf->host, mf->serv, 1); 1214 for(t = nt; mf->nreply < Nreply && t; t = t->entry){ 1215 cp = (*np->trans)(t, np, mf->serv, mf->rem, 0); 1216 if(cp){ 1217 mf->replylen[mf->nreply] = strlen(cp); 1218 mf->reply[mf->nreply++] = cp; 1219 rv++; 1220 } 1221 } 1222 ndbfree(nt); 1223 return rv; 1224 } else { 1225 /* 1226 * not a known network, don't translate host or service 1227 */ 1228 if(mf->serv) 1229 snprint(reply, sizeof(reply), "%s/%s/clone %s!%s", 1230 mntpt, mf->net, mf->host, mf->serv); 1231 else 1232 snprint(reply, sizeof(reply), "%s/%s/clone %s", 1233 mntpt, mf->net, mf->host); 1234 mf->reply[0] = strdup(reply); 1235 mf->replylen[0] = strlen(reply); 1236 mf->nreply = 1; 1237 return 1; 1238 } 1239 } 1240 1241 /* 1242 * translate an ip service name into a port number. If it's a numeric port 1243 * number, look for restricted access. 1244 * 1245 * the service '*' needs no translation. 1246 */ 1247 char* 1248 ipserv(Network *np, char *name, char *buf) 1249 { 1250 char *p; 1251 int alpha = 0; 1252 int restr = 0; 1253 char port[Ndbvlen]; 1254 Ndbtuple *t, *nt; 1255 Ndbs s; 1256 1257 /* '*' means any service */ 1258 if(strcmp(name, "*")==0){ 1259 strcpy(buf, name); 1260 return buf; 1261 } 1262 1263 /* see if it's numeric or symbolic */ 1264 port[0] = 0; 1265 for(p = name; *p; p++){ 1266 if(isdigit(*p)) 1267 {} 1268 else if(isalpha(*p) || *p == '-' || *p == '$') 1269 alpha = 1; 1270 else 1271 return 0; 1272 } 1273 if(alpha){ 1274 t = ndbgetval(db, &s, np->net, name, "port", port); 1275 if(t == 0) 1276 return 0; 1277 } else { 1278 /* look up only for tcp ports < 1024 to get the restricted 1279 * attribute 1280 */ 1281 t = nil; 1282 if(atoi(name) < 1024 && strcmp(np->net, "tcp") == 0) 1283 t = ndbgetval(db, &s, "port", name, "port", port); 1284 if(t == nil){ 1285 strncpy(port, name, sizeof(port)); 1286 port[sizeof(port)-1] = 0; 1287 } 1288 } 1289 1290 if(t){ 1291 for(nt = t; nt; nt = nt->entry) 1292 if(strcmp(nt->attr, "restricted") == 0) 1293 restr = 1; 1294 ndbfree(t); 1295 } 1296 sprint(buf, "%s%s", port, restr ? "!r" : ""); 1297 return buf; 1298 } 1299 1300 /* 1301 * lookup an ip attribute 1302 */ 1303 int 1304 ipattrlookup(Ndb *db, char *ipa, char *attr, char *val) 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 strcpy(val, nt->val); 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[Ndbvlen+1]; 1337 char th[Ndbvlen+1]; 1338 char dollar[Ndbvlen+1]; 1339 uchar ip[IPaddrlen]; 1340 uchar net[IPaddrlen]; 1341 uchar tnet[IPaddrlen]; 1342 Ipifc *ifc; 1343 Iplifc *lifc; 1344 1345 USED(nolookup); 1346 1347 /* 1348 * start with the service since it's the most likely to fail 1349 * and costs the least 1350 */ 1351 werrstr("can't translate address"); 1352 if(serv==0 || ipserv(np, serv, ts) == 0){ 1353 werrstr("can't translate service"); 1354 return 0; 1355 } 1356 1357 /* for dial strings with no host */ 1358 if(strcmp(host, "*") == 0) 1359 return mktuple("ip", "*"); 1360 1361 /* 1362 * hack till we go v6 :: = 0.0.0.0 1363 */ 1364 if(strcmp("::", host) == 0) 1365 return mktuple("ip", "*"); 1366 1367 /* 1368 * '$' means the rest of the name is an attribute that we 1369 * need to search for 1370 */ 1371 if(*host == '$'){ 1372 if(ipattrlookup(db, ipaddr, host+1, dollar)) 1373 host = dollar; 1374 } 1375 1376 /* 1377 * turn '[ip address]' into just 'ip address' 1378 */ 1379 if(*host == '[' && host[strlen(host)-1] == ']'){ 1380 host++; 1381 host[strlen(host)-1] = 0; 1382 } 1383 1384 /* 1385 * just accept addresses 1386 */ 1387 attr = ipattr(host); 1388 if(strcmp(attr, "ip") == 0) 1389 return mktuple("ip", host); 1390 1391 /* 1392 * give the domain name server the first opportunity to 1393 * resolve domain names. if that fails try the database. 1394 */ 1395 t = 0; 1396 werrstr("can't translate address"); 1397 if(strcmp(attr, "dom") == 0) 1398 t = dnsiplookup(host, &s); 1399 if(t == 0) 1400 t = ndbgetval(db, &s, attr, host, "ip", th); 1401 if(t == 0) 1402 t = dnsiplookup(host, &s); 1403 if(t == 0 && strcmp(attr, "dom") != 0) 1404 t = dnsiplookup(host, &s); 1405 if(t == 0) 1406 return 0; 1407 1408 /* 1409 * reorder the tuple to have the matched line first and 1410 * save that in the request structure. 1411 */ 1412 t = reorder(t, s.t); 1413 1414 /* 1415 * reorder according to our interfaces 1416 */ 1417 lock(&ipifclock); 1418 for(ifc = ipifcs; ifc != nil; ifc = ifc->next){ 1419 for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){ 1420 maskip(lifc->ip, lifc->mask, net); 1421 for(nt = t; nt; nt = nt->entry){ 1422 if(strcmp(nt->attr, "ip") != 0) 1423 continue; 1424 parseip(ip, nt->val); 1425 maskip(ip, lifc->mask, tnet); 1426 if(memcmp(net, tnet, IPaddrlen) == 0){ 1427 t = reorder(t, nt); 1428 unlock(&ipifclock); 1429 return t; 1430 } 1431 } 1432 } 1433 } 1434 unlock(&ipifclock); 1435 1436 return t; 1437 } 1438 1439 /* 1440 * translate an ip address 1441 */ 1442 char* 1443 iptrans(Ndbtuple *t, Network *np, char *serv, char *rem, int hack) 1444 { 1445 char ts[Ndbvlen+1]; 1446 char reply[Maxreply]; 1447 char x[Ndbvlen+1]; 1448 1449 if(strcmp(t->attr, "ip") != 0) 1450 return 0; 1451 1452 if(serv == 0 || ipserv(np, serv, ts) == 0){ 1453 werrstr("can't translate service"); 1454 return 0; 1455 } 1456 if(rem != nil) 1457 snprint(x, sizeof(x), "!%s", rem); 1458 else 1459 *x = 0; 1460 1461 if(*t->val == '*') 1462 snprint(reply, sizeof(reply), "%s/%s/clone %s%s", 1463 mntpt, np->net, ts, x); 1464 else 1465 snprint(reply, sizeof(reply), "%s/%s/clone %s!%s%s%s", 1466 mntpt, np->net, t->val, ts, x, hack?"!fasttimeout":""); 1467 1468 return strdup(reply); 1469 } 1470 1471 /* 1472 * lookup a telephone number 1473 */ 1474 Ndbtuple* 1475 telcolookup(Network *np, char *host, char *serv, int nolookup) 1476 { 1477 Ndbtuple *t; 1478 Ndbs s; 1479 char th[Ndbvlen+1]; 1480 1481 USED(np, nolookup, serv); 1482 1483 werrstr("can't translate address"); 1484 t = ndbgetval(db, &s, "sys", host, "telco", th); 1485 if(t == 0) 1486 return mktuple("telco", host); 1487 1488 return reorder(t, s.t); 1489 } 1490 1491 /* 1492 * translate a telephone address 1493 */ 1494 char* 1495 telcotrans(Ndbtuple *t, Network *np, char *serv, char *rem, int) 1496 { 1497 char reply[Maxreply]; 1498 char x[Ndbvlen+1]; 1499 1500 if(strcmp(t->attr, "telco") != 0) 1501 return 0; 1502 1503 if(rem != nil) 1504 snprint(x, sizeof(x), "!%s", rem); 1505 else 1506 *x = 0; 1507 if(serv) 1508 snprint(reply, sizeof(reply), "%s/%s/clone %s!%s%s", mntpt, np->net, 1509 t->val, serv, x); 1510 else 1511 snprint(reply, sizeof(reply), "%s/%s/clone %s%s", mntpt, np->net, 1512 t->val, x); 1513 return strdup(reply); 1514 } 1515 1516 /* 1517 * reorder the tuple to put x's line first in the entry 1518 */ 1519 Ndbtuple* 1520 reorder(Ndbtuple *t, Ndbtuple *x) 1521 { 1522 Ndbtuple *nt; 1523 Ndbtuple *line; 1524 1525 /* find start of this entry's line */ 1526 for(line = x; line->entry == line->line; line = line->line) 1527 ; 1528 line = line->line; 1529 if(line == t) 1530 return t; /* already the first line */ 1531 1532 /* remove this line and everything after it from the entry */ 1533 for(nt = t; nt->entry != line; nt = nt->entry) 1534 ; 1535 nt->entry = 0; 1536 1537 /* make that the start of the entry */ 1538 for(nt = line; nt->entry; nt = nt->entry) 1539 ; 1540 nt->entry = t; 1541 return line; 1542 } 1543 1544 /* 1545 * create a slave process to handle a request to avoid one request blocking 1546 * another. parent returns to job loop. 1547 */ 1548 void 1549 slave(void) 1550 { 1551 if(*isslave) 1552 return; /* we're already a slave process */ 1553 1554 switch(rfork(RFPROC|RFNOTEG|RFMEM|RFNOWAIT)){ 1555 case -1: 1556 break; 1557 case 0: 1558 if(debug) 1559 syslog(0, logfile, "slave %d", getpid()); 1560 *isslave = 1; 1561 break; 1562 default: 1563 longjmp(masterjmp, 1); 1564 } 1565 1566 } 1567 1568 /* 1569 * call the dns process and have it try to translate a name 1570 */ 1571 Ndbtuple* 1572 dnsiplookup(char *host, Ndbs *s) 1573 { 1574 char buf[Ndbvlen + 4]; 1575 Ndbtuple *t; 1576 1577 unlock(&dblock); 1578 1579 /* save the name before starting a slave */ 1580 snprint(buf, sizeof(buf), "%s", host); 1581 1582 slave(); 1583 1584 if(strcmp(ipattr(buf), "ip") == 0) 1585 t = dnsquery(mntpt, buf, "ptr"); 1586 else 1587 t = dnsquery(mntpt, buf, "ip"); 1588 s->t = t; 1589 1590 if(t == nil){ 1591 rerrstr(buf, sizeof buf); 1592 if(strstr(buf, "exist")) 1593 werrstr("can't translate address: %s", buf); 1594 else if(strstr(buf, "dns failure")) 1595 werrstr("temporary problem: %s", buf); 1596 } 1597 1598 lock(&dblock); 1599 return t; 1600 } 1601 1602 int 1603 qmatch(Ndbtuple *t, char **attr, char **val, int n) 1604 { 1605 int i, found; 1606 Ndbtuple *nt; 1607 1608 for(i = 1; i < n; i++){ 1609 found = 0; 1610 for(nt = t; nt; nt = nt->entry) 1611 if(strcmp(attr[i], nt->attr) == 0) 1612 if(strcmp(val[i], "*") == 0 1613 || strcmp(val[i], nt->val) == 0){ 1614 found = 1; 1615 break; 1616 } 1617 if(found == 0) 1618 break; 1619 } 1620 return i == n; 1621 } 1622 1623 void 1624 qreply(Mfile *mf, Ndbtuple *t) 1625 { 1626 int i; 1627 Ndbtuple *nt; 1628 char buf[2048]; 1629 1630 buf[0] = 0; 1631 for(nt = t; mf->nreply < Nreply && nt; nt = nt->entry){ 1632 strcat(buf, nt->attr); 1633 strcat(buf, "="); 1634 strcat(buf, nt->val); 1635 i = strlen(buf); 1636 if(nt->line != nt->entry || sizeof(buf) - i < 2*Ndbvlen+2){ 1637 mf->replylen[mf->nreply] = strlen(buf); 1638 mf->reply[mf->nreply++] = strdup(buf); 1639 buf[0] = 0; 1640 } else 1641 strcat(buf, " "); 1642 } 1643 } 1644 1645 enum 1646 { 1647 Maxattr= 32, 1648 }; 1649 1650 /* 1651 * generic query lookup. The query is of one of the following 1652 * forms: 1653 * 1654 * attr1=val1 attr2=val2 attr3=val3 ... 1655 * 1656 * returns the matching tuple 1657 * 1658 * ipinfo attr=val attr1 attr2 attr3 ... 1659 * 1660 * is like ipinfo and returns the attr{1-n} 1661 * associated with the ip address. 1662 */ 1663 char* 1664 genquery(Mfile *mf, char *query) 1665 { 1666 int i, n; 1667 char *p; 1668 char *attr[Maxattr]; 1669 char *val[Maxattr]; 1670 Ndbtuple *t; 1671 Ndbs s; 1672 1673 n = getfields(query, attr, 32, 1, " "); 1674 if(n == 0) 1675 return "bad query"; 1676 1677 if(strcmp(attr[0], "ipinfo") == 0) 1678 return ipinfoquery(mf, attr, n); 1679 1680 /* parse pairs */ 1681 for(i = 0; i < n; i++){ 1682 p = strchr(attr[i], '='); 1683 if(p == 0) 1684 return "bad query"; 1685 *p++ = 0; 1686 val[i] = p; 1687 } 1688 1689 /* give dns a chance */ 1690 if((strcmp(attr[0], "dom") == 0 || strcmp(attr[0], "ip") == 0) && val[0]){ 1691 t = dnsiplookup(val[0], &s); 1692 if(t){ 1693 if(qmatch(t, attr, val, n)){ 1694 qreply(mf, t); 1695 ndbfree(t); 1696 return 0; 1697 } 1698 ndbfree(t); 1699 } 1700 } 1701 1702 /* first pair is always the key. It can't be a '*' */ 1703 t = ndbsearch(db, &s, attr[0], val[0]); 1704 1705 /* search is the and of all the pairs */ 1706 while(t){ 1707 if(qmatch(t, attr, val, n)){ 1708 qreply(mf, t); 1709 ndbfree(t); 1710 return 0; 1711 } 1712 1713 ndbfree(t); 1714 t = ndbsnext(&s, attr[0], val[0]); 1715 } 1716 1717 return "no match"; 1718 } 1719 1720 /* 1721 * resolve an ip address 1722 */ 1723 static Ndbtuple* 1724 ipresolve(char *attr, char *host) 1725 { 1726 Ndbtuple *t, *nt, **l; 1727 1728 t = iplookup(&network[Ntcp], host, "*", 0); 1729 for(l = &t; *l != nil; ){ 1730 nt = *l; 1731 if(strcmp(nt->attr, "ip") != 0){ 1732 *l = nt->entry; 1733 nt->entry = nil; 1734 ndbfree(nt); 1735 continue; 1736 } 1737 strcpy(nt->attr, attr); 1738 l = &nt->entry; 1739 } 1740 return t; 1741 } 1742 1743 char* 1744 ipinfoquery(Mfile *mf, char **list, int n) 1745 { 1746 int i, nresolve; 1747 int resolve[Maxattr]; 1748 Ndbtuple *t, *nt, **l; 1749 char *attr, *val; 1750 1751 /* skip 'ipinfo' */ 1752 list++; n--; 1753 1754 if(n < 2) 1755 return "bad query"; 1756 1757 /* get search attribute=value */ 1758 attr = *list++; n--; 1759 val = strchr(attr, '='); 1760 if(val == nil) 1761 return "bad query"; 1762 *val++ = 0; 1763 1764 /* 1765 * don't let ndbipinfo resolve the addresses, we're 1766 * better at it. 1767 */ 1768 nresolve = 0; 1769 for(i = 0; i < n; i++) 1770 if(*list[i] == '@'){ 1771 list[i]++; 1772 resolve[i] = 1; 1773 nresolve++; 1774 } else 1775 resolve[i] = 0; 1776 1777 t = ndbipinfo(db, attr, val, list, n); 1778 if(t == nil) 1779 return "no match"; 1780 1781 if(nresolve != 0){ 1782 for(l = &t; *l != nil;){ 1783 nt = *l; 1784 1785 /* already an address? */ 1786 if(strcmp(ipattr(nt->val), "ip") == 0){ 1787 l = &(*l)->entry; 1788 continue; 1789 } 1790 1791 /* user wants it resolved? */ 1792 for(i = 0; i < n; i++) 1793 if(strcmp(list[i], nt->attr) == 0) 1794 break; 1795 if(i >= n || resolve[i] == 0){ 1796 l = &(*l)->entry; 1797 continue; 1798 } 1799 1800 /* resolve address and replace entry */ 1801 *l = ipresolve(nt->attr, nt->val); 1802 while(*l != nil) 1803 l = &(*l)->entry; 1804 *l = nt->entry; 1805 1806 nt->entry = nil; 1807 ndbfree(nt); 1808 } 1809 } 1810 1811 /* make it all one line */ 1812 for(nt = t; nt != nil; nt = nt->entry){ 1813 if(nt->entry == nil) 1814 nt->line = t; 1815 else 1816 nt->line = nt->entry; 1817 } 1818 1819 qreply(mf, t); 1820 1821 return nil; 1822 } 1823 1824 void* 1825 emalloc(int size) 1826 { 1827 void *x; 1828 1829 x = malloc(size); 1830 if(x == nil) 1831 abort(); 1832 memset(x, 0, size); 1833 return x; 1834 } 1835 1836 char* 1837 estrdup(char *s) 1838 { 1839 int size; 1840 char *p; 1841 1842 size = strlen(s)+1; 1843 p = malloc(size); 1844 if(p == nil) 1845 abort(); 1846 memmove(p, s, size); 1847 return p; 1848 } 1849