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