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