| #include <u.h> | 
 | #include <libc.h> | 
 | #include <ip.h> | 
 | #include <bio.h> | 
 | #include <ndb.h> | 
 | #include "dns.h" | 
 |  | 
 | static RR*	doextquery(DNSmsg*, Request*, int); | 
 | static void	hint(RR**, RR*); | 
 |  | 
 | extern char *logfile; | 
 |  | 
 | /* | 
 |  *  answer a dns request | 
 |  */ | 
 | void | 
 | dnserver(DNSmsg *reqp, DNSmsg *repp, Request *req) | 
 | { | 
 | 	RR *tp, *neg; | 
 | 	char *cp; | 
 | 	DN *nsdp, *dp; | 
 | 	Area *myarea; | 
 | 	char tname[32]; | 
 |  | 
 | 	dncheck(nil, 1); | 
 |  | 
 | 	memset(repp, 0, sizeof(*repp)); | 
 | 	repp->id = reqp->id; | 
 | 	repp->flags = Fresp | Fcanrec | Oquery; | 
 |  | 
 | 	/* move one question from reqp to repp */ | 
 | 	tp = reqp->qd; | 
 | 	reqp->qd = tp->next; | 
 | 	tp->next = 0; | 
 | 	repp->qd = tp; | 
 |  | 
 | 	if(!rrsupported(repp->qd->type)){ | 
 | 		syslog(0, logfile, "server: request %s", rrname(repp->qd->type, tname, sizeof tname)); | 
 | 		repp->flags = Runimplimented | Fresp | Fcanrec | Oquery; | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	if(repp->qd->owner->class != Cin){ | 
 | 		syslog(0, logfile, "server: class %d", repp->qd->owner->class); | 
 | 		repp->flags = Runimplimented | Fresp | Fcanrec | Oquery; | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	myarea = inmyarea(repp->qd->owner->name); | 
 | 	if(myarea != nil && (repp->qd->type == Tixfr || repp->qd->type == Taxfr)){ | 
 | 		syslog(0, logfile, "server: request %s", rrname(repp->qd->type, tname, sizeof tname)); | 
 | 		repp->flags = Runimplimented | Fresp | Fcanrec | Oquery; | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 *  get the answer if we can | 
 | 	 */ | 
 | 	if(reqp->flags & Frecurse) | 
 | 		neg = doextquery(repp, req, Recurse); | 
 | 	else | 
 | 		neg = doextquery(repp, req, Dontrecurse); | 
 |  | 
 | 	/* authority is transitive */ | 
 | 	if(myarea != nil || (repp->an && repp->an->auth)) | 
 | 		repp->flags |= Fauth; | 
 |  | 
 | 	/* pass on error codes */ | 
 | 	if(repp->an == 0){ | 
 | 		dp = dnlookup(repp->qd->owner->name, repp->qd->owner->class, 0); | 
 | 		if(dp->rr == 0) | 
 | 			if(reqp->flags & Frecurse) | 
 | 				repp->flags |= dp->nonexistent|Fauth; | 
 | 	} | 
 |  | 
 | 	if(myarea == nil){ | 
 | 		/* | 
 | 		 *  add name server if we know | 
 | 		 */ | 
 | 		for(cp = repp->qd->owner->name; cp; cp = walkup(cp)){ | 
 | 			nsdp = dnlookup(cp, repp->qd->owner->class, 0); | 
 | 			if(nsdp == 0) | 
 | 				continue; | 
 | 	 | 
 | 			repp->ns = rrlookup(nsdp, Tns, OKneg); | 
 | 			if(repp->ns){ | 
 | 				/* don't pass on anything we know is wrong */ | 
 | 				if(repp->ns->negative){ | 
 | 					rrfreelist(repp->ns); | 
 | 					repp->ns = nil; | 
 | 				} | 
 | 				break; | 
 | 			} | 
 | 	 | 
 | 			repp->ns = dblookup(cp, repp->qd->owner->class, Tns, 0, 0); | 
 | 			if(repp->ns) | 
 | 				break; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 *  add ip addresses as hints | 
 | 	 */ | 
 | 	if(repp->qd->type != Taxfr && repp->qd->type != Tixfr){ | 
 | 		for(tp = repp->ns; tp; tp = tp->next) | 
 | 			hint(&repp->ar, tp); | 
 | 		for(tp = repp->an; tp; tp = tp->next) | 
 | 			hint(&repp->ar, tp); | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 *  add an soa to the authority section to help client with negative caching | 
 | 	 */ | 
 | 	if(repp->an == nil){ | 
 | 		if(myarea != nil){ | 
 | 			rrcopy(myarea->soarr, &tp); | 
 | 			rrcat(&repp->ns, tp); | 
 | 		} else if(neg != nil) { | 
 | 			if(neg->negsoaowner != nil) | 
 | 				rrcat(&repp->ns, rrlookup(neg->negsoaowner, Tsoa, NOneg)); | 
 | 			repp->flags |= neg->negrcode; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 *  get rid of duplicates | 
 | 	 */ | 
 | 	unique(repp->an); | 
 | 	unique(repp->ns); | 
 | 	unique(repp->ar); | 
 |  | 
 | 	rrfreelist(neg); | 
 |  | 
 | 	dncheck(nil, 1); | 
 | } | 
 |  | 
 | /* | 
 |  *  satisfy a recursive request.  dnlookup will handle cnames. | 
 |  */ | 
 | static RR* | 
 | doextquery(DNSmsg *mp, Request *req, int recurse) | 
 | { | 
 | 	int type; | 
 | 	char *name; | 
 | 	RR *rp, *neg; | 
 |  | 
 | 	name = mp->qd->owner->name; | 
 | 	type = mp->qd->type; | 
 | 	rp = dnresolve(name, Cin, type, req, &mp->an, 0, recurse, 1, 0); | 
 |  | 
 | 	/* don't return soa hints as answers, it's wrong */ | 
 | 	if(rp && rp->db && !rp->auth && rp->type == Tsoa) | 
 | 		rrfreelist(rp); | 
 |  | 
 | 	/* don't let negative cached entries escape */ | 
 | 	neg = rrremneg(&rp); | 
 | 	rrcat(&mp->an, rp); | 
 | 	return neg; | 
 | } | 
 |  | 
 | static void | 
 | hint(RR **last, RR *rp) | 
 | { | 
 | 	RR *hp; | 
 |  | 
 | 	switch(rp->type){ | 
 | 	case Tns: | 
 | 	case Tmx: | 
 | 	case Tmb: | 
 | 	case Tmf: | 
 | 	case Tmd: | 
 | 		hp = rrlookup(rp->host, Ta, NOneg); | 
 | 		if(hp == nil) | 
 | 			hp = dblookup(rp->host->name, Cin, Ta, 0, 0); | 
 | 		rrcat(last, hp); | 
 | 		break; | 
 | 	} | 
 | } |