2016-11-25 17:18:40 +01:00
|
|
|
#include <u.h>
|
|
|
|
#include <libc.h>
|
|
|
|
#include <auth.h>
|
2016-12-01 00:09:42 +01:00
|
|
|
#include <9P2000.h>
|
2016-11-25 17:18:40 +01:00
|
|
|
#include <bio.h>
|
|
|
|
#include <ip.h>
|
|
|
|
#include "dns.h"
|
|
|
|
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
Maxrequest= 1024,
|
|
|
|
Maxreply= 8192, /* was 512 */
|
|
|
|
Maxrrr= 32, /* was 16 */
|
|
|
|
Maxfdata= 8192,
|
|
|
|
|
|
|
|
Defmaxage= 60*60, /* default domain name max. age */
|
|
|
|
|
|
|
|
Qdir= 0,
|
|
|
|
Qdns= 1,
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct Mfile Mfile;
|
|
|
|
typedef struct Job Job;
|
|
|
|
typedef struct Network Network;
|
|
|
|
|
|
|
|
int vers; /* incremented each clone/attach */
|
|
|
|
|
|
|
|
static int stop;
|
|
|
|
|
|
|
|
/* holds data to be returned via read of /net/dns, perhaps multiple reads */
|
|
|
|
struct Mfile
|
|
|
|
{
|
|
|
|
Mfile *next; /* next free mfile */
|
|
|
|
|
|
|
|
char *user;
|
|
|
|
Qid qid;
|
|
|
|
int fid;
|
|
|
|
|
|
|
|
int type; /* reply type */
|
|
|
|
char reply[Maxreply];
|
|
|
|
uint16_t rr[Maxrrr]; /* offset of rr's */
|
|
|
|
uint16_t nrr; /* number of rr's */
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* active local requests
|
|
|
|
*/
|
|
|
|
struct Job
|
|
|
|
{
|
|
|
|
Job *next;
|
|
|
|
int flushed;
|
|
|
|
Fcall request;
|
|
|
|
Fcall reply;
|
|
|
|
};
|
|
|
|
Lock joblock;
|
|
|
|
Job *joblist;
|
|
|
|
|
|
|
|
struct {
|
|
|
|
Lock;
|
|
|
|
Mfile *inuse; /* active mfile's */
|
|
|
|
} mfalloc;
|
|
|
|
|
|
|
|
Cfg cfg;
|
|
|
|
int debug;
|
|
|
|
int maxage = Defmaxage;
|
|
|
|
int mfd[2];
|
|
|
|
int needrefresh;
|
|
|
|
uint32_t now;
|
|
|
|
int64_t nowns;
|
|
|
|
int sendnotifies;
|
|
|
|
char *trace;
|
|
|
|
int traceactivity;
|
|
|
|
char *zonerefreshprogram;
|
|
|
|
|
|
|
|
char *logfile = "dns"; /* or "dns.test" */
|
|
|
|
char *dbfile;
|
|
|
|
char *dnsuser;
|
|
|
|
char mntpt[Maxpath];
|
|
|
|
|
|
|
|
int addforwtarg(char *);
|
|
|
|
int fillreply(Mfile*, int);
|
|
|
|
void freejob(Job*);
|
|
|
|
void io(void);
|
|
|
|
void mountinit(char*, char*);
|
|
|
|
Job* newjob(void);
|
|
|
|
void rattach(Job*, Mfile*);
|
|
|
|
void rauth(Job*);
|
|
|
|
void rclunk(Job*, Mfile*);
|
|
|
|
void rcreate(Job*, Mfile*);
|
|
|
|
void rflush(Job*);
|
|
|
|
void ropen(Job*, Mfile*);
|
|
|
|
void rread(Job*, Mfile*);
|
|
|
|
void rremove(Job*, Mfile*);
|
|
|
|
void rstat(Job*, Mfile*);
|
|
|
|
void rversion(Job*);
|
|
|
|
char* rwalk(Job*, Mfile*);
|
|
|
|
void rwrite(Job*, Mfile*, Request*);
|
|
|
|
void rwstat(Job*, Mfile*);
|
|
|
|
void sendmsg(Job*, char*);
|
|
|
|
void setext(char*, int, char*);
|
|
|
|
|
|
|
|
static char *lookupquery(Job*, Mfile*, Request*, char*, char*, int, int);
|
|
|
|
static char *respond(Job*, Mfile*, RR*, char*, int, int);
|
|
|
|
|
|
|
|
void
|
|
|
|
usage(void)
|
|
|
|
{
|
|
|
|
fprint(2, "usage: %s [-FnorRs] [-a maxage] [-f ndb-file] [-N target] "
|
|
|
|
"[-T forwip] [-x netmtpt] [-z refreshprog]\n", argv0);
|
|
|
|
exits("usage");
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
main(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
int kid, pid;
|
|
|
|
char servefile[Maxpath], ext[Maxpath];
|
|
|
|
Dir *dir;
|
|
|
|
|
|
|
|
setnetmtpt(mntpt, sizeof mntpt, nil);
|
|
|
|
ext[0] = 0;
|
|
|
|
ARGBEGIN{
|
|
|
|
case 'a':
|
|
|
|
maxage = atol(EARGF(usage()));
|
|
|
|
if (maxage <= 0)
|
|
|
|
maxage = Defmaxage;
|
|
|
|
break;
|
|
|
|
case 'd':
|
|
|
|
debug = 1;
|
|
|
|
traceactivity = 1;
|
|
|
|
break;
|
|
|
|
case 'f':
|
|
|
|
dbfile = EARGF(usage());
|
|
|
|
break;
|
|
|
|
case 'F':
|
|
|
|
cfg.justforw = cfg.resolver = 1;
|
|
|
|
break;
|
|
|
|
case 'n':
|
|
|
|
sendnotifies = 1;
|
|
|
|
break;
|
|
|
|
case 'N':
|
|
|
|
target = atol(EARGF(usage()));
|
|
|
|
if (target < 1000)
|
|
|
|
target = 1000;
|
|
|
|
break;
|
|
|
|
case 'o':
|
|
|
|
cfg.straddle = 1; /* straddle inside & outside networks */
|
|
|
|
break;
|
|
|
|
case 'r':
|
|
|
|
cfg.resolver = 1;
|
|
|
|
break;
|
|
|
|
case 'R':
|
|
|
|
norecursion = 1;
|
|
|
|
break;
|
|
|
|
case 's':
|
|
|
|
cfg.serve = 1; /* serve network */
|
|
|
|
cfg.cachedb = 1;
|
|
|
|
break;
|
|
|
|
case 'T':
|
|
|
|
addforwtarg(EARGF(usage()));
|
|
|
|
break;
|
|
|
|
case 'x':
|
|
|
|
setnetmtpt(mntpt, sizeof mntpt, EARGF(usage()));
|
|
|
|
setext(ext, sizeof ext, mntpt);
|
|
|
|
break;
|
|
|
|
case 'z':
|
|
|
|
zonerefreshprogram = EARGF(usage());
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
usage();
|
|
|
|
break;
|
|
|
|
}ARGEND
|
|
|
|
if(argc != 0)
|
|
|
|
usage();
|
|
|
|
|
|
|
|
rfork(RFREND|RFNOTEG);
|
|
|
|
|
|
|
|
cfg.inside = (*mntpt == '\0' || strcmp(mntpt, "/net") == 0);
|
|
|
|
|
|
|
|
/* start syslog before we fork */
|
|
|
|
fmtinstall('F', fcallfmt);
|
|
|
|
dninit();
|
|
|
|
dnslog("starting %s%sdns %s%s%son %s",
|
|
|
|
(cfg.straddle? "straddling ": ""),
|
|
|
|
(cfg.cachedb? "caching ": ""),
|
|
|
|
(cfg.serve? "udp server ": ""),
|
|
|
|
(cfg.justforw? "forwarding-only ": ""),
|
|
|
|
(cfg.resolver? "resolver ": ""), mntpt);
|
|
|
|
|
|
|
|
opendatabase();
|
|
|
|
now = time(nil); /* open time files before we fork */
|
|
|
|
nowns = nsec();
|
|
|
|
dnsuser = estrdup(getuser());
|
|
|
|
|
|
|
|
snprint(servefile, sizeof servefile, "#s/dns%s", ext);
|
|
|
|
dir = dirstat(servefile);
|
|
|
|
if (dir)
|
|
|
|
sysfatal("%s exists; another dns instance is running",
|
|
|
|
servefile);
|
|
|
|
free(dir);
|
|
|
|
mountinit(servefile, mntpt); /* forks, parent exits */
|
|
|
|
|
|
|
|
srand(now*getpid());
|
|
|
|
db2cache(1);
|
|
|
|
|
|
|
|
if (cfg.straddle && !seerootns())
|
|
|
|
dnslog("straddle server misconfigured; can't see root name servers");
|
|
|
|
/*
|
|
|
|
* fork without sharing heap.
|
|
|
|
* parent waits around for child to die, then forks & restarts.
|
|
|
|
* child may spawn udp server, notify procs, etc.; when it gets too
|
|
|
|
* big, it kills itself and any children.
|
|
|
|
* /srv/dns and /net/dns remain open and valid.
|
|
|
|
*/
|
|
|
|
for (;;) {
|
|
|
|
kid = rfork(RFPROC|RFFDG|RFNOTEG);
|
|
|
|
switch (kid) {
|
|
|
|
case -1:
|
|
|
|
sysfatal("fork failed: %r");
|
|
|
|
case 0:
|
|
|
|
if(cfg.serve)
|
|
|
|
dnudpserver(mntpt);
|
|
|
|
if(sendnotifies)
|
|
|
|
notifyproc();
|
|
|
|
io();
|
|
|
|
_exits("restart");
|
|
|
|
default:
|
|
|
|
while ((pid = waitpid()) != kid && pid != -1)
|
|
|
|
continue;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
dnslog("dns restarting");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* if a mount point is specified, set the cs extension to be the mount point
|
|
|
|
* with '_'s replacing '/'s
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
setext(char *ext, int n, char *p)
|
|
|
|
{
|
|
|
|
int i, c;
|
|
|
|
|
|
|
|
n--;
|
|
|
|
for(i = 0; i < n; i++){
|
|
|
|
c = p[i];
|
|
|
|
if(c == 0)
|
|
|
|
break;
|
|
|
|
if(c == '/')
|
|
|
|
c = '_';
|
|
|
|
ext[i] = c;
|
|
|
|
}
|
|
|
|
ext[i] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
mountinit(char *service, char *mntpt)
|
|
|
|
{
|
|
|
|
int f;
|
|
|
|
int p[2];
|
|
|
|
char buf[32];
|
|
|
|
|
|
|
|
if(pipe(p) < 0)
|
|
|
|
sysfatal("pipe failed: %r");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* make a /srv/dns
|
|
|
|
*/
|
|
|
|
if((f = create(service, OWRITE|ORCLOSE, 0666)) < 0)
|
|
|
|
sysfatal("create %s failed: %r", service);
|
|
|
|
snprint(buf, sizeof buf, "%d", p[1]);
|
|
|
|
if(write(f, buf, strlen(buf)) != strlen(buf))
|
|
|
|
sysfatal("write %s failed: %r", service);
|
|
|
|
|
|
|
|
/* copy namespace to avoid a deadlock */
|
|
|
|
switch(rfork(RFFDG|RFPROC|RFNAMEG)){
|
|
|
|
case 0: /* child: hang around and (re)start main proc */
|
|
|
|
close(p[1]);
|
|
|
|
procsetname("%s restarter", mntpt);
|
|
|
|
break;
|
|
|
|
case -1:
|
|
|
|
sysfatal("fork failed: %r");
|
|
|
|
default: /* parent: make /srv/dns, mount it, exit */
|
|
|
|
close(p[0]);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* put ourselves into the file system
|
|
|
|
*/
|
2016-12-01 00:09:42 +01:00
|
|
|
if(mount(p[1], -1, mntpt, MAFTER, "", '9') < 0)
|
2016-11-25 17:18:40 +01:00
|
|
|
fprint(2, "dns mount failed: %r\n");
|
|
|
|
_exits(0);
|
|
|
|
}
|
|
|
|
mfd[0] = mfd[1] = p[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
Mfile*
|
|
|
|
newfid(int fid, int needunused)
|
|
|
|
{
|
|
|
|
Mfile *mf;
|
|
|
|
|
|
|
|
lock(&mfalloc);
|
|
|
|
for(mf = mfalloc.inuse; mf != nil; mf = mf->next)
|
|
|
|
if(mf->fid == fid){
|
|
|
|
unlock(&mfalloc);
|
|
|
|
if(needunused)
|
|
|
|
return nil;
|
|
|
|
return mf;
|
|
|
|
}
|
|
|
|
mf = emalloc(sizeof(*mf));
|
|
|
|
mf->fid = fid;
|
|
|
|
mf->qid.vers = vers;
|
|
|
|
mf->qid.type = QTDIR;
|
|
|
|
mf->qid.path = 0LL;
|
|
|
|
mf->user = estrdup("none");
|
|
|
|
mf->next = mfalloc.inuse;
|
|
|
|
mfalloc.inuse = mf;
|
|
|
|
unlock(&mfalloc);
|
|
|
|
return mf;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
freefid(Mfile *mf)
|
|
|
|
{
|
|
|
|
Mfile **l;
|
|
|
|
|
|
|
|
lock(&mfalloc);
|
|
|
|
for(l = &mfalloc.inuse; *l != nil; l = &(*l)->next)
|
|
|
|
if(*l == mf){
|
|
|
|
*l = mf->next;
|
|
|
|
free(mf->user);
|
|
|
|
memset(mf, 0, sizeof *mf); /* cause trouble */
|
|
|
|
free(mf);
|
|
|
|
unlock(&mfalloc);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
unlock(&mfalloc);
|
|
|
|
sysfatal("freeing unused fid");
|
|
|
|
}
|
|
|
|
|
|
|
|
Mfile*
|
|
|
|
copyfid(Mfile *mf, int fid)
|
|
|
|
{
|
|
|
|
Mfile *nmf;
|
|
|
|
|
|
|
|
nmf = newfid(fid, 1);
|
|
|
|
if(nmf == nil)
|
|
|
|
return nil;
|
|
|
|
nmf->fid = fid;
|
|
|
|
free(nmf->user); /* estrdup("none") */
|
|
|
|
nmf->user = estrdup(mf->user);
|
|
|
|
nmf->qid.type = mf->qid.type;
|
|
|
|
nmf->qid.path = mf->qid.path;
|
|
|
|
nmf->qid.vers = vers++;
|
|
|
|
return nmf;
|
|
|
|
}
|
|
|
|
|
|
|
|
Job*
|
|
|
|
newjob(void)
|
|
|
|
{
|
|
|
|
Job *job;
|
|
|
|
|
|
|
|
job = emalloc(sizeof *job);
|
|
|
|
lock(&joblock);
|
|
|
|
job->next = joblist;
|
|
|
|
joblist = job;
|
|
|
|
job->request.tag = -1;
|
|
|
|
unlock(&joblock);
|
|
|
|
return job;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
freejob(Job *job)
|
|
|
|
{
|
|
|
|
Job **l;
|
|
|
|
|
|
|
|
lock(&joblock);
|
|
|
|
for(l = &joblist; *l; l = &(*l)->next)
|
|
|
|
if(*l == job){
|
|
|
|
*l = job->next;
|
|
|
|
memset(job, 0, sizeof *job); /* cause trouble */
|
|
|
|
free(job);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
unlock(&joblock);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
flushjob(int tag)
|
|
|
|
{
|
|
|
|
Job *job;
|
|
|
|
|
|
|
|
lock(&joblock);
|
|
|
|
for(job = joblist; job; job = job->next)
|
|
|
|
if(job->request.tag == tag && job->request.type != Tflush){
|
|
|
|
job->flushed = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
unlock(&joblock);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
io(void)
|
|
|
|
{
|
|
|
|
int32_t n;
|
|
|
|
uint8_t mdata[IOHDRSZ + Maxfdata];
|
|
|
|
Job *job;
|
|
|
|
Mfile *mf;
|
|
|
|
Request req;
|
|
|
|
|
|
|
|
memset(&req, 0, sizeof req);
|
|
|
|
/*
|
|
|
|
* a slave process is sometimes forked to wait for replies from other
|
|
|
|
* servers. The master process returns immediately via a longjmp
|
|
|
|
* through 'mret'.
|
|
|
|
*/
|
|
|
|
if(setjmp(req.mret))
|
|
|
|
putactivity(0);
|
|
|
|
req.isslave = 0;
|
|
|
|
stop = 0;
|
|
|
|
while(!stop){
|
|
|
|
procsetname("%d %s/dns Twrites of %d 9p rpcs read; %d alarms",
|
|
|
|
stats.qrecvd9p, mntpt, stats.qrecvd9prpc, stats.alarms);
|
|
|
|
while((n = read9pmsg(mfd[0], mdata, sizeof mdata)) == 0)
|
|
|
|
;
|
|
|
|
if(n < 0){
|
|
|
|
dnslog("error reading 9P from %s: %r", mntpt);
|
|
|
|
sleep(2000); /* don't thrash after read error */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
stats.qrecvd9prpc++;
|
|
|
|
job = newjob();
|
|
|
|
if(convM2S(mdata, n, &job->request) != n){
|
|
|
|
freejob(job);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
mf = newfid(job->request.fid, 0);
|
|
|
|
if(debug)
|
|
|
|
dnslog("%F", &job->request);
|
|
|
|
|
|
|
|
getactivity(&req, 0);
|
|
|
|
req.aborttime = timems() + Maxreqtm;
|
|
|
|
req.from = "9p";
|
|
|
|
|
|
|
|
switch(job->request.type){
|
|
|
|
default:
|
|
|
|
warning("unknown request type %d", job->request.type);
|
|
|
|
break;
|
|
|
|
case Tversion:
|
|
|
|
rversion(job);
|
|
|
|
break;
|
|
|
|
case Tauth:
|
|
|
|
rauth(job);
|
|
|
|
break;
|
|
|
|
case Tflush:
|
|
|
|
rflush(job);
|
|
|
|
break;
|
|
|
|
case Tattach:
|
|
|
|
rattach(job, mf);
|
|
|
|
break;
|
|
|
|
case Twalk:
|
|
|
|
rwalk(job, mf);
|
|
|
|
break;
|
|
|
|
case Topen:
|
|
|
|
ropen(job, mf);
|
|
|
|
break;
|
|
|
|
case Tcreate:
|
|
|
|
rcreate(job, mf);
|
|
|
|
break;
|
|
|
|
case Tread:
|
|
|
|
rread(job, mf);
|
|
|
|
break;
|
|
|
|
case Twrite:
|
|
|
|
/* &req is handed to dnresolve() */
|
|
|
|
rwrite(job, mf, &req);
|
|
|
|
break;
|
|
|
|
case Tclunk:
|
|
|
|
rclunk(job, mf);
|
|
|
|
break;
|
|
|
|
case Tremove:
|
|
|
|
rremove(job, mf);
|
|
|
|
break;
|
|
|
|
case Tstat:
|
|
|
|
rstat(job, mf);
|
|
|
|
break;
|
|
|
|
case Twstat:
|
|
|
|
rwstat(job, mf);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
freejob(job);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* slave processes die after replying
|
|
|
|
*/
|
|
|
|
if(req.isslave){
|
|
|
|
putactivity(0);
|
|
|
|
_exits(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
putactivity(0);
|
|
|
|
}
|
|
|
|
/* kill any udp server, notifier, etc. processes */
|
|
|
|
postnote(PNGROUP, getpid(), "die");
|
|
|
|
sleep(1000);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
rversion(Job *job)
|
|
|
|
{
|
|
|
|
if(job->request.msize > IOHDRSZ + Maxfdata)
|
|
|
|
job->reply.msize = IOHDRSZ + Maxfdata;
|
|
|
|
else
|
|
|
|
job->reply.msize = job->request.msize;
|
|
|
|
if(strncmp(job->request.version, "9P2000", 6) != 0)
|
|
|
|
sendmsg(job, "unknown 9P version");
|
|
|
|
else{
|
|
|
|
job->reply.version = "9P2000";
|
|
|
|
sendmsg(job, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
rauth(Job *job)
|
|
|
|
{
|
|
|
|
sendmsg(job, "dns: authentication not required");
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* don't flush till all the slaves are done
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
rflush(Job *job)
|
|
|
|
{
|
|
|
|
flushjob(job->request.oldtag);
|
|
|
|
sendmsg(job, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
rattach(Job *job, Mfile *mf)
|
|
|
|
{
|
|
|
|
if(mf->user != nil)
|
|
|
|
free(mf->user);
|
|
|
|
mf->user = estrdup(job->request.uname);
|
|
|
|
mf->qid.vers = vers++;
|
|
|
|
mf->qid.type = QTDIR;
|
|
|
|
mf->qid.path = 0LL;
|
|
|
|
job->reply.qid = mf->qid;
|
|
|
|
sendmsg(job, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
char*
|
|
|
|
rwalk(Job *job, Mfile *mf)
|
|
|
|
{
|
|
|
|
int i, nelems;
|
|
|
|
char *err;
|
|
|
|
char **elems;
|
|
|
|
Mfile *nmf;
|
|
|
|
Qid qid;
|
|
|
|
|
|
|
|
err = 0;
|
|
|
|
nmf = nil;
|
|
|
|
elems = job->request.wname;
|
|
|
|
nelems = job->request.nwname;
|
|
|
|
job->reply.nwqid = 0;
|
|
|
|
|
|
|
|
if(job->request.newfid != job->request.fid){
|
|
|
|
/* clone fid */
|
|
|
|
nmf = copyfid(mf, job->request.newfid);
|
|
|
|
if(nmf == nil){
|
|
|
|
err = "clone bad newfid";
|
|
|
|
goto send;
|
|
|
|
}
|
|
|
|
mf = nmf;
|
|
|
|
}
|
|
|
|
/* else nmf will be nil */
|
|
|
|
|
|
|
|
qid = mf->qid;
|
|
|
|
if(nelems > 0)
|
|
|
|
/* walk fid */
|
|
|
|
for(i=0; i<nelems && i<MAXWELEM; i++){
|
|
|
|
if((qid.type & QTDIR) == 0){
|
|
|
|
err = "not a directory";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (strcmp(elems[i], "..") == 0 ||
|
|
|
|
strcmp(elems[i], ".") == 0){
|
|
|
|
qid.type = QTDIR;
|
|
|
|
qid.path = Qdir;
|
|
|
|
Found:
|
|
|
|
job->reply.wqid[i] = qid;
|
|
|
|
job->reply.nwqid++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if(strcmp(elems[i], "dns") == 0){
|
|
|
|
qid.type = QTFILE;
|
|
|
|
qid.path = Qdns;
|
|
|
|
goto Found;
|
|
|
|
}
|
|
|
|
err = "file does not exist";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
send:
|
|
|
|
if(nmf != nil && (err!=nil || job->reply.nwqid<nelems))
|
|
|
|
freefid(nmf);
|
|
|
|
if(err == nil)
|
|
|
|
mf->qid = qid;
|
|
|
|
sendmsg(job, err);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
ropen(Job *job, Mfile *mf)
|
|
|
|
{
|
|
|
|
int mode;
|
|
|
|
char *err;
|
|
|
|
|
|
|
|
err = 0;
|
|
|
|
mode = job->request.mode;
|
|
|
|
if(mf->qid.type & QTDIR)
|
|
|
|
if(mode)
|
|
|
|
err = "permission denied";
|
|
|
|
job->reply.qid = mf->qid;
|
|
|
|
job->reply.iounit = 0;
|
|
|
|
sendmsg(job, err);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
rcreate(Job *job, Mfile *mf)
|
|
|
|
{
|
|
|
|
USED(mf);
|
|
|
|
sendmsg(job, "creation permission denied");
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
rread(Job *job, Mfile *mf)
|
|
|
|
{
|
|
|
|
int i, n;
|
|
|
|
int32_t clock;
|
|
|
|
uint32_t cnt;
|
|
|
|
int64_t off;
|
|
|
|
char *err;
|
|
|
|
uint8_t buf[Maxfdata];
|
|
|
|
Dir dir;
|
|
|
|
|
|
|
|
n = 0;
|
|
|
|
err = nil;
|
|
|
|
off = job->request.offset;
|
|
|
|
cnt = job->request.count;
|
|
|
|
*buf = '\0';
|
|
|
|
job->reply.data = (char*)buf;
|
|
|
|
if(mf->qid.type & QTDIR){
|
|
|
|
clock = time(nil);
|
|
|
|
if(off == 0){
|
|
|
|
memset(&dir, 0, sizeof dir);
|
|
|
|
dir.name = "dns";
|
|
|
|
dir.qid.type = QTFILE;
|
|
|
|
dir.qid.vers = vers;
|
|
|
|
dir.qid.path = Qdns;
|
|
|
|
dir.mode = 0666;
|
|
|
|
dir.length = 0;
|
|
|
|
dir.uid = dir.gid = dir.muid = mf->user;
|
|
|
|
dir.atime = dir.mtime = clock; /* wrong */
|
|
|
|
n = convD2M(&dir, buf, sizeof buf);
|
|
|
|
}
|
|
|
|
} else if (off < 0)
|
|
|
|
err = "negative read offset";
|
|
|
|
else {
|
|
|
|
/* first offset will always be zero */
|
|
|
|
for(i = 1; i <= mf->nrr; i++)
|
|
|
|
if(mf->rr[i] > off)
|
|
|
|
break;
|
|
|
|
if(i <= mf->nrr) {
|
|
|
|
if(off + cnt > mf->rr[i])
|
|
|
|
n = mf->rr[i] - off;
|
|
|
|
else
|
|
|
|
n = cnt;
|
|
|
|
assert(n >= 0);
|
|
|
|
job->reply.data = mf->reply + off;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
job->reply.count = n;
|
|
|
|
sendmsg(job, err);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
rwrite(Job *job, Mfile *mf, Request *req)
|
|
|
|
{
|
|
|
|
int rooted, wantsav, send;
|
|
|
|
uint32_t cnt;
|
|
|
|
char *err, *p, *atype;
|
|
|
|
char errbuf[ERRMAX];
|
|
|
|
|
|
|
|
err = nil;
|
|
|
|
cnt = job->request.count;
|
|
|
|
send = 1;
|
|
|
|
if(mf->qid.type & QTDIR)
|
|
|
|
err = "can't write directory";
|
|
|
|
else if (job->request.offset != 0)
|
|
|
|
err = "writing at non-zero offset";
|
|
|
|
else if(cnt >= Maxrequest)
|
|
|
|
err = "request too int32_t";
|
|
|
|
else
|
|
|
|
send = 0;
|
|
|
|
if (send)
|
|
|
|
goto send;
|
|
|
|
|
|
|
|
job->request.data[cnt] = 0;
|
|
|
|
if(cnt > 0 && job->request.data[cnt-1] == '\n')
|
|
|
|
job->request.data[cnt-1] = 0;
|
|
|
|
|
|
|
|
if(strcmp(mf->user, "none") == 0 || strcmp(mf->user, dnsuser) != 0)
|
|
|
|
goto query; /* skip special commands if not owner */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* special commands
|
|
|
|
*/
|
|
|
|
if(debug)
|
|
|
|
dnslog("rwrite got: %s", job->request.data);
|
|
|
|
send = 1;
|
|
|
|
if(strcmp(job->request.data, "debug")==0)
|
|
|
|
debug ^= 1;
|
|
|
|
else if(strcmp(job->request.data, "dump")==0)
|
|
|
|
dndump("/tmp/ndb-dnsdump");
|
|
|
|
else if(strcmp(job->request.data, "refresh")==0)
|
|
|
|
needrefresh = 1;
|
|
|
|
else if(strcmp(job->request.data, "restart")==0)
|
|
|
|
stop = 1;
|
|
|
|
else if(strcmp(job->request.data, "stats")==0)
|
|
|
|
dnstats("/tmp/ndb-dnsstats");
|
|
|
|
else if(strncmp(job->request.data, "target ", 7)==0){
|
|
|
|
target = atol(job->request.data + 7);
|
|
|
|
dnslog("target set to %ld", target);
|
|
|
|
} else
|
|
|
|
send = 0;
|
|
|
|
if (send)
|
|
|
|
goto send;
|
|
|
|
|
|
|
|
query:
|
|
|
|
/*
|
|
|
|
* kill previous reply
|
|
|
|
*/
|
|
|
|
mf->nrr = 0;
|
|
|
|
mf->rr[0] = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* break up request (into a name and a type)
|
|
|
|
*/
|
|
|
|
atype = strchr(job->request.data, ' ');
|
|
|
|
if(atype == 0){
|
|
|
|
snprint(errbuf, sizeof errbuf, "illegal request %s",
|
|
|
|
job->request.data);
|
|
|
|
err = errbuf;
|
|
|
|
goto send;
|
|
|
|
} else
|
|
|
|
*atype++ = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* tracing request
|
|
|
|
*/
|
|
|
|
if(strcmp(atype, "trace") == 0){
|
|
|
|
if(trace)
|
|
|
|
free(trace);
|
|
|
|
if(*job->request.data)
|
|
|
|
trace = estrdup(job->request.data);
|
|
|
|
else
|
|
|
|
trace = 0;
|
|
|
|
goto send;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* normal request: domain [type] */
|
|
|
|
stats.qrecvd9p++;
|
|
|
|
mf->type = rrtype(atype);
|
|
|
|
if(mf->type < 0){
|
|
|
|
snprint(errbuf, sizeof errbuf, "unknown type %s", atype);
|
|
|
|
err = errbuf;
|
|
|
|
goto send;
|
|
|
|
}
|
|
|
|
|
|
|
|
p = atype - 2;
|
|
|
|
if(p >= job->request.data && *p == '.'){
|
|
|
|
rooted = 1;
|
|
|
|
*p = 0;
|
|
|
|
} else
|
|
|
|
rooted = 0;
|
|
|
|
|
|
|
|
p = job->request.data;
|
|
|
|
if(*p == '!'){
|
|
|
|
wantsav = 1;
|
|
|
|
p++;
|
|
|
|
} else
|
|
|
|
wantsav = 0;
|
|
|
|
|
|
|
|
err = lookupquery(job, mf, req, errbuf, p, wantsav, rooted);
|
|
|
|
send:
|
|
|
|
job->reply.count = cnt;
|
|
|
|
sendmsg(job, err);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* dnsdebug calls
|
|
|
|
* rr = dnresolve(buf, Cin, type, &req, 0, 0, Recurse, rooted, 0);
|
|
|
|
* which generates a UDP query, which eventually calls
|
|
|
|
* dnserver(&reqmsg, &repmsg, &req, buf, rcode);
|
|
|
|
* which calls
|
|
|
|
* rp = dnresolve(name, Cin, type, req, &mp->an, 0, recurse, 1, 0);
|
|
|
|
*
|
|
|
|
* but here we just call dnresolve directly.
|
|
|
|
*/
|
|
|
|
static char *
|
|
|
|
lookupquery(Job *job, Mfile *mf, Request *req, char *errbuf, char *p,
|
|
|
|
int wantsav, int rooted)
|
|
|
|
{
|
|
|
|
int status;
|
|
|
|
RR *rp, *neg;
|
|
|
|
|
|
|
|
status = Rok;
|
|
|
|
rp = dnresolve(p, Cin, mf->type, req, 0, 0, Recurse, rooted, &status);
|
|
|
|
|
|
|
|
neg = rrremneg(&rp);
|
|
|
|
if(neg){
|
|
|
|
status = neg->negrcode;
|
|
|
|
rrfreelist(neg);
|
|
|
|
}
|
|
|
|
|
|
|
|
return respond(job, mf, rp, errbuf, status, wantsav);
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *
|
|
|
|
respond(Job *job, Mfile *mf, RR *rp, char *errbuf, int status, int wantsav)
|
|
|
|
{
|
|
|
|
int32_t n;
|
|
|
|
RR *tp;
|
|
|
|
|
|
|
|
if(rp == nil)
|
|
|
|
switch(status){
|
|
|
|
case Rname:
|
|
|
|
return "name does not exist";
|
|
|
|
case Rserver:
|
|
|
|
return "dns failure";
|
|
|
|
case Rok:
|
|
|
|
default:
|
|
|
|
snprint(errbuf, ERRMAX,
|
|
|
|
"resource does not exist; negrcode %d", status);
|
|
|
|
return errbuf;
|
|
|
|
}
|
|
|
|
|
|
|
|
lock(&joblock);
|
|
|
|
if(!job->flushed){
|
|
|
|
/* format data to be read later */
|
|
|
|
n = 0;
|
|
|
|
mf->nrr = 0;
|
|
|
|
for(tp = rp; mf->nrr < Maxrrr-1 && n < Maxreply && tp &&
|
|
|
|
tsame(mf->type, tp->type); tp = tp->next){
|
|
|
|
mf->rr[mf->nrr++] = n;
|
|
|
|
if(wantsav)
|
|
|
|
n += snprint(mf->reply+n, Maxreply-n, "%Q", tp);
|
|
|
|
else
|
|
|
|
n += snprint(mf->reply+n, Maxreply-n, "%R", tp);
|
|
|
|
}
|
|
|
|
mf->rr[mf->nrr] = n;
|
|
|
|
}
|
|
|
|
unlock(&joblock);
|
|
|
|
|
|
|
|
rrfreelist(rp);
|
|
|
|
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
rclunk(Job *job, Mfile *mf)
|
|
|
|
{
|
|
|
|
freefid(mf);
|
|
|
|
sendmsg(job, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
rremove(Job *job, Mfile *mf)
|
|
|
|
{
|
|
|
|
USED(mf);
|
|
|
|
sendmsg(job, "remove permission denied");
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
rstat(Job *job, Mfile *mf)
|
|
|
|
{
|
|
|
|
Dir dir;
|
|
|
|
uint8_t buf[IOHDRSZ+Maxfdata];
|
|
|
|
|
|
|
|
memset(&dir, 0, sizeof dir);
|
|
|
|
if(mf->qid.type & QTDIR){
|
|
|
|
dir.name = ".";
|
|
|
|
dir.mode = DMDIR|0555;
|
|
|
|
} else {
|
|
|
|
dir.name = "dns";
|
|
|
|
dir.mode = 0666;
|
|
|
|
}
|
|
|
|
dir.qid = mf->qid;
|
|
|
|
dir.length = 0;
|
|
|
|
dir.uid = dir.gid = dir.muid = mf->user;
|
|
|
|
dir.atime = dir.mtime = time(nil);
|
|
|
|
job->reply.nstat = convD2M(&dir, buf, sizeof buf);
|
|
|
|
job->reply.stat = buf;
|
|
|
|
sendmsg(job, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
rwstat(Job *job, Mfile *mf)
|
|
|
|
{
|
|
|
|
USED(mf);
|
|
|
|
sendmsg(job, "wstat permission denied");
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
sendmsg(Job *job, char *err)
|
|
|
|
{
|
|
|
|
int n;
|
|
|
|
uint8_t mdata[IOHDRSZ + Maxfdata];
|
|
|
|
char ename[ERRMAX];
|
|
|
|
|
|
|
|
if(err){
|
|
|
|
job->reply.type = Rerror;
|
|
|
|
snprint(ename, sizeof ename, "dns: %s", err);
|
|
|
|
job->reply.ename = ename;
|
|
|
|
}else
|
|
|
|
job->reply.type = job->request.type+1;
|
|
|
|
job->reply.tag = job->request.tag;
|
|
|
|
n = convS2M(&job->reply, mdata, sizeof mdata);
|
|
|
|
if(n == 0){
|
|
|
|
warning("sendmsg convS2M of %F returns 0", &job->reply);
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
lock(&joblock);
|
|
|
|
if(job->flushed == 0)
|
|
|
|
if(write(mfd[1], mdata, n)!=n)
|
|
|
|
sysfatal("mount write");
|
|
|
|
unlock(&joblock);
|
|
|
|
if(debug)
|
|
|
|
dnslog("%F %d", &job->reply, n);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* the following varies between dnsdebug and dns
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
logreply(int id, uint8_t *addr, DNSmsg *mp)
|
|
|
|
{
|
|
|
|
RR *rp;
|
|
|
|
|
|
|
|
dnslog("%d: rcvd %I flags:%s%s%s%s%s", id, addr,
|
|
|
|
mp->flags & Fauth? " auth": "",
|
|
|
|
mp->flags & Ftrunc? " trunc": "",
|
|
|
|
mp->flags & Frecurse? " rd": "",
|
|
|
|
mp->flags & Fcanrec? " ra": "",
|
|
|
|
(mp->flags & (Fauth|Rmask)) == (Fauth|Rname)? " nx": "");
|
|
|
|
for(rp = mp->qd; rp != nil; rp = rp->next)
|
|
|
|
dnslog("%d: rcvd %I qd %s", id, addr, rp->owner->name);
|
|
|
|
for(rp = mp->an; rp != nil; rp = rp->next)
|
|
|
|
dnslog("%d: rcvd %I an %R", id, addr, rp);
|
|
|
|
for(rp = mp->ns; rp != nil; rp = rp->next)
|
|
|
|
dnslog("%d: rcvd %I ns %R", id, addr, rp);
|
|
|
|
for(rp = mp->ar; rp != nil; rp = rp->next)
|
|
|
|
dnslog("%d: rcvd %I ar %R", id, addr, rp);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
logsend(int id, int subid, uint8_t *addr, char *sname, char *rname, int type)
|
|
|
|
{
|
|
|
|
char buf[12];
|
|
|
|
|
|
|
|
dnslog("[%d] %d.%d: sending to %I/%s %s %s",
|
|
|
|
getpid(), id, subid, addr, sname, rname,
|
|
|
|
rrname(type, buf, sizeof buf));
|
|
|
|
}
|
|
|
|
|
|
|
|
RR*
|
|
|
|
getdnsservers(int class)
|
|
|
|
{
|
|
|
|
return dnsservers(class);
|
|
|
|
}
|