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