| #include <u.h> | 
 | #include <libc.h> | 
 | #include <ip.h> | 
 | #include <ctype.h> | 
 | #include <bio.h> | 
 | #include <ndb.h> | 
 | #include <thread.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	lk; | 
 | 	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[Tall+2] = | 
 | { | 
 | 	nil, | 
 | 	"ip", | 
 | 	"ns", | 
 | 	"md", | 
 | 	"mf", | 
 | 	"cname", | 
 | 	"soa", | 
 | 	"mb", | 
 | 	"mg", | 
 | 	"mr", | 
 | 	"null", | 
 | 	"wks", | 
 | 	"ptr", | 
 | 	"hinfo", | 
 | 	"minfo", | 
 | 	"mx", | 
 | 	"txt", | 
 | 	"rp", | 
 | 	nil, | 
 | 	nil, | 
 | 	nil, | 
 | 	nil, | 
 | 	nil, | 
 | 	nil, | 
 | 	"sig", | 
 | 	"key", | 
 | 	nil, | 
 | 	nil, | 
 | 	"aaaa", | 
 | 	nil, | 
 | 	nil, | 
 | 	nil, | 
 | 	nil, | 
 | 	nil, | 
 | 	nil, | 
 | 	nil, | 
 | 	nil, | 
 | 	"cert", | 
 | 	nil, | 
 | 	nil, | 
 | 	 | 
 | /* 40 */	nil, nil, nil, nil, nil, nil, nil, nil, | 
 | /* 48 */	nil, nil, nil, nil, nil, nil, nil, nil, | 
 | /* 56 */	nil, nil, nil, nil, nil, nil, nil, nil, | 
 | /* 64 */	nil, nil, nil, nil, nil, nil, nil, nil, | 
 | /* 72 */	nil, nil, nil, nil, nil, nil, nil, nil, | 
 | /* 80 */	nil, nil, nil, nil, nil, nil, nil, nil, | 
 | /* 88 */	nil, nil, nil, nil, nil, nil, nil, nil, | 
 | /* 96 */	nil, nil, nil, nil, nil, nil, nil, nil, | 
 | /* 104 */	nil, nil, nil, nil, nil, nil, nil, nil, | 
 | /* 112 */	nil, nil, nil, nil, nil, nil, nil, nil, | 
 | /* 120 */	nil, nil, nil, nil, nil, nil, nil, nil, | 
 | /* 128 */	nil, nil, nil, nil, nil, nil, nil, nil, | 
 | /* 136 */	nil, nil, nil, nil, nil, nil, nil, nil, | 
 | /* 144 */	nil, nil, nil, nil, nil, nil, nil, nil, | 
 | /* 152 */	nil, nil, nil, nil, nil, nil, nil, nil, | 
 | /* 160 */	nil, nil, nil, nil, nil, nil, nil, nil, | 
 | /* 168 */	nil, nil, nil, nil, nil, nil, nil, nil, | 
 | /* 176 */	nil, nil, nil, nil, nil, nil, nil, nil, | 
 | /* 184 */	nil, nil, nil, nil, nil, nil, nil, nil, | 
 | /* 192 */	nil, nil, nil, nil, nil, nil, nil, nil, | 
 | /* 200 */	nil, nil, nil, nil, nil, nil, nil, nil, | 
 | /* 208 */	nil, nil, nil, nil, nil, nil, nil, nil, | 
 | /* 216 */	nil, nil, nil, nil, nil, nil, nil, nil, | 
 | /* 224 */	nil, nil, nil, nil, nil, nil, nil, nil, | 
 | /* 232 */	nil, nil, nil, nil, nil, nil, nil, nil, | 
 | /* 240 */	nil, nil, nil, nil, nil, nil, nil, nil, | 
 | /* 248 */	nil, nil, nil,  | 
 |  | 
 | 	"ixfr", | 
 | 	"axfr", | 
 | 	"mailb", | 
 | 	nil, | 
 | 	"all", | 
 | 	nil | 
 | }; | 
 |  | 
 | /* names of response codes */ | 
 | char *rname[Rmask+1] = | 
 | { | 
 | 	"ok", | 
 | 	"format error", | 
 | 	"server failure", | 
 | 	"bad name", | 
 | 	"unimplemented", | 
 | 	"we don't like you" | 
 | }; | 
 |  | 
 | 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, *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); | 
 | } | 
 |  | 
 | /* | 
 |  *  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: | 
 | 					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->rp); | 
 | 					break; | 
 | 				case Tmx: | 
 | 					REF(rp->host); | 
 | 					break; | 
 | 				case Ta: | 
 | 				case Taaaa: | 
 | 					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){ | 
 | 				assert(dp->magic == DNmagic); | 
 | 				*l = dp->next; | 
 | 				if(dp->name) | 
 | 					free(dp->name); | 
 | 				dp->magic = ~dp->magic; | 
 | 				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; | 
 |  | 
 | 	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; | 
 |  | 
 | 	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; | 
 |  | 
 | 	if(traceactivity) syslog(0, "dns", "get %d by %d.%d", dnvars.active, getpid(), threadid()); | 
 | 	lock(&dnvars.lk); | 
 | 	while(dnvars.mutex){ | 
 | 		unlock(&dnvars.lk); | 
 | 		sleep(200); | 
 | 		lock(&dnvars.lk); | 
 | 	} | 
 | 	rv = ++dnvars.active; | 
 | 	now = time(0); | 
 | 	req->id = ++dnvars.id; | 
 | 	unlock(&dnvars.lk); | 
 |  | 
 | 	return rv; | 
 | } | 
 | void | 
 | putactivity(void) | 
 | { | 
 | 	if(traceactivity) syslog(0, "dns", "put %d by %d.%d", dnvars.active, getpid(), threadid()); | 
 | 	lock(&dnvars.lk); | 
 | 	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.lk); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	/* wait till we're alone */ | 
 | 	dnvars.mutex = 1; | 
 | 	while(dnvars.active > 0){ | 
 | 		unlock(&dnvars.lk); | 
 | 		sleep(100); | 
 | 		lock(&dnvars.lk); | 
 | 	} | 
 | 	unlock(&dnvars.lk); | 
 |  | 
 | 	db2cache(needrefresh); | 
 | 	dnageall(0); | 
 |  | 
 | 	/* let others back in */ | 
 | 	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 | 
 | 	 */ | 
 | 	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; | 
 | 				rp->cached = 0; | 
 | 				rrfree(rp); | 
 | 				continue; | 
 | 			} | 
 |  | 
 | 			/* all things equal, pick the newer one */ | 
 | 			if(rp->arg0 == new->arg0 && rp->arg1 == new->arg1){ | 
 | 				/* new drives out old */ | 
 | 				if(new->ttl > rp->ttl || new->expire > rp->expire){ | 
 | 					*l = rp->next; | 
 | 					rp->cached = 0; | 
 | 					rrfree(rp); | 
 | 					continue; | 
 | 				} else { | 
 | 					rrfree(new); | 
 | 					return; | 
 | 				} | 
 | 			} | 
 |  | 
 | 			/*  Hack for pointer records.  This makes sure | 
 | 			 *  the ordering in the list reflects the ordering | 
 | 			 *  received or read from the database | 
 | 			 */ | 
 | 			if(rp->type == Tptr){ | 
 | 				if(!rp->negative && !new->negative | 
 | 				&& rp->ptr->ordinal > new->ptr->ordinal) | 
 | 					break; | 
 | 			} | 
 | 		} | 
 | 		l = &rp->next; | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 *  add to chain | 
 | 	 */ | 
 | 	new->cached = 1; | 
 | 	new->next = *l; | 
 | 	*l = new; | 
 | } | 
 |  | 
 | /* | 
 |  *  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; | 
 |  | 
 | 		/* avoid any outside spoofing */ | 
 | 		if(cachedb && !rp->db && inmyarea(rp->owner->name)) | 
 | 			rrfree(rp); | 
 | 		else | 
 | 			rrattach1(rp, auth); | 
 | 	} | 
 | 	unlock(&dnlock); | 
 | } | 
 |  | 
 | /* | 
 |  *  allocate a resource record of a given type | 
 |  */ | 
 | RR* | 
 | rralloc(int type) | 
 | { | 
 | 	RR *rp; | 
 |  | 
 | 	rp = emalloc(sizeof(*rp)); | 
 | 	rp->magic = RRmagic; | 
 | 	rp->pc = getcallerpc(&type); | 
 | 	rp->type = type; | 
 | 	switch(type){ | 
 | 	case Tsoa: | 
 | 		rp->soa = emalloc(sizeof(*rp->soa)); | 
 | 		rp->soa->slaves = nil; | 
 | 		break; | 
 | 	case Tkey: | 
 | 		rp->key = emalloc(sizeof(*rp->key)); | 
 | 		break; | 
 | 	case Tcert: | 
 | 		rp->cert = emalloc(sizeof(*rp->cert)); | 
 | 		break; | 
 | 	case Tsig: | 
 | 		rp->sig = emalloc(sizeof(*rp->sig)); | 
 | 		break; | 
 | 	case Tnull: | 
 | 		rp->null = emalloc(sizeof(*rp->null)); | 
 | 		break; | 
 | 	} | 
 | 	rp->ttl = 0; | 
 | 	rp->expire = 0; | 
 | 	rp->next = 0; | 
 | 	return rp; | 
 | } | 
 |  | 
 | /* | 
 |  *  free a resource record and any related structs | 
 |  */ | 
 | void | 
 | rrfree(RR *rp) | 
 | { | 
 | 	DN *dp; | 
 | 	RR *nrp; | 
 | 	Txt *t; | 
 |  | 
 | 	assert(rp->magic = RRmagic); | 
 | 	assert(!rp->cached); | 
 |  | 
 | 	dp = rp->owner; | 
 | 	if(dp){ | 
 | 		assert(dp->magic == DNmagic); | 
 | 		for(nrp = dp->rr; nrp; nrp = nrp->next) | 
 | 			assert(nrp != rp); /* "rrfree of live rr" */; | 
 | 	} | 
 |  | 
 | 	switch(rp->type){ | 
 | 	case Tsoa: | 
 | 		freeserverlist(rp->soa->slaves); | 
 | 		free(rp->soa); | 
 | 		break; | 
 | 	case Tkey: | 
 | 		free(rp->key->data); | 
 | 		free(rp->key); | 
 | 		break; | 
 | 	case Tcert: | 
 | 		free(rp->cert->data); | 
 | 		free(rp->cert); | 
 | 		break; | 
 | 	case Tsig: | 
 | 		free(rp->sig->data); | 
 | 		free(rp->sig); | 
 | 		break; | 
 | 	case Tnull: | 
 | 		free(rp->null->data); | 
 | 		free(rp->null); | 
 | 		break; | 
 | 	case Ttxt: | 
 | 		while(rp->txt != nil){ | 
 | 			t = rp->txt; | 
 | 			rp->txt = t->next; | 
 | 			free(t->p); | 
 | 			free(t); | 
 | 		} | 
 | 		break; | 
 | 	} | 
 |  | 
 | 	rp->magic = ~rp->magic; | 
 | 	free(rp); | 
 | } | 
 |  | 
 | /* | 
 |  *  free a list of resource records and any related structs | 
 |  */ | 
 | void | 
 | rrfreelist(RR *rp) | 
 | { | 
 | 	RR *next; | 
 |  | 
 | 	for(; rp; rp = next){ | 
 | 		next = rp->next; | 
 | 		rrfree(rp); | 
 | 	} | 
 | } | 
 |  | 
 | extern RR** | 
 | rrcopy(RR *rp, RR **last) | 
 | { | 
 | 	RR *nrp; | 
 | 	SOA *soa; | 
 | 	Key *key; | 
 | 	Cert *cert; | 
 | 	Sig *sig; | 
 | 	Null *null; | 
 | 	Txt *t, *nt, **l; | 
 |  | 
 | 	nrp = rralloc(rp->type); | 
 | 	switch(rp->type){ | 
 | 	case Ttxt: | 
 | 		*nrp = *rp; | 
 | 		l = &nrp->txt; | 
 | 		*l = nil; | 
 | 		for(t = rp->txt; t != nil; t = t->next){ | 
 | 			nt = emalloc(sizeof(*nt)); | 
 | 			nt->p = estrdup(t->p); | 
 | 			nt->next = nil; | 
 | 			*l = nt; | 
 | 			l = &nt->next; | 
 | 		} | 
 | 		break; | 
 | 	case Tsoa: | 
 | 		soa = nrp->soa; | 
 | 		*nrp = *rp; | 
 | 		nrp->soa = soa; | 
 | 		*nrp->soa = *rp->soa; | 
 | 		nrp->soa->slaves = copyserverlist(rp->soa->slaves); | 
 | 		break; | 
 | 	case Tkey: | 
 | 		key = nrp->key; | 
 | 		*nrp = *rp; | 
 | 		nrp->key = key; | 
 | 		*key = *rp->key; | 
 | 		key->data = emalloc(key->dlen); | 
 | 		memmove(key->data, rp->key->data, rp->key->dlen); | 
 | 		break; | 
 | 	case Tsig: | 
 | 		sig = nrp->sig; | 
 | 		*nrp = *rp; | 
 | 		nrp->sig = sig; | 
 | 		*sig = *rp->sig; | 
 | 		sig->data = emalloc(sig->dlen); | 
 | 		memmove(sig->data, rp->sig->data, rp->sig->dlen); | 
 | 		break; | 
 | 	case Tcert: | 
 | 		cert = nrp->cert; | 
 | 		*nrp = *rp; | 
 | 		nrp->cert = cert; | 
 | 		*cert = *rp->cert; | 
 | 		cert->data = emalloc(cert->dlen); | 
 | 		memmove(cert->data, rp->cert->data, rp->cert->dlen); | 
 | 		break; | 
 | 	case Tnull: | 
 | 		null = nrp->null; | 
 | 		*nrp = *rp; | 
 | 		nrp->null = null; | 
 | 		*null = *rp->null; | 
 | 		null->data = emalloc(null->dlen); | 
 | 		memmove(null->data, rp->null->data, rp->null->dlen); | 
 | 		break; | 
 | 	default: | 
 | 		*nrp = *rp; | 
 | 		break; | 
 | 	} | 
 | 	nrp->cached = 0; | 
 | 	nrp->next = 0; | 
 | 	*last = nrp; | 
 | 	return &nrp->next; | 
 | } | 
 |  | 
 | /* | 
 |  *  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); | 
 | 		} | 
 | 	} | 
 |  | 
 | out: | 
 | 	unlock(&dnlock); | 
 | 	unique(first); | 
 | 	return first; | 
 | } | 
 |  | 
 | /* | 
 |  *  convert an ascii RR type name to its integer representation | 
 |  */ | 
 | int | 
 | rrtype(char *atype) | 
 | { | 
 | 	int i; | 
 |  | 
 | 	for(i = 0; i <= Tall; i++) | 
 | 		if(rrtname[i] && strcmp(rrtname[i], atype) == 0) | 
 | 			return i; | 
 |  | 
 | 	/* make any a synonym for all */ | 
 | 	if(strcmp(atype, "any") == 0) | 
 | 		return Tall; | 
 | 	return atoi(atype); | 
 | } | 
 |  | 
 | /* | 
 |  *  convert an integer RR type to it's ascii name | 
 |  */ | 
 | char* | 
 | rrname(int type, char *buf, int len) | 
 | { | 
 | 	char *t; | 
 |  | 
 | 	t = 0; | 
 | 	if(type <= Tall) | 
 | 		t = rrtname[type]; | 
 | 	if(t==0){ | 
 | 		snprint(buf, len, "%d", type); | 
 | 		t = buf; | 
 | 	} | 
 | 	return t; | 
 | } | 
 |  | 
 | /* | 
 |  *  return 0 if not a supported rr type | 
 |  */ | 
 | int | 
 | rrsupported(int type) | 
 | { | 
 | 	if(type < 0 || type >Tall) | 
 | 		return 0; | 
 | 	return rrtname[type] != 0; | 
 | } | 
 |  | 
 | /* | 
 |  *  compare 2 types | 
 |  */ | 
 | int | 
 | tsame(int t1, int t2) | 
 | { | 
 | 	return t1 == t2 || t1 == Tall; | 
 | } | 
 |  | 
 | /* | 
 |  *  Add resource records to a list, duplicate them if they are cached | 
 |  *  RR's since these are shared. | 
 |  */ | 
 | RR* | 
 | rrcat(RR **start, RR *rp) | 
 | { | 
 | 	RR **last; | 
 |  | 
 | 	last = start; | 
 | 	while(*last != 0) | 
 | 		last = &(*last)->next; | 
 |  | 
 | 	*last = rp; | 
 | 	return *start; | 
 | } | 
 |  | 
 | /* | 
 |  *  remove negative cache rr's from an rr list | 
 |  */ | 
 | RR* | 
 | rrremneg(RR **l) | 
 | { | 
 | 	RR **nl, *rp; | 
 | 	RR *first; | 
 |  | 
 | 	first = nil; | 
 | 	nl = &first; | 
 | 	while(*l != nil){ | 
 | 		rp = *l; | 
 | 		if(rp->negative){ | 
 | 			*l = rp->next; | 
 | 			*nl = rp; | 
 | 			nl = &rp->next; | 
 | 			*nl = nil; | 
 | 		} else | 
 | 			l = &rp->next; | 
 | 	} | 
 |  | 
 | 	return first; | 
 | } | 
 |  | 
 | /* | 
 |  *  remove rr's of a particular type from an rr list | 
 |  */ | 
 | RR* | 
 | rrremtype(RR **l, int type) | 
 | { | 
 | 	RR **nl, *rp; | 
 | 	RR *first; | 
 |  | 
 | 	first = nil; | 
 | 	nl = &first; | 
 | 	while(*l != nil){ | 
 | 		rp = *l; | 
 | 		if(rp->type == type){ | 
 | 			*l = rp->next; | 
 | 			*nl = rp; | 
 | 			nl = &rp->next; | 
 | 			*nl = nil; | 
 | 		} else | 
 | 			l = &(*l)->next; | 
 | 	} | 
 |  | 
 | 	return first; | 
 | } | 
 |  | 
 | /* | 
 |  *  print conversion for rr records | 
 |  */ | 
 | int | 
 | rrfmt(Fmt *f) | 
 | { | 
 | 	RR *rp; | 
 | 	char *strp; | 
 | 	Fmt fstr; | 
 | 	int rv; | 
 | 	char buf[Domlen]; | 
 | 	Server *s; | 
 | 	Txt *t; | 
 |  | 
 | 	fmtstrinit(&fstr); | 
 |  | 
 | 	rp = va_arg(f->args, RR*); | 
 | 	if(rp == 0){ | 
 | 		fmtprint(&fstr, "<null>"); | 
 | 		goto out; | 
 | 	} | 
 |  | 
 | 	fmtprint(&fstr, "%s %s", rp->owner->name, | 
 | 		rrname(rp->type, buf, sizeof buf)); | 
 |  | 
 | 	if(rp->negative){ | 
 | 		fmtprint(&fstr, "\tnegative - rcode %d", rp->negrcode); | 
 | 		goto out; | 
 | 	} | 
 |  | 
 | 	switch(rp->type){ | 
 | 	case Thinfo: | 
 | 		fmtprint(&fstr, "\t%s %s", rp->cpu->name, rp->os->name); | 
 | 		break; | 
 | 	case Tcname: | 
 | 	case Tmb: | 
 | 	case Tmd: | 
 | 	case Tmf: | 
 | 	case Tns: | 
 | 		fmtprint(&fstr, "\t%s", rp->host->name); | 
 | 		break; | 
 | 	case Tmg: | 
 | 	case Tmr: | 
 | 		fmtprint(&fstr, "\t%s", rp->mb->name); | 
 | 		break; | 
 | 	case Tminfo: | 
 | 		fmtprint(&fstr, "\t%s %s", rp->mb->name, rp->rmb->name); | 
 | 		break; | 
 | 	case Tmx: | 
 | 		fmtprint(&fstr, "\t%lud %s", rp->pref, rp->host->name); | 
 | 		break; | 
 | 	case Ta: | 
 | 	case Taaaa: | 
 | 		fmtprint(&fstr, "\t%s", rp->ip->name); | 
 | 		break; | 
 | 	case Tptr: | 
 | /*		fmtprint(&fstr, "\t%s(%lud)", rp->ptr->name, rp->ptr->ordinal); */ | 
 | 		fmtprint(&fstr, "\t%s", rp->ptr->name); | 
 | 		break; | 
 | 	case Tsoa: | 
 | 		fmtprint(&fstr, "\t%s %s %lud %lud %lud %lud %lud", rp->host->name, | 
 | 			rp->rmb->name, rp->soa->serial, rp->soa->refresh, rp->soa->retry, | 
 | 			rp->soa->expire, rp->soa->minttl); | 
 | 		for(s = rp->soa->slaves; s != nil; s = s->next) | 
 | 			fmtprint(&fstr, " %s", s->name); | 
 | 		break; | 
 | 	case Tnull: | 
 | 		fmtprint(&fstr, "\t%.*H", rp->null->dlen, rp->null->data); | 
 | 		break; | 
 | 	case Ttxt: | 
 | 		fmtprint(&fstr, "\t"); | 
 | 		for(t = rp->txt; t != nil; t = t->next) | 
 | 			fmtprint(&fstr, "%s", t->p); | 
 | 		break; | 
 | 	case Trp: | 
 | 		fmtprint(&fstr, "\t%s %s", rp->rmb->name, rp->rp->name); | 
 | 		break; | 
 | 	case Tkey: | 
 | 		fmtprint(&fstr, "\t%d %d %d", rp->key->flags, rp->key->proto, | 
 | 			rp->key->alg); | 
 | 		break; | 
 | 	case Tsig: | 
 | 		fmtprint(&fstr, "\t%d %d %d %lud %lud %lud %d %s", | 
 | 			rp->sig->type, rp->sig->alg, rp->sig->labels, rp->sig->ttl, | 
 | 			rp->sig->exp, rp->sig->incep, rp->sig->tag, rp->sig->signer->name); | 
 | 		break; | 
 | 	case Tcert: | 
 | 		fmtprint(&fstr, "\t%d %d %d", | 
 | 			rp->sig->type, rp->sig->tag, rp->sig->alg); | 
 | 		break; | 
 | 	default: | 
 | 		break; | 
 | 	} | 
 | out: | 
 | 	strp = fmtstrflush(&fstr); | 
 | 	rv = fmtstrcpy(f, strp); | 
 | 	free(strp); | 
 | 	return rv; | 
 | } | 
 |  | 
 | /* | 
 |  *  print conversion for rr records in attribute value form | 
 |  */ | 
 | int | 
 | rravfmt(Fmt *f) | 
 | { | 
 | 	RR *rp; | 
 | 	char *strp; | 
 | 	Fmt fstr; | 
 | 	int rv; | 
 | 	Server *s; | 
 | 	Txt *t; | 
 | 	int quote; | 
 |  | 
 | 	fmtstrinit(&fstr); | 
 |  | 
 | 	rp = va_arg(f->args, RR*); | 
 | 	if(rp == 0){ | 
 | 		fmtprint(&fstr, "<null>"); | 
 | 		goto out; | 
 | 	} | 
 |  | 
 | 	if(rp->type == Tptr) | 
 | 		fmtprint(&fstr, "ptr=%s", rp->owner->name); | 
 | 	else | 
 | 		fmtprint(&fstr, "dom=%s", rp->owner->name); | 
 |  | 
 | 	switch(rp->type){ | 
 | 	case Thinfo: | 
 | 		fmtprint(&fstr, " cpu=%s os=%s", rp->cpu->name, rp->os->name); | 
 | 		break; | 
 | 	case Tcname: | 
 | 		fmtprint(&fstr, " cname=%s", rp->host->name); | 
 | 		break; | 
 | 	case Tmb: | 
 | 	case Tmd: | 
 | 	case Tmf: | 
 | 		fmtprint(&fstr, " mbox=%s", rp->host->name); | 
 | 		break; | 
 | 	case Tns: | 
 | 		fmtprint(&fstr,  " ns=%s", rp->host->name); | 
 | 		break; | 
 | 	case Tmg: | 
 | 	case Tmr: | 
 | 		fmtprint(&fstr, " mbox=%s", rp->mb->name); | 
 | 		break; | 
 | 	case Tminfo: | 
 | 		fmtprint(&fstr, " mbox=%s mbox=%s", rp->mb->name, rp->rmb->name); | 
 | 		break; | 
 | 	case Tmx: | 
 | 		fmtprint(&fstr, " pref=%lud mx=%s", rp->pref, rp->host->name); | 
 | 		break; | 
 | 	case Ta: | 
 | 	case Taaaa: | 
 | 		fmtprint(&fstr, " ip=%s", rp->ip->name); | 
 | 		break; | 
 | 	case Tptr: | 
 | 		fmtprint(&fstr, " dom=%s", rp->ptr->name); | 
 | 		break; | 
 | 	case Tsoa: | 
 | 		fmtprint(&fstr, " ns=%s mbox=%s serial=%lud refresh=%lud retry=%lud expire=%lud ttl=%lud", | 
 | 			rp->host->name, rp->rmb->name, rp->soa->serial, | 
 | 			rp->soa->refresh, rp->soa->retry, | 
 | 			rp->soa->expire, rp->soa->minttl); | 
 | 		for(s = rp->soa->slaves; s != nil; s = s->next) | 
 | 			fmtprint(&fstr, " dnsslave=%s", s->name); | 
 | 		break; | 
 | 	case Tnull: | 
 | 		fmtprint(&fstr, " null=%.*H", rp->null->dlen, rp->null->data); | 
 | 		break; | 
 | 	case Ttxt: | 
 | 		fmtprint(&fstr, " txt="); | 
 | 		quote = 0; | 
 | 		for(t = rp->txt; t != nil; t = t->next) | 
 | 			if(strchr(t->p, ' ')) | 
 | 				quote = 1; | 
 | 		if(quote) | 
 | 			fmtprint(&fstr, "\""); | 
 | 		for(t = rp->txt; t != nil; t = t->next) | 
 | 			fmtprint(&fstr, "%s", t->p); | 
 | 		if(quote) | 
 | 			fmtprint(&fstr, "\""); | 
 | 		break; | 
 | 	case Trp: | 
 | 		fmtprint(&fstr, " rp=%s txt=%s", rp->rmb->name, rp->rp->name); | 
 | 		break; | 
 | 	case Tkey: | 
 | 		fmtprint(&fstr, " flags=%d proto=%d alg=%d", | 
 | 			rp->key->flags, rp->key->proto, rp->key->alg); | 
 | 		break; | 
 | 	case Tsig: | 
 | 		fmtprint(&fstr, " type=%d alg=%d labels=%d ttl=%lud exp=%lud incep=%lud tag=%d signer=%s", | 
 | 			rp->sig->type, rp->sig->alg, rp->sig->labels, rp->sig->ttl, | 
 | 			rp->sig->exp, rp->sig->incep, rp->sig->tag, rp->sig->signer->name); | 
 | 		break; | 
 | 	case Tcert: | 
 | 		fmtprint(&fstr, " type=%d tag=%d alg=%d", | 
 | 			rp->sig->type, rp->sig->tag, rp->sig->alg); | 
 | 		break; | 
 | 	default: | 
 | 		break; | 
 | 	} | 
 | out: | 
 | 	strp = fmtstrflush(&fstr); | 
 | 	rv = fmtstrcpy(f, strp); | 
 | 	free(strp); | 
 | 	return rv; | 
 | } | 
 |  | 
 | void | 
 | warning(char *fmt, ...) | 
 | { | 
 | 	char dnserr[128]; | 
 | 	va_list arg; | 
 |  | 
 | 	va_start(arg, fmt); | 
 | 	vseprint(dnserr, dnserr+sizeof(dnserr), fmt, arg); | 
 | 	va_end(arg); | 
 | 	syslog(1, "dns", dnserr); | 
 | } | 
 |  | 
 | /* | 
 |  *  chasing down double free's | 
 |  */ | 
 | void | 
 | dncheck(void *p, int dolock) | 
 | { | 
 | 	DN *dp; | 
 | 	int i; | 
 | 	RR *rp; | 
 |  | 
 | 	if(p != nil){ | 
 | 		dp = p; | 
 | 		assert(dp->magic == DNmagic); | 
 | 	} | 
 |  | 
 | 	if(!testing) | 
 | 		return; | 
 |  | 
 | 	if(dolock) | 
 | 		lock(&dnlock); | 
 | 	for(i = 0; i < HTLEN; i++) | 
 | 		for(dp = ht[i]; dp; dp = dp->next){ | 
 | 			assert(dp != p); | 
 | 			assert(dp->magic == DNmagic); | 
 | 			for(rp = dp->rr; rp; rp = rp->next){ | 
 | 				assert(rp->magic == RRmagic); | 
 | 				assert(rp->cached); | 
 | 				assert(rp->owner == dp); | 
 | 			} | 
 | 		} | 
 | 	if(dolock) | 
 | 		unlock(&dnlock); | 
 | } | 
 |  | 
 | static int | 
 | rrequiv(RR *r1, RR *r2) | 
 | { | 
 | 	return r1->owner == r2->owner | 
 | 		&& r1->type == r2->type | 
 | 		&& r1->arg0 == r2->arg0 | 
 | 		&& r1->arg1 == r2->arg1; | 
 | } | 
 |  | 
 | void | 
 | unique(RR *rp) | 
 | { | 
 | 	RR **l, *nrp; | 
 |  | 
 | 	for(; rp; rp = rp->next){ | 
 | 		l = &rp->next; | 
 | 		for(nrp = *l; nrp; nrp = *l){ | 
 | 			if(rrequiv(rp, nrp)){ | 
 | 				*l = nrp->next; | 
 | 				rrfree(nrp); | 
 | 			} else | 
 | 				l = &nrp->next; | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | /* | 
 |  *  true if second domain is subsumed by the first | 
 |  */ | 
 | int | 
 | subsume(char *higher, char *lower) | 
 | { | 
 | 	int hn, ln; | 
 |  | 
 | 	ln = strlen(lower); | 
 | 	hn = strlen(higher); | 
 | 	if(ln < hn) | 
 | 		return 0; | 
 |  | 
 | 	if(cistrcmp(lower + ln - hn, higher) != 0) | 
 | 		return 0; | 
 |  | 
 | 	if(ln > hn && hn != 0 && lower[ln - hn - 1] != '.') | 
 | 		return 0; | 
 |  | 
 | 	return 1; | 
 | } | 
 |  | 
 | /* | 
 |  *  randomize the order we return items to provide some | 
 |  *  load balancing for servers. | 
 |  * | 
 |  *  only randomize the first class of entries | 
 |  */ | 
 | RR* | 
 | randomize(RR *rp) | 
 | { | 
 | 	RR *first, *last, *x, *base; | 
 | 	ulong n; | 
 |  | 
 | 	if(rp == nil || rp->next == nil) | 
 | 		return rp; | 
 |  | 
 | 	/* just randomize addresses and mx's */ | 
 | 	for(x = rp; x; x = x->next) | 
 | 		if(x->type != Ta && x->type != Tmx && x->type != Tns) | 
 | 			return rp; | 
 |  | 
 | 	base = rp;  | 
 |  | 
 | 	n = rand(); | 
 | 	last = first = nil; | 
 | 	while(rp != nil){ | 
 | 		/* stop randomizing if we've moved past our class */ | 
 | 		if(base->auth != rp->auth || base->db != rp->db){ | 
 | 			last->next = rp; | 
 | 			break; | 
 | 		} | 
 |  | 
 | 		/* unchain */ | 
 | 		x = rp; | 
 | 		rp = x->next; | 
 | 		x->next = nil; | 
 |  | 
 | 		if(n&1){ | 
 | 			/* add to tail */ | 
 | 			if(last == nil) | 
 | 				first = x; | 
 | 			else | 
 | 				last->next = x; | 
 | 			last = x; | 
 | 		} else { | 
 | 			/* add to head */ | 
 | 			if(last == nil) | 
 | 				last = x; | 
 | 			x->next = first; | 
 | 			first = x; | 
 | 		} | 
 |  | 
 | 		/* reroll the dice */ | 
 | 		n >>= 1; | 
 | 	} | 
 | 	return first; | 
 | } | 
 |  | 
 | static int | 
 | sencodefmt(Fmt *f) | 
 | { | 
 | 	char *out; | 
 | 	char *buf; | 
 | 	int i, len; | 
 | 	int ilen; | 
 | 	int rv; | 
 | 	uchar *b; | 
 | 	char obuf[64];	/* rsc optimization */ | 
 |  | 
 | 	if(!(f->flags&FmtPrec) || f->prec < 1) | 
 | 		goto error; | 
 |  | 
 | 	b = va_arg(f->args, uchar*); | 
 | 	if(b == nil) | 
 | 		goto error; | 
 |  | 
 | 	/* if it's a printable, go for it */ | 
 | 	len = f->prec; | 
 | 	for(i = 0; i < len; i++) | 
 | 		if(!isprint(b[i])) | 
 | 			break; | 
 | 	if(i == len){ | 
 | 		if(len >= sizeof obuf) | 
 | 			len = sizeof(obuf)-1; | 
 | 		memmove(obuf, b, len); | 
 | 		obuf[len] = 0; | 
 | 		fmtstrcpy(f, obuf); | 
 | 		return 0; | 
 | 	} | 
 |  | 
 | 	ilen = f->prec; | 
 | 	f->prec = 0; | 
 | 	f->flags &= ~FmtPrec; | 
 | 	switch(f->r){ | 
 | 	case '<': | 
 | 		len = (8*ilen+4)/5 + 3; | 
 | 		break; | 
 | 	case '[': | 
 | 		len = (8*ilen+5)/6 + 4; | 
 | 		break; | 
 | 	case 'H': | 
 | 		len = 2*ilen + 1; | 
 | 		break; | 
 | 	default: | 
 | 		goto error; | 
 | 	} | 
 |  | 
 | 	if(len > sizeof(obuf)){ | 
 | 		buf = malloc(len); | 
 | 		if(buf == nil) | 
 | 			goto error; | 
 | 	} else | 
 | 		buf = obuf; | 
 |  | 
 | 	/* convert */ | 
 | 	out = buf; | 
 | 	switch(f->r){ | 
 | 	case '<': | 
 | 		rv = enc32(out, len, b, ilen); | 
 | 		break; | 
 | 	case '[': | 
 | 		rv = enc64(out, len, b, ilen); | 
 | 		break; | 
 | 	case 'H': | 
 | 		rv = enc16(out, len, b, ilen); | 
 | 		break; | 
 | 	default: | 
 | 		rv = -1; | 
 | 		break; | 
 | 	} | 
 | 	if(rv < 0) | 
 | 		goto error; | 
 |  | 
 | 	fmtstrcpy(f, buf); | 
 | 	if(buf != obuf) | 
 | 		free(buf); | 
 | 	return 0; | 
 |  | 
 | error: | 
 | 	return fmtstrcpy(f, "<encodefmt>"); | 
 |  | 
 | } | 
 |  | 
 | void* | 
 | emalloc(int size) | 
 | { | 
 | 	char *x; | 
 |  | 
 | 	x = mallocz(size, 1); | 
 | 	if(x == nil) | 
 | 		abort(); | 
 | 	setmalloctag(x, getcallerpc(&size)); | 
 | 	return x; | 
 | } | 
 |  | 
 | char* | 
 | estrdup(char *s) | 
 | { | 
 | 	int size; | 
 | 	char *p; | 
 |  | 
 | 	size = strlen(s)+1; | 
 | 	p = mallocz(size, 0); | 
 | 	if(p == nil) | 
 | 		abort(); | 
 | 	memmove(p, s, size); | 
 | 	setmalloctag(p, getcallerpc(&s)); | 
 | 	return p; | 
 | } | 
 |  | 
 | /* | 
 |  *  create a pointer record | 
 |  */ | 
 | static RR* | 
 | mkptr(DN *dp, char *ptr, ulong ttl) | 
 | { | 
 | 	DN *ipdp; | 
 | 	RR *rp; | 
 |  | 
 | 	ipdp = dnlookup(ptr, Cin, 1); | 
 |  | 
 | 	rp = rralloc(Tptr); | 
 | 	rp->ptr = dp; | 
 | 	rp->owner = ipdp; | 
 | 	rp->db = 1; | 
 | 	if(ttl) | 
 | 		rp->ttl = ttl; | 
 | 	return rp; | 
 | } | 
 |  | 
 | /* | 
 |  *  look for all ip addresses in this network and make | 
 |  *  pointer records for them. | 
 |  */ | 
 | void | 
 | dnptr(uchar *net, uchar *mask, char *dom, int bytes, int ttl) | 
 | { | 
 | 	int i, j; | 
 | 	DN *dp; | 
 | 	RR *rp, *nrp, *first, **l; | 
 | 	uchar ip[IPaddrlen]; | 
 | 	uchar nnet[IPaddrlen]; | 
 | 	char ptr[Domlen]; | 
 | 	char *p, *e; | 
 |  | 
 | 	l = &first; | 
 | 	first = nil; | 
 | 	for(i = 0; i < HTLEN; i++){ | 
 | 		for(dp = ht[i]; dp; dp = dp->next){ | 
 | 			for(rp = dp->rr; rp; rp = rp->next){ | 
 | 				if(rp->type != Ta || rp->negative) | 
 | 					continue; | 
 | 				parseip(ip, rp->ip->name); | 
 | 				maskip(ip, mask, nnet); | 
 | 				if(ipcmp(net, nnet) != 0) | 
 | 					continue; | 
 | 				p = ptr; | 
 | 				e = ptr+sizeof(ptr); | 
 | 				for(j = IPaddrlen-1; j >= IPaddrlen-bytes; j--) | 
 | 					p = seprint(p, e, "%d.", ip[j]); | 
 | 				seprint(p, e, "%s", dom); | 
 | 				nrp = mkptr(dp, ptr, ttl); | 
 | 				*l = nrp; | 
 | 				l = &nrp->next; | 
 | 			} | 
 | 		} | 
 | 	} | 
 |  | 
 | 	for(rp = first; rp != nil; rp = nrp){ | 
 | 		nrp = rp->next; | 
 | 		rp->next = nil; | 
 | 		rrattach(rp, 1); | 
 | 	} | 
 | } | 
 |  | 
 | void | 
 | freeserverlist(Server *s) | 
 | { | 
 | 	Server *next; | 
 |  | 
 | 	for(; s != nil; s = next){ | 
 | 		next = s->next; | 
 | 		free(s); | 
 | 	} | 
 | } | 
 |  | 
 | void | 
 | addserver(Server **l, char *name) | 
 | { | 
 | 	Server *s; | 
 |  | 
 | 	while(*l) | 
 | 		l = &(*l)->next; | 
 | 	s = malloc(sizeof(Server)+strlen(name)+1); | 
 | 	if(s == nil) | 
 | 		return; | 
 | 	s->name = (char*)(s+1); | 
 | 	strcpy(s->name, name); | 
 | 	s->next = nil; | 
 | 	*l = s; | 
 | } | 
 |  | 
 | Server* | 
 | copyserverlist(Server *s) | 
 | { | 
 | 	Server *ns; | 
 |  | 
 | 	 | 
 | 	for(ns = nil; s != nil; s = s->next) | 
 | 		addserver(&ns, s->name); | 
 | 	return ns; | 
 | } |