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 <ip.h> 8 #include <pool.h> 9 #include "dns.h" 10 11 enum 12 { 13 Maxrequest= 1024, 14 Ncache= 8, 15 Maxpath= 128, 16 Maxreply= 512, 17 Maxrrr= 16, 18 Maxfdata= 8192, 19 20 Qdir= 0, 21 Qdns= 1, 22 }; 23 24 typedef struct Mfile Mfile; 25 typedef struct Job Job; 26 typedef struct Network Network; 27 28 int vers; /* incremented each clone/attach */ 29 30 struct Mfile 31 { 32 Mfile *next; /* next free mfile */ 33 int ref; 34 35 char *user; 36 Qid qid; 37 int fid; 38 39 int type; /* reply type */ 40 char reply[Maxreply]; 41 ushort rr[Maxrrr]; /* offset of rr's */ 42 ushort nrr; /* number of rr's */ 43 }; 44 45 // 46 // active local requests 47 // 48 struct Job 49 { 50 Job *next; 51 int flushed; 52 Fcall request; 53 Fcall reply; 54 }; 55 Lock joblock; 56 Job *joblist; 57 58 struct { 59 Lock; 60 Mfile *inuse; /* active mfile's */ 61 } mfalloc; 62 63 int mfd[2]; 64 int debug; 65 int traceactivity; 66 int cachedb; 67 ulong now; 68 int testing; 69 char *trace; 70 int needrefresh; 71 int resolver; 72 uchar ipaddr[IPaddrlen]; /* my ip address */ 73 int maxage; 74 char *zonerefreshprogram; 75 int sendnotifies; 76 77 void rversion(Job*); 78 void rauth(Job*); 79 void rflush(Job*); 80 void rattach(Job*, Mfile*); 81 char* rwalk(Job*, Mfile*); 82 void ropen(Job*, Mfile*); 83 void rcreate(Job*, Mfile*); 84 void rread(Job*, Mfile*); 85 void rwrite(Job*, Mfile*, Request*); 86 void rclunk(Job*, Mfile*); 87 void rremove(Job*, Mfile*); 88 void rstat(Job*, Mfile*); 89 void rwstat(Job*, Mfile*); 90 void sendmsg(Job*, char*); 91 void mountinit(char*, char*); 92 void io(void); 93 int fillreply(Mfile*, int); 94 Job* newjob(void); 95 void freejob(Job*); 96 void setext(char*, int, char*); 97 98 char *logfile = "dns"; 99 char *dbfile; 100 char mntpt[Maxpath]; 101 char *LOG; 102 103 void 104 usage(void) 105 { 106 fprint(2, "usage: %s [-rRs] [-f ndb-file] [-x netmtpt]\n", argv0); 107 exits("usage"); 108 } 109 110 void 111 main(int argc, char *argv[]) 112 { 113 int serve; 114 char servefile[Maxpath]; 115 char ext[Maxpath]; 116 char *p; 117 118 serve = 0; 119 setnetmtpt(mntpt, sizeof(mntpt), nil); 120 ext[0] = 0; 121 ARGBEGIN{ 122 case 'd': 123 debug = 1; 124 traceactivity = 1; 125 break; 126 case 'f': 127 p = ARGF(); 128 if(p == nil) 129 usage(); 130 dbfile = p; 131 break; 132 case 'x': 133 p = ARGF(); 134 if(p == nil) 135 usage(); 136 setnetmtpt(mntpt, sizeof(mntpt), p); 137 setext(ext, sizeof(ext), mntpt); 138 break; 139 case 'r': 140 resolver = 1; 141 break; 142 case 'R': 143 norecursion = 1; 144 break; 145 case 's': 146 serve = 1; /* serve network */ 147 cachedb = 1; 148 break; 149 case 'a': 150 p = ARGF(); 151 if(p == nil) 152 usage(); 153 maxage = atoi(p); 154 break; 155 case 't': 156 testing = 1; 157 break; 158 case 'z': 159 zonerefreshprogram = ARGF(); 160 break; 161 case 'n': 162 sendnotifies = 1; 163 break; 164 }ARGEND 165 USED(argc); 166 USED(argv); 167 168 if(testing) mainmem->flags |= POOL_NOREUSE; 169 rfork(RFREND|RFNOTEG); 170 171 /* start syslog before we fork */ 172 fmtinstall('F', fcallfmt); 173 dninit(); 174 if(myipaddr(ipaddr, mntpt) < 0) 175 sysfatal("can't read my ip address"); 176 177 syslog(0, logfile, "starting dns on %I", ipaddr); 178 179 opendatabase(); 180 181 snprint(servefile, sizeof(servefile), "#s/dns%s", ext); 182 unmount(servefile, mntpt); 183 remove(servefile); 184 mountinit(servefile, mntpt); 185 186 now = time(0); 187 srand(now*getpid()); 188 db2cache(1); 189 190 if(serve) 191 dnudpserver(mntpt); 192 if(sendnotifies) 193 notifyproc(); 194 195 io(); 196 syslog(0, logfile, "io returned, exiting"); 197 exits(0); 198 } 199 200 /* 201 * if a mount point is specified, set the cs extention to be the mount point 202 * with '_'s replacing '/'s 203 */ 204 void 205 setext(char *ext, int n, char *p) 206 { 207 int i, c; 208 209 n--; 210 for(i = 0; i < n; i++){ 211 c = p[i]; 212 if(c == 0) 213 break; 214 if(c == '/') 215 c = '_'; 216 ext[i] = c; 217 } 218 ext[i] = 0; 219 } 220 221 void 222 mountinit(char *service, char *mntpt) 223 { 224 int f; 225 int p[2]; 226 char buf[32]; 227 228 if(pipe(p) < 0) 229 abort(); /* "pipe failed" */; 230 switch(rfork(RFFDG|RFPROC|RFNAMEG)){ 231 case 0: 232 close(p[1]); 233 break; 234 case -1: 235 abort(); /* "fork failed\n" */; 236 default: 237 close(p[0]); 238 239 /* 240 * make a /srv/dns 241 */ 242 f = create(service, 1, 0666); 243 if(f < 0) 244 abort(); /* service */; 245 snprint(buf, sizeof(buf), "%d", p[1]); 246 if(write(f, buf, strlen(buf)) != strlen(buf)) 247 abort(); /* "write %s", service */; 248 close(f); 249 250 /* 251 * put ourselves into the file system 252 */ 253 if(mount(p[1], -1, mntpt, MAFTER, "") < 0) 254 fprint(2, "dns mount failed: %r\n"); 255 _exits(0); 256 } 257 mfd[0] = mfd[1] = p[0]; 258 } 259 260 Mfile* 261 newfid(int fid, int needunused) 262 { 263 Mfile *mf; 264 265 lock(&mfalloc); 266 for(mf = mfalloc.inuse; mf != nil; mf = mf->next){ 267 if(mf->fid == fid){ 268 unlock(&mfalloc); 269 if(needunused) 270 return nil; 271 return mf; 272 } 273 } 274 mf = emalloc(sizeof(*mf)); 275 if(mf == nil) 276 sysfatal("out of memory"); 277 mf->fid = fid; 278 mf->next = mfalloc.inuse; 279 mfalloc.inuse = mf; 280 unlock(&mfalloc); 281 return mf; 282 } 283 284 void 285 freefid(Mfile *mf) 286 { 287 Mfile **l; 288 289 lock(&mfalloc); 290 for(l = &mfalloc.inuse; *l != nil; l = &(*l)->next){ 291 if(*l == mf){ 292 *l = mf->next; 293 if(mf->user) 294 free(mf->user); 295 free(mf); 296 unlock(&mfalloc); 297 return; 298 } 299 } 300 sysfatal("freeing unused fid"); 301 } 302 303 Mfile* 304 copyfid(Mfile *mf, int fid) 305 { 306 Mfile *nmf; 307 308 nmf = newfid(fid, 1); 309 if(nmf == nil) 310 return nil; 311 nmf->fid = fid; 312 nmf->user = estrdup(mf->user); 313 nmf->qid.type = mf->qid.type; 314 nmf->qid.path = mf->qid.path; 315 nmf->qid.vers = vers++; 316 return nmf; 317 } 318 319 Job* 320 newjob(void) 321 { 322 Job *job; 323 324 job = emalloc(sizeof(*job)); 325 lock(&joblock); 326 job->next = joblist; 327 joblist = job; 328 job->request.tag = -1; 329 unlock(&joblock); 330 return job; 331 } 332 333 void 334 freejob(Job *job) 335 { 336 Job **l; 337 338 lock(&joblock); 339 for(l = &joblist; *l; l = &(*l)->next){ 340 if((*l) == job){ 341 *l = job->next; 342 free(job); 343 break; 344 } 345 } 346 unlock(&joblock); 347 } 348 349 void 350 flushjob(int tag) 351 { 352 Job *job; 353 354 lock(&joblock); 355 for(job = joblist; job; job = job->next){ 356 if(job->request.tag == tag && job->request.type != Tflush){ 357 job->flushed = 1; 358 break; 359 } 360 } 361 unlock(&joblock); 362 } 363 364 void 365 io(void) 366 { 367 long n; 368 Mfile *mf; 369 uchar mdata[IOHDRSZ + Maxfdata]; 370 Request req; 371 Job *job; 372 373 /* 374 * a slave process is sometimes forked to wait for replies from other 375 * servers. The master process returns immediately via a longjmp 376 * through 'mret'. 377 */ 378 if(setjmp(req.mret)) 379 putactivity(0); 380 req.isslave = 0; 381 for(;;){ 382 n = read9pmsg(mfd[0], mdata, sizeof mdata); 383 if(n<=0){ 384 syslog(0, logfile, "error reading mntpt: %r"); 385 exits(0); 386 } 387 job = newjob(); 388 if(convM2S(mdata, n, &job->request) != n){ 389 freejob(job); 390 continue; 391 } 392 mf = newfid(job->request.fid, 0); 393 if(debug) 394 syslog(0, logfile, "%F", &job->request); 395 396 getactivity(&req, 0); 397 req.aborttime = now + 60; /* don't spend more than 60 seconds */ 398 399 switch(job->request.type){ 400 default: 401 syslog(1, logfile, "unknown request type %d", job->request.type); 402 break; 403 case Tversion: 404 rversion(job); 405 break; 406 case Tauth: 407 rauth(job); 408 break; 409 case Tflush: 410 rflush(job); 411 break; 412 case Tattach: 413 rattach(job, mf); 414 break; 415 case Twalk: 416 rwalk(job, mf); 417 break; 418 case Topen: 419 ropen(job, mf); 420 break; 421 case Tcreate: 422 rcreate(job, mf); 423 break; 424 case Tread: 425 rread(job, mf); 426 break; 427 case Twrite: 428 rwrite(job, mf, &req); 429 break; 430 case Tclunk: 431 rclunk(job, mf); 432 break; 433 case Tremove: 434 rremove(job, mf); 435 break; 436 case Tstat: 437 rstat(job, mf); 438 break; 439 case Twstat: 440 rwstat(job, mf); 441 break; 442 } 443 444 freejob(job); 445 446 /* 447 * slave processes die after replying 448 */ 449 if(req.isslave){ 450 putactivity(0); 451 _exits(0); 452 } 453 454 putactivity(0); 455 } 456 } 457 458 void 459 rversion(Job *job) 460 { 461 if(job->request.msize > IOHDRSZ + Maxfdata) 462 job->reply.msize = IOHDRSZ + Maxfdata; 463 else 464 job->reply.msize = job->request.msize; 465 if(strncmp(job->request.version, "9P2000", 6) != 0) 466 sendmsg(job, "unknown 9P version"); 467 else{ 468 job->reply.version = "9P2000"; 469 sendmsg(job, 0); 470 } 471 } 472 473 void 474 rauth(Job *job) 475 { 476 sendmsg(job, "dns: authentication not required"); 477 } 478 479 /* 480 * don't flush till all the slaves are done 481 */ 482 void 483 rflush(Job *job) 484 { 485 flushjob(job->request.oldtag); 486 sendmsg(job, 0); 487 } 488 489 void 490 rattach(Job *job, Mfile *mf) 491 { 492 if(mf->user != nil) 493 free(mf->user); 494 mf->user = estrdup(job->request.uname); 495 mf->qid.vers = vers++; 496 mf->qid.type = QTDIR; 497 mf->qid.path = 0LL; 498 job->reply.qid = mf->qid; 499 sendmsg(job, 0); 500 } 501 502 char* 503 rwalk(Job *job, Mfile *mf) 504 { 505 char *err; 506 char **elems; 507 int nelems; 508 int i; 509 Mfile *nmf; 510 Qid qid; 511 512 err = 0; 513 nmf = nil; 514 elems = job->request.wname; 515 nelems = job->request.nwname; 516 job->reply.nwqid = 0; 517 518 if(job->request.newfid != job->request.fid){ 519 /* clone fid */ 520 nmf = copyfid(mf, job->request.newfid); 521 if(nmf == nil){ 522 err = "clone bad newfid"; 523 goto send; 524 } 525 mf = nmf; 526 } 527 /* else nmf will be nil */ 528 529 qid = mf->qid; 530 if(nelems > 0){ 531 /* walk fid */ 532 for(i=0; i<nelems && i<MAXWELEM; i++){ 533 if((qid.type & QTDIR) == 0){ 534 err = "not a directory"; 535 break; 536 } 537 if(strcmp(elems[i], "..") == 0 || strcmp(elems[i], ".") == 0){ 538 qid.type = QTDIR; 539 qid.path = Qdir; 540 Found: 541 job->reply.wqid[i] = qid; 542 job->reply.nwqid++; 543 continue; 544 } 545 if(strcmp(elems[i], "dns") == 0){ 546 qid.type = QTFILE; 547 qid.path = Qdns; 548 goto Found; 549 } 550 err = "file does not exist"; 551 break; 552 } 553 } 554 555 send: 556 if(nmf != nil && (err!=nil || job->reply.nwqid<nelems)) 557 freefid(nmf); 558 if(err == nil) 559 mf->qid = qid; 560 sendmsg(job, err); 561 return err; 562 } 563 564 void 565 ropen(Job *job, Mfile *mf) 566 { 567 int mode; 568 char *err; 569 570 err = 0; 571 mode = job->request.mode; 572 if(mf->qid.type & QTDIR){ 573 if(mode) 574 err = "permission denied"; 575 } 576 job->reply.qid = mf->qid; 577 job->reply.iounit = 0; 578 sendmsg(job, err); 579 } 580 581 void 582 rcreate(Job *job, Mfile *mf) 583 { 584 USED(mf); 585 sendmsg(job, "creation permission denied"); 586 } 587 588 void 589 rread(Job *job, Mfile *mf) 590 { 591 int i, n, cnt; 592 long off; 593 Dir dir; 594 uchar buf[Maxfdata]; 595 char *err; 596 long clock; 597 598 n = 0; 599 err = 0; 600 off = job->request.offset; 601 cnt = job->request.count; 602 if(mf->qid.type & QTDIR){ 603 clock = time(0); 604 if(off == 0){ 605 dir.name = "dns"; 606 dir.qid.type = QTFILE; 607 dir.qid.vers = vers; 608 dir.qid.path = Qdns; 609 dir.mode = 0666; 610 dir.length = 0; 611 dir.uid = mf->user; 612 dir.gid = mf->user; 613 dir.muid = mf->user; 614 dir.atime = clock; /* wrong */ 615 dir.mtime = clock; /* wrong */ 616 n = convD2M(&dir, buf, sizeof buf); 617 } 618 job->reply.data = (char*)buf; 619 } else { 620 for(i = 1; i <= mf->nrr; i++) 621 if(mf->rr[i] > off) 622 break; 623 if(i > mf->nrr) 624 goto send; 625 if(off + cnt > mf->rr[i]) 626 n = mf->rr[i] - off; 627 else 628 n = cnt; 629 job->reply.data = mf->reply + off; 630 } 631 send: 632 job->reply.count = n; 633 sendmsg(job, err); 634 } 635 636 void 637 rwrite(Job *job, Mfile *mf, Request *req) 638 { 639 int cnt, rooted, status; 640 long n; 641 char *err, *p, *atype; 642 RR *rp, *tp, *neg; 643 int wantsav; 644 645 err = 0; 646 cnt = job->request.count; 647 if(mf->qid.type & QTDIR){ 648 err = "can't write directory"; 649 goto send; 650 } 651 if(cnt >= Maxrequest){ 652 err = "request too long"; 653 goto send; 654 } 655 job->request.data[cnt] = 0; 656 if(cnt > 0 && job->request.data[cnt-1] == '\n') 657 job->request.data[cnt-1] = 0; 658 659 /* 660 * special commands 661 */ 662 if(strncmp(job->request.data, "debug", 5)==0 && job->request.data[5] == 0){ 663 debug ^= 1; 664 goto send; 665 } else if(strncmp(job->request.data, "dump", 4)==0 && job->request.data[4] == 0){ 666 dndump("/lib/ndb/dnsdump"); 667 goto send; 668 } else if(strncmp(job->request.data, "refresh", 7)==0 && job->request.data[7] == 0){ 669 needrefresh = 1; 670 goto send; 671 } else if(strncmp(job->request.data, "poolcheck", 9)==0 && job->request.data[9] == 0){ 672 poolcheck(mainmem); 673 goto send; 674 } 675 676 /* 677 * kill previous reply 678 */ 679 mf->nrr = 0; 680 mf->rr[0] = 0; 681 682 /* 683 * break up request (into a name and a type) 684 */ 685 atype = strchr(job->request.data, ' '); 686 if(atype == 0){ 687 err = "illegal request"; 688 goto send; 689 } else 690 *atype++ = 0; 691 692 /* 693 * tracing request 694 */ 695 if(strcmp(atype, "trace") == 0){ 696 if(trace) 697 free(trace); 698 if(*job->request.data) 699 trace = estrdup(job->request.data); 700 else 701 trace = 0; 702 goto send; 703 } 704 705 mf->type = rrtype(atype); 706 if(mf->type < 0){ 707 err = "unknown type"; 708 goto send; 709 } 710 711 p = atype - 2; 712 if(p >= job->request.data && *p == '.'){ 713 rooted = 1; 714 *p = 0; 715 } else 716 rooted = 0; 717 718 p = job->request.data; 719 if(*p == '!'){ 720 wantsav = 1; 721 p++; 722 } else 723 wantsav = 0; 724 dncheck(0, 1); 725 rp = dnresolve(p, Cin, mf->type, req, 0, 0, Recurse, rooted, &status); 726 dncheck(0, 1); 727 neg = rrremneg(&rp); 728 if(neg){ 729 status = neg->negrcode; 730 rrfreelist(neg); 731 } 732 if(rp == 0){ 733 switch(status){ 734 case Rname: 735 err = "name does not exist"; 736 break; 737 case Rserver: 738 err = "dns failure"; 739 break; 740 default: 741 err = "resource does not exist"; 742 break; 743 } 744 } else { 745 lock(&joblock); 746 if(!job->flushed){ 747 /* format data to be read later */ 748 n = 0; 749 mf->nrr = 0; 750 for(tp = rp; mf->nrr < Maxrrr-1 && n < Maxreply && tp && 751 tsame(mf->type, tp->type); tp = tp->next){ 752 mf->rr[mf->nrr++] = n; 753 if(wantsav) 754 n += snprint(mf->reply+n, Maxreply-n, "%Q", tp); 755 else 756 n += snprint(mf->reply+n, Maxreply-n, "%R", tp); 757 } 758 mf->rr[mf->nrr] = n; 759 } 760 unlock(&joblock); 761 rrfreelist(rp); 762 } 763 764 send: 765 dncheck(0, 1); 766 job->reply.count = cnt; 767 sendmsg(job, err); 768 } 769 770 void 771 rclunk(Job *job, Mfile *mf) 772 { 773 freefid(mf); 774 sendmsg(job, 0); 775 } 776 777 void 778 rremove(Job *job, Mfile *mf) 779 { 780 USED(mf); 781 sendmsg(job, "remove permission denied"); 782 } 783 784 void 785 rstat(Job *job, Mfile *mf) 786 { 787 Dir dir; 788 uchar buf[IOHDRSZ+Maxfdata]; 789 790 if(mf->qid.type & QTDIR){ 791 dir.name = "."; 792 dir.mode = DMDIR|0555; 793 } else { 794 dir.name = "dns"; 795 dir.mode = 0666; 796 } 797 dir.qid = mf->qid; 798 dir.length = 0; 799 dir.uid = mf->user; 800 dir.gid = mf->user; 801 dir.muid = mf->user; 802 dir.atime = dir.mtime = time(0); 803 job->reply.nstat = convD2M(&dir, buf, sizeof buf); 804 job->reply.stat = buf; 805 sendmsg(job, 0); 806 } 807 808 void 809 rwstat(Job *job, Mfile *mf) 810 { 811 USED(mf); 812 sendmsg(job, "wstat permission denied"); 813 } 814 815 void 816 sendmsg(Job *job, char *err) 817 { 818 int n; 819 uchar mdata[IOHDRSZ + Maxfdata]; 820 char ename[ERRMAX]; 821 822 if(err){ 823 job->reply.type = Rerror; 824 snprint(ename, sizeof(ename), "dns: %s", err); 825 job->reply.ename = ename; 826 }else{ 827 job->reply.type = job->request.type+1; 828 } 829 job->reply.tag = job->request.tag; 830 n = convS2M(&job->reply, mdata, sizeof mdata); 831 if(n == 0){ 832 syslog(1, logfile, "sendmsg convS2M of %F returns 0", &job->reply); 833 abort(); 834 } 835 lock(&joblock); 836 if(job->flushed == 0) 837 if(write(mfd[1], mdata, n)!=n) 838 sysfatal("mount write"); 839 unlock(&joblock); 840 if(debug) 841 syslog(0, logfile, "%F %d", &job->reply, n); 842 } 843 844 /* 845 * the following varies between dnsdebug and dns 846 */ 847 void 848 logreply(int id, uchar *addr, DNSmsg *mp) 849 { 850 RR *rp; 851 852 syslog(0, LOG, "%d: rcvd %I flags:%s%s%s%s%s", id, addr, 853 mp->flags & Fauth ? " auth" : "", 854 mp->flags & Ftrunc ? " trunc" : "", 855 mp->flags & Frecurse ? " rd" : "", 856 mp->flags & Fcanrec ? " ra" : "", 857 mp->flags & (Fauth|Rname) == (Fauth|Rname) ? 858 " nx" : ""); 859 for(rp = mp->qd; rp != nil; rp = rp->next) 860 syslog(0, LOG, "%d: rcvd %I qd %s", id, addr, rp->owner->name); 861 for(rp = mp->an; rp != nil; rp = rp->next) 862 syslog(0, LOG, "%d: rcvd %I an %R", id, addr, rp); 863 for(rp = mp->ns; rp != nil; rp = rp->next) 864 syslog(0, LOG, "%d: rcvd %I ns %R", id, addr, rp); 865 for(rp = mp->ar; rp != nil; rp = rp->next) 866 syslog(0, LOG, "%d: rcvd %I ar %R", id, addr, rp); 867 } 868 869 void 870 logsend(int id, int subid, uchar *addr, char *sname, char *rname, int type) 871 { 872 char buf[12]; 873 874 syslog(0, LOG, "[%d] %d.%d: sending to %I/%s %s %s", 875 getpid(), id, subid, addr, sname, rname, rrname(type, buf, sizeof buf)); 876 } 877 878 RR* 879 getdnsservers(int class) 880 { 881 return dnsservers(class); 882 } 883