2094 lines · 39.2 KB · C
RawDownload
#include <u.h>#include <libc.h>#include <ip.h>#include <bio.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 seems to corrupt the cache, so raise the trigger from 4000 until we * figure it out. */enum {	Deftarget = 8000,};enum {	Minage		= 10*60,	Defagefreq	= 30*60,	/* age names this often (seconds) */}; /* *  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",[Rbadkey]		"bad key",[Rbadtime]		"bad signature time",[Rbadmode]		"bad mode",[Rbadname]		"duplicate key name",[Rbadalg]		"bad algorithm",[Rbadtrunc]		"bad truncation",}; /* names of op codes */char *opname[] ={[Oquery]	"query",[Oinverse]	"inverse query (retired)",[Ostatus]	"status",[Oupdate]	"update",}; ulong target = Deftarget;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;	Biobuf b; 	fd = create(file, OWRITE, 0666);	if(fd < 0)		return;	if(Binit(&b, fd, OWRITE) == -1){		close(fd);		return;	} 	lock(&dnlock);	for(i = 0; i < HTLEN; i++)		for(dp = ht[i]; dp; dp = dp->next){			Bprint(&b, "%s\n", dp->name);			for(rp = dp->rr; rp; rp = rp->next) {				Bprint(&b, "\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))					Bprint(&b, "*** duplicate:\n");			}		}	unlock(&dnlock);	Bterm(&b);	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:		case Tspf:			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;		case Tsig:			MARK(rp->sig->signer);			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;	int i;	RR *rp;	static ulong nextage; 	if(dnvars.names < target || (now < nextage && !doit)){

Lines 1–500 of 2094.