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