xref: /plan9/sys/src/cmd/ndb/dnserver.c (revision ada60c6c3a2d9a124d15f23cbe5768095073de7d)
1 #include <u.h>
2 #include <libc.h>
3 #include <ip.h>
4 #include "dns.h"
5 
6 static RR*	doextquery(DNSmsg*, Request*, int);
7 static void	hint(RR**, RR*);
8 
9 /* set in dns.c */
10 int	norecursion;		/* don't allow recursive requests */
11 
12 /*
13  *  answer a dns request
14  */
15 void
dnserver(DNSmsg * reqp,DNSmsg * repp,Request * req,uchar * srcip,int rcode)16 dnserver(DNSmsg *reqp, DNSmsg *repp, Request *req, uchar *srcip, int rcode)
17 {
18 	int recursionflag;
19 	char *cp, *errmsg;
20 	char tname[32];
21 	DN *nsdp, *dp;
22 	Area *myarea;
23 	RR *tp, *neg, *rp;
24 
25 	dncheck(nil, 1);
26 
27 	recursionflag = norecursion? 0: Fcanrec;
28 	memset(repp, 0, sizeof(*repp));
29 	repp->id = reqp->id;
30 	repp->flags = Fresp | recursionflag | Oquery;
31 
32 	/* move one question from reqp to repp */
33 	tp = reqp->qd;
34 	reqp->qd = tp->next;
35 	tp->next = nil;
36 	repp->qd = tp;
37 
38 	if (rcode) {
39 		errmsg = "";
40 		if (rcode >= 0 && rcode < nrname)
41 			errmsg = rname[rcode];
42 		dnslog("server: response code 0%o (%s), req from %I",
43 			rcode, errmsg, srcip);
44 		/* provide feedback to clients who send us trash */
45 		repp->flags = (rcode&Rmask) | Fresp | Fcanrec | Oquery;
46 		return;
47 	}
48 	if(!rrsupported(repp->qd->type)){
49 		dnslog("server: unsupported request %s from %I",
50 			rrname(repp->qd->type, tname, sizeof tname), srcip);
51 		repp->flags = Runimplimented | Fresp | Fcanrec | Oquery;
52 		return;
53 	}
54 
55 	if(repp->qd->owner->class != Cin){
56 		dnslog("server: unsupported class %d from %I",
57 			repp->qd->owner->class, srcip);
58 		repp->flags = Runimplimented | Fresp | Fcanrec | Oquery;
59 		return;
60 	}
61 
62 	myarea = inmyarea(repp->qd->owner->name);
63 	if(myarea != nil) {
64 		if(repp->qd->type == Tixfr || repp->qd->type == Taxfr){
65 			dnslog("server: unsupported xfr request %s for %s from %I",
66 				rrname(repp->qd->type, tname, sizeof tname),
67 				repp->qd->owner->name, srcip);
68 			repp->flags = Runimplimented | Fresp | recursionflag |
69 				Oquery;
70 			return;
71 		}
72 	} else
73 		if(norecursion) {
74 			/* we don't recurse and we're not authoritative */
75 			repp->flags = Rok | Fresp | Oquery;
76 			return;
77 		}
78 
79 	/*
80 	 *  get the answer if we can, in *repp
81 	 */
82 	if(reqp->flags & Frecurse)
83 		neg = doextquery(repp, req, Recurse);
84 	else
85 		neg = doextquery(repp, req, Dontrecurse);
86 
87 	/* authority is transitive */
88 	if(myarea != nil || (repp->an && repp->an->auth))
89 		repp->flags |= Fauth;
90 
91 	/* pass on error codes */
92 	if(repp->an == nil){
93 		dp = dnlookup(repp->qd->owner->name, repp->qd->owner->class, 0);
94 		if(dp->rr == nil)
95 			if(reqp->flags & Frecurse)
96 				repp->flags |= dp->respcode | Fauth;
97 	}
98 
99 	if(myarea == nil)
100 		/*
101 		 *  add name server if we know
102 		 */
103 		for(cp = repp->qd->owner->name; cp; cp = walkup(cp)){
104 			nsdp = dnlookup(cp, repp->qd->owner->class, 0);
105 			if(nsdp == nil)
106 				continue;
107 
108 			repp->ns = rrlookup(nsdp, Tns, OKneg);
109 			if(repp->ns){
110 				/* don't pass on anything we know is wrong */
111 				if(repp->ns->negative){
112 					lock(&dnlock);
113 					rp = repp->ns;
114 					repp->ns = nil;
115 					rrfreelist(rp);
116 					unlock(&dnlock);
117 				}
118 				break;
119 			}
120 
121 			if (strncmp(nsdp->name, "local#", 6) == 0)
122 				dnslog("returning %s as nameserver", nsdp->name);
123 			repp->ns = dblookup(cp, repp->qd->owner->class, Tns, 0, 0);
124 			if(repp->ns)
125 				break;
126 		}
127 
128 	/*
129 	 *  add ip addresses as hints
130 	 */
131 	if(repp->qd->type != Taxfr && repp->qd->type != Tixfr){
132 		for(tp = repp->ns; tp; tp = tp->next)
133 			hint(&repp->ar, tp);
134 		for(tp = repp->an; tp; tp = tp->next)
135 			hint(&repp->ar, tp);
136 	}
137 
138 	/* hint calls rrlookup which holds dnlock, so don't lock before this. */
139 
140 	/*
141 	 *  add an soa to the authority section to help client
142 	 *  with negative caching
143 	 */
144 	if(repp->an == nil)
145 		if(myarea != nil){
146 			lock(&dnlock);
147 			rrcopy(myarea->soarr, &tp);
148 			rrcat(&repp->ns, tp);
149 			unlock(&dnlock);
150 		} else if(neg != nil) {
151 			if(neg->negsoaowner != nil) {
152 				tp = rrlookup(neg->negsoaowner, Tsoa, NOneg);
153 				lock(&dnlock);
154 				rrcat(&repp->ns, tp);
155 				unlock(&dnlock);
156 			}
157 			repp->flags |= neg->negrcode;
158 		}
159 
160 	/*
161 	 *  get rid of duplicates
162 	 */
163 	lock(&dnlock);
164 	unique(repp->an);
165 	unique(repp->ns);
166 	unique(repp->ar);
167 
168 	rrfreelist(neg);
169 	unlock(&dnlock);
170 
171 	dncheck(nil, 1);
172 }
173 
174 /*
175  *  satisfy a recursive request.  dnlookup will handle cnames.
176  */
177 static RR*
doextquery(DNSmsg * mp,Request * req,int recurse)178 doextquery(DNSmsg *mp, Request *req, int recurse)
179 {
180 	ushort type;
181 	char *name;
182 	RR *rp, *neg;
183 
184 	name = mp->qd->owner->name;
185 	type = mp->qd->type;
186 	rp = dnresolve(name, Cin, type, req, &mp->an, 0, recurse, 1, 0);
187 
188 	lock(&dnlock);
189 	/* don't return soa hints as answers, it's wrong */
190 	if(rp && rp->db && !rp->auth && rp->type == Tsoa) {
191 		rrfreelist(rp);
192 		rp = nil;
193 	}
194 
195 	/* don't let negative cached entries escape */
196 	neg = rrremneg(&rp);
197 	rrcat(&mp->an, rp);
198 	unlock(&dnlock);
199 	return neg;
200 }
201 
202 static void
hint(RR ** last,RR * rp)203 hint(RR **last, RR *rp)
204 {
205 	RR *hp;
206 
207 	switch(rp->type){
208 	case Tns:
209 	case Tmx:
210 	case Tmb:
211 	case Tmf:
212 	case Tmd:
213 		assert(rp->host != nil);
214 		hp = rrlookup(rp->host, Ta, NOneg);
215 		if(hp == nil)
216 			hp = dblookup(rp->host->name, Cin, Ta, 0, 0);
217 		if(hp == nil)
218 			hp = rrlookup(rp->host, Taaaa, NOneg);
219 		if(hp == nil)
220 			hp = dblookup(rp->host->name, Cin, Taaaa, 0, 0);
221 		if (hp && hp->owner && hp->owner->name &&
222 		    strncmp(hp->owner->name, "local#", 6) == 0)
223 			dnslog("returning %s as hint", hp->owner->name);
224 		lock(&dnlock);
225 		rrcat(last, hp);
226 		unlock(&dnlock);
227 		break;
228 	}
229 }
230