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