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