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