Blob


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 "dns.h"
11 enum
12 {
13 Maxrequest= 1024,
14 Ncache= 8,
15 Maxpath= 128,
16 Maxreply= 512,
17 Maxrrr= 16,
18 Maxfdata= 8192,
20 Qdir= 0,
21 Qdns= 1,
22 };
24 typedef struct Mfile Mfile;
25 typedef struct Job Job;
26 typedef struct Network Network;
28 int vers; /* incremented each clone/attach */
30 struct Mfile
31 {
32 Mfile *next; /* next free mfile */
33 int ref;
35 char *user;
36 Qid qid;
37 int fid;
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 };
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;
58 struct {
59 Lock lk;
60 Mfile *inuse; /* active mfile's */
61 } mfalloc;
63 int haveip;
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;
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*, char*);
93 void io(void);
94 int fillreply(Mfile*, int);
95 Job* newjob(void);
96 void freejob(Job*);
97 void setext(char*, int, char*);
99 char *logfile = "dns";
100 char *dbfile;
101 char mntpt[Maxpath];
102 char *LOG;
104 void
105 usage(void)
107 fprint(2, "usage: %s [-rs] [-f ndb-file] [-x netmtpt]\n", argv0);
108 exits("usage");
111 void
112 main(int argc, char *argv[])
114 int serve;
115 char servefile[Maxpath];
116 char ext[Maxpath];
117 char *p;
119 serve = 0;
120 // setnetmtpt(mntpt, sizeof(mntpt), nil);
121 ext[0] = 0;
122 ARGBEGIN{
123 case 'd':
124 debug = 1;
125 traceactivity = 1;
126 break;
127 case 'f':
128 p = ARGF();
129 if(p == nil)
130 usage();
131 dbfile = p;
132 break;
133 case 'i':
134 haveip = 1;
135 parseip(ipaddr, EARGF(usage()));
136 break;
137 // case 'x':
138 // p = ARGF();
139 // if(p == nil)
140 // usage();
141 // setnetmtpt(mntpt, sizeof(mntpt), p);
142 // setext(ext, sizeof(ext), mntpt);
143 // break;
144 case 'r':
145 resolver = 1;
146 break;
147 case 's':
148 serve = 1; /* serve network */
149 cachedb = 1;
150 break;
151 case 'a':
152 p = ARGF();
153 if(p == nil)
154 usage();
155 maxage = atoi(p);
156 break;
157 case 't':
158 testing = 1;
159 break;
160 case 'z':
161 zonerefreshprogram = ARGF();
162 break;
163 case 'n':
164 sendnotifies = 1;
165 break;
166 }ARGEND
167 USED(argc);
168 USED(argv);
170 //if(testing) mainmem->flags |= POOL_NOREUSE;
171 #define RFREND 0
172 rfork(RFREND|RFNOTEG);
174 /* start syslog before we fork */
175 fmtinstall('F', fcallfmt);
176 dninit();
177 if(!haveip && myipaddr(ipaddr, mntpt) < 0)
178 sysfatal("can't read my ip address");
180 syslog(0, logfile, "starting dns on %I", ipaddr);
182 opendatabase();
184 /*
185 snprint(servefile, sizeof(servefile), "#s/dns%s", ext);
186 unmount(servefile, mntpt);
187 remove(servefile);
188 */
189 mountinit(servefile, mntpt);
191 now = time(0);
192 srand(now*getpid());
193 db2cache(1);
195 // if(serve)
196 // proccreate(dnudpserver, mntpt, STACK);
197 if(sendnotifies)
198 notifyproc();
200 io();
201 syslog(0, logfile, "io returned, exiting");
202 exits(0);
205 int
206 myipaddr(uchar *ip, char *net)
208 ipmove(ip, ipaddr);
209 return 0;
212 /*
213 * if a mount point is specified, set the cs extention to be the mount point
214 * with '_'s replacing '/'s
215 */
216 void
217 setext(char *ext, int n, char *p)
219 int i, c;
221 n--;
222 for(i = 0; i < n; i++){
223 c = p[i];
224 if(c == 0)
225 break;
226 if(c == '/')
227 c = '_';
228 ext[i] = c;
230 ext[i] = 0;
233 void
234 mountinit(char *service, char *mntpt)
236 int p[2];
238 if(pipe(p) < 0)
239 abort(); /* "pipe failed" */;
240 switch(rfork(RFFDG|RFPROC|RFNAMEG)){
241 case 0:
242 close(p[1]);
243 break;
244 case -1:
245 abort(); /* "fork failed\n" */;
246 default:
247 close(p[0]);
249 if(post9pservice(p[1], "dns") < 0)
250 fprint(2, "post9pservice dns: %r\n");
251 _exits(0);
253 mfd[0] = mfd[1] = p[0];
256 Mfile*
257 newfid(int fid, int needunused)
259 Mfile *mf;
261 lock(&mfalloc.lk);
262 for(mf = mfalloc.inuse; mf != nil; mf = mf->next){
263 if(mf->fid == fid){
264 unlock(&mfalloc.lk);
265 if(needunused)
266 return nil;
267 return mf;
270 if(!needunused){
271 unlock(&mfalloc.lk);
272 return nil;
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.lk);
281 return mf;
284 void
285 freefid(Mfile *mf)
287 Mfile **l;
289 fprint(2, "freefid %d\n", mf->fid);
290 lock(&mfalloc.lk);
291 for(l = &mfalloc.inuse; *l != nil; l = &(*l)->next){
292 if(*l == mf){
293 *l = mf->next;
294 if(mf->user)
295 free(mf->user);
296 free(mf);
297 unlock(&mfalloc.lk);
298 return;
301 sysfatal("freeing unused fid");
304 Mfile*
305 copyfid(Mfile *mf, int fid)
307 Mfile *nmf;
309 nmf = newfid(fid, 1);
310 if(nmf == nil)
311 return nil;
312 nmf->fid = fid;
313 nmf->user = estrdup(mf->user);
314 nmf->qid.type = mf->qid.type;
315 nmf->qid.path = mf->qid.path;
316 nmf->qid.vers = vers++;
317 return nmf;
320 Job*
321 newjob(void)
323 Job *job;
325 job = emalloc(sizeof(*job));
326 lock(&joblock);
327 job->next = joblist;
328 joblist = job;
329 job->request.tag = -1;
330 unlock(&joblock);
331 return job;
334 void
335 freejob(Job *job)
337 Job **l;
339 lock(&joblock);
340 for(l = &joblist; *l; l = &(*l)->next){
341 if((*l) == job){
342 *l = job->next;
343 free(job);
344 break;
347 unlock(&joblock);
350 void
351 flushjob(int tag)
353 Job *job;
355 lock(&joblock);
356 for(job = joblist; job; job = job->next){
357 if(job->request.tag == tag && job->request.type != Tflush){
358 job->flushed = 1;
359 break;
362 unlock(&joblock);
365 void
366 io(void)
368 long n;
369 Mfile *mf;
370 uchar mdata[IOHDRSZ + Maxfdata];
371 Request req;
372 Job *job;
374 /*
375 * a slave process is sometimes forked to wait for replies from other
376 * servers. The master process returns immediately via a longjmp
377 * through 'mret'.
378 */
379 if(setjmp(req.mret))
380 putactivity();
381 req.isslave = 0;
382 for(;;){
383 n = read9pmsg(mfd[0], mdata, sizeof mdata);
384 if(n<=0){
385 syslog(0, logfile, "error reading mntpt: %r");
386 exits(0);
388 job = newjob();
389 if(convM2S(mdata, n, &job->request) != n){
390 freejob(job);
391 continue;
393 if(debug)
394 syslog(0, logfile, "%F", &job->request);
396 getactivity(&req);
397 req.aborttime = now + 60; /* don't spend more than 60 seconds */
399 mf = nil;
400 switch(job->request.type){
401 case Tversion:
402 case Tauth:
403 case Tflush:
404 break;
405 case Tattach:
406 mf = newfid(job->request.fid, 1);
407 if(mf == nil){
408 sendmsg(job, "fid in use");
409 goto skip;
411 break;
412 default:
413 mf = newfid(job->request.fid, 0);
414 if(mf == nil){
415 sendmsg(job, "unknown fid");
416 goto skip;
418 break;
421 switch(job->request.type){
422 default:
423 syslog(1, logfile, "unknown request type %d", job->request.type);
424 break;
425 case Tversion:
426 rversion(job);
427 break;
428 case Tauth:
429 rauth(job);
430 break;
431 case Tflush:
432 rflush(job);
433 break;
434 case Tattach:
435 rattach(job, mf);
436 break;
437 case Twalk:
438 rwalk(job, mf);
439 break;
440 case Topen:
441 ropen(job, mf);
442 break;
443 case Tcreate:
444 rcreate(job, mf);
445 break;
446 case Tread:
447 rread(job, mf);
448 break;
449 case Twrite:
450 rwrite(job, mf, &req);
451 break;
452 case Tclunk:
453 rclunk(job, mf);
454 break;
455 case Tremove:
456 rremove(job, mf);
457 break;
458 case Tstat:
459 rstat(job, mf);
460 break;
461 case Twstat:
462 rwstat(job, mf);
463 break;
465 skip:
466 freejob(job);
468 /*
469 * slave processes die after replying
470 */
471 if(req.isslave){
472 putactivity();
473 _exits(0);
476 putactivity();
480 void
481 rversion(Job *job)
483 if(job->request.msize > IOHDRSZ + Maxfdata)
484 job->reply.msize = IOHDRSZ + Maxfdata;
485 else
486 job->reply.msize = job->request.msize;
487 if(strncmp(job->request.version, "9P2000", 6) != 0)
488 sendmsg(job, "unknown 9P version");
489 else{
490 job->reply.version = "9P2000";
491 sendmsg(job, 0);
495 void
496 rauth(Job *job)
498 sendmsg(job, "dns: authentication not required");
501 /*
502 * don't flush till all the slaves are done
503 */
504 void
505 rflush(Job *job)
507 flushjob(job->request.oldtag);
508 sendmsg(job, 0);
511 void
512 rattach(Job *job, Mfile *mf)
514 if(mf->user != nil)
515 free(mf->user);
516 mf->user = estrdup(job->request.uname);
517 mf->qid.vers = vers++;
518 mf->qid.type = QTDIR;
519 mf->qid.path = 0LL;
520 job->reply.qid = mf->qid;
521 sendmsg(job, 0);
524 char*
525 rwalk(Job *job, Mfile *mf)
527 char *err;
528 char **elems;
529 int nelems;
530 int i;
531 Mfile *nmf;
532 Qid qid;
534 err = 0;
535 nmf = nil;
536 elems = job->request.wname;
537 nelems = job->request.nwname;
538 job->reply.nwqid = 0;
540 if(job->request.newfid != job->request.fid){
541 /* clone fid */
542 if(job->request.newfid<0){
543 err = "clone newfid out of range";
544 goto send;
546 nmf = copyfid(mf, job->request.newfid);
547 if(nmf == nil){
548 err = "clone bad newfid";
549 goto send;
551 mf = nmf;
553 /* else nmf will be nil */
555 qid = mf->qid;
556 if(nelems > 0){
557 /* walk fid */
558 for(i=0; i<nelems && i<MAXWELEM; i++){
559 if((qid.type & QTDIR) == 0){
560 err = "not a directory";
561 break;
563 if(strcmp(elems[i], "..") == 0 || strcmp(elems[i], ".") == 0){
564 qid.type = QTDIR;
565 qid.path = Qdir;
566 Found:
567 job->reply.wqid[i] = qid;
568 job->reply.nwqid++;
569 continue;
571 if(strcmp(elems[i], "dns") == 0){
572 qid.type = QTFILE;
573 qid.path = Qdns;
574 goto Found;
576 err = "file does not exist";
577 break;
581 send:
582 if(nmf != nil && (err!=nil || job->reply.nwqid<nelems))
583 freefid(nmf);
584 if(err == nil)
585 mf->qid = qid;
586 sendmsg(job, err);
587 return err;
590 void
591 ropen(Job *job, Mfile *mf)
593 int mode;
594 char *err;
596 err = 0;
597 mode = job->request.mode;
598 if(mf->qid.type & QTDIR){
599 if(mode)
600 err = "permission denied";
602 job->reply.qid = mf->qid;
603 job->reply.iounit = 0;
604 sendmsg(job, err);
607 void
608 rcreate(Job *job, Mfile *mf)
610 USED(mf);
611 sendmsg(job, "creation permission denied");
614 void
615 rread(Job *job, Mfile *mf)
617 int i, n, cnt;
618 long off;
619 Dir dir;
620 uchar buf[Maxfdata];
621 char *err;
622 long clock;
624 n = 0;
625 err = 0;
626 off = job->request.offset;
627 cnt = job->request.count;
628 if(mf->qid.type & QTDIR){
629 clock = time(0);
630 if(off == 0){
631 dir.name = "dns";
632 dir.qid.type = QTFILE;
633 dir.qid.vers = vers;
634 dir.qid.path = Qdns;
635 dir.mode = 0666;
636 dir.length = 0;
637 dir.uid = mf->user;
638 dir.gid = mf->user;
639 dir.muid = mf->user;
640 dir.atime = clock; /* wrong */
641 dir.mtime = clock; /* wrong */
642 n = convD2M(&dir, buf, sizeof buf);
644 job->reply.data = (char*)buf;
645 } else {
646 for(i = 1; i <= mf->nrr; i++)
647 if(mf->rr[i] > off)
648 break;
649 if(i > mf->nrr)
650 goto send;
651 if(off + cnt > mf->rr[i])
652 n = mf->rr[i] - off;
653 else
654 n = cnt;
655 job->reply.data = mf->reply + off;
657 send:
658 job->reply.count = n;
659 sendmsg(job, err);
662 void
663 rwrite(Job *job, Mfile *mf, Request *req)
665 int cnt, rooted, status;
666 long n;
667 char *err, *p, *atype;
668 RR *rp, *tp, *neg;
669 int wantsav;
671 err = 0;
672 cnt = job->request.count;
673 if(mf->qid.type & QTDIR){
674 err = "can't write directory";
675 goto send;
677 if(cnt >= Maxrequest){
678 err = "request too long";
679 goto send;
681 job->request.data[cnt] = 0;
682 if(cnt > 0 && job->request.data[cnt-1] == '\n')
683 job->request.data[cnt-1] = 0;
685 /*
686 * special commands
687 */
688 if(strncmp(job->request.data, "debug", 5)==0 && job->request.data[5] == 0){
689 debug ^= 1;
690 goto send;
691 } else if(strncmp(job->request.data, "dump", 4)==0 && job->request.data[4] == 0){
692 dndump("/lib/ndb/dnsdump");
693 goto send;
694 } else if(strncmp(job->request.data, "refresh", 7)==0 && job->request.data[7] == 0){
695 needrefresh = 1;
696 goto send;
697 // } else if(strncmp(job->request.data, "poolcheck", 9)==0 && job->request.data[9] == 0){
698 // poolcheck(mainmem);
699 // goto send;
702 /*
703 * kill previous reply
704 */
705 mf->nrr = 0;
706 mf->rr[0] = 0;
708 /*
709 * break up request (into a name and a type)
710 */
711 atype = strchr(job->request.data, ' ');
712 if(atype == 0){
713 err = "illegal request";
714 goto send;
715 } else
716 *atype++ = 0;
718 /*
719 * tracing request
720 */
721 if(strcmp(atype, "trace") == 0){
722 if(trace)
723 free(trace);
724 if(*job->request.data)
725 trace = estrdup(job->request.data);
726 else
727 trace = 0;
728 goto send;
731 mf->type = rrtype(atype);
732 if(mf->type < 0){
733 err = "unknown type";
734 goto send;
737 p = atype - 2;
738 if(p >= job->request.data && *p == '.'){
739 rooted = 1;
740 *p = 0;
741 } else
742 rooted = 0;
744 p = job->request.data;
745 if(*p == '!'){
746 wantsav = 1;
747 p++;
748 } else
749 wantsav = 0;
750 dncheck(0, 1);
751 rp = dnresolve(p, Cin, mf->type, req, 0, 0, Recurse, rooted, &status);
752 dncheck(0, 1);
753 neg = rrremneg(&rp);
754 if(neg){
755 status = neg->negrcode;
756 rrfreelist(neg);
758 if(rp == 0){
759 switch(status){
760 case Rname:
761 err = "name does not exist";
762 break;
763 case Rserver:
764 err = "dns failure";
765 break;
766 default:
767 err = "resource does not exist";
768 break;
770 } else {
771 lock(&joblock);
772 if(!job->flushed){
773 /* format data to be read later */
774 n = 0;
775 mf->nrr = 0;
776 for(tp = rp; mf->nrr < Maxrrr-1 && n < Maxreply && tp &&
777 tsame(mf->type, tp->type); tp = tp->next){
778 mf->rr[mf->nrr++] = n;
779 if(wantsav)
780 n += snprint(mf->reply+n, Maxreply-n, "%Q", tp);
781 else
782 n += snprint(mf->reply+n, Maxreply-n, "%R", tp);
784 mf->rr[mf->nrr] = n;
786 unlock(&joblock);
787 rrfreelist(rp);
790 send:
791 dncheck(0, 1);
792 job->reply.count = cnt;
793 sendmsg(job, err);
796 void
797 rclunk(Job *job, Mfile *mf)
799 freefid(mf);
800 sendmsg(job, 0);
803 void
804 rremove(Job *job, Mfile *mf)
806 USED(mf);
807 sendmsg(job, "remove permission denied");
810 void
811 rstat(Job *job, Mfile *mf)
813 Dir dir;
814 uchar buf[IOHDRSZ+Maxfdata];
816 if(mf->qid.type & QTDIR){
817 dir.name = ".";
818 dir.mode = DMDIR|0555;
819 } else {
820 dir.name = "dns";
821 dir.mode = 0666;
823 dir.qid = mf->qid;
824 dir.length = 0;
825 dir.uid = mf->user;
826 dir.gid = mf->user;
827 dir.muid = mf->user;
828 dir.atime = dir.mtime = time(0);
829 job->reply.nstat = convD2M(&dir, buf, sizeof buf);
830 job->reply.stat = buf;
831 sendmsg(job, 0);
834 void
835 rwstat(Job *job, Mfile *mf)
837 USED(mf);
838 sendmsg(job, "wstat permission denied");
841 void
842 sendmsg(Job *job, char *err)
844 int n;
845 uchar mdata[IOHDRSZ + Maxfdata];
846 char ename[ERRMAX];
848 if(err){
849 job->reply.type = Rerror;
850 snprint(ename, sizeof(ename), "dns: %s", err);
851 job->reply.ename = ename;
852 }else{
853 job->reply.type = job->request.type+1;
855 job->reply.tag = job->request.tag;
856 n = convS2M(&job->reply, mdata, sizeof mdata);
857 if(n == 0){
858 syslog(1, logfile, "sendmsg convS2M of %F returns 0", &job->reply);
859 abort();
861 lock(&joblock);
862 if(job->flushed == 0)
863 if(write(mfd[1], mdata, n)!=n)
864 sysfatal("mount write");
865 unlock(&joblock);
866 if(debug)
867 syslog(0, logfile, "%F %d", &job->reply, n);
870 /*
871 * the following varies between dnsdebug and dns
872 */
873 void
874 logreply(int id, uchar *addr, DNSmsg *mp)
876 RR *rp;
878 syslog(0, LOG, "%d: rcvd %I flags:%s%s%s%s%s", id, addr,
879 mp->flags & Fauth ? " auth" : "",
880 mp->flags & Ftrunc ? " trunc" : "",
881 mp->flags & Frecurse ? " rd" : "",
882 mp->flags & Fcanrec ? " ra" : "",
883 mp->flags & (Fauth|Rname) == (Fauth|Rname) ?
884 " nx" : "");
885 for(rp = mp->qd; rp != nil; rp = rp->next)
886 syslog(0, LOG, "%d: rcvd %I qd %s", id, addr, rp->owner->name);
887 for(rp = mp->an; rp != nil; rp = rp->next)
888 syslog(0, LOG, "%d: rcvd %I an %R", id, addr, rp);
889 for(rp = mp->ns; rp != nil; rp = rp->next)
890 syslog(0, LOG, "%d: rcvd %I ns %R", id, addr, rp);
891 for(rp = mp->ar; rp != nil; rp = rp->next)
892 syslog(0, LOG, "%d: rcvd %I ar %R", id, addr, rp);
895 void
896 logsend(int id, int subid, uchar *addr, char *sname, char *rname, int type)
898 char buf[12];
900 syslog(0, LOG, "%d.%d: sending to %I/%s %s %s",
901 id, subid, addr, sname, rname, rrname(type, buf, sizeof buf));
904 RR*
905 getdnsservers(int class)
907 return dnsservers(class);