/*
* Hash table for domain names. The hash is based only on the
* first element of the domain name.
*/
DN *ht[HTLEN];
static struct
{
Lock;
ulong names; /* names allocated */
ulong oldest; /* longest we'll leave a name around */
int active;
int mutex;
int id;
} dnvars;
/* names of op codes */
char *opname[] =
{
[Oquery] "query",
[Oinverse] "inverse",
[Ostatus] "status",
};
Lock dnlock;
static int sencodefmt(Fmt*);
/*
* set up a pipe to use as a lock
*/
void
dninit(void)
{
fmtinstall('E', eipfmt);
fmtinstall('I', eipfmt);
fmtinstall('V', eipfmt);
fmtinstall('R', rrfmt);
fmtinstall('Q', rravfmt);
fmtinstall('H', sencodefmt);
dnvars.oldest = maxage;
dnvars.names = 0;
}
/*
* hash for a domain name
*/
static ulong
dnhash(char *name)
{
ulong hash;
uchar *val = (uchar*)name;
/*
* purge all records
*/
void
dnpurge(void)
{
DN *dp;
RR *rp;
int i;
lock(&dnlock);
for(i = 0; i < HTLEN; i++)
for(dp = ht[i]; dp; dp = dp->next){
rp = dp->rr;
dp->rr = nil;
for(; rp != nil; rp = rp->next)
rp->cached = 0;
rrfreelist(dp->rr);
}
unlock(&dnlock);
}
/*
* check the age of resource records, free any that have timed out
*/
void
dnage(DN *dp)
{
RR **l;
RR *rp, *next;
ulong diff;
diff = now - dp->referenced;
if(diff < Reserved)
return;
l = &dp->rr;
for(rp = dp->rr; rp; rp = next){
assert(rp->magic == RRmagic && rp->cached);
next = rp->next;
if(!rp->db)
if(rp->expire < now || diff > dnvars.oldest){
*l = next;
rp->cached = 0;
rrfree(rp);
continue;
}
l = &rp->next;
}
}
#define REF(x) if(x) x->refs++
/*
* our target is 4000 names cached, this should be larger on large servers
*/
#define TARGET 4000
/*
* periodicly sweep for old records and remove unreferenced domain names
*
* only called when all other threads are locked out
*/
void
dnageall(int doit)
{
DN *dp, **l;
int i;
RR *rp;
static ulong nextage;
/* time out all old entries (and set refs to 0) */
for(i = 0; i < HTLEN; i++)
for(dp = ht[i]; dp; dp = dp->next){
dp->refs = 0;
dnage(dp);
}
/* mark all referenced domain names */
for(i = 0; i < HTLEN; i++)
for(dp = ht[i]; dp; dp = dp->next)
for(rp = dp->rr; rp; rp = rp->next){
REF(rp->owner);
if(rp->negative){
REF(rp->negsoaowner);
continue;
}
switch(rp->type){
case Thinfo:
REF(rp->cpu);
REF(rp->os);
break;
case Ttxt:
REF(rp->txt);
break;
case Tcname:
case Tmb:
case Tmd:
case Tmf:
case Tns:
REF(rp->host);
break;
case Tmg:
case Tmr:
REF(rp->mb);
break;
case Tminfo:
REF(rp->rmb);
REF(rp->mb);
break;
case Trp:
REF(rp->rmb);
REF(rp->txt);
break;
case Tmx:
REF(rp->host);
break;
case Ta:
REF(rp->ip);
break;
case Tptr:
REF(rp->ptr);
break;
case Tsoa:
REF(rp->host);
REF(rp->rmb);
break;
}
}
/*
* timeout all database records (used when rereading db)
*/
void
dnagedb(void)
{
DN *dp;
int i;
RR *rp;
static ulong nextage;
lock(&dnlock);
/* time out all database entries */
for(i = 0; i < HTLEN; i++)
for(dp = ht[i]; dp; dp = dp->next)
for(rp = dp->rr; rp; rp = rp->next)
if(rp->db)
rp->expire = 0;
unlock(&dnlock);
}
/*
* mark all local db records about my area as authoritative, time out any others
*/
void
dnauthdb(void)
{
DN *dp;
int i;
Area *area;
RR *rp;
static ulong nextage;
lock(&dnlock);
/* time out all database entries */
for(i = 0; i < HTLEN; i++)
for(dp = ht[i]; dp; dp = dp->next){
area = inmyarea(dp->name);
for(rp = dp->rr; rp; rp = rp->next)
if(rp->db){
if(area){
if(rp->ttl < area->soarr->soa->minttl)
rp->ttl = area->soarr->soa->minttl;
rp->auth = 1;
}
if(rp->expire == 0){
rp->db = 0;
dp->referenced = now - Reserved - 1;
}
}
}
unlock(&dnlock);
}
/*
* keep track of other processes to know if we can
* garbage collect. block while garbage collecting.
*/
int
getactivity(Request *req)
{
int rv;
/* let others back in */
lastclean = now;
needrefresh = 0;
dnvars.mutex = 0;
}
/*
* Attach a single resource record to a domain name.
* - Avoid duplicates with already present RR's
* - Chain all RR's of the same type adjacent to one another
* - chain authoritative RR's ahead of non-authoritative ones
*/
static void
rrattach1(RR *new, int auth)
{
RR **l;
RR *rp;
DN *dp;
/*
* find first rr of the right type, similar types
* are grouped mostly for debugging
*/
l = &dp->rr;
for(rp = *l; rp; rp = *l){
assert(rp->magic == RRmagic && rp->cached);
if(rp->type == new->type)
break;
l = &rp->next;
}
/*
* negative entries replace positive entries
* positive entries replace negative entries
* newer entries replace older entries with the same fields
*/
for(rp = *l; rp; rp = *l){
assert(rp->magic == RRmagic && rp->cached);
if(rp->type != new->type)
break;
/*
* Attach a list of resource records to a domain name.
* - Avoid duplicates with already present RR's
* - Chain all RR's of the same type adjacent to one another
* - chain authoritative RR's ahead of non-authoritative ones
* - remove any expired RR's
*/
void
rrattach(RR *rp, int auth)
{
RR *next;
lock(&dnlock);
for(; rp; rp = next){
next = rp->next;
rp->next = 0;
/*
* lookup a resource record of a particular type and
* class attached to a domain name. Return copies.
*
* Priority ordering is:
* db authoritative
* not timed out network authoritative
* not timed out network unauthoritative
* unauthoritative db
*
* if flag NOneg is set, don't return negative cached entries.
* return nothing instead.
*/
RR*
rrlookup(DN *dp, int type, int flag)
{
RR *rp, *first, **last;
assert(dp->magic == DNmagic);
first = 0;
last = &first;
lock(&dnlock);
/* try for an authoritative db entry */
for(rp = dp->rr; rp; rp = rp->next){
assert(rp->magic == RRmagic && rp->cached);
if(rp->db)
if(rp->auth)
if(tsame(type, rp->type))
last = rrcopy(rp, last);
}
if(first)
goto out;
/* try for an living authoritative network entry */
for(rp = dp->rr; rp; rp = rp->next){
if(!rp->db)
if(rp->auth)
if(rp->ttl + 60 > now)
if(tsame(type, rp->type)){
if(flag == NOneg && rp->negative)
goto out;
last = rrcopy(rp, last);
}
}
if(first)
goto out;
/* try for an living unauthoritative network entry */
for(rp = dp->rr; rp; rp = rp->next){
if(!rp->db)
if(rp->ttl + 60 > now)
if(tsame(type, rp->type)){
if(flag == NOneg && rp->negative)
goto out;
last = rrcopy(rp, last);
}
}
if(first)
goto out;
/* try for an unauthoritative db entry */
for(rp = dp->rr; rp; rp = rp->next){
if(rp->db)
if(tsame(type, rp->type))
last = rrcopy(rp, last);
}
if(first)
goto out;
/* otherwise, settle for anything we got (except for negative caches) */
for(rp = dp->rr; rp; rp = rp->next){
if(tsame(type, rp->type)){
if(rp->negative)
goto out;
last = rrcopy(rp, last);
}
}