This commit is contained in:
rsc 2005-12-27 04:30:05 +00:00
parent cff43a06f2
commit 3e0d8fb3ea
15 changed files with 7047 additions and 0 deletions

380
src/cmd/ndb/convDNS2M.c Executable file
View file

@ -0,0 +1,380 @@
#include <u.h>
#include <libc.h>
#include <ip.h>
#include <bio.h>
#include <ndb.h>
#include "dns.h"
/*
* a dictionary of domain names for packing messages
*/
enum
{
Ndict= 64,
};
typedef struct Dict Dict;
struct Dict
{
struct {
ushort offset; /* pointer to packed name in message */
char *name; /* pointer to unpacked name in buf */
} x[Ndict];
int n; /* size of dictionary */
uchar *start; /* start of packed message */
char buf[4*1024]; /* buffer for unpacked names */
char *ep; /* first free char in buf */
};
#define NAME(x) p = pname(p, ep, x, dp)
#define SYMBOL(x) p = psym(p, ep, x)
#define STRING(x) p = pstr(p, ep, x)
#define BYTES(x, n) p = pbytes(p, ep, x, n)
#define USHORT(x) p = pushort(p, ep, x)
#define UCHAR(x) p = puchar(p, ep, x)
#define ULONG(x) p = pulong(p, ep, x)
#define V4ADDR(x) p = pv4addr(p, ep, x)
#define V6ADDR(x) p = pv6addr(p, ep, x)
static uchar*
psym(uchar *p, uchar *ep, char *np)
{
int n;
n = strlen(np);
if(n >= Strlen) /* DNS maximum length string */
n = Strlen - 1;
if(ep - p < n+1) /* see if it fits in the buffer */
return ep+1;
*p++ = n;
memcpy(p, np, n);
return p + n;
}
static uchar*
pstr(uchar *p, uchar *ep, char *np)
{
int n;
n = strlen(np);
if(n >= Strlen) /* DNS maximum length string */
n = Strlen - 1;
if(ep - p < n+1) /* see if it fits in the buffer */
return ep+1;
*p++ = n;
memcpy(p, np, n);
return p + n;
}
static uchar*
pbytes(uchar *p, uchar *ep, uchar *np, int n)
{
if(ep - p < n)
return ep+1;
memcpy(p, np, n);
return p + n;
}
static uchar*
puchar(uchar *p, uchar *ep, int val)
{
if(ep - p < 1)
return ep+1;
*p++ = val;
return p;
}
static uchar*
pushort(uchar *p, uchar *ep, int val)
{
if(ep - p < 2)
return ep+1;
*p++ = val>>8;
*p++ = val;
return p;
}
static uchar*
pulong(uchar *p, uchar *ep, int val)
{
if(ep - p < 4)
return ep+1;
*p++ = val>>24;
*p++ = val>>16;
*p++ = val>>8;
*p++ = val;
return p;
}
static uchar*
pv4addr(uchar *p, uchar *ep, char *name)
{
uchar ip[IPaddrlen];
if(ep - p < 4)
return ep+1;
parseip(ip, name);
v6tov4(p, ip);
return p + 4;
}
static uchar*
pv6addr(uchar *p, uchar *ep, char *name)
{
if(ep - p < IPaddrlen)
return ep+1;
parseip(p, name);
return p + IPaddrlen;
}
static uchar*
pname(uchar *p, uchar *ep, char *np, Dict *dp)
{
char *cp;
int i;
char *last; /* last component packed */
if(strlen(np) >= Domlen) /* make sure we don't exceed DNS limits */
return ep+1;
last = 0;
while(*np){
/* look through every component in the dictionary for a match */
for(i = 0; i < dp->n; i++){
if(strcmp(np, dp->x[i].name) == 0){
if(ep - p < 2)
return ep+1;
*p++ = (dp->x[i].offset>>8) | 0xc0;
*p++ = dp->x[i].offset;
return p;
}
}
/* if there's room, enter this name in dictionary */
if(dp->n < Ndict){
if(last){
/* the whole name is already in dp->buf */
last = strchr(last, '.') + 1;
dp->x[dp->n].name = last;
dp->x[dp->n].offset = p - dp->start;
dp->n++;
} else {
/* add to dp->buf */
i = strlen(np);
if(dp->ep + i + 1 < &dp->buf[sizeof(dp->buf)]){
strcpy(dp->ep, np);
dp->x[dp->n].name = dp->ep;
last = dp->ep;
dp->x[dp->n].offset = p - dp->start;
dp->ep += i + 1;
dp->n++;
}
}
}
/* put next component into message */
cp = strchr(np, '.');
if(cp == 0){
i = strlen(np);
cp = np + i; /* point to null terminator */
} else {
i = cp - np;
cp++; /* point past '.' */
}
if(ep-p < i+1)
return ep+1;
*p++ = i; /* count of chars in label */
memcpy(p, np, i);
np = cp;
p += i;
}
if(p >= ep)
return ep+1;
*p++ = 0; /* add top level domain */
return p;
}
static uchar*
convRR2M(RR *rp, uchar *p, uchar *ep, Dict *dp)
{
uchar *lp, *data;
int len, ttl;
Txt *t;
NAME(rp->owner->name);
USHORT(rp->type);
USHORT(rp->owner->class);
/* egregious overuse of ttl (it's absolute time in the cache) */
if(rp->db)
ttl = rp->ttl;
else
ttl = rp->ttl - now;
if(ttl < 0)
ttl = 0;
ULONG(ttl);
lp = p; /* leave room for the rdata length */
p += 2;
data = p;
if(data >= ep)
return p+1;
switch(rp->type){
case Thinfo:
SYMBOL(rp->cpu->name);
SYMBOL(rp->os->name);
break;
case Tcname:
case Tmb:
case Tmd:
case Tmf:
case Tns:
NAME(rp->host->name);
break;
case Tmg:
case Tmr:
NAME(rp->mb->name);
break;
case Tminfo:
NAME(rp->rmb->name);
NAME(rp->mb->name);
break;
case Tmx:
USHORT(rp->pref);
NAME(rp->host->name);
break;
case Ta:
V4ADDR(rp->ip->name);
break;
case Taaaa:
V6ADDR(rp->ip->name);
break;
case Tptr:
NAME(rp->ptr->name);
break;
case Tsoa:
NAME(rp->host->name);
NAME(rp->rmb->name);
ULONG(rp->soa->serial);
ULONG(rp->soa->refresh);
ULONG(rp->soa->retry);
ULONG(rp->soa->expire);
ULONG(rp->soa->minttl);
break;
case Ttxt:
for(t = rp->txt; t != nil; t = t->next)
STRING(t->p);
break;
case Tnull:
BYTES(rp->null->data, rp->null->dlen);
break;
case Trp:
NAME(rp->rmb->name);
NAME(rp->rp->name);
break;
case Tkey:
USHORT(rp->key->flags);
UCHAR(rp->key->proto);
UCHAR(rp->key->alg);
BYTES(rp->key->data, rp->key->dlen);
break;
case Tsig:
USHORT(rp->sig->type);
UCHAR(rp->sig->alg);
UCHAR(rp->sig->labels);
ULONG(rp->sig->ttl);
ULONG(rp->sig->exp);
ULONG(rp->sig->incep);
USHORT(rp->sig->tag);
NAME(rp->sig->signer->name);
BYTES(rp->sig->data, rp->sig->dlen);
break;
case Tcert:
USHORT(rp->cert->type);
USHORT(rp->cert->tag);
UCHAR(rp->cert->alg);
BYTES(rp->cert->data, rp->cert->dlen);
break;
}
/* stuff in the rdata section length */
len = p - data;
*lp++ = len >> 8;
*lp = len;
return p;
}
static uchar*
convQ2M(RR *rp, uchar *p, uchar *ep, Dict *dp)
{
NAME(rp->owner->name);
USHORT(rp->type);
USHORT(rp->owner->class);
return p;
}
static uchar*
rrloop(RR *rp, int *countp, uchar *p, uchar *ep, Dict *dp, int quest)
{
uchar *np;
*countp = 0;
for(; rp && p < ep; rp = rp->next){
if(quest)
np = convQ2M(rp, p, ep, dp);
else
np = convRR2M(rp, p, ep, dp);
if(np > ep)
break;
p = np;
(*countp)++;
}
return p;
}
/*
* convert into a message
*/
int
convDNS2M(DNSmsg *m, uchar *buf, int len)
{
uchar *p, *ep, *np;
Dict d;
d.n = 0;
d.start = buf;
d.ep = d.buf;
memset(buf, 0, len);
m->qdcount = m->ancount = m->nscount = m->arcount = 0;
/* first pack in the RR's so we can get real counts */
p = buf + 12;
ep = buf + len;
p = rrloop(m->qd, &m->qdcount, p, ep, &d, 1);
p = rrloop(m->an, &m->ancount, p, ep, &d, 0);
p = rrloop(m->ns, &m->nscount, p, ep, &d, 0);
p = rrloop(m->ar, &m->arcount, p, ep, &d, 0);
if(p > ep)
return -1;
/* now pack the rest */
np = p;
p = buf;
ep = buf + len;
USHORT(m->id);
USHORT(m->flags);
USHORT(m->qdcount);
USHORT(m->ancount);
USHORT(m->nscount);
USHORT(m->arcount);
if(p > ep)
return -1;
return np - buf;
}

460
src/cmd/ndb/convM2DNS.c Executable file
View file

@ -0,0 +1,460 @@
#include <u.h>
#include <libc.h>
#include <ip.h>
#include <bio.h>
#include <ndb.h>
#include "dns.h"
typedef struct Scan Scan;
struct Scan
{
uchar *base;
uchar *p;
uchar *ep;
char *err;
};
#define NAME(x) gname(x, sp)
#define SYMBOL(x) (x = gsym(sp))
#define STRING(x) (x = gstr(sp))
#define USHORT(x) (x = gshort(sp))
#define ULONG(x) (x = glong(sp))
#define UCHAR(x) (x = gchar(sp))
#define V4ADDR(x) (x = gv4addr(sp))
#define V6ADDR(x) (x = gv6addr(sp))
#define BYTES(x, y) (y = gbytes(sp, &x, len - (sp->p - data)))
static char *toolong = "too long";
/*
* get a ushort/ulong
*/
static ushort
gchar(Scan *sp)
{
ushort x;
if(sp->err)
return 0;
if(sp->ep - sp->p < 1){
sp->err = toolong;
return 0;
}
x = sp->p[0];
sp->p += 1;
return x;
}
static ushort
gshort(Scan *sp)
{
ushort x;
if(sp->err)
return 0;
if(sp->ep - sp->p < 2){
sp->err = toolong;
return 0;
}
x = (sp->p[0]<<8) | sp->p[1];
sp->p += 2;
return x;
}
static ulong
glong(Scan *sp)
{
ulong x;
if(sp->err)
return 0;
if(sp->ep - sp->p < 4){
sp->err = toolong;
return 0;
}
x = (sp->p[0]<<24) | (sp->p[1]<<16) | (sp->p[2]<<8) | sp->p[3];
sp->p += 4;
return x;
}
/*
* get an ip address
*/
static DN*
gv4addr(Scan *sp)
{
char addr[32];
if(sp->err)
return 0;
if(sp->ep - sp->p < 4){
sp->err = toolong;
return 0;
}
snprint(addr, sizeof(addr), "%V", sp->p);
sp->p += 4;
return dnlookup(addr, Cin, 1);
}
static DN*
gv6addr(Scan *sp)
{
char addr[64];
if(sp->err)
return 0;
if(sp->ep - sp->p < IPaddrlen){
sp->err = toolong;
return 0;
}
snprint(addr, sizeof(addr), "%I", sp->p);
sp->p += IPaddrlen;
return dnlookup(addr, Cin, 1);
}
/*
* get a string. make it an internal symbol.
*/
static DN*
gsym(Scan *sp)
{
int n;
char sym[Strlen+1];
if(sp->err)
return 0;
n = *(sp->p++);
if(sp->p+n > sp->ep){
sp->err = toolong;
return 0;
}
if(n > Strlen){
sp->err = "illegal string";
return 0;
}
strncpy(sym, (char*)sp->p, n);
sym[n] = 0;
sp->p += n;
return dnlookup(sym, Csym, 1);
}
/*
* get a string. don't make it an internal symbol.
*/
static Txt*
gstr(Scan *sp)
{
int n;
char sym[Strlen+1];
Txt *t;
if(sp->err)
return 0;
n = *(sp->p++);
if(sp->p+n > sp->ep){
sp->err = toolong;
return 0;
}
if(n > Strlen){
sp->err = "illegal string";
return 0;
}
strncpy(sym, (char*)sp->p, n);
sym[n] = 0;
sp->p += n;
t = emalloc(sizeof(*t));
t->next = nil;
t->p = estrdup(sym);
return t;
}
/*
* get a sequence of bytes
*/
static int
gbytes(Scan *sp, uchar **p, int n)
{
if(sp->err)
return 0;
if(sp->p+n > sp->ep || n < 0){
sp->err = toolong;
return 0;
}
*p = emalloc(n);
memmove(*p, sp->p, n);
sp->p += n;
return n;
}
/*
* get a domain name. 'to' must point to a buffer at least Domlen+1 long.
*/
static char*
gname(char *to, Scan *sp)
{
int len, off;
int pointer;
int n;
char *tostart;
char *toend;
uchar *p;
tostart = to;
if(sp->err)
goto err;
pointer = 0;
p = sp->p;
toend = to + Domlen;
for(len = 0; *p; len += pointer ? 0 : (n+1)){
if((*p & 0xc0) == 0xc0){
/* pointer to other spot in message */
if(pointer++ > 10){
sp->err = "pointer loop";
goto err;
}
off = ((p[0]<<8) + p[1]) & 0x3ff;
p = sp->base + off;
if(p >= sp->ep){
sp->err = "bad pointer";
goto err;
}
n = 0;
continue;
}
n = *p++;
if(len + n < Domlen - 1){
if(to + n > toend){
sp->err = toolong;
goto err;
}
memmove(to, p, n);
to += n;
}
p += n;
if(*p){
if(to >= toend){
sp->err = toolong;
goto err;
}
*to++ = '.';
}
}
*to = 0;
if(pointer)
sp->p += len + 2; /* + 2 for pointer */
else
sp->p += len + 1; /* + 1 for the null domain */
return tostart;
err:
*tostart = 0;
return tostart;
}
/*
* convert the next RR from a message
*/
static RR*
convM2RR(Scan *sp)
{
RR *rp;
int type;
int class;
uchar *data;
int len;
char dname[Domlen+1];
Txt *t, **l;
retry:
NAME(dname);
USHORT(type);
USHORT(class);
rp = rralloc(type);
rp->owner = dnlookup(dname, class, 1);
rp->type = type;
ULONG(rp->ttl);
rp->ttl += now;
USHORT(len);
data = sp->p;
if(sp->p + len > sp->ep)
sp->err = toolong;
if(sp->err){
rrfree(rp);
return 0;
}
switch(type){
default:
/* unknown type, just ignore it */
sp->p = data + len;
rrfree(rp);
goto retry;
case Thinfo:
SYMBOL(rp->cpu);
SYMBOL(rp->os);
break;
case Tcname:
case Tmb:
case Tmd:
case Tmf:
case Tns:
rp->host = dnlookup(NAME(dname), Cin, 1);
break;
case Tmg:
case Tmr:
rp->mb = dnlookup(NAME(dname), Cin, 1);
break;
case Tminfo:
rp->rmb = dnlookup(NAME(dname), Cin, 1);
rp->mb = dnlookup(NAME(dname), Cin, 1);
break;
case Tmx:
USHORT(rp->pref);
rp->host = dnlookup(NAME(dname), Cin, 1);
break;
case Ta:
V4ADDR(rp->ip);
break;
case Taaaa:
V6ADDR(rp->ip);
break;
case Tptr:
rp->ptr = dnlookup(NAME(dname), Cin, 1);
break;
case Tsoa:
rp->host = dnlookup(NAME(dname), Cin, 1);
rp->rmb = dnlookup(NAME(dname), Cin, 1);
ULONG(rp->soa->serial);
ULONG(rp->soa->refresh);
ULONG(rp->soa->retry);
ULONG(rp->soa->expire);
ULONG(rp->soa->minttl);
break;
case Ttxt:
l = &rp->txt;
*l = nil;
while(sp->p-data < len){
STRING(t);
*l = t;
l = &t->next;
}
break;
case Tnull:
BYTES(rp->null->data, rp->null->dlen);
break;
case Trp:
rp->rmb = dnlookup(NAME(dname), Cin, 1);
rp->rp = dnlookup(NAME(dname), Cin, 1);
break;
case Tkey:
USHORT(rp->key->flags);
UCHAR(rp->key->proto);
UCHAR(rp->key->alg);
BYTES(rp->key->data, rp->key->dlen);
break;
case Tsig:
USHORT(rp->sig->type);
UCHAR(rp->sig->alg);
UCHAR(rp->sig->labels);
ULONG(rp->sig->ttl);
ULONG(rp->sig->exp);
ULONG(rp->sig->incep);
USHORT(rp->sig->tag);
rp->sig->signer = dnlookup(NAME(dname), Cin, 1);
BYTES(rp->sig->data, rp->sig->dlen);
break;
case Tcert:
USHORT(rp->cert->type);
USHORT(rp->cert->tag);
UCHAR(rp->cert->alg);
BYTES(rp->cert->data, rp->cert->dlen);
break;
}
if(sp->p - data != len)
sp->err = "bad RR len";
return rp;
}
/*
* convert the next question from a message
*/
static RR*
convM2Q(Scan *sp)
{
char dname[Domlen+1];
int type;
int class;
RR *rp;
NAME(dname);
USHORT(type);
USHORT(class);
if(sp->err)
return 0;
rp = rralloc(type);
rp->owner = dnlookup(dname, class, 1);
return rp;
}
static RR*
rrloop(Scan *sp, int count, int quest)
{
int i;
static char errbuf[64];
RR *first, *rp, **l;
if(sp->err)
return 0;
l = &first;
first = 0;
for(i = 0; i < count; i++){
rp = quest ? convM2Q(sp) : convM2RR(sp);
if(rp == 0)
break;
if(sp->err){
rrfree(rp);
break;
}
*l = rp;
l = &rp->next;
}
return first;
}
/*
* convert the next DNS from a message stream
*/
char*
convM2DNS(uchar *buf, int len, DNSmsg *m)
{
Scan scan;
Scan *sp;
char *err;
scan.base = buf;
scan.p = buf;
scan.ep = buf + len;
scan.err = 0;
sp = &scan;
memset(m, 0, sizeof(DNSmsg));
USHORT(m->id);
USHORT(m->flags);
USHORT(m->qdcount);
USHORT(m->ancount);
USHORT(m->nscount);
USHORT(m->arcount);
m->qd = rrloop(sp, m->qdcount, 1);
m->an = rrloop(sp, m->ancount, 0);
m->ns = rrloop(sp, m->nscount, 0);
err = scan.err; /* live with bad ar's */
m->ar = rrloop(sp, m->arcount, 0);
return err;
}

946
src/cmd/ndb/dblookup.c Executable file
View file

@ -0,0 +1,946 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <ndb.h>
#include <ip.h>
#include "dns.h"
static Ndb *db;
static RR* dblookup1(char*, int, int, int);
static RR* addrrr(Ndbtuple*, Ndbtuple*);
static RR* nsrr(Ndbtuple*, Ndbtuple*);
static RR* cnamerr(Ndbtuple*, Ndbtuple*);
static RR* mxrr(Ndbtuple*, Ndbtuple*);
static RR* soarr(Ndbtuple*, Ndbtuple*);
static RR* ptrrr(Ndbtuple*, Ndbtuple*);
static Ndbtuple* look(Ndbtuple*, Ndbtuple*, char*);
static RR* doaxfr(Ndb*, char*);
static RR* nullrr(Ndbtuple *entry, Ndbtuple *pair);
static RR* txtrr(Ndbtuple *entry, Ndbtuple *pair);
static Lock dblock;
static void createptrs(void);
static int implemented[Tall] =
{
[Ta] 1,
[Tns] 1,
[Tsoa] 1,
[Tmx] 1,
[Tptr] 1,
[Tcname] 1,
[Tnull] 1,
[Ttxt] 1,
};
static void
nstrcpy(char *to, char *from, int len)
{
strncpy(to, from, len);
to[len-1] = 0;
}
int
opendatabase(void)
{
char buf[256];
Ndb *xdb;
if(db == nil){
snprint(buf, sizeof(buf), "%s/ndb", mntpt);
xdb = ndbopen(dbfile);
if(xdb != nil)
xdb->nohash = 1;
db = ndbcat(ndbopen(buf), xdb);
}
if(db == nil)
return -1;
else
return 0;
}
/*
* lookup an RR in the network database, look for matches
* against both the domain name and the wildcarded domain name.
*
* the lock makes sure only one process can be accessing the data
* base at a time. This is important since there's a lot of
* shared state there.
*
* e.g. for x.research.bell-labs.com, first look for a match against
* the x.research.bell-labs.com. If nothing matches, try *.research.bell-labs.com.
*/
RR*
dblookup(char *name, int class, int type, int auth, int ttl)
{
RR *rp, *tp;
char buf[256];
char *wild, *cp;
DN *dp, *ndp;
int err;
static int parallel;
static int parfd[2];
static char token[1];
/* so far only internet lookups are implemented */
if(class != Cin)
return 0;
err = Rname;
if(type == Tall){
rp = 0;
for (type = Ta; type < Tall; type++)
if(implemented[type])
rrcat(&rp, dblookup(name, class, type, auth, ttl));
return rp;
}
lock(&dblock);
dp = dnlookup(name, class, 1);
if(opendatabase() < 0)
goto out;
if(dp->rr)
err = 0;
/* first try the given name */
rp = 0;
if(cachedb)
rp = rrlookup(dp, type, NOneg);
else
rp = dblookup1(name, type, auth, ttl);
if(rp)
goto out;
/* try lower case version */
for(cp = name; *cp; cp++)
*cp = tolower(*cp);
if(cachedb)
rp = rrlookup(dp, type, NOneg);
else
rp = dblookup1(name, type, auth, ttl);
if(rp)
goto out;
/* walk the domain name trying the wildcard '*' at each position */
for(wild = strchr(name, '.'); wild; wild = strchr(wild+1, '.')){
snprint(buf, sizeof(buf), "*%s", wild);
ndp = dnlookup(buf, class, 1);
if(ndp->rr)
err = 0;
if(cachedb)
rp = rrlookup(ndp, type, NOneg);
else
rp = dblookup1(buf, type, auth, ttl);
if(rp)
break;
}
out:
/* add owner to uncached records */
if(rp){
for(tp = rp; tp; tp = tp->next)
tp->owner = dp;
} else {
/* don't call it non-existent if it's not ours */
if(err == Rname && !inmyarea(name))
err = Rserver;
dp->nonexistent = err;
}
unlock(&dblock);
return rp;
}
/*
* lookup an RR in the network database
*/
static RR*
dblookup1(char *name, int type, int auth, int ttl)
{
Ndbtuple *t, *nt;
RR *rp, *list, **l;
Ndbs s;
char dname[Domlen];
char *attr;
DN *dp;
RR *(*f)(Ndbtuple*, Ndbtuple*);
int found, x;
dp = 0;
switch(type){
case Tptr:
attr = "ptr";
f = ptrrr;
break;
case Ta:
attr = "ip";
f = addrrr;
break;
case Tnull:
attr = "nullrr";
f = nullrr;
break;
case Tns:
attr = "ns";
f = nsrr;
break;
case Tsoa:
attr = "soa";
f = soarr;
break;
case Tmx:
attr = "mx";
f = mxrr;
break;
case Tcname:
attr = "cname";
f = cnamerr;
break;
case Taxfr:
case Tixfr:
return doaxfr(db, name);
default:
return nil;
}
/*
* find a matching entry in the database
*/
free(ndbgetvalue(db, &s, "dom", name, attr, &t));
/*
* hack for local names
*/
if(t == 0 && strchr(name, '.') == 0)
free(ndbgetvalue(db, &s, "sys", name, attr, &t));
if(t == 0)
return nil;
/* search whole entry for default domain name */
strncpy(dname, name, sizeof dname);
for(nt = t; nt; nt = nt->entry)
if(strcmp(nt->attr, "dom") == 0){
nstrcpy(dname, nt->val, sizeof dname);
break;
}
/* ttl is maximum of soa minttl and entry's ttl ala rfc883 */
nt = look(t, s.t, "ttl");
if(nt){
x = atoi(nt->val);
if(x > ttl)
ttl = x;
}
/* default ttl is one day */
if(ttl < 0)
ttl = DEFTTL;
/*
* The database has 2 levels of precedence; line and entry.
* Pairs on the same line bind tighter than pairs in the
* same entry, so we search the line first.
*/
found = 0;
list = 0;
l = &list;
for(nt = s.t;; ){
if(found == 0 && strcmp(nt->attr, "dom") == 0){
nstrcpy(dname, nt->val, sizeof dname);
found = 1;
}
if(cistrcmp(attr, nt->attr) == 0){
rp = (*f)(t, nt);
rp->auth = auth;
rp->db = 1;
if(ttl)
rp->ttl = ttl;
if(dp == 0)
dp = dnlookup(dname, Cin, 1);
rp->owner = dp;
*l = rp;
l = &rp->next;
nt->ptr = 1;
}
nt = nt->line;
if(nt == s.t)
break;
}
/* search whole entry */
for(nt = t; nt; nt = nt->entry)
if(nt->ptr == 0 && cistrcmp(attr, nt->attr) == 0){
rp = (*f)(t, nt);
rp->db = 1;
if(ttl)
rp->ttl = ttl;
rp->auth = auth;
if(dp == 0)
dp = dnlookup(dname, Cin, 1);
rp->owner = dp;
*l = rp;
l = &rp->next;
}
ndbfree(t);
return list;
}
/*
* make various types of resource records from a database entry
*/
static RR*
addrrr(Ndbtuple *entry, Ndbtuple *pair)
{
RR *rp;
uchar addr[IPaddrlen];
USED(entry);
parseip(addr, pair->val);
if(isv4(addr))
rp = rralloc(Ta);
else
rp = rralloc(Taaaa);
rp->ip = dnlookup(pair->val, Cin, 1);
return rp;
}
static RR*
nullrr(Ndbtuple *entry, Ndbtuple *pair)
{
RR *rp;
USED(entry);
rp = rralloc(Tnull);
rp->null->data = (uchar*)estrdup(pair->val);
rp->null->dlen = strlen((char*)rp->null->data);
return rp;
}
/*
* txt rr strings are at most 255 bytes long. one
* can represent longer strings by multiple concatenated
* <= 255 byte ones.
*/
static RR*
txtrr(Ndbtuple *entry, Ndbtuple *pair)
{
RR *rp;
Txt *t, **l;
int i, len, sofar;
USED(entry);
rp = rralloc(Ttxt);
l = &rp->txt;
rp->txt = nil;
len = strlen(pair->val);
sofar = 0;
while(len > sofar){
t = emalloc(sizeof(*t));
t->next = nil;
i = len-sofar;
if(i > 255)
i = 255;
t->p = emalloc(i+1);
memmove(t->p, pair->val+sofar, i);
t->p[i] = 0;
sofar += i;
*l = t;
l = &t->next;
}
return rp;
}
static RR*
cnamerr(Ndbtuple *entry, Ndbtuple *pair)
{
RR *rp;
USED(entry);
rp = rralloc(Tcname);
rp->host = dnlookup(pair->val, Cin, 1);
return rp;
}
static RR*
mxrr(Ndbtuple *entry, Ndbtuple *pair)
{
RR * rp;
rp = rralloc(Tmx);
rp->host = dnlookup(pair->val, Cin, 1);
pair = look(entry, pair, "pref");
if(pair)
rp->pref = atoi(pair->val);
else
rp->pref = 1;
return rp;
}
static RR*
nsrr(Ndbtuple *entry, Ndbtuple *pair)
{
RR *rp;
Ndbtuple *t;
rp = rralloc(Tns);
rp->host = dnlookup(pair->val, Cin, 1);
t = look(entry, pair, "soa");
if(t && t->val[0] == 0)
rp->local = 1;
return rp;
}
static RR*
ptrrr(Ndbtuple *entry, Ndbtuple *pair)
{
RR *rp;
USED(entry);
rp = rralloc(Tns);
rp->ptr = dnlookup(pair->val, Cin, 1);
return rp;
}
static RR*
soarr(Ndbtuple *entry, Ndbtuple *pair)
{
RR *rp;
Ndbtuple *ns, *mb, *t;
char mailbox[Domlen];
Ndb *ndb;
char *p;
rp = rralloc(Tsoa);
rp->soa->serial = 1;
for(ndb = db; ndb; ndb = ndb->next)
if(ndb->mtime > rp->soa->serial)
rp->soa->serial = ndb->mtime;
rp->soa->refresh = Day;
rp->soa->retry = Hour;
rp->soa->expire = Day;
rp->soa->minttl = Day;
t = look(entry, pair, "ttl");
if(t)
rp->soa->minttl = atoi(t->val);
t = look(entry, pair, "refresh");
if(t)
rp->soa->refresh = atoi(t->val);
t = look(entry, pair, "serial");
if(t)
rp->soa->serial = strtoul(t->val, 0, 10);
ns = look(entry, pair, "ns");
if(ns == 0)
ns = look(entry, pair, "dom");
rp->host = dnlookup(ns->val, Cin, 1);
/* accept all of:
* mbox=person
* mbox=person@machine.dom
* mbox=person.machine.dom
*/
mb = look(entry, pair, "mbox");
if(mb == nil)
mb = look(entry, pair, "mb");
if(mb){
if(strchr(mb->val, '.')) {
p = strchr(mb->val, '@');
if(p != nil)
*p = '.';
rp->rmb = dnlookup(mb->val, Cin, 1);
} else {
snprint(mailbox, sizeof(mailbox), "%s.%s",
mb->val, ns->val);
rp->rmb = dnlookup(mailbox, Cin, 1);
}
} else {
snprint(mailbox, sizeof(mailbox), "postmaster.%s",
ns->val);
rp->rmb = dnlookup(mailbox, Cin, 1);
}
/* hang dns slaves off of the soa. this is
* for managing the area.
*/
for(t = entry; t != nil; t = t->entry)
if(strcmp(t->attr, "dnsslave") == 0)
addserver(&rp->soa->slaves, t->val);
return rp;
}
/*
* Look for a pair with the given attribute. look first on the same line,
* then in the whole entry.
*/
static Ndbtuple*
look(Ndbtuple *entry, Ndbtuple *line, char *attr)
{
Ndbtuple *nt;
/* first look on same line (closer binding) */
for(nt = line;;){
if(cistrcmp(attr, nt->attr) == 0)
return nt;
nt = nt->line;
if(nt == line)
break;
}
/* search whole tuple */
for(nt = entry; nt; nt = nt->entry)
if(cistrcmp(attr, nt->attr) == 0)
return nt;
return 0;
}
static RR**
linkrr(RR *rp, DN *dp, RR **l)
{
rp->owner = dp;
rp->auth = 1;
rp->db = 1;
*l = rp;
return &rp->next;
}
/* these are answered specially by the tcp version */
static RR*
doaxfr(Ndb *db, char *name)
{
USED(db);
USED(name);
return 0;
}
/*
* read the all the soa's from the database to determine area's.
* this is only used when we're not caching the database.
*/
static void
dbfile2area(Ndb *db)
{
Ndbtuple *t;
if(debug)
syslog(0, logfile, "rereading %s", db->file);
Bseek(&db->b, 0, 0);
while(t = ndbparse(db)){
ndbfree(t);
}
}
/*
* read the database into the cache
*/
static void
dbpair2cache(DN *dp, Ndbtuple *entry, Ndbtuple *pair)
{
RR *rp;
Ndbtuple *t;
static ulong ord;
rp = 0;
if(cistrcmp(pair->attr, "ip") == 0){
dp->ordinal = ord++;
rp = addrrr(entry, pair);
} else if(cistrcmp(pair->attr, "ns") == 0){
rp = nsrr(entry, pair);
} else if(cistrcmp(pair->attr, "soa") == 0){
rp = soarr(entry, pair);
addarea(dp, rp, pair);
} else if(cistrcmp(pair->attr, "mx") == 0){
rp = mxrr(entry, pair);
} else if(cistrcmp(pair->attr, "cname") == 0){
rp = cnamerr(entry, pair);
} else if(cistrcmp(pair->attr, "nullrr") == 0){
rp = nullrr(entry, pair);
} else if(cistrcmp(pair->attr, "txtrr") == 0){
rp = txtrr(entry, pair);
}
if(rp == 0)
return;
rp->owner = dp;
rp->db = 1;
t = look(entry, pair, "ttl");
if(t)
rp->ttl = atoi(t->val);
rrattach(rp, 0);
}
static void
dbtuple2cache(Ndbtuple *t)
{
Ndbtuple *et, *nt;
DN *dp;
for(et = t; et; et = et->entry){
if(strcmp(et->attr, "dom") == 0){
dp = dnlookup(et->val, Cin, 1);
/* first same line */
for(nt = et->line; nt != et; nt = nt->line){
dbpair2cache(dp, t, nt);
nt->ptr = 1;
}
/* then rest of entry */
for(nt = t; nt; nt = nt->entry){
if(nt->ptr == 0)
dbpair2cache(dp, t, nt);
nt->ptr = 0;
}
}
}
}
static void
dbfile2cache(Ndb *db)
{
Ndbtuple *t;
if(debug)
syslog(0, logfile, "rereading %s", db->file);
Bseek(&db->b, 0, 0);
while(t = ndbparse(db)){
dbtuple2cache(t);
ndbfree(t);
}
}
void
db2cache(int doit)
{
Ndb *ndb;
Dir *d;
ulong youngest, temp;
static ulong lastcheck;
static ulong lastyoungest;
/* no faster than once every 2 minutes */
if(now < lastcheck + 2*Min && !doit)
return;
refresh_areas(owned);
lock(&dblock);
if(opendatabase() < 0){
unlock(&dblock);
return;
}
/*
* file may be changing as we are reading it, so loop till
* mod times are consistent.
*
* we don't use the times in the ndb records because they may
* change outside of refreshing our cached knowledge.
*/
for(;;){
lastcheck = now;
youngest = 0;
for(ndb = db; ndb; ndb = ndb->next){
/* the dirfstat avoids walking the mount table each time */
if((d = dirfstat(Bfildes(&ndb->b))) != nil ||
(d = dirstat(ndb->file)) != nil){
temp = d->mtime; /* ulong vs int crap */
if(temp > youngest)
youngest = temp;
free(d);
}
}
if(!doit && youngest == lastyoungest){
unlock(&dblock);
return;
}
/* forget our area definition */
freearea(&owned);
freearea(&delegated);
/* reopen all the files (to get oldest for time stamp) */
for(ndb = db; ndb; ndb = ndb->next)
ndbreopen(ndb);
if(cachedb){
/* mark all db records as timed out */
dnagedb();
/* read in new entries */
for(ndb = db; ndb; ndb = ndb->next)
dbfile2cache(ndb);
/* mark as authentic anything in our domain */
dnauthdb();
/* remove old entries */
dnageall(1);
} else {
/* read all the soa's to get database defaults */
for(ndb = db; ndb; ndb = ndb->next)
dbfile2area(ndb);
}
doit = 0;
lastyoungest = youngest;
createptrs();
}
unlock(&dblock);
}
extern uchar ipaddr[IPaddrlen];
/*
* get all my xxx
*/
Ndbtuple*
lookupinfo(char *attr)
{
char buf[64];
char *a[2];
static Ndbtuple *t;
snprint(buf, sizeof buf, "%I", ipaddr);
a[0] = attr;
lock(&dblock);
if(opendatabase() < 0){
unlock(&dblock);
return nil;
}
t = ndbipinfo(db, "ip", buf, a, 1);
unlock(&dblock);
return t;
}
char *localservers = "local#dns#servers";
char *localserverprefix = "local#dns#server";
/*
* return non-zero is this is a bad delegation
*/
int
baddelegation(RR *rp, RR *nsrp, uchar *addr)
{
Ndbtuple *nt;
static Ndbtuple *t;
if(t == nil)
t = lookupinfo("dom");
if(t == nil)
return 0;
for(; rp; rp = rp->next){
if(rp->type != Tns)
continue;
/* see if delegation is looping */
if(nsrp)
if(rp->owner != nsrp->owner)
if(subsume(rp->owner->name, nsrp->owner->name) &&
strcmp(nsrp->owner->name, localservers) != 0){
syslog(0, logfile, "delegation loop %R -> %R from %I", nsrp, rp, addr);
return 1;
}
/* see if delegating to us what we don't own */
for(nt = t; nt != nil; nt = nt->entry)
if(rp->host && cistrcmp(rp->host->name, nt->val) == 0)
break;
if(nt != nil && !inmyarea(rp->owner->name)){
syslog(0, logfile, "bad delegation %R from %I", rp, addr);
return 1;
}
}
return 0;
}
static void
addlocaldnsserver(DN *dp, int class, char *ipaddr, int i)
{
DN *nsdp;
RR *rp;
char buf[32];
/* ns record for name server, make up an impossible name */
rp = rralloc(Tns);
snprint(buf, sizeof(buf), "%s%d", localserverprefix, i);
nsdp = dnlookup(buf, class, 1);
rp->host = nsdp;
rp->owner = dp;
rp->local = 1;
rp->db = 1;
rp->ttl = 10*Min;
rrattach(rp, 1);
print("dns %s\n", ipaddr);
/* A record */
rp = rralloc(Ta);
rp->ip = dnlookup(ipaddr, class, 1);
rp->owner = nsdp;
rp->local = 1;
rp->db = 1;
rp->ttl = 10*Min;
rrattach(rp, 1);
}
/*
* return list of dns server addresses to use when
* acting just as a resolver.
*/
RR*
dnsservers(int class)
{
Ndbtuple *t, *nt;
RR *nsrp;
DN *dp;
char *p;
int i, n;
char *buf, *args[5];
dp = dnlookup(localservers, class, 1);
nsrp = rrlookup(dp, Tns, NOneg);
if(nsrp != nil)
return nsrp;
p = getenv("DNSSERVER");
if(p != nil){
buf = estrdup(p);
n = tokenize(buf, args, nelem(args));
for(i = 0; i < n; i++)
addlocaldnsserver(dp, class, args[i], i);
free(buf);
} else {
t = lookupinfo("@dns");
if(t == nil)
return nil;
i = 0;
for(nt = t; nt != nil; nt = nt->entry){
addlocaldnsserver(dp, class, nt->val, i);
i++;
}
ndbfree(t);
}
return rrlookup(dp, Tns, NOneg);
}
static void
addlocaldnsdomain(DN *dp, int class, char *domain)
{
RR *rp;
/* A record */
rp = rralloc(Tptr);
rp->ptr = dnlookup(domain, class, 1);
rp->owner = dp;
rp->db = 1;
rp->ttl = 10*Min;
rrattach(rp, 1);
}
/*
* return list of domains to use when resolving names without '.'s
*/
RR*
domainlist(int class)
{
Ndbtuple *t, *nt;
RR *rp;
DN *dp;
dp = dnlookup("local#dns#domains", class, 1);
rp = rrlookup(dp, Tptr, NOneg);
if(rp != nil)
return rp;
t = lookupinfo("dnsdomain");
if(t == nil)
return nil;
for(nt = t; nt != nil; nt = nt->entry)
addlocaldnsdomain(dp, class, nt->val);
ndbfree(t);
return rrlookup(dp, Tptr, NOneg);
}
char *v4ptrdom = ".in-addr.arpa";
char *v6ptrdom = ".ip6.arpa"; /* ip6.int deprecated, rfc 3152 */
char *attribs[] = {
"ipmask",
0
};
/*
* create ptrs that are in our areas
*/
static void
createptrs(void)
{
int len, dlen, n;
Area *s;
char *f[40];
char buf[Domlen+1];
uchar net[IPaddrlen];
uchar mask[IPaddrlen];
char ipa[48];
Ndbtuple *t, *nt;
dlen = strlen(v4ptrdom);
for(s = owned; s; s = s->next){
len = strlen(s->soarr->owner->name);
if(len <= dlen)
continue;
if(cistrcmp(s->soarr->owner->name+len-dlen, v4ptrdom) != 0)
continue;
/* get mask and net value */
strncpy(buf, s->soarr->owner->name, sizeof(buf));
buf[sizeof(buf)-1] = 0;
n = getfields(buf, f, nelem(f), 0, ".");
memset(mask, 0xff, IPaddrlen);
ipmove(net, v4prefix);
switch(n){
case 3: /* /8 */
net[IPv4off] = atoi(f[0]);
mask[IPv4off+1] = 0;
mask[IPv4off+2] = 0;
mask[IPv4off+3] = 0;
break;
case 4: /* /16 */
net[IPv4off] = atoi(f[1]);
net[IPv4off+1] = atoi(f[0]);
mask[IPv4off+2] = 0;
mask[IPv4off+3] = 0;
break;
case 5: /* /24 */
net[IPv4off] = atoi(f[2]);
net[IPv4off+1] = atoi(f[1]);
net[IPv4off+2] = atoi(f[0]);
mask[IPv4off+3] = 0;
break;
case 6: /* rfc2317 */
net[IPv4off] = atoi(f[3]);
net[IPv4off+1] = atoi(f[2]);
net[IPv4off+2] = atoi(f[1]);
net[IPv4off+3] = atoi(f[0]);
sprint(ipa, "%I", net);
t = ndbipinfo(db, "ip", ipa, attribs, 1);
if(t == nil) /* could be a reverse with no forward */
continue;
nt = look(t, t, "ipmask");
if(nt == nil){ /* we're confused */
ndbfree(t);
continue;
}
parseipmask(mask, nt->val);
n = 5;
break;
default:
continue;
}
/* go through all domain entries looking for RR's in this network and create ptrs */
dnptr(net, mask, s->soarr->owner->name, 6-n, 0);
}
}

1563
src/cmd/ndb/dn.c Executable file

File diff suppressed because it is too large Load diff

130
src/cmd/ndb/dnarea.c Executable file
View file

@ -0,0 +1,130 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <ndb.h>
#include <ip.h>
#include "dns.h"
Area *owned;
Area *delegated;
/*
* true if a name is in our area
*/
Area*
inmyarea(char *name)
{
int len;
Area *s, *d;
len = strlen(name);
for(s = owned; s; s = s->next){
if(s->len > len)
continue;
if(cistrcmp(s->soarr->owner->name, name + len - s->len) == 0)
if(len == s->len || name[len - s->len - 1] == '.')
break;
}
if(s == 0)
return 0;
for(d = delegated; d; d = d->next){
if(d->len > len)
continue;
if(cistrcmp(d->soarr->owner->name, name + len - d->len) == 0)
if(len == d->len || name[len - d->len - 1] == '.')
return 0;
}
return s;
}
/*
* our area is the part of the domain tree that
* we serve
*/
void
addarea(DN *dp, RR *rp, Ndbtuple *t)
{
Area **l, *s;
if(t->val[0])
l = &delegated;
else
l = &owned;
/*
* The area contains a copy of the soa rr that created it.
* The owner of the the soa rr should stick around as long
* as the area does.
*/
s = emalloc(sizeof(*s));
s->len = strlen(dp->name);
rrcopy(rp, &s->soarr);
s->soarr->owner = dp;
s->soarr->db = 1;
s->soarr->ttl = Hour;
s->neednotify = 1;
s->needrefresh = 0;
syslog(0, logfile, "new area %s", dp->name);
s->next = *l;
*l = s;
}
void
freearea(Area **l)
{
Area *s;
while(s = *l){
*l = s->next;
rrfree(s->soarr);
free(s);
}
}
/*
* refresh all areas that need it
* this entails running a command 'zonerefreshprogram'. This could
* copy over databases from elsewhere or just do a zone transfer.
*/
void
refresh_areas(Area *s)
{
int pid;
Waitmsg *w;
for(; s != nil; s = s->next){
if(!s->needrefresh)
continue;
if(zonerefreshprogram == nil){
s->needrefresh = 0;
continue;
}
switch(pid = fork()){
case -1:
break;
case 0:
execl(zonerefreshprogram, "zonerefresh", s->soarr->owner->name, 0);
exits(0);
break;
default:
for(;;){
w = wait();
if(w == nil)
break;
if(w->pid == pid){
if(w->msg == nil || *w->msg == 0)
s->needrefresh = 0;
free(w);
break;
}
free(w);
}
}
}
}

160
src/cmd/ndb/dnnotify.c Executable file
View file

@ -0,0 +1,160 @@
#include <u.h>
#include <libc.h>
#include <ip.h>
#include <bio.h>
#include <ndb.h>
#include "dns.h"
/* get a notification from another system of a changed zone */
void
dnnotify(DNSmsg *reqp, DNSmsg *repp, Request *r)
{
RR *tp;
Area *a;
USED(r);
/* move one question from reqp to repp */
memset(repp, 0, sizeof(*repp));
tp = reqp->qd;
reqp->qd = tp->next;
tp->next = 0;
repp->qd = tp;
repp->id = reqp->id;
repp->flags = Fresp | Onotify | Fauth;
/* anything to do? */
if(zonerefreshprogram == nil)
return;
/* make sure its the right type */
if(repp->qd->type != Tsoa)
return;
syslog(0, logfile, "notification for %s", repp->qd->owner->name);
/* is it something we care about? */
a = inmyarea(repp->qd->owner->name);
if(a == nil)
return;
syslog(0, logfile, "serial old %lud new %lud", a->soarr->soa->serial, repp->qd->soa->serial);
/* do nothing if it didn't change */
if(a->soarr->soa->serial== repp->qd->soa->serial)
return;
a->needrefresh = 1;
}
static void
ding(void *u, char *msg)
{
USED(u);
if(strstr(msg, "alarm"))
noted(NCONT);
else
noted(NDFLT);
}
/* notify a slave that an area has changed. */
static void
send_notify(char *slave, RR *soa, Request *req)
{
int i, len, n, reqno, status, fd;
uchar obuf[Maxudp+OUdphdrsize];
uchar ibuf[Maxudp+OUdphdrsize];
RR *rp;
OUdphdr *up = (OUdphdr*)obuf;
char *err;
DNSmsg repmsg;
/* create the request */
reqno = rand();
n = mkreq(soa->owner, Cin, obuf, Fauth | Onotify, reqno);
/* get an address */
if(strcmp(ipattr(slave), "ip") == 0) {
parseip(up->raddr, slave);
} else {
rp = dnresolve(slave, Cin, Ta, req, nil, 0, 1, 1, &status);
if(rp == nil)
return;
parseip(up->raddr, rp->ip->name);
rrfree(rp);
}
fd = udpport();
if(fd < 0)
return;
notify(ding);
/* send 3 times or until we get anything back */
for(i = 0; i < 3; i++){
syslog(0, logfile, "sending %d byte notify to %s/%I.%d about %s", n, slave, up->raddr, nhgets(up->rport), soa->owner->name);
if(udpwrite(fd, (OUdphdr*)obuf, obuf+OUdphdrsize, n) != n)
break;
alarm(2*1000);
len = udpread(fd, (Udphdr*)ibuf, ibuf+OUdphdrsize, Maxudp);
alarm(0);
if(len <= OUdphdrsize)
continue;
err = convM2DNS(&ibuf[OUdphdrsize], len, &repmsg);
if(err != nil)
continue;
if(repmsg.id == reqno && (repmsg.flags & Omask) == Onotify)
break;
}
close(fd);
}
/* send notifies for any updated areas */
static void
notify_areas(Area *a, Request *req)
{
Server *s;
for(; a != nil; a = a->next){
if(!a->neednotify)
continue;
/* send notifies to all slaves */
for(s = a->soarr->soa->slaves; s != nil; s = s->next)
send_notify(s->name, a->soarr, req);
a->neednotify = 0;
}
}
/*
* process to notify other servers of changes
* (also reads in new databases)
*/
void
notifyproc(void)
{
Request req;
static int already;
if(already)
return;
switch(rfork(RFPROC|RFNOTEG|RFMEM|RFNOWAIT)){
case -1:
return;
case 0:
break;
default:
return;
}
req.isslave = 1; /* son't fork off subprocesses */
for(;;){
getactivity(&req);
notify_areas(owned, &req);
putactivity();
sleep(60*1000);
}
}

753
src/cmd/ndb/dnresolve.c Executable file
View file

@ -0,0 +1,753 @@
#include <u.h>
#include <libc.h>
#include <ip.h>
#include <bio.h>
#include <ndb.h>
#include "dns.h"
enum
{
Maxdest= 24, /* maximum destinations for a request message */
Maxtrans= 3, /* maximum transmissions to a server */
};
static int netquery(DN*, int, RR*, Request*, int);
static RR* dnresolve1(char*, int, int, Request*, int, int);
char *LOG = "dns";
/*
* lookup 'type' info for domain name 'name'. If it doesn't exist, try
* looking it up as a canonical name.
*/
RR*
dnresolve(char *name, int class, int type, Request *req, RR **cn, int depth, int recurse, int rooted, int *status)
{
RR *rp, *nrp, *drp;
DN *dp;
int loops;
char nname[Domlen];
if(status)
*status = 0;
/*
* hack for systems that don't have resolve search
* lists. Just look up the simple name in the database.
*/
if(!rooted && strchr(name, '.') == 0){
rp = nil;
drp = domainlist(class);
for(nrp = drp; nrp != nil; nrp = nrp->next){
snprint(nname, sizeof(nname), "%s.%s", name, nrp->ptr->name);
rp = dnresolve(nname, class, type, req,cn, depth, recurse, rooted, status);
rrfreelist(rrremneg(&rp));
if(rp != nil)
break;
}
if(drp != nil)
rrfree(drp);
return rp;
}
/*
* try the name directly
*/
rp = dnresolve1(name, class, type, req, depth, recurse);
if(rp)
return randomize(rp);
/* try it as a canonical name if we weren't told the name didn't exist */
dp = dnlookup(name, class, 0);
if(type != Tptr && dp->nonexistent != Rname){
for(loops=0; rp == nil && loops < 32; loops++){
rp = dnresolve1(name, class, Tcname, req, depth, recurse);
if(rp == nil)
break;
if(rp->negative){
rrfreelist(rp);
rp = nil;
break;
}
name = rp->host->name;
if(cn)
rrcat(cn, rp);
else
rrfreelist(rp);
rp = dnresolve1(name, class, type, req, depth, recurse);
}
}
/* distinction between not found and not good */
if(rp == 0 && status != 0 && dp->nonexistent != 0)
*status = dp->nonexistent;
return randomize(rp);
}
static RR*
dnresolve1(char *name, int class, int type, Request *req, int depth, int recurse)
{
DN *dp, *nsdp;
RR *rp, *nsrp, *dbnsrp;
char *cp;
if(debug)
syslog(0, LOG, "dnresolve1 %s %d %d", name, type, class);
/* only class Cin implemented so far */
if(class != Cin)
return 0;
dp = dnlookup(name, class, 1);
/*
* Try the cache first
*/
rp = rrlookup(dp, type, OKneg);
if(rp){
if(rp->db){
/* unauthenticated db entries are hints */
if(rp->auth)
return rp;
} else {
/* cached entry must still be valid */
if(rp->ttl > now){
/* but Tall entries are special */
if(type != Tall || rp->query == Tall)
return rp;
}
}
}
rrfreelist(rp);
/*
* try the cache for a canonical name. if found punt
* since we'll find it during the canonical name search
* in dnresolve().
*/
if(type != Tcname){
rp = rrlookup(dp, Tcname, NOneg);
rrfreelist(rp);
if(rp)
return 0;
}
/*
* if we're running as just a resolver, go to our
* designated name servers
*/
if(resolver){
nsrp = randomize(getdnsservers(class));
if(nsrp != nil) {
if(netquery(dp, type, nsrp, req, depth+1)){
rrfreelist(nsrp);
return rrlookup(dp, type, OKneg);
}
rrfreelist(nsrp);
}
}
/*
* walk up the domain name looking for
* a name server for the domain.
*/
for(cp = name; cp; cp = walkup(cp)){
/*
* if this is a local (served by us) domain,
* return answer
*/
dbnsrp = randomize(dblookup(cp, class, Tns, 0, 0));
if(dbnsrp && dbnsrp->local){
rp = dblookup(name, class, type, 1, dbnsrp->ttl);
rrfreelist(dbnsrp);
return rp;
}
/*
* if recursion isn't set, just accept local
* entries
*/
if(recurse == Dontrecurse){
if(dbnsrp)
rrfreelist(dbnsrp);
continue;
}
/* look for ns in cache */
nsdp = dnlookup(cp, class, 0);
nsrp = nil;
if(nsdp)
nsrp = randomize(rrlookup(nsdp, Tns, NOneg));
/* if the entry timed out, ignore it */
if(nsrp && nsrp->ttl < now){
rrfreelist(nsrp);
nsrp = nil;
}
if(nsrp){
rrfreelist(dbnsrp);
/* try the name servers found in cache */
if(netquery(dp, type, nsrp, req, depth+1)){
rrfreelist(nsrp);
return rrlookup(dp, type, OKneg);
}
rrfreelist(nsrp);
continue;
}
/* use ns from db */
if(dbnsrp){
/* try the name servers found in db */
if(netquery(dp, type, dbnsrp, req, depth+1)){
/* we got an answer */
rrfreelist(dbnsrp);
return rrlookup(dp, type, NOneg);
}
rrfreelist(dbnsrp);
}
}
/* settle for a non-authoritative answer */
rp = rrlookup(dp, type, OKneg);
if(rp)
return rp;
/* noone answered. try the database, we might have a chance. */
return dblookup(name, class, type, 0, 0);
}
/*
* walk a domain name one element to the right. return a pointer to that element.
* in other words, return a pointer to the parent domain name.
*/
char*
walkup(char *name)
{
char *cp;
cp = strchr(name, '.');
if(cp)
return cp+1;
else if(*name)
return "";
else
return 0;
}
/*
* Get a udpport for requests and replies.
*/
int
udpport(void)
{
int fd;
if((fd = dial("udp!0!53", nil, nil, nil)) < 0)
warning("can't get udp port");
return fd;
}
int
mkreq(DN *dp, int type, uchar *buf, int flags, ushort reqno)
{
DNSmsg m;
int len;
OUdphdr *uh = (OUdphdr*)buf;
/* stuff port number into output buffer */
memset(uh, 0, sizeof(*uh));
hnputs(uh->rport, 53);
/* make request and convert it to output format */
memset(&m, 0, sizeof(m));
m.flags = flags;
m.id = reqno;
m.qd = rralloc(type);
m.qd->owner = dp;
m.qd->type = type;
len = convDNS2M(&m, &buf[OUdphdrsize], Maxudp);
if(len < 0)
abort(); /* "can't convert" */;
rrfree(m.qd);
return len;
}
/* for alarms in readreply */
static void
ding(void *x, char *msg)
{
USED(x);
if(strcmp(msg, "alarm") == 0)
noted(NCONT);
else
noted(NDFLT);
}
static void
freeanswers(DNSmsg *mp)
{
rrfreelist(mp->qd);
rrfreelist(mp->an);
rrfreelist(mp->ns);
rrfreelist(mp->ar);
}
/*
* read replies to a request. ignore any of the wrong type. wait at most 5 seconds.
*/
static int
readreply(int fd, DN *dp, int type, ushort req,
uchar *ibuf, DNSmsg *mp, ulong endtime, Request *reqp)
{
char *err;
int len;
ulong now;
RR *rp;
notify(ding);
for(; ; freeanswers(mp)){
now = time(0);
if(now >= endtime)
return -1; /* timed out */
/* timed read */
alarm((endtime - now) * 1000);
len = udpread(fd, (OUdphdr*)ibuf, ibuf+OUdphdrsize, Maxudpin);
alarm(0);
if(len < 0)
return -1; /* timed out */
/* convert into internal format */
memset(mp, 0, sizeof(*mp));
err = convM2DNS(&ibuf[OUdphdrsize], len, mp);
if(err){
syslog(0, LOG, "input err %s: %I", err, ibuf);
continue;
}
if(debug)
logreply(reqp->id, ibuf, mp);
/* answering the right question? */
if(mp->id != req){
syslog(0, LOG, "%d: id %d instead of %d: %I", reqp->id,
mp->id, req, ibuf);
continue;
}
if(mp->qd == 0){
syslog(0, LOG, "%d: no question RR: %I", reqp->id, ibuf);
continue;
}
if(mp->qd->owner != dp){
syslog(0, LOG, "%d: owner %s instead of %s: %I", reqp->id,
mp->qd->owner->name, dp->name, ibuf);
continue;
}
if(mp->qd->type != type){
syslog(0, LOG, "%d: type %d instead of %d: %I", reqp->id,
mp->qd->type, type, ibuf);
continue;
}
/* remember what request this is in answer to */
for(rp = mp->an; rp; rp = rp->next)
rp->query = type;
return 0;
}
return 0; /* never reached */
}
/*
* return non-0 if first list includes second list
*/
int
contains(RR *rp1, RR *rp2)
{
RR *trp1, *trp2;
for(trp2 = rp2; trp2; trp2 = trp2->next){
for(trp1 = rp1; trp1; trp1 = trp1->next){
if(trp1->type == trp2->type)
if(trp1->host == trp2->host)
if(trp1->owner == trp2->owner)
break;
}
if(trp1 == 0)
return 0;
}
return 1;
}
typedef struct Dest Dest;
struct Dest
{
uchar a[IPaddrlen]; /* ip address */
DN *s; /* name server */
int nx; /* number of transmissions */
int code;
};
/*
* return multicast version if any
*/
int
ipisbm(uchar *ip)
{
if(isv4(ip)){
if(ip[IPv4off] >= 0xe0 && ip[IPv4off] < 0xf0)
return 4;
if(ipcmp(ip, IPv4bcast) == 0)
return 4;
} else {
if(ip[0] == 0xff)
return 6;
}
return 0;
}
/*
* Get next server address
*/
static int
serveraddrs(RR *nsrp, Dest *dest, int nd, int depth, Request *reqp)
{
RR *rp, *arp, *trp;
Dest *cur;
if(nd >= Maxdest)
return 0;
/*
* look for a server whose address we already know.
* if we find one, mark it so we ignore this on
* subsequent passes.
*/
arp = 0;
for(rp = nsrp; rp; rp = rp->next){
assert(rp->magic == RRmagic);
if(rp->marker)
continue;
arp = rrlookup(rp->host, Ta, NOneg);
if(arp){
rp->marker = 1;
break;
}
arp = dblookup(rp->host->name, Cin, Ta, 0, 0);
if(arp){
rp->marker = 1;
break;
}
}
/*
* if the cache and database lookup didn't find any new
* server addresses, try resolving one via the network.
* Mark any we try to resolve so we don't try a second time.
*/
if(arp == 0){
for(rp = nsrp; rp; rp = rp->next){
if(rp->marker)
continue;
rp->marker = 1;
/*
* avoid loops looking up a server under itself
*/
if(subsume(rp->owner->name, rp->host->name))
continue;
arp = dnresolve(rp->host->name, Cin, Ta, reqp, 0, depth+1, Recurse, 1, 0);
rrfreelist(rrremneg(&arp));
if(arp)
break;
}
}
/* use any addresses that we found */
for(trp = arp; trp; trp = trp->next){
if(nd >= Maxdest)
break;
cur = &dest[nd];
parseip(cur->a, trp->ip->name);
if(ipisbm(cur->a))
continue;
cur->nx = 0;
cur->s = trp->owner;
cur->code = Rtimeout;
nd++;
}
rrfreelist(arp);
return nd;
}
/*
* cache negative responses
*/
static void
cacheneg(DN *dp, int type, int rcode, RR *soarr)
{
RR *rp;
DN *soaowner;
ulong ttl;
/* no cache time specified, don' make anything up */
if(soarr != nil){
if(soarr->next != nil){
rrfreelist(soarr->next);
soarr->next = nil;
}
soaowner = soarr->owner;
} else
soaowner = nil;
/* the attach can cause soarr to be freed so mine it now */
if(soarr != nil && soarr->soa != nil)
ttl = soarr->soa->minttl+now;
else
ttl = 5*Min;
/* add soa and negative RR to the database */
rrattach(soarr, 1);
rp = rralloc(type);
rp->owner = dp;
rp->negative = 1;
rp->negsoaowner = soaowner;
rp->negrcode = rcode;
rp->ttl = ttl;
rrattach(rp, 1);
}
/*
* query name servers. If the name server returns a pointer to another
* name server, recurse.
*/
static int
netquery1(int fd, DN *dp, int type, RR *nsrp, Request *reqp, int depth, uchar *ibuf, uchar *obuf)
{
int ndest, j, len, replywaits, rv;
ushort req;
RR *tp, *soarr;
Dest *p, *l, *np;
DN *ndp;
Dest dest[Maxdest];
DNSmsg m;
ulong endtime;
/* pack request into a message */
req = rand();
len = mkreq(dp, type, obuf, Frecurse|Oquery, req);
/* no server addresses yet */
l = dest;
/*
* transmit requests and wait for answers.
* at most Maxtrans attempts to each address.
* each cycle send one more message than the previous.
*/
for(ndest = 1; ndest < Maxdest; ndest++){
p = dest;
endtime = time(0);
if(endtime >= reqp->aborttime)
break;
/* get a server address if we need one */
if(ndest > l - p){
j = serveraddrs(nsrp, dest, l - p, depth, reqp);
l = &dest[j];
}
/* no servers, punt */
if(l == dest)
break;
/* send to first 'ndest' destinations */
j = 0;
for(; p < &dest[ndest] && p < l; p++){
/* skip destinations we've finished with */
if(p->nx >= Maxtrans)
continue;
j++;
/* exponential backoff of requests */
if((1<<p->nx) > ndest)
continue;
memmove(obuf, p->a, sizeof(p->a));
if(debug)
logsend(reqp->id, depth, obuf, p->s->name,
dp->name, type);
{Udphdr *uh = (Udphdr*)obuf;
print("send %I %I %d %d\n", uh->raddr, uh->laddr, nhgets(uh->rport), nhgets(uh->lport));
}
if(udpwrite(fd, (OUdphdr*)obuf, obuf+OUdphdrsize, len) < 0)
warning("sending udp msg %r");
p->nx++;
}
if(j == 0)
break; /* no destinations left */
/* wait up to 5 seconds for replies */
endtime = time(0) + 5;
if(endtime > reqp->aborttime)
endtime = reqp->aborttime;
for(replywaits = 0; replywaits < ndest; replywaits++){
if(readreply(fd, dp, type, req, ibuf, &m, endtime, reqp) < 0)
break; /* timed out */
/* find responder */
for(p = dest; p < l; p++)
if(memcmp(p->a, ibuf, sizeof(p->a)) == 0)
break;
/* remove all addrs of responding server from list */
for(np = dest; np < l; np++)
if(np->s == p->s)
p->nx = Maxtrans;
/* ignore any error replies */
if((m.flags & Rmask) == Rserver){
rrfreelist(m.qd);
rrfreelist(m.an);
rrfreelist(m.ar);
rrfreelist(m.ns);
if(p != l)
p->code = Rserver;
continue;
}
/* ignore any bad delegations */
if(m.ns && baddelegation(m.ns, nsrp, ibuf)){
rrfreelist(m.ns);
m.ns = nil;
if(m.an == nil){
rrfreelist(m.qd);
rrfreelist(m.ar);
if(p != l)
p->code = Rserver;
continue;
}
}
/* remove any soa's from the authority section */
soarr = rrremtype(&m.ns, Tsoa);
/* incorporate answers */
if(m.an)
rrattach(m.an, (m.flags & Fauth) ? 1 : 0);
if(m.ar)
rrattach(m.ar, 0);
if(m.ns){
ndp = m.ns->owner;
rrattach(m.ns, 0);
} else
ndp = 0;
/* free the question */
if(m.qd)
rrfreelist(m.qd);
/*
* Any reply from an authoritative server,
* or a positive reply terminates the search
*/
if(m.an != nil || (m.flags & Fauth)){
if(m.an == nil && (m.flags & Rmask) == Rname)
dp->nonexistent = Rname;
else
dp->nonexistent = 0;
/*
* cache any negative responses, free soarr
*/
if((m.flags & Fauth) && m.an == nil)
cacheneg(dp, type, (m.flags & Rmask), soarr);
else
rrfreelist(soarr);
return 1;
}
rrfreelist(soarr);
/*
* if we've been given better name servers
* recurse
*/
if(m.ns){
tp = rrlookup(ndp, Tns, NOneg);
if(!contains(nsrp, tp)){
rv = netquery(dp, type, tp, reqp, depth+1);
rrfreelist(tp);
return rv;
} else
rrfreelist(tp);
}
}
}
/* if all servers returned failure, propogate it */
dp->nonexistent = Rserver;
for(p = dest; p < l; p++)
if(p->code != Rserver)
dp->nonexistent = 0;
return 0;
}
typedef struct Qarg Qarg;
struct Qarg
{
DN *dp;
int type;
RR *nsrp;
Request *reqp;
int depth;
};
static int
netquery(DN *dp, int type, RR *nsrp, Request *reqp, int depth)
{
uchar *obuf;
uchar *ibuf;
RR *rp;
int fd, rv;
if(depth > 12)
return 0;
/* use alloced buffers rather than ones from the stack */
ibuf = emalloc(Maxudpin+OUdphdrsize);
obuf = emalloc(Maxudp+OUdphdrsize);
slave(reqp);
/* prepare server RR's for incremental lookup */
for(rp = nsrp; rp; rp = rp->next)
rp->marker = 0;
fd = udpport();
if(fd < 0)
return 0;
rv = netquery1(fd, dp, type, nsrp, reqp, depth, ibuf, obuf);
close(fd);
free(ibuf);
free(obuf);
return rv;
}

882
src/cmd/ndb/dns.c Executable file
View file

@ -0,0 +1,882 @@
#include <u.h>
#include <libc.h>
#include <auth.h>
#include <fcall.h>
#include <bio.h>
#include <ctype.h>
#include <ip.h>
#include <ndb.h>
#include "dns.h"
enum
{
Maxrequest= 1024,
Ncache= 8,
Maxpath= 128,
Maxreply= 512,
Maxrrr= 16,
Maxfdata= 8192,
Qdir= 0,
Qdns= 1,
};
typedef struct Mfile Mfile;
typedef struct Job Job;
typedef struct Network Network;
int vers; /* incremented each clone/attach */
struct Mfile
{
Mfile *next; /* next free mfile */
int ref;
char *user;
Qid qid;
int fid;
int type; /* reply type */
char reply[Maxreply];
ushort rr[Maxrrr]; /* offset of rr's */
ushort nrr; /* number of rr's */
};
//
// active local requests
//
struct Job
{
Job *next;
int flushed;
Fcall request;
Fcall reply;
};
Lock joblock;
Job *joblist;
struct {
Lock lk;
Mfile *inuse; /* active mfile's */
} mfalloc;
int haveip;
int mfd[2];
int debug;
int traceactivity;
int cachedb;
ulong now;
int testing;
char *trace;
int needrefresh;
int resolver;
uchar ipaddr[IPaddrlen]; /* my ip address */
int maxage;
char *zonerefreshprogram;
int sendnotifies;
void rversion(Job*);
void rauth(Job*);
void rflush(Job*);
void rattach(Job*, Mfile*);
char* rwalk(Job*, Mfile*);
void ropen(Job*, Mfile*);
void rcreate(Job*, Mfile*);
void rread(Job*, Mfile*);
void rwrite(Job*, Mfile*, Request*);
void rclunk(Job*, Mfile*);
void rremove(Job*, Mfile*);
void rstat(Job*, Mfile*);
void rwstat(Job*, Mfile*);
void sendmsg(Job*, char*);
void mountinit(char*, char*);
void io(void);
int fillreply(Mfile*, int);
Job* newjob(void);
void freejob(Job*);
void setext(char*, int, char*);
char *logfile = "dns";
char *dbfile;
char mntpt[Maxpath];
char *LOG;
void
usage(void)
{
fprint(2, "usage: %s [-rs] [-f ndb-file] [-x netmtpt]\n", argv0);
exits("usage");
}
void
main(int argc, char *argv[])
{
int serve;
char servefile[Maxpath];
char ext[Maxpath];
char *p;
serve = 0;
// setnetmtpt(mntpt, sizeof(mntpt), nil);
ext[0] = 0;
ARGBEGIN{
case 'd':
debug = 1;
traceactivity = 1;
break;
case 'f':
p = ARGF();
if(p == nil)
usage();
dbfile = p;
break;
case 'i':
haveip = 1;
parseip(ipaddr, EARGF(usage()));
break;
// case 'x':
// p = ARGF();
// if(p == nil)
// usage();
// setnetmtpt(mntpt, sizeof(mntpt), p);
// setext(ext, sizeof(ext), mntpt);
// break;
case 'r':
resolver = 1;
break;
case 's':
serve = 1; /* serve network */
cachedb = 1;
break;
case 'a':
p = ARGF();
if(p == nil)
usage();
maxage = atoi(p);
break;
case 't':
testing = 1;
break;
case 'z':
zonerefreshprogram = ARGF();
break;
case 'n':
sendnotifies = 1;
break;
}ARGEND
USED(argc);
USED(argv);
//if(testing) mainmem->flags |= POOL_NOREUSE;
#define RFREND 0
rfork(RFREND|RFNOTEG);
/* start syslog before we fork */
fmtinstall('F', fcallfmt);
dninit();
if(!haveip && myipaddr(ipaddr, mntpt) < 0)
sysfatal("can't read my ip address");
syslog(0, logfile, "starting dns on %I", ipaddr);
opendatabase();
/*
snprint(servefile, sizeof(servefile), "#s/dns%s", ext);
unmount(servefile, mntpt);
remove(servefile);
*/
mountinit(servefile, mntpt);
now = time(0);
srand(now*getpid());
db2cache(1);
if(serve)
proccreate(dnudpserver, mntpt, STACK);
if(sendnotifies)
notifyproc();
io();
syslog(0, logfile, "io returned, exiting");
exits(0);
}
int
myipaddr(uchar *ip, char *net)
{
ipmove(ip, ipaddr);
return 0;
}
/*
* if a mount point is specified, set the cs extention 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 p[2];
if(pipe(p) < 0)
abort(); /* "pipe failed" */;
switch(rfork(RFFDG|RFPROC|RFNAMEG)){
case 0:
close(p[1]);
break;
case -1:
abort(); /* "fork failed\n" */;
default:
close(p[0]);
if(post9pservice(p[1], "dns") < 0)
fprint(2, "post9pservice dns: %r\n");
_exits(0);
}
mfd[0] = mfd[1] = p[0];
}
Mfile*
newfid(int fid, int needunused)
{
Mfile *mf;
lock(&mfalloc.lk);
for(mf = mfalloc.inuse; mf != nil; mf = mf->next){
if(mf->fid == fid){
unlock(&mfalloc.lk);
if(needunused)
return nil;
return mf;
}
}
mf = emalloc(sizeof(*mf));
if(mf == nil)
sysfatal("out of memory");
mf->fid = fid;
mf->next = mfalloc.inuse;
mfalloc.inuse = mf;
unlock(&mfalloc.lk);
return mf;
}
void
freefid(Mfile *mf)
{
Mfile **l;
lock(&mfalloc.lk);
for(l = &mfalloc.inuse; *l != nil; l = &(*l)->next){
if(*l == mf){
*l = mf->next;
if(mf->user)
free(mf->user);
free(mf);
unlock(&mfalloc.lk);
return;
}
}
sysfatal("freeing unused fid");
}
Mfile*
copyfid(Mfile *mf, int fid)
{
Mfile *nmf;
nmf = newfid(fid, 1);
if(nmf == nil)
return nil;
nmf->fid = fid;
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;
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)
{
long n;
Mfile *mf;
uchar mdata[IOHDRSZ + Maxfdata];
Request req;
Job *job;
/*
* 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();
req.isslave = 0;
for(;;){
n = read9pmsg(mfd[0], mdata, sizeof mdata);
if(n<=0){
syslog(0, logfile, "error reading mntpt: %r");
exits(0);
}
job = newjob();
if(convM2S(mdata, n, &job->request) != n){
freejob(job);
continue;
}
mf = newfid(job->request.fid, 0);
if(debug)
syslog(0, logfile, "%F", &job->request);
getactivity(&req);
req.aborttime = now + 60; /* don't spend more than 60 seconds */
switch(job->request.type){
default:
syslog(1, logfile, "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:
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();
_exits(0);
}
putactivity();
}
}
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)
{
char *err;
char **elems;
int nelems;
int i;
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 */
if(job->request.newfid<0){
err = "clone newfid out of range";
goto send;
}
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, cnt;
long off;
Dir dir;
uchar buf[Maxfdata];
char *err;
long clock;
n = 0;
err = 0;
off = job->request.offset;
cnt = job->request.count;
if(mf->qid.type & QTDIR){
clock = time(0);
if(off == 0){
dir.name = "dns";
dir.qid.type = QTFILE;
dir.qid.vers = vers;
dir.qid.path = Qdns;
dir.mode = 0666;
dir.length = 0;
dir.uid = mf->user;
dir.gid = mf->user;
dir.muid = mf->user;
dir.atime = clock; /* wrong */
dir.mtime = clock; /* wrong */
n = convD2M(&dir, buf, sizeof buf);
}
job->reply.data = (char*)buf;
} else {
for(i = 1; i <= mf->nrr; i++)
if(mf->rr[i] > off)
break;
if(i > mf->nrr)
goto send;
if(off + cnt > mf->rr[i])
n = mf->rr[i] - off;
else
n = cnt;
job->reply.data = mf->reply + off;
}
send:
job->reply.count = n;
sendmsg(job, err);
}
void
rwrite(Job *job, Mfile *mf, Request *req)
{
int cnt, rooted, status;
long n;
char *err, *p, *atype;
RR *rp, *tp, *neg;
int wantsav;
err = 0;
cnt = job->request.count;
if(mf->qid.type & QTDIR){
err = "can't write directory";
goto send;
}
if(cnt >= Maxrequest){
err = "request too long";
goto send;
}
job->request.data[cnt] = 0;
if(cnt > 0 && job->request.data[cnt-1] == '\n')
job->request.data[cnt-1] = 0;
/*
* special commands
*/
if(strncmp(job->request.data, "debug", 5)==0 && job->request.data[5] == 0){
debug ^= 1;
goto send;
} else if(strncmp(job->request.data, "dump", 4)==0 && job->request.data[4] == 0){
dndump("/lib/ndb/dnsdump");
goto send;
} else if(strncmp(job->request.data, "refresh", 7)==0 && job->request.data[7] == 0){
needrefresh = 1;
goto send;
// } else if(strncmp(job->request.data, "poolcheck", 9)==0 && job->request.data[9] == 0){
// poolcheck(mainmem);
// goto send;
}
/*
* 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){
err = "illegal request";
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;
}
mf->type = rrtype(atype);
if(mf->type < 0){
err = "unknown type";
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;
dncheck(0, 1);
rp = dnresolve(p, Cin, mf->type, req, 0, 0, Recurse, rooted, &status);
dncheck(0, 1);
neg = rrremneg(&rp);
if(neg){
status = neg->negrcode;
rrfreelist(neg);
}
if(rp == 0){
switch(status){
case Rname:
err = "name does not exist";
break;
case Rserver:
err = "dns failure";
break;
default:
err = "resource does not exist";
break;
}
} else {
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);
}
send:
dncheck(0, 1);
job->reply.count = cnt;
sendmsg(job, err);
}
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;
uchar buf[IOHDRSZ+Maxfdata];
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 = mf->user;
dir.gid = mf->user;
dir.muid = mf->user;
dir.atime = dir.mtime = time(0);
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;
uchar 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){
syslog(1, logfile, "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)
syslog(0, logfile, "%F %d", &job->reply, n);
}
/*
* the following varies between dnsdebug and dns
*/
void
logreply(int id, uchar *addr, DNSmsg *mp)
{
RR *rp;
syslog(0, LOG, "%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|Rname) == (Fauth|Rname) ?
" nx" : "");
for(rp = mp->qd; rp != nil; rp = rp->next)
syslog(0, LOG, "%d: rcvd %I qd %s", id, addr, rp->owner->name);
for(rp = mp->an; rp != nil; rp = rp->next)
syslog(0, LOG, "%d: rcvd %I an %R", id, addr, rp);
for(rp = mp->ns; rp != nil; rp = rp->next)
syslog(0, LOG, "%d: rcvd %I ns %R", id, addr, rp);
for(rp = mp->ar; rp != nil; rp = rp->next)
syslog(0, LOG, "%d: rcvd %I ar %R", id, addr, rp);
}
void
logsend(int id, int subid, uchar *addr, char *sname, char *rname, int type)
{
char buf[12];
syslog(0, LOG, "%d.%d: sending to %I/%s %s %s",
id, subid, addr, sname, rname, rrname(type, buf, sizeof buf));
}
RR*
getdnsservers(int class)
{
return dnsservers(class);
}

403
src/cmd/ndb/dns.h Executable file
View file

@ -0,0 +1,403 @@
#define OUdphdrsize Udphdrsize
#define OUdphdr Udphdr
enum
{
/* RR types */
Ta= 1,
Tns= 2,
Tmd= 3,
Tmf= 4,
Tcname= 5,
Tsoa= 6,
Tmb= 7,
Tmg= 8,
Tmr= 9,
Tnull= 10,
Twks= 11,
Tptr= 12,
Thinfo= 13,
Tminfo= 14,
Tmx= 15,
Ttxt= 16,
Trp= 17,
Tsig= 24,
Tkey= 25,
Taaaa= 28,
Tcert= 37,
/* query types (all RR types are also queries) */
Tixfr= 251, /* incremental zone transfer */
Taxfr= 252, /* zone transfer */
Tmailb= 253, /* { Tmb, Tmg, Tmr } */
Tall= 255, /* all records */
/* classes */
Csym= 0, /* internal symbols */
Cin= 1, /* internet */
Ccs, /* CSNET (obsolete) */
Cch, /* Chaos net */
Chs, /* Hesiod (?) */
/* class queries (all class types are also queries) */
Call= 255, /* all classes */
/* opcodes */
Oquery= 0<<11, /* normal query */
Oinverse= 1<<11, /* inverse query */
Ostatus= 2<<11, /* status request */
Onotify= 4<<11, /* notify slaves of updates */
Omask= 0xf<<11, /* mask for opcode */
/* response codes */
Rok= 0,
Rformat= 1, /* format error */
Rserver= 2, /* server failure (e.g. no answer from something) */
Rname= 3, /* bad name */
Runimplimented= 4, /* unimplemented */
Rrefused= 5, /* we don't like you */
Rmask= 0xf, /* mask for response */
Rtimeout= 0x10, /* timeout sending (for internal use only) */
/* bits in flag word (other than opcode and response) */
Fresp= 1<<15, /* message is a response */
Fauth= 1<<10, /* true if an authoritative response */
Ftrunc= 1<<9, /* truncated message */
Frecurse= 1<<8, /* request recursion */
Fcanrec= 1<<7, /* server can recurse */
Domlen= 256, /* max domain name length (with NULL) */
Labellen= 256, /* max domain label length (with NULL) */
Strlen= 256, /* max string length (with NULL) */
Iplen= 32, /* max ascii ip address length (with NULL) */
/* time to live values (in seconds) */
Min= 60,
Hour= 60*Min, /* */
Day= 24*Hour, /* Ta, Tmx */
Week= 7*Day, /* Tsoa, Tns */
Year= 52*Week,
DEFTTL= Day,
/* reserved time (can't be timed out earlier) */
Reserved= 5*Min,
/* packet sizes */
Maxudp= 512, /* maximum bytes per udp message */
Maxudpin= 2048, /* maximum bytes per udp message */
/* length of domain name hash table */
HTLEN= 4*1024,
RRmagic= 0xdeadbabe,
DNmagic= 0xa110a110,
/* parallelism */
Maxactive= 32,
};
typedef struct DN DN;
typedef struct DNSmsg DNSmsg;
typedef struct RR RR;
typedef struct SOA SOA;
typedef struct Area Area;
typedef struct Request Request;
typedef struct Key Key;
typedef struct Cert Cert;
typedef struct Sig Sig;
typedef struct Null Null;
typedef struct Server Server;
typedef struct Txt Txt;
/*
* a structure to track a request and any slave process handling it
*/
struct Request
{
int isslave; /* pid of slave */
ulong aborttime; /* time at which we give up */
jmp_buf mret; /* where master jumps to after starting a slave */
int id;
};
/*
* a domain name
*/
struct DN
{
DN *next; /* hash collision list */
ulong magic;
char *name; /* owner */
RR *rr; /* resource records off this name */
ulong referenced; /* time last referenced */
ulong lookuptime; /* last time we tried to get a better value */
ushort class; /* RR class */
char refs; /* for mark and sweep */
char nonexistent; /* true if we get an authoritative nx for this domain */
ulong ordinal;
};
/*
* security info
*/
struct Key
{
int flags;
int proto;
int alg;
int dlen;
uchar *data;
};
struct Cert
{
int type;
int tag;
int alg;
int dlen;
uchar *data;
};
struct Sig
{
int type;
int alg;
int labels;
ulong ttl;
ulong exp;
ulong incep;
int tag;
DN *signer;
int dlen;
uchar *data;
};
struct Null
{
int dlen;
uchar *data;
};
/*
* text strings
*/
struct Txt
{
Txt *next;
char *p;
};
/*
* an unpacked resource record
*/
struct RR
{
RR *next;
ulong magic;
DN *owner; /* domain that owns this resource record */
uchar negative; /* this is a cached negative response */
ulong pc;
ulong ttl; /* time to live to be passed on */
ulong expire; /* time this entry expires locally */
ushort type; /* RR type */
ushort query; /* query tyis is in response to */
uchar auth; /* authoritative */
uchar db; /* from database */
uchar cached; /* rr in cache */
ulong marker; /* used locally when scanning rrlists */
union {
DN *negsoaowner; /* soa for cached negative response */
DN *host; /* hostname - soa, cname, mb, md, mf, mx, ns */
DN *cpu; /* cpu type - hinfo */
DN *mb; /* mailbox - mg, minfo */
DN *ip; /* ip addrss - a */
DN *rp; /* rp arg - rp */
int cruftlen;
ulong arg0;
};
union {
int negrcode; /* response code for cached negative response */
DN *rmb; /* responsible maibox - minfo, soa, rp */
DN *ptr; /* pointer to domain name - ptr */
DN *os; /* operating system - hinfo */
ulong pref; /* preference value - mx */
ulong local; /* ns served from local database - ns */
ulong arg1;
};
union {
SOA *soa; /* soa timers - soa */
Key *key;
Cert *cert;
Sig *sig;
Null *null;
Txt *txt;
};
};
/*
* list of servers
*/
struct Server
{
Server *next;
char *name;
};
/*
* timers for a start of authenticated record
*/
struct SOA
{
ulong serial; /* zone serial # (sec) - soa */
ulong refresh; /* zone refresh interval (sec) - soa */
ulong retry; /* zone retry interval (sec) - soa */
ulong expire; /* time to expiration (sec) - soa */
ulong minttl; /* minimum time to live for any entry (sec) - soa */
Server *slaves; /* slave servers */
};
/*
* domain messages
*/
struct DNSmsg
{
ushort id;
int flags;
int qdcount; /* questions */
RR *qd;
int ancount; /* answers */
RR *an;
int nscount; /* name servers */
RR *ns;
int arcount; /* hints */
RR *ar;
};
/*
* definition of local area for dblookup
*/
struct Area
{
Area *next;
int len; /* strlen(area->soarr->owner->name) */
RR *soarr; /* soa defining this area */
int neednotify;
int needrefresh;
};
enum
{
Recurse,
Dontrecurse,
NOneg,
OKneg,
};
/* dn.c */
extern char *rrtname[];
extern char *rname[];
extern char *opname[];
extern void db2cache(int);
extern void dninit(void);
extern DN* dnlookup(char*, int, int);
extern void dnage(DN*);
extern void dnageall(int);
extern void dnagedb(void);
extern void dnauthdb(void);
extern void dnget(void);
extern void dnpurge(void);
extern void dnput(void);
extern Area* inmyarea(char*);
extern void rrattach(RR*, int);
extern RR* rralloc(int);
extern void rrfree(RR*);
extern void rrfreelist(RR*);
extern RR* rrlookup(DN*, int, int);
extern RR* rrcat(RR**, RR*);
extern RR** rrcopy(RR*, RR**);
extern RR* rrremneg(RR**);
extern RR* rrremtype(RR**, int);
extern int rrfmt(Fmt*);
extern int rravfmt(Fmt*);
extern int rrsupported(int);
extern int rrtype(char*);
extern char* rrname(int, char*, int);
extern int tsame(int, int);
extern void dndump(char*);
extern int getactivity(Request*);
extern void putactivity(void);
extern void abort(); /* char*, ... */;
extern void warning(char*, ...);
extern void slave(Request*);
extern void dncheck(void*, int);
extern void unique(RR*);
extern int subsume(char*, char*);
extern RR* randomize(RR*);
extern void* emalloc(int);
extern char* estrdup(char*);
extern void dnptr(uchar*, uchar*, char*, int, int);
extern void addserver(Server**, char*);
extern Server* copyserverlist(Server*);
extern void freeserverlist(Server*);
/* dnarea.c */
extern void refresh_areas(Area*);
extern void freearea(Area**);
extern void addarea(DN *dp, RR *rp, Ndbtuple *t);
/* dblookup.c */
extern RR* dblookup(char*, int, int, int, int);
extern RR* dbinaddr(DN*, int);
extern int baddelegation(RR*, RR*, uchar*);
extern RR* dnsservers(int);
extern RR* domainlist(int);
extern int opendatabase(void);
/* dns.c */
extern char* walkup(char*);
extern RR* getdnsservers(int);
extern void logreply(int, uchar*, DNSmsg*);
extern void logsend(int, int, uchar*, char*, char*, int);
/* dnresolve.c */
extern RR* dnresolve(char*, int, int, Request*, RR**, int, int, int, int*);
extern int udpport(void);
extern int mkreq(DN *dp, int type, uchar *buf, int flags, ushort reqno);
/* dnserver.c */
extern void dnserver(DNSmsg*, DNSmsg*, Request*);
extern void dnudpserver(void*);
extern void dntcpserver(char*);
/* dnnotify.c */
extern void dnnotify(DNSmsg*, DNSmsg*, Request*);
extern void notifyproc(void);
/* convDNS2M.c */
extern int convDNS2M(DNSmsg*, uchar*, int);
/* convM2DNS.c */
extern char* convM2DNS(uchar*, int, DNSmsg*);
/* malloc.c */
extern void mallocsanity(void*);
extern void lasthist(void*, int, ulong);
extern int debug;
extern int traceactivity;
extern char *trace;
extern int testing; /* test cache whenever removing a DN */
extern int cachedb;
extern int needrefresh;
extern char *dbfile;
extern char mntpt[];
extern char *logfile;
extern int resolver;
extern int maxage; /* age of oldest entry in cache (secs) */
extern char *zonerefreshprogram;
extern int sendnotifies;
extern ulong now; /* time base */
extern Area *owned;
extern Area *delegated;
#pragma varargck type "R" RR*
#pragma varargck type "Q" RR*

473
src/cmd/ndb/dnsdebug.c Executable file
View file

@ -0,0 +1,473 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <ctype.h>
#include <ip.h>
#include <ndb.h>
#include "dns.h"
enum
{
Maxrequest= 128,
Ncache= 8,
Maxpath= 128,
Maxreply= 512,
Maxrrr= 16,
};
static char *servername;
static RR *serverrr;
static RR *serveraddrs;
int debug;
int cachedb;
ulong now;
int testing;
int traceactivity;
char *trace;
int needrefresh;
int resolver;
uchar ipaddr[IPaddrlen]; /* my ip address */
int maxage;
char *logfile = "dns";
char *dbfile;
char mntpt[Maxpath];
char *zonerefreshprogram;
int prettyrrfmt(Fmt*);
void preloadserveraddrs(void);
void squirrelserveraddrs(void);
int setserver(char*);
void doquery(char*, char*);
void docmd(int, char**);
void
main(int argc, char *argv[])
{
int n;
Biobuf in;
char *p;
char *f[4];
strcpy(mntpt, "/net");
ARGBEGIN{
case 'r':
resolver = 1;
break;
case 'x':
dbfile = "/lib/ndb/external";
strcpy(mntpt, "/net.alt");
break;
case 'f':
dbfile = ARGF();
break;
}ARGEND
now = time(0);
dninit();
fmtinstall('R', prettyrrfmt);
if(myipaddr(ipaddr, mntpt) < 0)
sysfatal("can't read my ip address");
opendatabase();
if(resolver)
squirrelserveraddrs();
debug = 1;
if(argc > 0){
docmd(argc, argv);
exits(0);
}
Binit(&in, 0, OREAD);
for(print("> "); p = Brdline(&in, '\n'); print("> ")){
p[Blinelen(&in)-1] = 0;
n = tokenize(p, f, 3);
if(n<1)
continue;
/* flush the cache */
dnpurge();
docmd(n, f);
}
exits(0);
}
static char*
longtime(long t)
{
int d, h, m, n;
static char x[128];
for(d = 0; t >= 24*60*60; t -= 24*60*60)
d++;
for(h = 0; t >= 60*60; t -= 60*60)
h++;
for(m = 0; t >= 60; t -= 60)
m++;
n = 0;
if(d)
n += sprint(x, "%d day ", d);
if(h)
n += sprint(x+n, "%d hr ", h);
if(m)
n += sprint(x+n, "%d min ", m);
if(t || n == 0)
sprint(x+n, "%ld sec", t);
return x;
}
int
prettyrrfmt(Fmt *f)
{
RR *rp;
char buf[3*Domlen];
char *p, *e;
Txt *t;
rp = va_arg(f->args, RR*);
if(rp == 0){
strcpy(buf, "<null>");
goto out;
}
p = buf;
e = buf + sizeof(buf);
p = seprint(p, e, "%-32.32s %-15.15s %-5.5s", rp->owner->name,
longtime(rp->db ? rp->ttl : (rp->ttl-now)),
rrname(rp->type, buf, sizeof buf));
if(rp->negative){
seprint(p, e, "negative rcode %d\n", rp->negrcode);
goto out;
}
switch(rp->type){
case Thinfo:
seprint(p, e, "\t%s %s", rp->cpu->name, rp->os->name);
break;
case Tcname:
case Tmb:
case Tmd:
case Tmf:
case Tns:
seprint(p, e, "\t%s", rp->host->name);
break;
case Tmg:
case Tmr:
seprint(p, e, "\t%s", rp->mb->name);
break;
case Tminfo:
seprint(p, e, "\t%s %s", rp->mb->name, rp->rmb->name);
break;
case Tmx:
seprint(p, e, "\t%lud %s", rp->pref, rp->host->name);
break;
case Ta:
case Taaaa:
seprint(p, e, "\t%s", rp->ip->name);
break;
case Tptr:
seprint(p, e, "\t%s", rp->ptr->name);
break;
case Tsoa:
seprint(p, e, "\t%s %s %lud %lud %lud %lud %lud", rp->host->name,
rp->rmb->name, rp->soa->serial, rp->soa->refresh, rp->soa->retry,
rp->soa->expire, rp->soa->minttl);
break;
case Tnull:
seprint(p, e, "\t%.*H", rp->null->dlen, rp->null->data);
break;
case Ttxt:
p = seprint(p, e, "\t");
for(t = rp->txt; t != nil; t = t->next)
p = seprint(p, e, "%s", t->p);
break;
case Trp:
seprint(p, e, "\t%s %s", rp->rmb->name, rp->rp->name);
break;
case Tkey:
seprint(p, e, "\t%d %d %d", rp->key->flags, rp->key->proto,
rp->key->alg);
break;
case Tsig:
seprint(p, e, "\t%d %d %d %lud %lud %lud %d %s",
rp->sig->type, rp->sig->alg, rp->sig->labels, rp->sig->ttl,
rp->sig->exp, rp->sig->incep, rp->sig->tag, rp->sig->signer->name);
break;
case Tcert:
seprint(p, e, "\t%d %d %d",
rp->sig->type, rp->sig->tag, rp->sig->alg);
break;
default:
break;
}
out:
return fmtstrcpy(f, buf);
}
void
logsection(char *flag, RR *rp)
{
if(rp == nil)
return;
print("\t%s%R\n", flag, rp);
for(rp = rp->next; rp != nil; rp = rp->next)
print("\t %R\n", rp);
}
void
logreply(int id, uchar *addr, DNSmsg *mp)
{
RR *rp;
char buf[12];
char resp[32];
switch(mp->flags & Rmask){
case Rok:
strcpy(resp, "OK");
break;
case Rformat:
strcpy(resp, "Format error");
break;
case Rserver:
strcpy(resp, "Server failed");
break;
case Rname:
strcpy(resp, "Nonexistent");
break;
case Runimplimented:
strcpy(resp, "Unimplemented");
break;
case Rrefused:
strcpy(resp, "Refused");
break;
default:
sprint(resp, "%d", mp->flags & Rmask);
break;
}
print("%d: rcvd %s from %I (%s%s%s%s%s)\n", id, resp, addr,
mp->flags & Fauth ? "authoritative" : "",
mp->flags & Ftrunc ? " truncated" : "",
mp->flags & Frecurse ? " recurse" : "",
mp->flags & Fcanrec ? " can_recurse" : "",
mp->flags & (Fauth|Rname) == (Fauth|Rname) ?
" nx" : "");
for(rp = mp->qd; rp != nil; rp = rp->next)
print("\tQ: %s %s\n", rp->owner->name, rrname(rp->type, buf, sizeof buf));
logsection("Ans: ", mp->an);
logsection("Auth: ", mp->ns);
logsection("Hint: ", mp->ar);
}
void
logsend(int id, int subid, uchar *addr, char *sname, char *rname, int type)
{
char buf[12];
print("%d.%d: sending to %I/%s %s %s\n", id, subid,
addr, sname, rname, rrname(type, buf, sizeof buf));
}
RR*
getdnsservers(int class)
{
RR *rr;
if(servername == nil)
return dnsservers(class);
rr = rralloc(Tns);
rr->owner = dnlookup("local#dns#servers", class, 1);
rr->host = dnlookup(servername, class, 1);
return rr;
}
void
squirrelserveraddrs(void)
{
RR *rr, *rp, **l;
Request req;
/* look up the resolver address first */
resolver = 0;
debug = 0;
if(serveraddrs)
rrfreelist(serveraddrs);
serveraddrs = nil;
rr = getdnsservers(Cin);
l = &serveraddrs;
for(rp = rr; rp != nil; rp = rp->next){
if(strcmp(ipattr(rp->host->name), "ip") == 0){
*l = rralloc(Ta);
(*l)->owner = rp->host;
(*l)->ip = rp->host;
l = &(*l)->next;
continue;
}
req.isslave = 1;
req.aborttime = now + 60; /* don't spend more than 60 seconds */
*l = dnresolve(rp->host->name, Cin, Ta, &req, 0, 0, Recurse, 0, 0);
while(*l != nil)
l = &(*l)->next;
}
resolver = 1;
debug = 1;
}
void
preloadserveraddrs(void)
{
RR *rp, **l, *first;
l = &first;
for(rp = serveraddrs; rp != nil; rp = rp->next){
rrcopy(rp, l);
rrattach(first, 1);
}
}
int
setserver(char *server)
{
if(servername != nil){
free(servername);
servername = nil;
resolver = 0;
}
if(server == nil || *server == 0)
return 0;
servername = strdup(server);
squirrelserveraddrs();
if(serveraddrs == nil){
print("can't resolve %s\n", servername);
resolver = 0;
} else {
resolver = 1;
}
return resolver ? 0 : -1;
}
void
doquery(char *name, char *tstr)
{
Request req;
RR *rr, *rp;
int len, type;
char *p, *np;
int rooted;
char buf[1024];
if(resolver)
preloadserveraddrs();
/* default to an "ip" request if alpha, "ptr" if numeric */
if(tstr == nil || *tstr == 0) {
if(strcmp(ipattr(name), "ip") == 0)
tstr = "ptr";
else
tstr = "ip";
}
/* if name end in '.', remove it */
len = strlen(name);
if(len > 0 && name[len-1] == '.'){
rooted = 1;
name[len-1] = 0;
} else
rooted = 0;
/* inverse queries may need to be permuted */
strncpy(buf, name, sizeof buf);
if(strcmp("ptr", tstr) == 0
&& strstr(name, "IN-ADDR") == 0
&& strstr(name, "in-addr") == 0){
for(p = name; *p; p++)
;
*p = '.';
np = buf;
len = 0;
while(p >= name){
len++;
p--;
if(*p == '.'){
memmove(np, p+1, len);
np += len;
len = 0;
}
}
memmove(np, p+1, len);
np += len;
strcpy(np, "in-addr.arpa");
}
/* look it up */
type = rrtype(tstr);
if(type < 0){
print("!unknown type %s\n", tstr);
return;
}
getactivity(&req);
req.isslave = 1;
req.aborttime = now + 60; /* don't spend more than 60 seconds */
rr = dnresolve(buf, Cin, type, &req, 0, 0, Recurse, rooted, 0);
if(rr){
print("----------------------------\n");
for(rp = rr; rp; rp = rp->next)
print("answer %R\n", rp);
print("----------------------------\n");
}
rrfreelist(rr);
putactivity();
}
void
docmd(int n, char **f)
{
int tmpsrv;
char *name, *type;
name = nil;
type = nil;
tmpsrv = 0;
if(*f[0] == '@') {
if(setserver(f[0]+1) < 0)
return;
switch(n){
case 3:
type = f[2];
/* fall through */
case 2:
name = f[1];
tmpsrv = 1;
break;
}
} else {
switch(n){
case 2:
type = f[1];
/* fall through */
case 1:
name = f[0];
break;
}
}
if(name == nil)
return;
doquery(name, type);
if(tmpsrv)
setserver("");
}

178
src/cmd/ndb/dnserver.c Executable file
View file

@ -0,0 +1,178 @@
#include <u.h>
#include <libc.h>
#include <ip.h>
#include <bio.h>
#include <ndb.h>
#include "dns.h"
static RR* doextquery(DNSmsg*, Request*, int);
static void hint(RR**, RR*);
extern char *logfile;
/*
* answer a dns request
*/
void
dnserver(DNSmsg *reqp, DNSmsg *repp, Request *req)
{
RR *tp, *neg;
char *cp;
DN *nsdp, *dp;
Area *myarea;
char tname[32];
dncheck(nil, 1);
memset(repp, 0, sizeof(*repp));
repp->id = reqp->id;
repp->flags = Fresp | Fcanrec | Oquery;
/* move one question from reqp to repp */
tp = reqp->qd;
reqp->qd = tp->next;
tp->next = 0;
repp->qd = tp;
if(!rrsupported(repp->qd->type)){
syslog(0, logfile, "server: request %s", rrname(repp->qd->type, tname, sizeof tname));
repp->flags = Runimplimented | Fresp | Fcanrec | Oquery;
return;
}
if(repp->qd->owner->class != Cin){
syslog(0, logfile, "server: class %d", repp->qd->owner->class);
repp->flags = Runimplimented | Fresp | Fcanrec | Oquery;
return;
}
myarea = inmyarea(repp->qd->owner->name);
if(myarea != nil && (repp->qd->type == Tixfr || repp->qd->type == Taxfr)){
syslog(0, logfile, "server: request %s", rrname(repp->qd->type, tname, sizeof tname));
repp->flags = Runimplimented | Fresp | Fcanrec | Oquery;
return;
}
/*
* get the answer if we can
*/
if(reqp->flags & Frecurse)
neg = doextquery(repp, req, Recurse);
else
neg = doextquery(repp, req, Dontrecurse);
/* authority is transitive */
if(myarea != nil || (repp->an && repp->an->auth))
repp->flags |= Fauth;
/* pass on error codes */
if(repp->an == 0){
dp = dnlookup(repp->qd->owner->name, repp->qd->owner->class, 0);
if(dp->rr == 0)
if(reqp->flags & Frecurse)
repp->flags |= dp->nonexistent|Fauth;
}
if(myarea == nil){
/*
* add name server if we know
*/
for(cp = repp->qd->owner->name; cp; cp = walkup(cp)){
nsdp = dnlookup(cp, repp->qd->owner->class, 0);
if(nsdp == 0)
continue;
repp->ns = rrlookup(nsdp, Tns, OKneg);
if(repp->ns){
/* don't pass on anything we know is wrong */
if(repp->ns->negative){
rrfreelist(repp->ns);
repp->ns = nil;
}
break;
}
repp->ns = dblookup(cp, repp->qd->owner->class, Tns, 0, 0);
if(repp->ns)
break;
}
}
/*
* add ip addresses as hints
*/
if(repp->qd->type != Taxfr && repp->qd->type != Tixfr){
for(tp = repp->ns; tp; tp = tp->next)
hint(&repp->ar, tp);
for(tp = repp->an; tp; tp = tp->next)
hint(&repp->ar, tp);
}
/*
* add an soa to the authority section to help client with negative caching
*/
if(repp->an == nil){
if(myarea != nil){
rrcopy(myarea->soarr, &tp);
rrcat(&repp->ns, tp);
} else if(neg != nil) {
if(neg->negsoaowner != nil)
rrcat(&repp->ns, rrlookup(neg->negsoaowner, Tsoa, NOneg));
repp->flags |= neg->negrcode;
}
}
/*
* get rid of duplicates
*/
unique(repp->an);
unique(repp->ns);
unique(repp->ar);
rrfreelist(neg);
dncheck(nil, 1);
}
/*
* satisfy a recursive request. dnlookup will handle cnames.
*/
static RR*
doextquery(DNSmsg *mp, Request *req, int recurse)
{
int type;
char *name;
RR *rp, *neg;
name = mp->qd->owner->name;
type = mp->qd->type;
rp = dnresolve(name, Cin, type, req, &mp->an, 0, recurse, 1, 0);
/* don't return soa hints as answers, it's wrong */
if(rp && rp->db && !rp->auth && rp->type == Tsoa)
rrfreelist(rp);
/* don't let negative cached entries escape */
neg = rrremneg(&rp);
rrcat(&mp->an, rp);
return neg;
}
static void
hint(RR **last, RR *rp)
{
RR *hp;
switch(rp->type){
case Tns:
case Tmx:
case Tmb:
case Tmf:
case Tmd:
hp = rrlookup(rp->host, Ta, NOneg);
if(hp == nil)
hp = dblookup(rp->host->name, Cin, Ta, 0, 0);
rrcat(last, hp);
break;
}
}

113
src/cmd/ndb/dnsquery.c Executable file
View file

@ -0,0 +1,113 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <ndb.h>
#include "dns.h"
#include "ip.h"
void
main(int argc, char *argv[])
{
int fd, n, len, domount;
Biobuf in;
char line[1024], *lp, *p, *np, *mtpt, *srv, *dns;
char buf[1024];
dns = "/net/dns";
mtpt = "/net";
srv = "/srv/dns";
domount = 1;
ARGBEGIN {
case 'x':
dns = "/net.alt/dns";
mtpt = "/net.alt";
srv = "/srv/dns_net.alt";
break;
default:
fprint(2, "usage: %s -x [dns-mount-point]\n", argv0);
exits("usage");
} ARGEND;
if(argc == 1){
domount = 0;
mtpt = argv[0];
}
fd = open(dns, ORDWR);
if(fd < 0){
if(domount == 0){
fprint(2, "can't open %s: %r\n", mtpt);
exits(0);
}
fd = open(srv, ORDWR);
if(fd < 0){
print("can't open %s: %r\n", srv);
exits(0);
}
if(mount(fd, -1, mtpt, MBEFORE, "") < 0){
print("can't mount(%s, %s): %r\n", srv, mtpt);
exits(0);
}
fd = open(mtpt, ORDWR);
if(fd < 0){
print("can't open %s: %r\n", mtpt);
exits(0);
}
}
Binit(&in, 0, OREAD);
for(print("> "); lp = Brdline(&in, '\n'); print("> ")){
n = Blinelen(&in)-1;
strncpy(line, lp, n);
line[n] = 0;
if (n<=1)
continue;
/* default to an "ip" request if alpha, "ptr" if numeric */
if (strchr(line, ' ')==0) {
if(strcmp(ipattr(line), "ip") == 0) {
strcat(line, " ptr");
n += 4;
} else {
strcat(line, " ip");
n += 3;
}
}
/* inverse queries may need to be permuted */
if(n > 4 && strcmp("ptr", &line[n-3]) == 0
&& strstr(line, "IN-ADDR") == 0 && strstr(line, "in-addr") == 0){
for(p = line; *p; p++)
if(*p == ' '){
*p = '.';
break;
}
np = buf;
len = 0;
while(p >= line){
len++;
p--;
if(*p == '.'){
memmove(np, p+1, len);
np += len;
len = 0;
}
}
memmove(np, p+1, len);
np += len;
strcpy(np, "in-addr.arpa ptr");
strcpy(line, buf);
n = strlen(line);
}
seek(fd, 0, 0);
if(write(fd, line, n) < 0) {
print("!%r\n");
continue;
}
seek(fd, 0, 0);
while((n = read(fd, buf, sizeof(buf))) > 0){
buf[n] = 0;
print("%s\n", buf);
}
}
exits(0);
}

362
src/cmd/ndb/dnstcp.c Executable file
View file

@ -0,0 +1,362 @@
#include <u.h>
#include <libc.h>
#include <ip.h>
#include "dns.h"
enum
{
Maxpath= 128,
};
char *logfile = "dns";
char *dbfile;
int debug;
int cachedb = 1;
int testing;
int traceactivity;
int needrefresh;
int resolver;
char mntpt[Maxpath];
char *caller = "";
ulong now;
int maxage;
uchar ipaddr[IPaddrlen]; /* my ip address */
char *LOG;
char *zonerefreshprogram;
static int readmsg(int, uchar*, int);
static void reply(int, DNSmsg*, Request*);
static void dnzone(DNSmsg*, DNSmsg*, Request*);
static void getcaller(char*);
static void refreshmain(char*);
void
main(int argc, char *argv[])
{
int len;
Request req;
DNSmsg reqmsg, repmsg;
uchar buf[512];
char tname[32];
char *err;
char *ext = "";
ARGBEGIN{
case 'd':
debug++;
break;
case 'f':
dbfile = ARGF();
break;
case 'r':
resolver = 1;
break;
case 'x':
ext = ARGF();
break;
}ARGEND
if(debug < 2)
debug = 0;
if(argc > 0)
getcaller(argv[0]);
dninit();
snprint(mntpt, sizeof(mntpt), "/net%s", ext);
if(myipaddr(ipaddr, mntpt) < 0)
sysfatal("can't read my ip address");
syslog(0, logfile, "dnstcp call from %s to %I", caller, ipaddr);
db2cache(1);
setjmp(req.mret);
req.isslave = 0;
/* loop on requests */
for(;; putactivity()){
now = time(0);
memset(&repmsg, 0, sizeof(repmsg));
alarm(10*60*1000);
len = readmsg(0, buf, sizeof(buf));
alarm(0);
if(len <= 0)
break;
getactivity(&req);
req.aborttime = now + 15*Min;
err = convM2DNS(buf, len, &reqmsg);
if(err){
syslog(0, logfile, "server: input error: %s from %I", err, buf);
break;
}
if(reqmsg.qdcount < 1){
syslog(0, logfile, "server: no questions from %I", buf);
break;
}
if(reqmsg.flags & Fresp){
syslog(0, logfile, "server: reply not request from %I", buf);
break;
}
if((reqmsg.flags & Omask) != Oquery){
syslog(0, logfile, "server: op %d from %I", reqmsg.flags & Omask, buf);
break;
}
if(debug)
syslog(0, logfile, "%d: serve (%s) %d %s %s",
req.id, caller,
reqmsg.id,
reqmsg.qd->owner->name,
rrname(reqmsg.qd->type, tname, sizeof tname));
/* loop through each question */
while(reqmsg.qd){
if(reqmsg.qd->type == Taxfr){
dnzone(&reqmsg, &repmsg, &req);
} else {
dnserver(&reqmsg, &repmsg, &req);
reply(1, &repmsg, &req);
rrfreelist(repmsg.qd);
rrfreelist(repmsg.an);
rrfreelist(repmsg.ns);
rrfreelist(repmsg.ar);
}
}
rrfreelist(reqmsg.qd);
rrfreelist(reqmsg.an);
rrfreelist(reqmsg.ns);
rrfreelist(reqmsg.ar);
if(req.isslave){
putactivity();
_exits(0);
}
}
refreshmain(mntpt);
}
static int
readmsg(int fd, uchar *buf, int max)
{
int n;
uchar x[2];
if(readn(fd, x, 2) != 2)
return -1;
n = (x[0]<<8) | x[1];
if(n > max)
return -1;
if(readn(fd, buf, n) != n)
return -1;
return n;
}
static void
reply(int fd, DNSmsg *rep, Request *req)
{
int len, rv;
char tname[32];
uchar buf[4096];
RR *rp;
if(debug){
syslog(0, logfile, "%d: reply (%s) %s %s %ux",
req->id, caller,
rep->qd->owner->name,
rrname(rep->qd->type, tname, sizeof tname),
rep->flags);
for(rp = rep->an; rp; rp = rp->next)
syslog(0, logfile, "an %R", rp);
for(rp = rep->ns; rp; rp = rp->next)
syslog(0, logfile, "ns %R", rp);
for(rp = rep->ar; rp; rp = rp->next)
syslog(0, logfile, "ar %R", rp);
}
len = convDNS2M(rep, buf+2, sizeof(buf) - 2);
if(len <= 0)
abort(); /* "dnserver: converting reply" */;
buf[0] = len>>8;
buf[1] = len;
rv = write(fd, buf, len+2);
if(rv != len+2){
syslog(0, logfile, "sending reply: %d instead of %d", rv, len+2);
exits(0);
}
}
/*
* Hash table for domain names. The hash is based only on the
* first element of the domain name.
*/
extern DN *ht[HTLEN];
static int
numelem(char *name)
{
int i;
i = 1;
for(; *name; name++)
if(*name == '.')
i++;
return i;
}
int
inzone(DN *dp, char *name, int namelen, int depth)
{
int n;
if(dp->name == 0)
return 0;
if(numelem(dp->name) != depth)
return 0;
n = strlen(dp->name);
if(n < namelen)
return 0;
if(strcmp(name, dp->name + n - namelen) != 0)
return 0;
if(n > namelen && dp->name[n - namelen - 1] != '.')
return 0;
return 1;
}
static void
dnzone(DNSmsg *reqp, DNSmsg *repp, Request *req)
{
DN *dp, *ndp;
RR r, *rp;
int h, depth, found, nlen;
memset(repp, 0, sizeof(*repp));
repp->id = reqp->id;
repp->flags = Fauth | Fresp | Fcanrec | Oquery;
repp->qd = reqp->qd;
reqp->qd = reqp->qd->next;
repp->qd->next = 0;
dp = repp->qd->owner;
/* send the soa */
repp->an = rrlookup(dp, Tsoa, NOneg);
reply(1, repp, req);
if(repp->an == 0)
goto out;
rrfreelist(repp->an);
nlen = strlen(dp->name);
/* construct a breadth first search of the name space (hard with a hash) */
repp->an = &r;
for(depth = numelem(dp->name); ; depth++){
found = 0;
for(h = 0; h < HTLEN; h++)
for(ndp = ht[h]; ndp; ndp = ndp->next)
if(inzone(ndp, dp->name, nlen, depth)){
for(rp = ndp->rr; rp; rp = rp->next){
/* there shouldn't be negatives, but just in case */
if(rp->negative)
continue;
/* don't send an soa's, ns's are enough */
if(rp->type == Tsoa)
continue;
r = *rp;
r.next = 0;
reply(1, repp, req);
}
found = 1;
}
if(!found)
break;
}
/* resend the soa */
repp->an = rrlookup(dp, Tsoa, NOneg);
reply(1, repp, req);
rrfreelist(repp->an);
out:
rrfree(repp->qd);
}
static void
getcaller(char *dir)
{
int fd, n;
static char remote[128];
snprint(remote, sizeof(remote), "%s/remote", dir);
fd = open(remote, OREAD);
if(fd < 0)
return;
n = read(fd, remote, sizeof(remote)-1);
close(fd);
if(n <= 0)
return;
if(remote[n-1] == '\n')
n--;
remote[n] = 0;
caller = remote;
}
static void
refreshmain(char *net)
{
int fd;
char file[128];
snprint(file, sizeof(file), "%s/dns", net);
if(debug)
syslog(0, logfile, "refreshing %s", file);
fd = open(file, ORDWR);
if(fd < 0){
syslog(0, logfile, "can't refresh %s", file);
return;
}
fprint(fd, "refresh");
close(fd);
}
/*
* the following varies between dnsdebug and dns
*/
void
logreply(int id, uchar *addr, DNSmsg *mp)
{
RR *rp;
syslog(0, LOG, "%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|Rname) == (Fauth|Rname) ?
" nx" : "");
for(rp = mp->qd; rp != nil; rp = rp->next)
syslog(0, LOG, "%d: rcvd %I qd %s", id, addr, rp->owner->name);
for(rp = mp->an; rp != nil; rp = rp->next)
syslog(0, LOG, "%d: rcvd %I an %R", id, addr, rp);
for(rp = mp->ns; rp != nil; rp = rp->next)
syslog(0, LOG, "%d: rcvd %I ns %R", id, addr, rp);
for(rp = mp->ar; rp != nil; rp = rp->next)
syslog(0, LOG, "%d: rcvd %I ar %R", id, addr, rp);
}
void
logsend(int id, int subid, uchar *addr, char *sname, char *rname, int type)
{
char buf[12];
syslog(0, LOG, "%d.%d: sending to %I/%s %s %s",
id, subid, addr, sname, rname, rrname(type, buf, sizeof buf));
}
RR*
getdnsservers(int class)
{
return dnsservers(class);
}

228
src/cmd/ndb/dnudpserver.c Executable file
View file

@ -0,0 +1,228 @@
#include <u.h>
#include <libc.h>
#include <ip.h>
#include <bio.h>
#include <ndb.h>
#include "dns.h"
static int udpannounce(char*);
static void reply(int, uchar*, DNSmsg*, Request*);
extern char *logfile;
static void
ding(void *x, char *msg)
{
USED(x);
if(strcmp(msg, "alarm") == 0)
noted(NCONT);
else
noted(NDFLT);
}
typedef struct Inprogress Inprogress;
struct Inprogress
{
int inuse;
OUdphdr uh;
DN *owner;
int type;
int id;
};
Inprogress inprog[Maxactive+2];
/*
* record client id and ignore retransmissions.
* we're still single thread at this point.
*/
static Inprogress*
clientrxmit(DNSmsg *req, uchar *buf)
{
Inprogress *p, *empty;
OUdphdr *uh;
uh = (OUdphdr *)buf;
empty = 0;
for(p = inprog; p < &inprog[Maxactive]; p++){
if(p->inuse == 0){
if(empty == 0)
empty = p;
continue;
}
if(req->id == p->id)
if(req->qd->owner == p->owner)
if(req->qd->type == p->type)
if(memcmp(uh, &p->uh, OUdphdrsize) == 0)
return 0;
}
if(empty == 0)
return 0; /* shouldn't happen - see slave() and definition of Maxactive */
empty->id = req->id;
empty->owner = req->qd->owner;
empty->type = req->qd->type;
memmove(&empty->uh, uh, OUdphdrsize);
empty->inuse = 1;
return empty;
}
/*
* a process to act as a dns server for outside reqeusts
*/
void
dnudpserver(char *mntpt)
{
int fd, len, op;
Request req;
DNSmsg reqmsg, repmsg;
uchar buf[OUdphdrsize + Maxudp + 1024];
char *err;
Inprogress *p;
char tname[32];
OUdphdr *uh;
/* fork sharing text, data, and bss with parent */
switch(rfork(RFPROC|RFNOTEG|RFMEM|RFNOWAIT)){
case -1:
break;
case 0:
break;
default:
return;
}
fd = -1;
notify(ding);
restart:
if(fd >= 0)
close(fd);
while((fd = udpannounce(mntpt)) < 0)
sleep(5000);
if(setjmp(req.mret))
putactivity();
req.isslave = 0;
/* loop on requests */
for(;; putactivity()){
memset(&repmsg, 0, sizeof(repmsg));
memset(&reqmsg, 0, sizeof(reqmsg));
alarm(60*1000);
len = udpread(fd, (OUdphdr*)buf, buf+OUdphdrsize, sizeof(buf)-OUdphdrsize);
alarm(0);
if(len <= 0)
goto restart;
uh = (OUdphdr*)buf;
getactivity(&req);
req.aborttime = now + 30; /* don't spend more than 30 seconds */
err = convM2DNS(&buf[OUdphdrsize], len, &reqmsg);
if(err){
syslog(0, logfile, "server: input error: %s from %I", err, buf);
continue;
}
if(reqmsg.qdcount < 1){
syslog(0, logfile, "server: no questions from %I", buf);
goto freereq;
}
if(reqmsg.flags & Fresp){
syslog(0, logfile, "server: reply not request from %I", buf);
goto freereq;
}
op = reqmsg.flags & Omask;
if(op != Oquery && op != Onotify){
syslog(0, logfile, "server: op %d from %I", reqmsg.flags & Omask, buf);
goto freereq;
}
if(debug || (trace && subsume(trace, reqmsg.qd->owner->name))){
syslog(0, logfile, "%d: serve (%I/%d) %d %s %s",
req.id, buf, ((uh->rport[0])<<8)+uh->rport[1],
reqmsg.id,
reqmsg.qd->owner->name,
rrname(reqmsg.qd->type, tname, sizeof tname));
}
p = clientrxmit(&reqmsg, buf);
if(p == 0){
if(debug)
syslog(0, logfile, "%d: duplicate", req.id);
goto freereq;
}
/* loop through each question */
while(reqmsg.qd){
memset(&repmsg, 0, sizeof(repmsg));
switch(op){
case Oquery:
dnserver(&reqmsg, &repmsg, &req);
break;
case Onotify:
dnnotify(&reqmsg, &repmsg, &req);
break;
}
reply(fd, buf, &repmsg, &req);
rrfreelist(repmsg.qd);
rrfreelist(repmsg.an);
rrfreelist(repmsg.ns);
rrfreelist(repmsg.ar);
}
p->inuse = 0;
freereq:
rrfreelist(reqmsg.qd);
rrfreelist(reqmsg.an);
rrfreelist(reqmsg.ns);
rrfreelist(reqmsg.ar);
if(req.isslave){
putactivity();
_exits(0);
}
}
}
/*
* announce on udp port
*/
static int
udpannounce(char *mntpt)
{
int fd;
char buf[40];
USED(mntpt);
if((fd=announce("udp!*!nameserver", buf)) < 0)
warning("can't announce on dns udp port");
return fd;
}
static void
reply(int fd, uchar *buf, DNSmsg *rep, Request *reqp)
{
int len;
char tname[32];
RR *rp;
if(debug || (trace && subsume(trace, rep->qd->owner->name)))
syslog(0, logfile, "%d: reply (%I/%d) %d %s %s an %R ns %R ar %R",
reqp->id, buf, ((buf[4])<<8)+buf[5],
rep->id, rep->qd->owner->name,
rrname(rep->qd->type, tname, sizeof tname), rep->an, rep->ns, rep->ar);
len = convDNS2M(rep, &buf[OUdphdrsize], Maxudp);
if(len <= 0){
syslog(0, logfile, "error converting reply: %s %d", rep->qd->owner->name,
rep->qd->type);
for(rp = rep->an; rp; rp = rp->next)
syslog(0, logfile, "an %R", rp);
for(rp = rep->ns; rp; rp = rp->next)
syslog(0, logfile, "ns %R", rp);
for(rp = rep->ar; rp; rp = rp->next)
syslog(0, logfile, "ar %R", rp);
return;
}
if(udpwrite(fd, (OUdphdr*)buf, buf+OUdphdrsize, len) != len)
syslog(0, logfile, "error sending reply: %r");
}

View file

@ -1,6 +1,7 @@
<$PLAN9/src/mkhdr
TARG=\
# dns\
ndbmkdb\
ndbquery\
ndbmkhash\
@ -11,3 +12,18 @@ LIB=$PLAN9/lib/libndb.a
<$PLAN9/src/mkmany
DNSOFILES=\
convDNS2M.$O\
convM2DNS.$O\
dblookup.$O\
dnarea.$O\
dn.$O\
dnresolve.$O\
dnserver.$O\
$DNSOFILES dns.$O dnstcp.$O dnsdebug.$O: dns.h
$O.dns: $DNSOFILES dnnotify.$O dnudpserver.$O
$O.dnstcp: $DNSOFILES
$O.dnsdebug: $DNSOFILES