add dns
This commit is contained in:
parent
cff43a06f2
commit
3e0d8fb3ea
15 changed files with 7047 additions and 0 deletions
380
src/cmd/ndb/convDNS2M.c
Executable file
380
src/cmd/ndb/convDNS2M.c
Executable 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
460
src/cmd/ndb/convM2DNS.c
Executable 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
946
src/cmd/ndb/dblookup.c
Executable 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
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
130
src/cmd/ndb/dnarea.c
Executable 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
160
src/cmd/ndb/dnnotify.c
Executable 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
753
src/cmd/ndb/dnresolve.c
Executable 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
882
src/cmd/ndb/dns.c
Executable 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
403
src/cmd/ndb/dns.h
Executable 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
473
src/cmd/ndb/dnsdebug.c
Executable 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
178
src/cmd/ndb/dnserver.c
Executable 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
113
src/cmd/ndb/dnsquery.c
Executable 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
362
src/cmd/ndb/dnstcp.c
Executable 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
228
src/cmd/ndb/dnudpserver.c
Executable 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");
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue