#include <u.h>#include <libc.h>#include <ip.h>#include <pool.h>#include <ctype.h>#include "dns.h" /* * 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 RR types */char *rrtname[] ={[Ta] "ip",[Tns] "ns",[Tmd] "md",[Tmf] "mf",[Tcname] "cname",[Tsoa] "soa",[Tmb] "mb",[Tmg] "mg",[Tmr] "mr",[Tnull] "null",[Twks] "wks",[Tptr] "ptr",[Thinfo] "hinfo",[Tminfo] "minfo",[Tmx] "mx",[Ttxt] "txt",[Trp] "rp",[Tkey] "key",[Tcert] "cert",[Tsig] "sig",[Tixfr] "ixfr",[Taxfr] "axfr",[Tall] "all", 0,}; /* names of response codes */char *rname[] ={[Rok] "ok",[Rformat] "format error",[Rserver] "server failure",[Rname] "bad name",[Runimplimented] "unimplemented",[Rrefused] "we don't like you",}; /* 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 */voiddninit(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 ulongdnhash(char *name){ ulong hash; uchar *val = (uchar*)name; for(hash = 0; *val; val++) hash = (hash*13) + tolower(*val)-'a'; return hash % HTLEN;} /* * lookup a symbol. if enter is not zero and the name is * not found, create it. */DN*dnlookup(char *name, int class, int enter){ DN **l; DN *dp; l = &ht[dnhash(name)]; lock(&dnlock); for(dp = *l; dp; dp = dp->next) { assert(dp->magic == DNmagic); if(dp->class == class && cistrcmp(dp->name, name) == 0){ dp->referenced = now; unlock(&dnlock); return dp; } l = &dp->next; } if(enter == 0){ unlock(&dnlock); return 0; } dnvars.names++; dp = emalloc(sizeof(*dp)); dp->magic = DNmagic; dp->name = estrdup(name); assert(dp->name != 0); dp->class = class; dp->rr = 0; dp->next = 0; dp->referenced = now; *l = dp; unlock(&dnlock); return dp;} /* * dump the cache */voiddndump(char *file){ DN *dp; int i, fd; RR *rp; fd = open(file, OWRITE|OTRUNC); if(fd < 0) return; lock(&dnlock); for(i = 0; i < HTLEN; i++){ for(dp = ht[i]; dp; dp = dp->next){ fprint(fd, "%s\n", dp->name); for(rp = dp->rr; rp; rp = rp->next) fprint(fd, " %R %c%c %lud/%lud\n", rp, rp->auth?'A':'U', rp->db?'D':'N', rp->expire, rp->ttl); } } unlock(&dnlock); close(fd);} /* * purge all records */voiddnpurge(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 */voiddnage(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 */voiddnageall(int doit){ DN *dp, **l; int i; RR *rp; static ulong nextage; if(dnvars.names < TARGET && now < nextage && !doit){ dnvars.oldest = maxage; return; } if(dnvars.names > TARGET) dnvars.oldest /= 2; nextage = now + maxage; lock(&dnlock); /* 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; } } /* sweep and remove unreferenced domain names */ for(i = 0; i < HTLEN; i++){ l = &ht[i]; for(dp = *l; dp; dp = *l){ if(dp->rr == 0 && dp->refs == 0){ *l = dp->next; if(dp->name) free(dp->name); dnvars.names--; free(dp); continue; } l = &dp->next; } } unlock(&dnlock);} /* * timeout all database records (used when rereading db) */voiddnagedb(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 */voiddnauthdb(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. */intgetactivity(Request *req){ int rv; lock(&dnvars); while(dnvars.mutex){ unlock(&dnvars); sleep(200); lock(&dnvars); } rv = ++dnvars.active; now = time(0); req->id = ++dnvars.id; unlock(&dnvars); return rv;}voidputactivity(void){ static ulong lastclean; lock(&dnvars); dnvars.active--; assert(dnvars.active >= 0); /* "dnvars.active %d", dnvars.active */; /* * clean out old entries and check for new db periodicly */ if(dnvars.mutex || (needrefresh == 0 && dnvars.active > 0)){ unlock(&dnvars); return; } /* wait till we're alone */ dnvars.mutex = 1; while(dnvars.active > 0){ unlock(&dnvars); sleep(100); lock(&dnvars); } unlock(&dnvars); db2cache(needrefresh); dnageall(0); /* 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 voidrrattach1(RR *new, int auth){ RR **l; RR *rp; DN *dp; assert(new->magic == RRmagic && !new->cached); if(!new->db) new->expire = new->ttl; else new->expire = now + Year; dp = new->owner; assert(dp->magic == DNmagic); new->auth |= auth; new->next = 0; /* * 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; if(rp->db == new->db && rp->auth == new->auth){ /* negative drives out positive and vice versa */ if(rp->negative != new->negative){ *l = rp->next;Lines 1–500 of 1373. Load more
#include <u.h> #include <libc.h> #include <ip.h> #include <pool.h> #include <ctype.h> #include "dns.h" /* * 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 RR types */ char *rrtname[] = { [Ta] "ip", [Tns] "ns", [Tmd] "md", [Tmf] "mf", [Tcname] "cname", [Tsoa] "soa", [Tmb] "mb", [Tmg] "mg", [Tmr] "mr", [Tnull] "null", [Twks] "wks", [Tptr] "ptr", [Thinfo] "hinfo", [Tminfo] "minfo", [Tmx] "mx", [Ttxt] "txt", [Trp] "rp", [Tkey] "key", [Tcert] "cert", [Tsig] "sig", [Tixfr] "ixfr", [Taxfr] "axfr", [Tall] "all", 0, }; /* names of response codes */ char *rname[] = { [Rok] "ok", [Rformat] "format error", [Rserver] "server failure", [Rname] "bad name", [Runimplimented] "unimplemented", [Rrefused] "we don't like you", }; /* 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; for(hash = 0; *val; val++) hash = (hash*13) + tolower(*val)-'a'; return hash % HTLEN; } /* * lookup a symbol. if enter is not zero and the name is * not found, create it. */ DN* dnlookup(char *name, int class, int enter) { DN **l; DN *dp; l = &ht[dnhash(name)]; lock(&dnlock); for(dp = *l; dp; dp = dp->next) { assert(dp->magic == DNmagic); if(dp->class == class && cistrcmp(dp->name, name) == 0){ dp->referenced = now; unlock(&dnlock); return dp; } l = &dp->next; } if(enter == 0){ unlock(&dnlock); return 0; } dnvars.names++; dp = emalloc(sizeof(*dp)); dp->magic = DNmagic; dp->name = estrdup(name); assert(dp->name != 0); dp->class = class; dp->rr = 0; dp->next = 0; dp->referenced = now; *l = dp; unlock(&dnlock); return dp; } /* * dump the cache */ void dndump(char *file) { DN *dp; int i, fd; RR *rp; fd = open(file, OWRITE|OTRUNC); if(fd < 0) return; lock(&dnlock); for(i = 0; i < HTLEN; i++){ for(dp = ht[i]; dp; dp = dp->next){ fprint(fd, "%s\n", dp->name); for(rp = dp->rr; rp; rp = rp->next) fprint(fd, " %R %c%c %lud/%lud\n", rp, rp->auth?'A':'U', rp->db?'D':'N', rp->expire, rp->ttl); } } unlock(&dnlock); close(fd); } /* * 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; if(dnvars.names < TARGET && now < nextage && !doit){ dnvars.oldest = maxage; return; } if(dnvars.names > TARGET) dnvars.oldest /= 2; nextage = now + maxage; lock(&dnlock); /* 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; } } /* sweep and remove unreferenced domain names */ for(i = 0; i < HTLEN; i++){ l = &ht[i]; for(dp = *l; dp; dp = *l){ if(dp->rr == 0 && dp->refs == 0){ *l = dp->next; if(dp->name) free(dp->name); dnvars.names--; free(dp); continue; } l = &dp->next; } } unlock(&dnlock); } /* * 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; lock(&dnvars); while(dnvars.mutex){ unlock(&dnvars); sleep(200); lock(&dnvars); } rv = ++dnvars.active; now = time(0); req->id = ++dnvars.id; unlock(&dnvars); return rv; } void putactivity(void) { static ulong lastclean; lock(&dnvars); dnvars.active--; assert(dnvars.active >= 0); /* "dnvars.active %d", dnvars.active */; /* * clean out old entries and check for new db periodicly */ if(dnvars.mutex || (needrefresh == 0 && dnvars.active > 0)){ unlock(&dnvars); return; } /* wait till we're alone */ dnvars.mutex = 1; while(dnvars.active > 0){ unlock(&dnvars); sleep(100); lock(&dnvars); } unlock(&dnvars); db2cache(needrefresh); dnageall(0); /* 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; assert(new->magic == RRmagic && !new->cached); if(!new->db) new->expire = new->ttl; else new->expire = now + Year; dp = new->owner; assert(dp->magic == DNmagic); new->auth |= auth; new->next = 0; /* * 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; if(rp->db == new->db && rp->auth == new->auth){ /* negative drives out positive and vice versa */ if(rp->negative != new->negative){ *l = rp->next;