]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ndb/dnserver.c
dns: import changes from sources
[plan9front.git] / sys / src / cmd / ndb / dnserver.c
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
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*
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
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                 hp = rrlookup(rp->host, Ta, NOneg);
214                 if(hp == nil)
215                         hp = dblookup(rp->host->name, Cin, Ta, 0, 0);
216                 if(hp == nil)
217                         hp = rrlookup(rp->host, Taaaa, NOneg);
218                 if(hp == nil)
219                         hp = dblookup(rp->host->name, Cin, Taaaa, 0, 0);
220                 if (hp && strncmp(hp->owner->name, "local#", 6) == 0)
221                         dnslog("returning %s as hint", hp->owner->name);
222                 lock(&dnlock);
223                 rrcat(last, hp);
224                 unlock(&dnlock);
225                 break;
226         }
227 }