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