xref: /plan9/sys/src/cmd/ndb/dnserver.c (revision 530fef6600a0fb31e4c0a6ecda1320e97bcd937c)
13e12c5d1SDavid du Colombier #include <u.h>
23e12c5d1SDavid du Colombier #include <libc.h>
3219b2ee8SDavid du Colombier #include <ip.h>
43e12c5d1SDavid du Colombier #include "dns.h"
53e12c5d1SDavid du Colombier 
67dd7cddfSDavid du Colombier static RR*	doextquery(DNSmsg*, Request*, int);
73e12c5d1SDavid du Colombier static void	hint(RR**, RR*);
83e12c5d1SDavid du Colombier 
9b85a8364SDavid du Colombier /* set in dns.c */
10b85a8364SDavid du Colombier int	norecursion;		/* don't allow recursive requests */
11b85a8364SDavid du Colombier 
123e12c5d1SDavid du Colombier /*
137dd7cddfSDavid du Colombier  *  answer a dns request
143e12c5d1SDavid du Colombier  */
153e12c5d1SDavid du Colombier void
164f8f669cSDavid du Colombier dnserver(DNSmsg *reqp, DNSmsg *repp, Request *req, uchar *srcip, int rcode)
173e12c5d1SDavid du Colombier {
184f8f669cSDavid du Colombier 	int recursionflag;
194f8f669cSDavid du Colombier 	char *cp, *errmsg;
204f8f669cSDavid du Colombier 	char tname[32];
217dd7cddfSDavid du Colombier 	DN *nsdp, *dp;
227dd7cddfSDavid du Colombier 	Area *myarea;
234f8f669cSDavid du Colombier 	RR *tp, *neg;
243e12c5d1SDavid du Colombier 
259a747e4fSDavid du Colombier 	dncheck(nil, 1);
269a747e4fSDavid du Colombier 
27b85a8364SDavid du Colombier 	recursionflag = norecursion? 0: Fcanrec;
287dd7cddfSDavid du Colombier 	memset(repp, 0, sizeof(*repp));
297dd7cddfSDavid du Colombier 	repp->id = reqp->id;
30b85a8364SDavid du Colombier 	repp->flags = Fresp | recursionflag | Oquery;
317dd7cddfSDavid du Colombier 
327dd7cddfSDavid du Colombier 	/* move one question from reqp to repp */
337dd7cddfSDavid du Colombier 	tp = reqp->qd;
347dd7cddfSDavid du Colombier 	reqp->qd = tp->next;
354f8f669cSDavid du Colombier 	tp->next = nil;
367dd7cddfSDavid du Colombier 	repp->qd = tp;
377dd7cddfSDavid du Colombier 
384f8f669cSDavid du Colombier 	if (rcode) {
394f8f669cSDavid du Colombier 		errmsg = "";
404f8f669cSDavid du Colombier 		if (rcode >= 0 && rcode < nrname)
414f8f669cSDavid du Colombier 			errmsg = rname[rcode];
424f8f669cSDavid du Colombier 		dnslog("server: response code 0%o (%s), req from %I",
434f8f669cSDavid du Colombier 			rcode, errmsg, srcip);
444f8f669cSDavid du Colombier 		/* provide feedback to clients who send us trash */
454f8f669cSDavid du Colombier 		repp->flags = (rcode&Rmask) | Fresp | Fcanrec | Oquery;
464f8f669cSDavid du Colombier 		return;
474f8f669cSDavid du Colombier 	}
485d459b5aSDavid du Colombier 	if(!rrsupported(repp->qd->type)){
494f8f669cSDavid du Colombier 		dnslog("server: unsupported request %s from %I",
504f8f669cSDavid du Colombier 			rrname(repp->qd->type, tname, sizeof tname), srcip);
515d459b5aSDavid du Colombier 		repp->flags = Runimplimented | Fresp | Fcanrec | Oquery;
525d459b5aSDavid du Colombier 		return;
535d459b5aSDavid du Colombier 	}
545d459b5aSDavid du Colombier 
557dd7cddfSDavid du Colombier 	if(repp->qd->owner->class != Cin){
564f8f669cSDavid du Colombier 		dnslog("server: unsupported class %d from %I",
574f8f669cSDavid du Colombier 			repp->qd->owner->class, srcip);
587dd7cddfSDavid du Colombier 		repp->flags = Runimplimented | Fresp | Fcanrec | Oquery;
593e12c5d1SDavid du Colombier 		return;
603e12c5d1SDavid du Colombier 	}
613e12c5d1SDavid du Colombier 
627dd7cddfSDavid du Colombier 	myarea = inmyarea(repp->qd->owner->name);
63b85a8364SDavid du Colombier 	if(myarea != nil) {
64b85a8364SDavid du Colombier 		if(repp->qd->type == Tixfr || repp->qd->type == Taxfr){
6576783259SDavid du Colombier 			dnslog("server: unsupported xfr request %s for %s from %I",
664f8f669cSDavid du Colombier 				rrname(repp->qd->type, tname, sizeof tname),
674f8f669cSDavid du Colombier 				repp->qd->owner->name, srcip);
684f8f669cSDavid du Colombier 			repp->flags = Runimplimented | Fresp | recursionflag |
694f8f669cSDavid du Colombier 				Oquery;
707dd7cddfSDavid du Colombier 			return;
713e12c5d1SDavid du Colombier 		}
724f8f669cSDavid du Colombier 	} else
73b85a8364SDavid du Colombier 		if(norecursion) {
74b85a8364SDavid du Colombier 			/* we don't recurse and we're not authoritative */
75b85a8364SDavid du Colombier 			repp->flags = Rok | Fresp | Oquery;
76b85a8364SDavid du Colombier 			return;
77b85a8364SDavid du Colombier 		}
783e12c5d1SDavid du Colombier 
793e12c5d1SDavid du Colombier 	/*
806d8e4566SDavid du Colombier 	 *  get the answer if we can, in *repp
813e12c5d1SDavid du Colombier 	 */
827dd7cddfSDavid du Colombier 	if(reqp->flags & Frecurse)
837dd7cddfSDavid du Colombier 		neg = doextquery(repp, req, Recurse);
843e12c5d1SDavid du Colombier 	else
857dd7cddfSDavid du Colombier 		neg = doextquery(repp, req, Dontrecurse);
867dd7cddfSDavid du Colombier 
877dd7cddfSDavid du Colombier 	/* authority is transitive */
887dd7cddfSDavid du Colombier 	if(myarea != nil || (repp->an && repp->an->auth))
897dd7cddfSDavid du Colombier 		repp->flags |= Fauth;
907dd7cddfSDavid du Colombier 
917dd7cddfSDavid du Colombier 	/* pass on error codes */
924f8f669cSDavid du Colombier 	if(repp->an == nil){
937dd7cddfSDavid du Colombier 		dp = dnlookup(repp->qd->owner->name, repp->qd->owner->class, 0);
944f8f669cSDavid du Colombier 		if(dp->rr == nil)
957dd7cddfSDavid du Colombier 			if(reqp->flags & Frecurse)
964f8f669cSDavid du Colombier 				repp->flags |= dp->respcode | Fauth;
977dd7cddfSDavid du Colombier 	}
987dd7cddfSDavid du Colombier 
994f8f669cSDavid du Colombier 	if(myarea == nil)
1007dd7cddfSDavid du Colombier 		/*
1017dd7cddfSDavid du Colombier 		 *  add name server if we know
1027dd7cddfSDavid du Colombier 		 */
1037dd7cddfSDavid du Colombier 		for(cp = repp->qd->owner->name; cp; cp = walkup(cp)){
1047dd7cddfSDavid du Colombier 			nsdp = dnlookup(cp, repp->qd->owner->class, 0);
1054f8f669cSDavid du Colombier 			if(nsdp == nil)
1067dd7cddfSDavid du Colombier 				continue;
1077dd7cddfSDavid du Colombier 
1087dd7cddfSDavid du Colombier 			repp->ns = rrlookup(nsdp, Tns, OKneg);
1097dd7cddfSDavid du Colombier 			if(repp->ns){
1107dd7cddfSDavid du Colombier 				/* don't pass on anything we know is wrong */
1117dd7cddfSDavid du Colombier 				if(repp->ns->negative){
1127dd7cddfSDavid du Colombier 					rrfreelist(repp->ns);
1137dd7cddfSDavid du Colombier 					repp->ns = nil;
1147dd7cddfSDavid du Colombier 				}
1157dd7cddfSDavid du Colombier 				break;
1167dd7cddfSDavid du Colombier 			}
1177dd7cddfSDavid du Colombier 
1186dc4800dSDavid du Colombier 			if (strncmp(nsdp->name, "local#", 6) == 0)
1196dc4800dSDavid du Colombier 				dnslog("returning %s as nameserver", nsdp->name);
1207dd7cddfSDavid du Colombier 			repp->ns = dblookup(cp, repp->qd->owner->class, Tns, 0, 0);
1217dd7cddfSDavid du Colombier 			if(repp->ns)
1227dd7cddfSDavid du Colombier 				break;
1237dd7cddfSDavid du Colombier 		}
1243e12c5d1SDavid du Colombier 
1253e12c5d1SDavid du Colombier 	/*
1263e12c5d1SDavid du Colombier 	 *  add ip addresses as hints
1273e12c5d1SDavid du Colombier 	 */
1287dd7cddfSDavid du Colombier 	if(repp->qd->type != Taxfr && repp->qd->type != Tixfr){
1297dd7cddfSDavid du Colombier 		for(tp = repp->ns; tp; tp = tp->next)
1307dd7cddfSDavid du Colombier 			hint(&repp->ar, tp);
1317dd7cddfSDavid du Colombier 		for(tp = repp->an; tp; tp = tp->next)
1327dd7cddfSDavid du Colombier 			hint(&repp->ar, tp);
1337dd7cddfSDavid du Colombier 	}
1343e12c5d1SDavid du Colombier 
135*530fef66SDavid du Colombier 	/* hint calls rrlookup which holds dnlock, so don't lock before this. */
136*530fef66SDavid du Colombier 
1377dd7cddfSDavid du Colombier 	/*
1384f8f669cSDavid du Colombier 	 *  add an soa to the authority section to help client
1394f8f669cSDavid du Colombier 	 *  with negative caching
1407dd7cddfSDavid du Colombier 	 */
1414f8f669cSDavid du Colombier 	if(repp->an == nil)
1427dd7cddfSDavid du Colombier 		if(myarea != nil){
143*530fef66SDavid du Colombier 			lock(&dnlock);
1447dd7cddfSDavid du Colombier 			rrcopy(myarea->soarr, &tp);
145b7b24591SDavid du Colombier 			rrcat(&repp->ns, tp);
146*530fef66SDavid du Colombier 			unlock(&dnlock);
1477dd7cddfSDavid du Colombier 		} else if(neg != nil) {
148*530fef66SDavid du Colombier 			if(neg->negsoaowner != nil) {
149*530fef66SDavid du Colombier 				tp = rrlookup(neg->negsoaowner, Tsoa, NOneg);
150*530fef66SDavid du Colombier 				lock(&dnlock);
151*530fef66SDavid du Colombier 				rrcat(&repp->ns, tp);
152*530fef66SDavid du Colombier 				unlock(&dnlock);
153*530fef66SDavid du Colombier 			}
1547dd7cddfSDavid du Colombier 			repp->flags |= neg->negrcode;
1553e12c5d1SDavid du Colombier 		}
156bd389b36SDavid du Colombier 
1577dd7cddfSDavid du Colombier 	/*
1587dd7cddfSDavid du Colombier 	 *  get rid of duplicates
1597dd7cddfSDavid du Colombier 	 */
160*530fef66SDavid du Colombier 	lock(&dnlock);
1617dd7cddfSDavid du Colombier 	unique(repp->an);
1627dd7cddfSDavid du Colombier 	unique(repp->ns);
1637dd7cddfSDavid du Colombier 	unique(repp->ar);
1647dd7cddfSDavid du Colombier 
1657dd7cddfSDavid du Colombier 	rrfreelist(neg);
166*530fef66SDavid du Colombier 	unlock(&dnlock);
1679a747e4fSDavid du Colombier 
1689a747e4fSDavid du Colombier 	dncheck(nil, 1);
1693e12c5d1SDavid du Colombier }
1703e12c5d1SDavid du Colombier 
1713e12c5d1SDavid du Colombier /*
1723e12c5d1SDavid du Colombier  *  satisfy a recursive request.  dnlookup will handle cnames.
1733e12c5d1SDavid du Colombier  */
1747dd7cddfSDavid du Colombier static RR*
1757dd7cddfSDavid du Colombier doextquery(DNSmsg *mp, Request *req, int recurse)
1763e12c5d1SDavid du Colombier {
177adb31a62SDavid du Colombier 	ushort type;
1783e12c5d1SDavid du Colombier 	char *name;
1797dd7cddfSDavid du Colombier 	RR *rp, *neg;
1803e12c5d1SDavid du Colombier 
1813e12c5d1SDavid du Colombier 	name = mp->qd->owner->name;
1823e12c5d1SDavid du Colombier 	type = mp->qd->type;
1837dd7cddfSDavid du Colombier 	rp = dnresolve(name, Cin, type, req, &mp->an, 0, recurse, 1, 0);
1843e12c5d1SDavid du Colombier 
185*530fef66SDavid du Colombier 	lock(&dnlock);
1867dd7cddfSDavid du Colombier 	/* don't return soa hints as answers, it's wrong */
1876dc4800dSDavid du Colombier 	if(rp && rp->db && !rp->auth && rp->type == Tsoa) {
1887dd7cddfSDavid du Colombier 		rrfreelist(rp);
1896dc4800dSDavid du Colombier 		rp = nil;
1906dc4800dSDavid du Colombier 	}
1913e12c5d1SDavid du Colombier 
1927dd7cddfSDavid du Colombier 	/* don't let negative cached entries escape */
1937dd7cddfSDavid du Colombier 	neg = rrremneg(&rp);
1947dd7cddfSDavid du Colombier 	rrcat(&mp->an, rp);
195*530fef66SDavid du Colombier 	unlock(&dnlock);
1967dd7cddfSDavid du Colombier 	return neg;
1973e12c5d1SDavid du Colombier }
1983e12c5d1SDavid du Colombier 
1993e12c5d1SDavid du Colombier static void
2003e12c5d1SDavid du Colombier hint(RR **last, RR *rp)
2013e12c5d1SDavid du Colombier {
2027dd7cddfSDavid du Colombier 	RR *hp;
2037dd7cddfSDavid du Colombier 
2043e12c5d1SDavid du Colombier 	switch(rp->type){
2053e12c5d1SDavid du Colombier 	case Tns:
2063e12c5d1SDavid du Colombier 	case Tmx:
2073e12c5d1SDavid du Colombier 	case Tmb:
2083e12c5d1SDavid du Colombier 	case Tmf:
2093e12c5d1SDavid du Colombier 	case Tmd:
2107dd7cddfSDavid du Colombier 		hp = rrlookup(rp->host, Ta, NOneg);
2117dd7cddfSDavid du Colombier 		if(hp == nil)
2127dd7cddfSDavid du Colombier 			hp = dblookup(rp->host->name, Cin, Ta, 0, 0);
2136dc4800dSDavid du Colombier 		if (hp && strncmp(hp->owner->name, "local#", 6) == 0)
2146dc4800dSDavid du Colombier 			dnslog("returning %s as hint", hp->owner->name);
2157dd7cddfSDavid du Colombier 		rrcat(last, hp);
2163e12c5d1SDavid du Colombier 		break;
2173e12c5d1SDavid du Colombier 	}
2183e12c5d1SDavid du Colombier }
219