henesyinit from p9fd787fda5 years agoHistory
1373 lines · 22.7 KB · C
RawDownload
#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.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t