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