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