]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ndb/dns.c
ndb/cs: fix use after free caused by flush/clunk happening before dns lookup finishes
[plan9front.git] / sys / src / cmd / ndb / dns.c
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         Maxreply=               8192,           /* was 512 */
15         Maxrrr=                 32,             /* was 16 */
16         Maxfdata=               8192,
17
18         Defmaxage=              60*60,  /* default domain name max. age */
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 static volatile int stop;
31
32 /* holds data to be returned via read of /net/dns, perhaps multiple reads */
33 struct Mfile
34 {
35         Mfile           *next;          /* next free mfile */
36
37         char            *user;
38         Qid             qid;
39         int             fid;
40
41         int             type;           /* reply type */
42         char            reply[Maxreply];
43         ushort          rr[Maxrrr];     /* offset of rr's */
44         ushort          nrr;            /* number of rr's */
45 };
46
47 /*
48  *  active local requests
49  */
50 struct Job
51 {
52         Job     *next;
53         int     flushed;
54         Fcall   request;
55         Fcall   reply;
56 };
57 Lock    joblock;
58 Job     *joblist;
59
60 struct {
61         Lock;
62         Mfile   *inuse;         /* active mfile's */
63 } mfalloc;
64
65 Cfg     cfg;
66 int     debug;
67 int     maxage = Defmaxage;
68 int     mfd[2];
69 int     needrefresh;
70 ulong   now;
71 vlong   nowns;
72 int     sendnotifies;
73 int     testing;
74 char    *trace;
75 int     traceactivity;
76 char    *zonerefreshprogram;
77
78 char    *logfile = "dns";       /* or "dns.test" */
79 char    *dbfile;
80 char    mntpt[Maxpath];
81
82 int     addforwtarg(char *);
83 int     fillreply(Mfile*, int);
84 void    freejob(Job*);
85 void    io(void);
86 void    mountinit(char*, char*);
87 Job*    newjob(void);
88 void    rattach(Job*, Mfile*);
89 void    rauth(Job*);
90 void    rclunk(Job*, Mfile*);
91 void    rcreate(Job*, Mfile*);
92 void    rflush(Job*);
93 void    ropen(Job*, Mfile*);
94 void    rread(Job*, Mfile*);
95 void    rremove(Job*, Mfile*);
96 void    rstat(Job*, Mfile*);
97 void    rversion(Job*);
98 char*   rwalk(Job*, Mfile*);
99 void    rwrite(Job*, Mfile*, Request*);
100 void    rwstat(Job*, Mfile*);
101 void    sendmsg(Job*, char*);
102 void    setext(char*, int, char*);
103
104 static char *lookupqueryold(Job*, Mfile*, Request*, char*, char*, int, int);
105 static char *lookupquerynew(Job*, Mfile*, Request*, char*, char*, int, int);
106 static char *respond(Job*, Mfile*, RR*, char*, int, int);
107
108 void
109 usage(void)
110 {
111         fprint(2, "usage: %s [-FnorRst] [-a maxage] [-f ndb-file] [-N target] "
112                 "[-T forwip] [-x netmtpt] [-z refreshprog]\n", argv0);
113         exits("usage");
114 }
115
116 void
117 main(int argc, char *argv[])
118 {
119         int kid, pid;
120         char servefile[Maxpath], ext[Maxpath];
121         Dir *dir;
122
123         setnetmtpt(mntpt, sizeof mntpt, nil);
124         ext[0] = 0;
125         ARGBEGIN{
126         case 'a':
127                 maxage = atol(EARGF(usage()));
128                 if (maxage <= 0)
129                         maxage = Defmaxage;
130                 break;
131         case 'd':
132                 debug = 1;
133                 traceactivity = 1;
134                 break;
135         case 'f':
136                 dbfile = EARGF(usage());
137                 break;
138         case 'F':
139                 cfg.justforw = cfg.resolver = 1;
140                 break;
141         case 'n':
142                 sendnotifies = 1;
143                 break;
144         case 'N':
145                 target = atol(EARGF(usage()));
146                 if (target < 1000)
147                         target = 1000;
148                 break;
149         case 'o':
150                 cfg.straddle = 1;       /* straddle inside & outside networks */
151                 break;
152         case 'r':
153                 cfg.resolver = 1;
154                 break;
155         case 'R':
156                 norecursion = 1;
157                 break;
158         case 's':
159                 cfg.serve = 1;          /* serve network */
160                 cfg.cachedb = 1;
161                 break;
162         case 't':
163                 testing = 1;
164                 break;
165         case 'T':
166                 addforwtarg(EARGF(usage()));
167                 break;
168         case 'x':
169                 setnetmtpt(mntpt, sizeof mntpt, EARGF(usage()));
170                 setext(ext, sizeof ext, mntpt);
171                 break;
172         case 'z':
173                 zonerefreshprogram = EARGF(usage());
174                 break;
175         default:
176                 usage();
177                 break;
178         }ARGEND
179         if(argc != 0)
180                 usage();
181
182         if(testing)
183                 mainmem->flags |= POOL_NOREUSE | POOL_ANTAGONISM;
184         mainmem->flags |= POOL_ANTAGONISM;
185         rfork(RFREND|RFNOTEG);
186
187         cfg.inside = (*mntpt == '\0' || strcmp(mntpt, "/net") == 0);
188
189         /* start syslog before we fork */
190         fmtinstall('F', fcallfmt);
191         dninit();
192         dnslog("starting %s%sdns %s%s%son %s",
193                 (cfg.straddle? "straddling ": ""),
194                 (cfg.cachedb? "caching ": ""),
195                 (cfg.serve?   "udp server ": ""),
196                 (cfg.justforw? "forwarding-only ": ""),
197                 (cfg.resolver? "resolver ": ""), mntpt);
198
199         opendatabase();
200         now = time(nil);                /* open time files before we fork */
201         nowns = nsec();
202
203         snprint(servefile, sizeof servefile, "#s/dns%s", ext);
204         dir = dirstat(servefile);
205         if (dir)
206                 sysfatal("%s exists; another dns instance is running",
207                         servefile);
208         free(dir);
209 //      unmount(servefile, mntpt);
210 //      remove(servefile);
211
212         mountinit(servefile, mntpt);    /* forks, parent exits */
213
214         srand(now*getpid());
215         db2cache(1);
216 //      dnageallnever();
217
218         if (cfg.straddle && !seerootns())
219                 dnslog("straddle server misconfigured; can't see root name servers");
220         /*
221          * fork without sharing heap.
222          * parent waits around for child to die, then forks & restarts.
223          * child may spawn udp server, notify procs, etc.; when it gets too
224          * big, it kills itself and any children.
225          * /srv/dns and /net/dns remain open and valid.
226          */
227         for (;;) {
228                 kid = rfork(RFPROC|RFFDG|RFNOTEG);
229                 switch (kid) {
230                 case -1:
231                         sysfatal("fork failed: %r");
232                 case 0:
233                         if(cfg.serve)
234                                 dnudpserver(mntpt);
235                         if(sendnotifies)
236                                 notifyproc();
237                         io();
238                         _exits("restart");
239                 default:
240                         while ((pid = waitpid()) != kid && pid != -1)
241                                 continue;
242                         break;
243                 }
244                 dnslog("dns restarting");
245         }
246 }
247
248 /*
249  *  if a mount point is specified, set the cs extension to be the mount point
250  *  with '_'s replacing '/'s
251  */
252 void
253 setext(char *ext, int n, char *p)
254 {
255         int i, c;
256
257         n--;
258         for(i = 0; i < n; i++){
259                 c = p[i];
260                 if(c == 0)
261                         break;
262                 if(c == '/')
263                         c = '_';
264                 ext[i] = c;
265         }
266         ext[i] = 0;
267 }
268
269 void
270 mountinit(char *service, char *mntpt)
271 {
272         int f;
273         int p[2];
274         char buf[32];
275
276         if(pipe(p) < 0)
277                 abort(); /* "pipe failed" */;
278         /* copy namespace to avoid a deadlock */
279         switch(rfork(RFFDG|RFPROC|RFNAMEG)){
280         case 0:                 /* child: hang around and (re)start main proc */
281                 close(p[1]);
282                 procsetname("%s restarter", mntpt);
283                 break;
284         case -1:
285                 abort(); /* "fork failed\n" */;
286         default:                /* parent: make /srv/dns, mount it, exit */
287                 close(p[0]);
288
289                 /*
290                  *  make a /srv/dns
291                  */
292                 f = create(service, 1, 0666);
293                 if(f < 0)
294                         abort(); /* service */;
295                 snprint(buf, sizeof buf, "%d", p[1]);
296                 if(write(f, buf, strlen(buf)) != strlen(buf))
297                         abort(); /* "write %s", service */;
298                 close(f);
299
300                 /*
301                  *  put ourselves into the file system
302                  */
303                 if(mount(p[1], -1, mntpt, MAFTER, "") < 0)
304                         fprint(2, "dns mount failed: %r\n");
305                 _exits(0);
306         }
307         mfd[0] = mfd[1] = p[0];
308 }
309
310 Mfile*
311 newfid(int fid, int needunused)
312 {
313         Mfile *mf;
314
315         lock(&mfalloc);
316         for(mf = mfalloc.inuse; mf != nil; mf = mf->next)
317                 if(mf->fid == fid){
318                         unlock(&mfalloc);
319                         if(needunused)
320                                 return nil;
321                         return mf;
322                 }
323         mf = emalloc(sizeof(*mf));
324         mf->fid = fid;
325         mf->user = estrdup("dummy");
326         mf->next = mfalloc.inuse;
327         mfalloc.inuse = mf;
328         unlock(&mfalloc);
329         return mf;
330 }
331
332 void
333 freefid(Mfile *mf)
334 {
335         Mfile **l;
336
337         lock(&mfalloc);
338         for(l = &mfalloc.inuse; *l != nil; l = &(*l)->next)
339                 if(*l == mf){
340                         *l = mf->next;
341                         free(mf->user);
342                         memset(mf, 0, sizeof *mf);      /* cause trouble */
343                         free(mf);
344                         unlock(&mfalloc);
345                         return;
346                 }
347         unlock(&mfalloc);
348         sysfatal("freeing unused fid");
349 }
350
351 Mfile*
352 copyfid(Mfile *mf, int fid)
353 {
354         Mfile *nmf;
355
356         nmf = newfid(fid, 1);
357         if(nmf == nil)
358                 return nil;
359         nmf->fid = fid;
360         free(nmf->user);                        /* estrdup("dummy") */
361         nmf->user = estrdup(mf->user);
362         nmf->qid.type = mf->qid.type;
363         nmf->qid.path = mf->qid.path;
364         nmf->qid.vers = vers++;
365         return nmf;
366 }
367
368 Job*
369 newjob(void)
370 {
371         Job *job;
372
373         job = emalloc(sizeof *job);
374         lock(&joblock);
375         job->next = joblist;
376         joblist = job;
377         job->request.tag = -1;
378         unlock(&joblock);
379         return job;
380 }
381
382 void
383 freejob(Job *job)
384 {
385         Job **l;
386
387         lock(&joblock);
388         for(l = &joblist; *l; l = &(*l)->next)
389                 if(*l == job){
390                         *l = job->next;
391                         memset(job, 0, sizeof *job);    /* cause trouble */
392                         free(job);
393                         break;
394                 }
395         unlock(&joblock);
396 }
397
398 void
399 flushjob(int tag)
400 {
401         Job *job;
402
403         lock(&joblock);
404         for(job = joblist; job; job = job->next)
405                 if(job->request.tag == tag && job->request.type != Tflush){
406                         job->flushed = 1;
407                         break;
408                 }
409         unlock(&joblock);
410 }
411
412 void
413 io(void)
414 {
415         volatile long n;
416         volatile uchar mdata[IOHDRSZ + Maxfdata];
417         Job *volatile job;
418         Mfile *volatile mf;
419         volatile Request req;
420
421         memset(&req, 0, sizeof req);
422         /*
423          *  a slave process is sometimes forked to wait for replies from other
424          *  servers.  The master process returns immediately via a longjmp
425          *  through 'mret'.
426          */
427         if(setjmp(req.mret))
428                 putactivity(0);
429         req.isslave = 0;
430         stop = 0;
431         while(!stop){
432                 procsetname("%d %s/dns Twrites of %d 9p rpcs read; %d alarms",
433                         stats.qrecvd9p, mntpt, stats.qrecvd9prpc, stats.alarms);
434                 n = read9pmsg(mfd[0], mdata, sizeof mdata);
435                 if(n<=0){
436                         dnslog("error reading 9P from %s: %r", mntpt);
437                         sleep(2000);    /* don't thrash after read error */
438                         return;
439                 }
440
441                 stats.qrecvd9prpc++;
442                 job = newjob();
443                 if(convM2S(mdata, n, &job->request) != n){
444                         freejob(job);
445                         continue;
446                 }
447                 mf = newfid(job->request.fid, 0);
448                 if(debug)
449                         dnslog("%F", &job->request);
450
451                 getactivity(&req, 0);
452                 req.aborttime = timems() + Maxreqtm;
453                 req.from = "9p";
454
455                 switch(job->request.type){
456                 default:
457                         warning("unknown request type %d", job->request.type);
458                         break;
459                 case Tversion:
460                         rversion(job);
461                         break;
462                 case Tauth:
463                         rauth(job);
464                         break;
465                 case Tflush:
466                         rflush(job);
467                         break;
468                 case Tattach:
469                         rattach(job, mf);
470                         break;
471                 case Twalk:
472                         rwalk(job, mf);
473                         break;
474                 case Topen:
475                         ropen(job, mf);
476                         break;
477                 case Tcreate:
478                         rcreate(job, mf);
479                         break;
480                 case Tread:
481                         rread(job, mf);
482                         break;
483                 case Twrite:
484                         /* &req is handed to dnresolve() */
485                         rwrite(job, mf, &req);
486                         break;
487                 case Tclunk:
488                         rclunk(job, mf);
489                         break;
490                 case Tremove:
491                         rremove(job, mf);
492                         break;
493                 case Tstat:
494                         rstat(job, mf);
495                         break;
496                 case Twstat:
497                         rwstat(job, mf);
498                         break;
499                 }
500
501                 freejob(job);
502
503                 /*
504                  *  slave processes die after replying
505                  */
506                 if(req.isslave){
507                         putactivity(0);
508                         _exits(0);
509                 }
510
511                 putactivity(0);
512         }
513         /* kill any udp server, notifier, etc. processes */
514         postnote(PNGROUP, getpid(), "die");
515         sleep(1000);
516 }
517
518 void
519 rversion(Job *job)
520 {
521         if(job->request.msize > IOHDRSZ + Maxfdata)
522                 job->reply.msize = IOHDRSZ + Maxfdata;
523         else
524                 job->reply.msize = job->request.msize;
525         if(strncmp(job->request.version, "9P2000", 6) != 0)
526                 sendmsg(job, "unknown 9P version");
527         else{
528                 job->reply.version = "9P2000";
529                 sendmsg(job, 0);
530         }
531 }
532
533 void
534 rauth(Job *job)
535 {
536         sendmsg(job, "dns: authentication not required");
537 }
538
539 /*
540  *  don't flush till all the slaves are done
541  */
542 void
543 rflush(Job *job)
544 {
545         flushjob(job->request.oldtag);
546         sendmsg(job, 0);
547 }
548
549 void
550 rattach(Job *job, Mfile *mf)
551 {
552         if(mf->user != nil)
553                 free(mf->user);
554         mf->user = estrdup(job->request.uname);
555         mf->qid.vers = vers++;
556         mf->qid.type = QTDIR;
557         mf->qid.path = 0LL;
558         job->reply.qid = mf->qid;
559         sendmsg(job, 0);
560 }
561
562 char*
563 rwalk(Job *job, Mfile *mf)
564 {
565         int i, nelems;
566         char *err;
567         char **elems;
568         Mfile *nmf;
569         Qid qid;
570
571         err = 0;
572         nmf = nil;
573         elems  = job->request.wname;
574         nelems = job->request.nwname;
575         job->reply.nwqid = 0;
576
577         if(job->request.newfid != job->request.fid){
578                 /* clone fid */
579                 nmf = copyfid(mf, job->request.newfid);
580                 if(nmf == nil){
581                         err = "clone bad newfid";
582                         goto send;
583                 }
584                 mf = nmf;
585         }
586         /* else nmf will be nil */
587
588         qid = mf->qid;
589         if(nelems > 0)
590                 /* walk fid */
591                 for(i=0; i<nelems && i<MAXWELEM; i++){
592                         if((qid.type & QTDIR) == 0){
593                                 err = "not a directory";
594                                 break;
595                         }
596                         if (strcmp(elems[i], "..") == 0 ||
597                             strcmp(elems[i], ".") == 0){
598                                 qid.type = QTDIR;
599                                 qid.path = Qdir;
600 Found:
601                                 job->reply.wqid[i] = qid;
602                                 job->reply.nwqid++;
603                                 continue;
604                         }
605                         if(strcmp(elems[i], "dns") == 0){
606                                 qid.type = QTFILE;
607                                 qid.path = Qdns;
608                                 goto Found;
609                         }
610                         err = "file does not exist";
611                         break;
612                 }
613
614 send:
615         if(nmf != nil && (err!=nil || job->reply.nwqid<nelems))
616                 freefid(nmf);
617         if(err == nil)
618                 mf->qid = qid;
619         sendmsg(job, err);
620         return err;
621 }
622
623 void
624 ropen(Job *job, Mfile *mf)
625 {
626         int mode;
627         char *err;
628
629         err = 0;
630         mode = job->request.mode;
631         if(mf->qid.type & QTDIR)
632                 if(mode)
633                         err = "permission denied";
634         job->reply.qid = mf->qid;
635         job->reply.iounit = 0;
636         sendmsg(job, err);
637 }
638
639 void
640 rcreate(Job *job, Mfile *mf)
641 {
642         USED(mf);
643         sendmsg(job, "creation permission denied");
644 }
645
646 void
647 rread(Job *job, Mfile *mf)
648 {
649         int i, n;
650         long clock;
651         ulong cnt;
652         vlong off;
653         char *err;
654         uchar buf[Maxfdata];
655         Dir dir;
656
657         n = 0;
658         err = nil;
659         off = job->request.offset;
660         cnt = job->request.count;
661         *buf = '\0';
662         job->reply.data = (char*)buf;
663         if(mf->qid.type & QTDIR){
664                 clock = time(nil);
665                 if(off == 0){
666                         memset(&dir, 0, sizeof dir);
667                         dir.name = "dns";
668                         dir.qid.type = QTFILE;
669                         dir.qid.vers = vers;
670                         dir.qid.path = Qdns;
671                         dir.mode = 0666;
672                         dir.length = 0;
673                         dir.uid = dir.gid = dir.muid = mf->user;
674                         dir.atime = dir.mtime = clock;          /* wrong */
675                         n = convD2M(&dir, buf, sizeof buf);
676                 }
677         } else if (off < 0)
678                 err = "negative read offset";
679         else {
680                 /* first offset will always be zero */
681                 for(i = 1; i <= mf->nrr; i++)
682                         if(mf->rr[i] > off)
683                                 break;
684                 if(i <= mf->nrr) {
685                         if(off + cnt > mf->rr[i])
686                                 n = mf->rr[i] - off;
687                         else
688                                 n = cnt;
689                         assert(n >= 0);
690                         job->reply.data = mf->reply + off;
691                 }
692         }
693         job->reply.count = n;
694         sendmsg(job, err);
695 }
696
697 void
698 rwrite(Job *job, Mfile *mf, Request *req)
699 {
700         int rooted, wantsav, send;
701         ulong cnt;
702         char *err, *p, *atype;
703         char errbuf[ERRMAX];
704
705         err = nil;
706         cnt = job->request.count;
707         send = 1;
708         if(mf->qid.type & QTDIR)
709                 err = "can't write directory";
710         else if (job->request.offset != 0)
711                 err = "writing at non-zero offset";
712         else if(cnt >= Maxrequest)
713                 err = "request too long";
714         else
715                 send = 0;
716         if (send)
717                 goto send;
718
719         job->request.data[cnt] = 0;
720         if(cnt > 0 && job->request.data[cnt-1] == '\n')
721                 job->request.data[cnt-1] = 0;
722
723         /*
724          *  special commands
725          */
726 //      dnslog("rwrite got: %s", job->request.data);
727         send = 1;
728         if(strcmp(job->request.data, "age")==0){
729                 dnslog("dump, age & dump forced");
730                 dndump("/lib/ndb/dnsdump1");
731                 dnforceage();
732                 dndump("/lib/ndb/dnsdump2");
733         } else if(strcmp(job->request.data, "debug")==0)
734                 debug ^= 1;
735         else if(strcmp(job->request.data, "dump")==0)
736                 dndump("/lib/ndb/dnsdump");
737         else if(strcmp(job->request.data, "poolcheck")==0)
738                 poolcheck(mainmem);
739         else if(strcmp(job->request.data, "refresh")==0)
740                 needrefresh = 1;
741         else if(strcmp(job->request.data, "restart")==0)
742                 stop = 1;
743         else if(strcmp(job->request.data, "stats")==0)
744                 dnstats("/lib/ndb/dnsstats");
745         else if(strncmp(job->request.data, "target ", 7)==0){
746                 target = atol(job->request.data + 7);
747                 dnslog("target set to %ld", target);
748         } else
749                 send = 0;
750         if (send)
751                 goto send;
752
753         /*
754          *  kill previous reply
755          */
756         mf->nrr = 0;
757         mf->rr[0] = 0;
758
759         /*
760          *  break up request (into a name and a type)
761          */
762         atype = strchr(job->request.data, ' ');
763         if(atype == 0){
764                 snprint(errbuf, sizeof errbuf, "illegal request %s",
765                         job->request.data);
766                 err = errbuf;
767                 goto send;
768         } else
769                 *atype++ = 0;
770
771         /*
772          *  tracing request
773          */
774         if(strcmp(atype, "trace") == 0){
775                 if(trace)
776                         free(trace);
777                 if(*job->request.data)
778                         trace = estrdup(job->request.data);
779                 else
780                         trace = 0;
781                 goto send;
782         }
783
784         /* normal request: domain [type] */
785         stats.qrecvd9p++;
786         mf->type = rrtype(atype);
787         if(mf->type < 0){
788                 snprint(errbuf, sizeof errbuf, "unknown type %s", atype);
789                 err = errbuf;
790                 goto send;
791         }
792
793         p = atype - 2;
794         if(p >= job->request.data && *p == '.'){
795                 rooted = 1;
796                 *p = 0;
797         } else
798                 rooted = 0;
799
800         p = job->request.data;
801         if(*p == '!'){
802                 wantsav = 1;
803                 p++;
804         } else
805                 wantsav = 0;
806
807         err = lookupqueryold(job, mf, req, errbuf, p, wantsav, rooted);
808 send:
809         dncheck(0, 1);
810         job->reply.count = cnt;
811         sendmsg(job, err);
812 }
813
814 /*
815  * dnsdebug calls
816  *      rr = dnresolve(buf, Cin, type, &req, 0, 0, Recurse, rooted, 0);
817  * which generates a UDP query, which eventually calls
818  *      dnserver(&reqmsg, &repmsg, &req, buf, rcode);
819  * which calls
820  *      rp = dnresolve(name, Cin, type, req, &mp->an, 0, recurse, 1, 0);
821  *
822  * but here we just call dnresolve directly.
823  */
824 static char *
825 lookupqueryold(Job *job, Mfile *mf, Request *req, char *errbuf, char *p,
826         int wantsav, int rooted)
827 {
828         int status;
829         RR *rp, *neg;
830
831         dncheck(0, 1);
832         status = Rok;
833         rp = dnresolve(p, Cin, mf->type, req, 0, 0, Recurse, rooted, &status);
834
835         dncheck(0, 1);
836         lock(&dnlock);
837         neg = rrremneg(&rp);
838         if(neg){
839                 status = neg->negrcode;
840                 rrfreelist(neg);
841         }
842         unlock(&dnlock);
843
844         return respond(job, mf, rp, errbuf, status, wantsav);
845 }
846
847 static char *
848 respond(Job *job, Mfile *mf, RR *rp, char *errbuf, int status, int wantsav)
849 {
850         long n;
851         RR *tp;
852
853         if(rp == nil)
854                 switch(status){
855                 case Rname:
856                         return "name does not exist";
857                 case Rserver:
858                         return "dns failure";
859                 case Rok:
860                 default:
861                         snprint(errbuf, ERRMAX,
862                                 "resource does not exist; negrcode %d", status);
863                         return errbuf;
864                 }
865
866         lock(&joblock);
867         if(!job->flushed){
868                 /* format data to be read later */
869                 n = 0;
870                 mf->nrr = 0;
871                 for(tp = rp; mf->nrr < Maxrrr-1 && n < Maxreply && tp &&
872                     tsame(mf->type, tp->type); tp = tp->next){
873                         mf->rr[mf->nrr++] = n;
874                         if(wantsav)
875                                 n += snprint(mf->reply+n, Maxreply-n, "%Q", tp);
876                         else
877                                 n += snprint(mf->reply+n, Maxreply-n, "%R", tp);
878                 }
879                 mf->rr[mf->nrr] = n;
880         }
881         unlock(&joblock);
882         rrfreelist(rp);
883         return nil;
884 }
885
886 /* simulate what dnsudpserver does */
887 static char *
888 lookupquerynew(Job *job, Mfile *mf, Request *req, char *errbuf, char *p,
889         int wantsav, int)
890 {
891         char *err;
892         uchar buf[Udphdrsize + Maxudp + 1024];
893         DNSmsg *mp;
894         DNSmsg repmsg;
895         RR *rp;
896
897         dncheck(0, 1);
898
899         memset(&repmsg, 0, sizeof repmsg);
900         rp = rralloc(mf->type);
901         rp->owner = dnlookup(p, Cin, 1);
902         mp = newdnsmsg(rp, Frecurse|Oquery, (ushort)rand());
903
904         dnserver(mp, &repmsg, req, buf, Rok);
905
906         freeanswers(mp);
907         err = respond(job, mf, repmsg.an, errbuf, Rok, wantsav);
908         repmsg.an = nil;                /* freed above */
909         freeanswers(&repmsg);
910         return err;
911 }
912
913 void
914 rclunk(Job *job, Mfile *mf)
915 {
916         freefid(mf);
917         sendmsg(job, 0);
918 }
919
920 void
921 rremove(Job *job, Mfile *mf)
922 {
923         USED(mf);
924         sendmsg(job, "remove permission denied");
925 }
926
927 void
928 rstat(Job *job, Mfile *mf)
929 {
930         Dir dir;
931         uchar buf[IOHDRSZ+Maxfdata];
932
933         memset(&dir, 0, sizeof dir);
934         if(mf->qid.type & QTDIR){
935                 dir.name = ".";
936                 dir.mode = DMDIR|0555;
937         } else {
938                 dir.name = "dns";
939                 dir.mode = 0666;
940         }
941         dir.qid = mf->qid;
942         dir.length = 0;
943         dir.uid = dir.gid = dir.muid = mf->user;
944         dir.atime = dir.mtime = time(nil);
945         job->reply.nstat = convD2M(&dir, buf, sizeof buf);
946         job->reply.stat = buf;
947         sendmsg(job, 0);
948 }
949
950 void
951 rwstat(Job *job, Mfile *mf)
952 {
953         USED(mf);
954         sendmsg(job, "wstat permission denied");
955 }
956
957 void
958 sendmsg(Job *job, char *err)
959 {
960         int n;
961         uchar mdata[IOHDRSZ + Maxfdata];
962         char ename[ERRMAX];
963
964         if(err){
965                 job->reply.type = Rerror;
966                 snprint(ename, sizeof ename, "dns: %s", err);
967                 job->reply.ename = ename;
968         }else
969                 job->reply.type = job->request.type+1;
970         job->reply.tag = job->request.tag;
971         n = convS2M(&job->reply, mdata, sizeof mdata);
972         if(n == 0){
973                 warning("sendmsg convS2M of %F returns 0", &job->reply);
974                 abort();
975         }
976         lock(&joblock);
977         if(job->flushed == 0)
978                 if(write(mfd[1], mdata, n)!=n)
979                         sysfatal("mount write");
980         unlock(&joblock);
981         if(debug)
982                 dnslog("%F %d", &job->reply, n);
983 }
984
985 /*
986  *  the following varies between dnsdebug and dns
987  */
988 void
989 logreply(int id, uchar *addr, DNSmsg *mp)
990 {
991         RR *rp;
992
993         dnslog("%d: rcvd %I flags:%s%s%s%s%s", id, addr,
994                 mp->flags & Fauth? " auth": "",
995                 mp->flags & Ftrunc? " trunc": "",
996                 mp->flags & Frecurse? " rd": "",
997                 mp->flags & Fcanrec? " ra": "",
998                 (mp->flags & (Fauth|Rmask)) == (Fauth|Rname)? " nx": "");
999         for(rp = mp->qd; rp != nil; rp = rp->next)
1000                 dnslog("%d: rcvd %I qd %s", id, addr, rp->owner->name);
1001         for(rp = mp->an; rp != nil; rp = rp->next)
1002                 dnslog("%d: rcvd %I an %R", id, addr, rp);
1003         for(rp = mp->ns; rp != nil; rp = rp->next)
1004                 dnslog("%d: rcvd %I ns %R", id, addr, rp);
1005         for(rp = mp->ar; rp != nil; rp = rp->next)
1006                 dnslog("%d: rcvd %I ar %R", id, addr, rp);
1007 }
1008
1009 void
1010 logsend(int id, int subid, uchar *addr, char *sname, char *rname, int type)
1011 {
1012         char buf[12];
1013
1014         dnslog("[%d] %d.%d: sending to %I/%s %s %s",
1015                 getpid(), id, subid, addr, sname, rname,
1016                 rrname(type, buf, sizeof buf));
1017 }
1018
1019 RR*
1020 getdnsservers(int class)
1021 {
1022         return dnsservers(class);
1023 }