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
usage(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
checkaddress(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 void
threadmain(int argc,char * argv[])125 threadmain(int argc, char *argv[])
126 {
127 int serveudp, servetcp;
128 char *service;
129
130 serveudp = 0;
131 servetcp = 0;
132 service = "dns";
133 ARGBEGIN{
134 case 'd':
135 debug = 1;
136 traceactivity = 1;
137 break;
138 case 'f':
139 dbfile = EARGF(usage());
140 break;
141 case 'x':
142 service = EARGF(usage());
143 break;
144 case 'r':
145 resolver = 1;
146 break;
147 case 's':
148 serveudp = 1;
149 cachedb = 1;
150 break;
151 case 't':
152 servetcp = 1;
153 cachedb = 1;
154 break;
155 case 'a':
156 maxage = atoi(EARGF(usage()));
157 break;
158 case 'z':
159 zonerefreshprogram = EARGF(usage());
160 break;
161 case 'n':
162 sendnotifies = 1;
163 break;
164 case 'U':
165 udpaddr = estrdup(netmkaddr(EARGF(usage()), "udp", "domain"));
166 break;
167 case 'T':
168 tcpaddr = estrdup(netmkaddr(EARGF(usage()), "tcp", "domain"));
169 break;
170 default:
171 usage();
172 }ARGEND
173
174 if(argc)
175 usage();
176 if(serveudp && servetcp)
177 checkaddress();
178
179 rfork(RFNOTEG);
180
181 /* start syslog before we fork */
182 fmtinstall('F', fcallfmt);
183 dninit();
184 if(myipaddr(ipaddr, mntpt) < 0)
185 sysfatal("can't read my ip address");
186
187 syslog(0, logfile, "starting dns on %I", ipaddr);
188
189 opendatabase();
190
191 mountinit(service);
192
193 now = time(0);
194 srand(now*getpid());
195 db2cache(1);
196
197 if(serveudp)
198 proccreate(dnudpserver, nil, STACK);
199 if(servetcp)
200 proccreate(dntcpserver, nil, STACK);
201 if(sendnotifies)
202 proccreate(notifyproc, nil, STACK);
203
204 io();
205 }
206
207 /*
208 * if a mount point is specified, set the cs extention to be the mount point
209 * with '_'s replacing '/'s
210 */
211 void
setext(char * ext,int n,char * p)212 setext(char *ext, int n, char *p)
213 {
214 int i, c;
215
216 n--;
217 for(i = 0; i < n; i++){
218 c = p[i];
219 if(c == 0)
220 break;
221 if(c == '/')
222 c = '_';
223 ext[i] = c;
224 }
225 ext[i] = 0;
226 }
227
228 void
mountinit(char * service)229 mountinit(char *service)
230 {
231 int p[2];
232
233 if(pipe(p) < 0)
234 abort(); /* "pipe failed" */;
235 if(post9pservice(p[1], service, nil) < 0)
236 fprint(2, "post9pservice dns: %r\n");
237 close(p[1]);
238 mfd[0] = mfd[1] = p[0];
239 }
240
241 Mfile*
newfid(int fid,int needunused)242 newfid(int fid, int needunused)
243 {
244 Mfile *mf;
245
246 lock(&mfalloc.lk);
247 for(mf = mfalloc.inuse; mf != nil; mf = mf->next){
248 if(mf->fid == fid){
249 unlock(&mfalloc.lk);
250 if(needunused)
251 return nil;
252 return mf;
253 }
254 }
255 if(!needunused){
256 unlock(&mfalloc.lk);
257 return nil;
258 }
259 mf = emalloc(sizeof(*mf));
260 if(mf == nil)
261 sysfatal("out of memory");
262 mf->fid = fid;
263 mf->next = mfalloc.inuse;
264 mfalloc.inuse = mf;
265 unlock(&mfalloc.lk);
266 return mf;
267 }
268
269 void
freefid(Mfile * mf)270 freefid(Mfile *mf)
271 {
272 Mfile **l;
273
274 lock(&mfalloc.lk);
275 for(l = &mfalloc.inuse; *l != nil; l = &(*l)->next){
276 if(*l == mf){
277 *l = mf->next;
278 if(mf->user)
279 free(mf->user);
280 free(mf);
281 unlock(&mfalloc.lk);
282 return;
283 }
284 }
285 sysfatal("freeing unused fid");
286 }
287
288 Mfile*
copyfid(Mfile * mf,int fid)289 copyfid(Mfile *mf, int fid)
290 {
291 Mfile *nmf;
292
293 nmf = newfid(fid, 1);
294 if(nmf == nil)
295 return nil;
296 nmf->fid = fid;
297 nmf->user = estrdup(mf->user);
298 nmf->qid.type = mf->qid.type;
299 nmf->qid.path = mf->qid.path;
300 nmf->qid.vers = vers++;
301 return nmf;
302 }
303
304 Job*
newjob(void)305 newjob(void)
306 {
307 Job *job;
308
309 job = emalloc(sizeof(*job));
310 lock(&joblock);
311 job->next = joblist;
312 joblist = job;
313 job->request.tag = -1;
314 unlock(&joblock);
315 return job;
316 }
317
318 void
freejob(Job * job)319 freejob(Job *job)
320 {
321 Job **l;
322
323 lock(&joblock);
324 for(l = &joblist; *l; l = &(*l)->next){
325 if((*l) == job){
326 *l = job->next;
327 free(job);
328 break;
329 }
330 }
331 unlock(&joblock);
332 }
333
334 void
flushjob(int tag)335 flushjob(int tag)
336 {
337 Job *job;
338
339 lock(&joblock);
340 for(job = joblist; job; job = job->next){
341 if(job->request.tag == tag && job->request.type != Tflush){
342 job->flushed = 1;
343 break;
344 }
345 }
346 unlock(&joblock);
347 }
348
349 void
ioproc0(void * v)350 ioproc0(void *v)
351 {
352 long n;
353 Mfile *mf;
354 uchar mdata[IOHDRSZ + Maxfdata];
355 Request req;
356 Job *job;
357
358 USED(v);
359
360 for(;;){
361 n = read9pmsg(mfd[0], mdata, sizeof mdata);
362 if(n <= 0){
363 syslog(0, logfile, "error reading mntpt: %r");
364 break;
365 }
366 job = newjob();
367 if(convM2S(mdata, n, &job->request) != n){
368 freejob(job);
369 continue;
370 }
371 if(debug)
372 syslog(0, logfile, "%F", &job->request);
373
374 getactivity(&req);
375 req.aborttime = now + 60; /* don't spend more than 60 seconds */
376
377 mf = nil;
378 switch(job->request.type){
379 case Tversion:
380 case Tauth:
381 case Tflush:
382 break;
383 case Tattach:
384 mf = newfid(job->request.fid, 1);
385 if(mf == nil){
386 sendmsg(job, "fid in use");
387 goto skip;
388 }
389 break;
390 default:
391 mf = newfid(job->request.fid, 0);
392 if(mf == nil){
393 sendmsg(job, "unknown fid");
394 goto skip;
395 }
396 break;
397 }
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 skip:
444 freejob(job);
445 putactivity();
446 }
447 }
448
449 void
io(void)450 io(void)
451 {
452 int i;
453
454 for(i=0; i<Maxactive; i++)
455 proccreate(ioproc0, 0, STACK);
456 }
457
458 void
rversion(Job * job)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
rauth(Job * job)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
rflush(Job * job)483 rflush(Job *job)
484 {
485 flushjob(job->request.oldtag);
486 sendmsg(job, 0);
487 }
488
489 void
rattach(Job * job,Mfile * mf)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*
rwalk(Job * job,Mfile * mf)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
ropen(Job * job,Mfile * mf)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
rcreate(Job * job,Mfile * mf)582 rcreate(Job *job, Mfile *mf)
583 {
584 USED(mf);
585 sendmsg(job, "creation permission denied");
586 }
587
588 void
rread(Job * job,Mfile * mf)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
rwrite(Job * job,Mfile * mf,Request * req)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 static char *dumpfile;
645
646 err = 0;
647 cnt = job->request.count;
648 if(mf->qid.type & QTDIR){
649 err = "can't write directory";
650 goto send;
651 }
652 if(cnt >= Maxrequest){
653 err = "request too long";
654 goto send;
655 }
656 job->request.data[cnt] = 0;
657 if(cnt > 0 && job->request.data[cnt-1] == '\n')
658 job->request.data[cnt-1] = 0;
659
660 /*
661 * special commands
662 */
663 p = job->request.data;
664 if(strcmp(p, "debug")==0){
665 debug ^= 1;
666 goto send;
667 } else if(strcmp(p, "dump")==0){
668 if(dumpfile == nil)
669 dumpfile = unsharp("#9/ndb/dnsdump");
670 dndump(dumpfile);
671 goto send;
672 } else if(strncmp(p, "dump ", 5) == 0){
673 if(*(p+5))
674 dndump(p+5);
675 else
676 err = "bad filename";
677 goto send;
678 } else if(strcmp(p, "refresh")==0){
679 needrefresh = 1;
680 goto send;
681 }
682
683 /*
684 * kill previous reply
685 */
686 mf->nrr = 0;
687 mf->rr[0] = 0;
688
689 /*
690 * break up request (into a name and a type)
691 */
692 atype = strchr(job->request.data, ' ');
693 if(atype == 0){
694 err = "illegal request";
695 goto send;
696 } else
697 *atype++ = 0;
698
699 /*
700 * tracing request
701 */
702 if(strcmp(atype, "trace") == 0){
703 if(trace)
704 free(trace);
705 if(*job->request.data)
706 trace = estrdup(job->request.data);
707 else
708 trace = 0;
709 goto send;
710 }
711
712 mf->type = rrtype(atype);
713 if(mf->type < 0){
714 err = "unknown type";
715 goto send;
716 }
717
718 p = atype - 2;
719 if(p >= job->request.data && *p == '.'){
720 rooted = 1;
721 *p = 0;
722 } else
723 rooted = 0;
724
725 p = job->request.data;
726 if(*p == '!'){
727 wantsav = 1;
728 p++;
729 } else
730 wantsav = 0;
731 dncheck(0, 1);
732 rp = dnresolve(p, Cin, mf->type, req, 0, 0, Recurse, rooted, &status);
733 dncheck(0, 1);
734 neg = rrremneg(&rp);
735 if(neg){
736 status = neg->negrcode;
737 rrfreelist(neg);
738 }
739 if(rp == 0){
740 switch(status){
741 case Rname:
742 err = "name does not exist";
743 break;
744 case Rserver:
745 err = "dns failure";
746 break;
747 default:
748 err = "resource does not exist";
749 break;
750 }
751 } else {
752 lock(&joblock);
753 if(!job->flushed){
754 /* format data to be read later */
755 n = 0;
756 mf->nrr = 0;
757 for(tp = rp; mf->nrr < Maxrrr-1 && n < Maxreply && tp &&
758 tsame(mf->type, tp->type); tp = tp->next){
759 mf->rr[mf->nrr++] = n;
760 if(wantsav)
761 n += snprint(mf->reply+n, Maxreply-n, "%Q", tp);
762 else
763 n += snprint(mf->reply+n, Maxreply-n, "%R", tp);
764 }
765 mf->rr[mf->nrr] = n;
766 }
767 unlock(&joblock);
768 rrfreelist(rp);
769 }
770
771 send:
772 dncheck(0, 1);
773 job->reply.count = cnt;
774 sendmsg(job, err);
775 }
776
777 void
rclunk(Job * job,Mfile * mf)778 rclunk(Job *job, Mfile *mf)
779 {
780 freefid(mf);
781 sendmsg(job, 0);
782 }
783
784 void
rremove(Job * job,Mfile * mf)785 rremove(Job *job, Mfile *mf)
786 {
787 USED(mf);
788 sendmsg(job, "remove permission denied");
789 }
790
791 void
rstat(Job * job,Mfile * mf)792 rstat(Job *job, Mfile *mf)
793 {
794 Dir dir;
795 uchar buf[IOHDRSZ+Maxfdata];
796
797 if(mf->qid.type & QTDIR){
798 dir.name = ".";
799 dir.mode = DMDIR|0555;
800 } else {
801 dir.name = "dns";
802 dir.mode = 0666;
803 }
804 dir.qid = mf->qid;
805 dir.length = 0;
806 dir.uid = mf->user;
807 dir.gid = mf->user;
808 dir.muid = mf->user;
809 dir.atime = dir.mtime = time(0);
810 job->reply.nstat = convD2M(&dir, buf, sizeof buf);
811 job->reply.stat = buf;
812 sendmsg(job, 0);
813 }
814
815 void
rwstat(Job * job,Mfile * mf)816 rwstat(Job *job, Mfile *mf)
817 {
818 USED(mf);
819 sendmsg(job, "wstat permission denied");
820 }
821
822 void
sendmsg(Job * job,char * err)823 sendmsg(Job *job, char *err)
824 {
825 int n;
826 uchar mdata[IOHDRSZ + Maxfdata];
827 char ename[ERRMAX];
828
829 if(err){
830 job->reply.type = Rerror;
831 snprint(ename, sizeof(ename), "dns: %s", err);
832 job->reply.ename = ename;
833 }else{
834 job->reply.type = job->request.type+1;
835 }
836 job->reply.tag = job->request.tag;
837 n = convS2M(&job->reply, mdata, sizeof mdata);
838 if(n == 0){
839 syslog(1, logfile, "sendmsg convS2M of %F returns 0", &job->reply);
840 abort();
841 }
842 lock(&joblock);
843 if(job->flushed == 0)
844 if(write(mfd[1], mdata, n)!=n)
845 sysfatal("mount write");
846 unlock(&joblock);
847 if(debug)
848 syslog(0, logfile, "%F %d", &job->reply, n);
849 }
850
851 /*
852 * the following varies between dnsdebug and dns
853 */
854 void
logreply(int id,uchar * addr,DNSmsg * mp)855 logreply(int id, uchar *addr, DNSmsg *mp)
856 {
857 RR *rp;
858
859 syslog(0, LOG, "%d: rcvd %I flags:%s%s%s%s%s", id, addr,
860 mp->flags & Fauth ? " auth" : "",
861 mp->flags & Ftrunc ? " trunc" : "",
862 mp->flags & Frecurse ? " rd" : "",
863 mp->flags & Fcanrec ? " ra" : "",
864 mp->flags & (Fauth|Rname) == (Fauth|Rname) ?
865 " nx" : "");
866 for(rp = mp->qd; rp != nil; rp = rp->next)
867 syslog(0, LOG, "%d: rcvd %I qd %s", id, addr, rp->owner->name);
868 for(rp = mp->an; rp != nil; rp = rp->next)
869 syslog(0, LOG, "%d: rcvd %I an %R", id, addr, rp);
870 for(rp = mp->ns; rp != nil; rp = rp->next)
871 syslog(0, LOG, "%d: rcvd %I ns %R", id, addr, rp);
872 for(rp = mp->ar; rp != nil; rp = rp->next)
873 syslog(0, LOG, "%d: rcvd %I ar %R", id, addr, rp);
874 }
875
876 void
logsend(int id,int subid,uchar * addr,char * sname,char * rname,int type)877 logsend(int id, int subid, uchar *addr, char *sname, char *rname, int type)
878 {
879 char buf[12];
880
881 syslog(0, LOG, "%d.%d: sending to %I/%s %s %s",
882 id, subid, addr, sname, rname, rrname(type, buf, sizeof buf));
883 }
884
885 RR*
getdnsservers(int class)886 getdnsservers(int class)
887 {
888 return dnsservers(class);
889 }
890