/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ #include <u.h>#include <libc.h>#include <ip.h>#include <pool.h>#include <ctype.h>#include "dns.h" /* * this comment used to say `our target is 4000 names cached, this should * be larger on large servers'. dns at Bell Labs starts off with * about 1780 names. * * aging corrupts the cache, so raise the trigger to avoid it. */enum { Deftarget = 1<<30, /* effectively disable aging */ Minage = 1<<30, Defagefreq = 1<<30, /* age names this often (seconds) */ /* these settings will trigger frequent aging */// Deftarget = 4000,// Minage = 5*60,// Defagefreq = 15*60, /* age names this often (seconds) */ Restartmins = 0,// Restartmins = 600,}; /* * 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; ushort id; /* same size as in packet */} 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",[Tafsdb] "afsdb",[Tx25] "x.25",[Tisdn] "isdn",[Trt] "rt",[Tnsap] "nsap",[Tnsapptr] "nsap-ptr",[Tsig] "sig",[Tkey] "key",[Tpx] "px",[Tgpos] "gpos",[Taaaa] "ipv6",[Tloc] "loc",[Tnxt] "nxt",[Teid] "eid",[Tnimloc] "nimrod",[Tsrv] "srv",[Tatma] "atma",[Tnaptr] "naptr",[Tkx] "kx",[Tcert] "cert",[Ta6] "a6",[Tdname] "dname",[Tsink] "sink",[Topt] "opt",[Tapl] "apl",[Tds] "ds",[Tsshfp] "sshfp",[Tipseckey] "ipseckey",[Trrsig] "rrsig",[Tnsec] "nsec",[Tdnskey] "dnskey",[Tspf] "spf",[Tuinfo] "uinfo",[Tuid] "uid",[Tgid] "gid",[Tunspec] "unspec",[Ttkey] "tkey",[Ttsig] "tsig",[Tixfr] "ixfr",[Taxfr] "axfr",[Tmailb] "mailb",[Tmaila] "maila",[Tall] "all", 0,}; /* names of response codes */char *rname[Rmask+1] ={[Rok] "ok",[Rformat] "format error",[Rserver] "server failure",[Rname] "bad name",[Runimplimented] "unimplemented",[Rrefused] "we don't like you",[Ryxdomain] "name should not exist",[Ryxrrset] "rr set should not exist",[Rnxrrset] "rr set should exist",[Rnotauth] "not authorative",[Rnotzone] "not in zone",[Rbadvers] "bad opt version",/* [Rbadsig] "bad signature", */[Rbadkey] "bad key",[Rbadtime] "bad signature time",[Rbadmode] "bad mode",[Rbadname] "duplicate key name",[Rbadalg] "bad algorithm",};unsigned nrname = nelem(rname); /* names of op codes */char *opname[] ={[Oquery] "query",[Oinverse] "inverse query (retired)",[Ostatus] "status",[Oupdate] "update",}; ulong target = Deftarget;ulong start;Lock dnlock; static ulong agefreq = Defagefreq; static int rrequiv(RR *r1, RR *r2);static int sencodefmt(Fmt*); static voidding(void*, char *msg){ if(strstr(msg, "alarm") != nil) { stats.alarms++; noted(NCONT); /* resume with system call error */ } else noted(NDFLT); /* die */} 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; dnvars.id = truerand(); /* don't start with same id every time */ notify(ding);} /* * 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){ unlock(&dnlock); return 0; } dnvars.names++; dp = emalloc(sizeof(*dp)); dp->magic = DNmagic; dp->name = estrdup(name); assert(dp->name != nil); dp->class = class; dp->rr = 0; dp->referenced = now; /* add new DN to tail of the hash list. *l points to last next ptr. */ dp->next = nil; *l = dp; unlock(&dnlock); return dp;} static intrrsame(RR *rr1, RR *rr2){ return rr1 == rr2 || rr2 && rrequiv(rr1, rr2) && rr1->db == rr2->db && rr1->auth == rr2->auth;} static intrronlist(RR *rp, RR *lp){ for(; lp; lp = lp->next) if (rrsame(lp, rp)) return 1; return 0;} /* * dump the stats */voiddnstats(char *file){ int i, fd; fd = create(file, OWRITE, 0666); if(fd < 0) return; qlock(&stats); fprint(fd, "# system %s\n", sysname()); fprint(fd, "# slave procs high-water mark\t%lud\n", stats.slavehiwat); fprint(fd, "# queries received by 9p\t%lud\n", stats.qrecvd9p); fprint(fd, "# queries received by udp\t%lud\n", stats.qrecvdudp); fprint(fd, "# queries answered from memory\t%lud\n", stats.answinmem); fprint(fd, "# queries sent by udp\t%lud\n", stats.qsent); for (i = 0; i < nelem(stats.under10ths); i++) if (stats.under10ths[i] || i == nelem(stats.under10ths) - 1) fprint(fd, "# responses arriving within %.1f s.\t%lud\n", (double)(i+1)/10, stats.under10ths[i]); fprint(fd, "\n# queries sent & timed-out\t%lud\n", stats.tmout); fprint(fd, "# cname queries timed-out\t%lud\n", stats.tmoutcname); fprint(fd, "# ipv6 queries timed-out\t%lud\n", stats.tmoutv6); fprint(fd, "\n# negative answers received\t%lud\n", stats.negans); fprint(fd, "# negative answers w Rserver set\t%lud\n", stats.negserver); fprint(fd, "# negative answers w bad delegation\t%lud\n", stats.negbaddeleg); fprint(fd, "# negative answers w bad delegation & no answers\t%lud\n", stats.negbdnoans); fprint(fd, "# negative answers w no Rname set\t%lud\n", stats.negnorname); fprint(fd, "# negative answers cached\t%lud\n", stats.negcached); qunlock(&stats); lock(&dnlock); fprint(fd, "\n# domain names %lud target %lud\n", dnvars.names, target); unlock(&dnlock); close(fd);} /* * dump the cache */voiddndump(char *file){ int i, fd; DN *dp; RR *rp; fd = create(file, OWRITE, 0666); 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, "\t%R %c%c %lud/%lud\n", rp, rp->auth? 'A': 'U', rp->db? 'D': 'N', rp->expire, rp->ttl); if (rronlist(rp, rp->next)) fprint(fd, "*** duplicate:\n"); } } unlock(&dnlock); close(fd);} /* * purge all records */voiddnpurge(void){ DN *dp; RR *rp, *srp; int i; lock(&dnlock); for(i = 0; i < HTLEN; i++) for(dp = ht[i]; dp; dp = dp->next){ srp = rp = dp->rr; dp->rr = nil; for(; rp != nil; rp = rp->next) rp->cached = 0; rrfreelist(srp); } unlock(&dnlock);} /* * delete head of *l and free the old head. * call with dnlock held. */static voidrrdelhead(RR **l){ RR *rp; if (canlock(&dnlock)) abort(); /* rrdelhead called with dnlock not held */ rp = *l; if(rp == nil) return; *l = rp->next; /* unlink head */ rp->cached = 0; /* avoid blowing an assertion in rrfree */ rrfree(rp);} /* * check the age of resource records, free any that have timed out. * call with dnlock held. */voiddnage(DN *dp){ RR **l; RR *rp, *next; ulong diff; if (canlock(&dnlock)) abort(); /* dnage called with dnlock not held */ diff = now - dp->referenced; if(diff < Reserved || dp->keep) return; l = &dp->rr; for(rp = dp->rr; rp; rp = next){ assert(rp->magic == RRmagic && rp->cached); next = rp->next; if(!rp->db && (rp->expire < now || diff > dnvars.oldest)) rrdelhead(l); /* rp == *l before; *l == rp->next after */ else l = &rp->next; }} #define MARK(dp) { if (dp) (dp)->keep = 1; } /* mark a domain name and those in its RRs as never to be aged */voiddnagenever(DN *dp, int dolock){ RR *rp; if (dolock) lock(&dnlock); /* mark all referenced domain names */ MARK(dp); for(rp = dp->rr; rp; rp = rp->next){ MARK(rp->owner); if(rp->negative){ MARK(rp->negsoaowner); continue; } switch(rp->type){ case Thinfo: MARK(rp->cpu); MARK(rp->os); break; case Ttxt: break; case Tcname: case Tmb: case Tmd: case Tmf: case Tns: case Tmx: case Tsrv: MARK(rp->host); break; case Tmg: case Tmr: MARK(rp->mb); break; case Tminfo: MARK(rp->rmb); MARK(rp->mb); break; case Trp: MARK(rp->rmb); MARK(rp->rp); break; case Ta: case Taaaa: MARK(rp->ip); break; case Tptr: MARK(rp->ptr); break; case Tsoa: MARK(rp->host); MARK(rp->rmb); break; } } if (dolock) unlock(&dnlock);} /* mark all current domain names as never to be aged */voiddnageallnever(void){ int i; DN *dp; lock(&dnlock); /* mark all referenced domain names */ for(i = 0; i < HTLEN; i++) for(dp = ht[i]; dp; dp = dp->next) dnagenever(dp, 0); unlock(&dnlock); dnslog("%ld initial domain names; target is %ld", dnvars.names, target); if(dnvars.names >= target) dnslog("more initial domain names (%ld) than target (%ld)", dnvars.names, target);} #define REF(dp) { if (dp) (dp)->refs++; } /* * 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;Lines 1–500 of 2088. Load more
/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ #include <u.h> #include <libc.h> #include <ip.h> #include <pool.h> #include <ctype.h> #include "dns.h" /* * this comment used to say `our target is 4000 names cached, this should * be larger on large servers'. dns at Bell Labs starts off with * about 1780 names. * * aging corrupts the cache, so raise the trigger to avoid it. */ enum { Deftarget = 1<<30, /* effectively disable aging */ Minage = 1<<30, Defagefreq = 1<<30, /* age names this often (seconds) */ /* these settings will trigger frequent aging */ // Deftarget = 4000, // Minage = 5*60, // Defagefreq = 15*60, /* age names this often (seconds) */ Restartmins = 0, // Restartmins = 600, }; /* * 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; ushort id; /* same size as in packet */ } 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", [Tafsdb] "afsdb", [Tx25] "x.25", [Tisdn] "isdn", [Trt] "rt", [Tnsap] "nsap", [Tnsapptr] "nsap-ptr", [Tsig] "sig", [Tkey] "key", [Tpx] "px", [Tgpos] "gpos", [Taaaa] "ipv6", [Tloc] "loc", [Tnxt] "nxt", [Teid] "eid", [Tnimloc] "nimrod", [Tsrv] "srv", [Tatma] "atma", [Tnaptr] "naptr", [Tkx] "kx", [Tcert] "cert", [Ta6] "a6", [Tdname] "dname", [Tsink] "sink", [Topt] "opt", [Tapl] "apl", [Tds] "ds", [Tsshfp] "sshfp", [Tipseckey] "ipseckey", [Trrsig] "rrsig", [Tnsec] "nsec", [Tdnskey] "dnskey", [Tspf] "spf", [Tuinfo] "uinfo", [Tuid] "uid", [Tgid] "gid", [Tunspec] "unspec", [Ttkey] "tkey", [Ttsig] "tsig", [Tixfr] "ixfr", [Taxfr] "axfr", [Tmailb] "mailb", [Tmaila] "maila", [Tall] "all", 0, }; /* names of response codes */ char *rname[Rmask+1] = { [Rok] "ok", [Rformat] "format error", [Rserver] "server failure", [Rname] "bad name", [Runimplimented] "unimplemented", [Rrefused] "we don't like you", [Ryxdomain] "name should not exist", [Ryxrrset] "rr set should not exist", [Rnxrrset] "rr set should exist", [Rnotauth] "not authorative", [Rnotzone] "not in zone", [Rbadvers] "bad opt version", /* [Rbadsig] "bad signature", */ [Rbadkey] "bad key", [Rbadtime] "bad signature time", [Rbadmode] "bad mode", [Rbadname] "duplicate key name", [Rbadalg] "bad algorithm", }; unsigned nrname = nelem(rname); /* names of op codes */ char *opname[] = { [Oquery] "query", [Oinverse] "inverse query (retired)", [Ostatus] "status", [Oupdate] "update", }; ulong target = Deftarget; ulong start; Lock dnlock; static ulong agefreq = Defagefreq; static int rrequiv(RR *r1, RR *r2); static int sencodefmt(Fmt*); static void ding(void*, char *msg) { if(strstr(msg, "alarm") != nil) { stats.alarms++; noted(NCONT); /* resume with system call error */ } else noted(NDFLT); /* die */ } 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; dnvars.id = truerand(); /* don't start with same id every time */ notify(ding); } /* * 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){ unlock(&dnlock); return 0; } dnvars.names++; dp = emalloc(sizeof(*dp)); dp->magic = DNmagic; dp->name = estrdup(name); assert(dp->name != nil); dp->class = class; dp->rr = 0; dp->referenced = now; /* add new DN to tail of the hash list. *l points to last next ptr. */ dp->next = nil; *l = dp; unlock(&dnlock); return dp; } static int rrsame(RR *rr1, RR *rr2) { return rr1 == rr2 || rr2 && rrequiv(rr1, rr2) && rr1->db == rr2->db && rr1->auth == rr2->auth; } static int rronlist(RR *rp, RR *lp) { for(; lp; lp = lp->next) if (rrsame(lp, rp)) return 1; return 0; } /* * dump the stats */ void dnstats(char *file) { int i, fd; fd = create(file, OWRITE, 0666); if(fd < 0) return; qlock(&stats); fprint(fd, "# system %s\n", sysname()); fprint(fd, "# slave procs high-water mark\t%lud\n", stats.slavehiwat); fprint(fd, "# queries received by 9p\t%lud\n", stats.qrecvd9p); fprint(fd, "# queries received by udp\t%lud\n", stats.qrecvdudp); fprint(fd, "# queries answered from memory\t%lud\n", stats.answinmem); fprint(fd, "# queries sent by udp\t%lud\n", stats.qsent); for (i = 0; i < nelem(stats.under10ths); i++) if (stats.under10ths[i] || i == nelem(stats.under10ths) - 1) fprint(fd, "# responses arriving within %.1f s.\t%lud\n", (double)(i+1)/10, stats.under10ths[i]); fprint(fd, "\n# queries sent & timed-out\t%lud\n", stats.tmout); fprint(fd, "# cname queries timed-out\t%lud\n", stats.tmoutcname); fprint(fd, "# ipv6 queries timed-out\t%lud\n", stats.tmoutv6); fprint(fd, "\n# negative answers received\t%lud\n", stats.negans); fprint(fd, "# negative answers w Rserver set\t%lud\n", stats.negserver); fprint(fd, "# negative answers w bad delegation\t%lud\n", stats.negbaddeleg); fprint(fd, "# negative answers w bad delegation & no answers\t%lud\n", stats.negbdnoans); fprint(fd, "# negative answers w no Rname set\t%lud\n", stats.negnorname); fprint(fd, "# negative answers cached\t%lud\n", stats.negcached); qunlock(&stats); lock(&dnlock); fprint(fd, "\n# domain names %lud target %lud\n", dnvars.names, target); unlock(&dnlock); close(fd); } /* * dump the cache */ void dndump(char *file) { int i, fd; DN *dp; RR *rp; fd = create(file, OWRITE, 0666); 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, "\t%R %c%c %lud/%lud\n", rp, rp->auth? 'A': 'U', rp->db? 'D': 'N', rp->expire, rp->ttl); if (rronlist(rp, rp->next)) fprint(fd, "*** duplicate:\n"); } } unlock(&dnlock); close(fd); } /* * purge all records */ void dnpurge(void) { DN *dp; RR *rp, *srp; int i; lock(&dnlock); for(i = 0; i < HTLEN; i++) for(dp = ht[i]; dp; dp = dp->next){ srp = rp = dp->rr; dp->rr = nil; for(; rp != nil; rp = rp->next) rp->cached = 0; rrfreelist(srp); } unlock(&dnlock); } /* * delete head of *l and free the old head. * call with dnlock held. */ static void rrdelhead(RR **l) { RR *rp; if (canlock(&dnlock)) abort(); /* rrdelhead called with dnlock not held */ rp = *l; if(rp == nil) return; *l = rp->next; /* unlink head */ rp->cached = 0; /* avoid blowing an assertion in rrfree */ rrfree(rp); } /* * check the age of resource records, free any that have timed out. * call with dnlock held. */ void dnage(DN *dp) { RR **l; RR *rp, *next; ulong diff; if (canlock(&dnlock)) abort(); /* dnage called with dnlock not held */ diff = now - dp->referenced; if(diff < Reserved || dp->keep) return; l = &dp->rr; for(rp = dp->rr; rp; rp = next){ assert(rp->magic == RRmagic && rp->cached); next = rp->next; if(!rp->db && (rp->expire < now || diff > dnvars.oldest)) rrdelhead(l); /* rp == *l before; *l == rp->next after */ else l = &rp->next; } } #define MARK(dp) { if (dp) (dp)->keep = 1; } /* mark a domain name and those in its RRs as never to be aged */ void dnagenever(DN *dp, int dolock) { RR *rp; if (dolock) lock(&dnlock); /* mark all referenced domain names */ MARK(dp); for(rp = dp->rr; rp; rp = rp->next){ MARK(rp->owner); if(rp->negative){ MARK(rp->negsoaowner); continue; } switch(rp->type){ case Thinfo: MARK(rp->cpu); MARK(rp->os); break; case Ttxt: break; case Tcname: case Tmb: case Tmd: case Tmf: case Tns: case Tmx: case Tsrv: MARK(rp->host); break; case Tmg: case Tmr: MARK(rp->mb); break; case Tminfo: MARK(rp->rmb); MARK(rp->mb); break; case Trp: MARK(rp->rmb); MARK(rp->rp); break; case Ta: case Taaaa: MARK(rp->ip); break; case Tptr: MARK(rp->ptr); break; case Tsoa: MARK(rp->host); MARK(rp->rmb); break; } } if (dolock) unlock(&dnlock); } /* mark all current domain names as never to be aged */ void dnageallnever(void) { int i; DN *dp; lock(&dnlock); /* mark all referenced domain names */ for(i = 0; i < HTLEN; i++) for(dp = ht[i]; dp; dp = dp->next) dnagenever(dp, 0); unlock(&dnlock); dnslog("%ld initial domain names; target is %ld", dnvars.names, target); if(dnvars.names >= target) dnslog("more initial domain names (%ld) than target (%ld)", dnvars.names, target); } #define REF(dp) { if (dp) (dp)->refs++; } /* * 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;