Blob


1 #include <u.h>
2 #include <libc.h>
3 #include <ip.h>
4 #include "dns.h"
6 enum
7 {
8 Maxpath= 128,
9 };
11 char *logfile = "dns";
12 char *dbfile;
13 int debug;
14 int cachedb = 1;
15 int testing;
16 int traceactivity;
17 int needrefresh;
18 int resolver;
19 char mntpt[Maxpath];
20 char *caller = "";
21 ulong now;
22 int maxage;
23 uchar ipaddr[IPaddrlen]; /* my ip address */
24 char *LOG;
25 char *zonerefreshprogram;
27 static int readmsg(int, uchar*, int);
28 static void reply(int, DNSmsg*, Request*);
29 static void dnzone(DNSmsg*, DNSmsg*, Request*);
30 static void getcaller(char*);
31 static void refreshmain(char*);
33 void
34 main(int argc, char *argv[])
35 {
36 int len;
37 Request req;
38 DNSmsg reqmsg, repmsg;
39 uchar buf[512];
40 char tname[32];
41 char *err;
42 char *ext = "";
44 ARGBEGIN{
45 case 'd':
46 debug++;
47 break;
48 case 'f':
49 dbfile = ARGF();
50 break;
51 case 'r':
52 resolver = 1;
53 break;
54 case 'x':
55 ext = ARGF();
56 break;
57 }ARGEND
59 if(debug < 2)
60 debug = 0;
62 if(argc > 0)
63 getcaller(argv[0]);
65 dninit();
67 snprint(mntpt, sizeof(mntpt), "/net%s", ext);
68 if(myipaddr(ipaddr, mntpt) < 0)
69 sysfatal("can't read my ip address");
70 syslog(0, logfile, "dnstcp call from %s to %I", caller, ipaddr);
72 db2cache(1);
74 setjmp(req.mret);
75 req.isslave = 0;
77 /* loop on requests */
78 for(;; putactivity()){
79 now = time(0);
80 memset(&repmsg, 0, sizeof(repmsg));
81 alarm(10*60*1000);
82 len = readmsg(0, buf, sizeof(buf));
83 alarm(0);
84 if(len <= 0)
85 break;
86 getactivity(&req);
87 req.aborttime = now + 15*Min;
88 err = convM2DNS(buf, len, &reqmsg);
89 if(err){
90 syslog(0, logfile, "server: input error: %s from %I", err, buf);
91 break;
92 }
93 if(reqmsg.qdcount < 1){
94 syslog(0, logfile, "server: no questions from %I", buf);
95 break;
96 }
97 if(reqmsg.flags & Fresp){
98 syslog(0, logfile, "server: reply not request from %I", buf);
99 break;
101 if((reqmsg.flags & Omask) != Oquery){
102 syslog(0, logfile, "server: op %d from %I", reqmsg.flags & Omask, buf);
103 break;
106 if(debug)
107 syslog(0, logfile, "%d: serve (%s) %d %s %s",
108 req.id, caller,
109 reqmsg.id,
110 reqmsg.qd->owner->name,
111 rrname(reqmsg.qd->type, tname, sizeof tname));
113 /* loop through each question */
114 while(reqmsg.qd){
115 if(reqmsg.qd->type == Taxfr){
116 dnzone(&reqmsg, &repmsg, &req);
117 } else {
118 dnserver(&reqmsg, &repmsg, &req);
119 reply(1, &repmsg, &req);
120 rrfreelist(repmsg.qd);
121 rrfreelist(repmsg.an);
122 rrfreelist(repmsg.ns);
123 rrfreelist(repmsg.ar);
127 rrfreelist(reqmsg.qd);
128 rrfreelist(reqmsg.an);
129 rrfreelist(reqmsg.ns);
130 rrfreelist(reqmsg.ar);
132 if(req.isslave){
133 putactivity();
134 _exits(0);
137 refreshmain(mntpt);
140 static int
141 readmsg(int fd, uchar *buf, int max)
143 int n;
144 uchar x[2];
146 if(readn(fd, x, 2) != 2)
147 return -1;
148 n = (x[0]<<8) | x[1];
149 if(n > max)
150 return -1;
151 if(readn(fd, buf, n) != n)
152 return -1;
153 return n;
156 static void
157 reply(int fd, DNSmsg *rep, Request *req)
159 int len, rv;
160 char tname[32];
161 uchar buf[4096];
162 RR *rp;
164 if(debug){
165 syslog(0, logfile, "%d: reply (%s) %s %s %ux",
166 req->id, caller,
167 rep->qd->owner->name,
168 rrname(rep->qd->type, tname, sizeof tname),
169 rep->flags);
170 for(rp = rep->an; rp; rp = rp->next)
171 syslog(0, logfile, "an %R", rp);
172 for(rp = rep->ns; rp; rp = rp->next)
173 syslog(0, logfile, "ns %R", rp);
174 for(rp = rep->ar; rp; rp = rp->next)
175 syslog(0, logfile, "ar %R", rp);
179 len = convDNS2M(rep, buf+2, sizeof(buf) - 2);
180 if(len <= 0)
181 abort(); /* "dnserver: converting reply" */;
182 buf[0] = len>>8;
183 buf[1] = len;
184 rv = write(fd, buf, len+2);
185 if(rv != len+2){
186 syslog(0, logfile, "sending reply: %d instead of %d", rv, len+2);
187 exits(0);
191 /*
192 * Hash table for domain names. The hash is based only on the
193 * first element of the domain name.
194 */
195 extern DN *ht[HTLEN];
197 static int
198 numelem(char *name)
200 int i;
202 i = 1;
203 for(; *name; name++)
204 if(*name == '.')
205 i++;
206 return i;
209 int
210 inzone(DN *dp, char *name, int namelen, int depth)
212 int n;
214 if(dp->name == 0)
215 return 0;
216 if(numelem(dp->name) != depth)
217 return 0;
218 n = strlen(dp->name);
219 if(n < namelen)
220 return 0;
221 if(strcmp(name, dp->name + n - namelen) != 0)
222 return 0;
223 if(n > namelen && dp->name[n - namelen - 1] != '.')
224 return 0;
225 return 1;
228 static void
229 dnzone(DNSmsg *reqp, DNSmsg *repp, Request *req)
231 DN *dp, *ndp;
232 RR r, *rp;
233 int h, depth, found, nlen;
235 memset(repp, 0, sizeof(*repp));
236 repp->id = reqp->id;
237 repp->flags = Fauth | Fresp | Fcanrec | Oquery;
238 repp->qd = reqp->qd;
239 reqp->qd = reqp->qd->next;
240 repp->qd->next = 0;
241 dp = repp->qd->owner;
243 /* send the soa */
244 repp->an = rrlookup(dp, Tsoa, NOneg);
245 reply(1, repp, req);
246 if(repp->an == 0)
247 goto out;
248 rrfreelist(repp->an);
250 nlen = strlen(dp->name);
252 /* construct a breadth first search of the name space (hard with a hash) */
253 repp->an = &r;
254 for(depth = numelem(dp->name); ; depth++){
255 found = 0;
256 for(h = 0; h < HTLEN; h++)
257 for(ndp = ht[h]; ndp; ndp = ndp->next)
258 if(inzone(ndp, dp->name, nlen, depth)){
259 for(rp = ndp->rr; rp; rp = rp->next){
260 /* there shouldn't be negatives, but just in case */
261 if(rp->negative)
262 continue;
264 /* don't send an soa's, ns's are enough */
265 if(rp->type == Tsoa)
266 continue;
268 r = *rp;
269 r.next = 0;
270 reply(1, repp, req);
272 found = 1;
274 if(!found)
275 break;
278 /* resend the soa */
279 repp->an = rrlookup(dp, Tsoa, NOneg);
280 reply(1, repp, req);
281 rrfreelist(repp->an);
282 out:
283 rrfree(repp->qd);
286 static void
287 getcaller(char *dir)
289 int fd, n;
290 static char remote[128];
292 snprint(remote, sizeof(remote), "%s/remote", dir);
293 fd = open(remote, OREAD);
294 if(fd < 0)
295 return;
296 n = read(fd, remote, sizeof(remote)-1);
297 close(fd);
298 if(n <= 0)
299 return;
300 if(remote[n-1] == '\n')
301 n--;
302 remote[n] = 0;
303 caller = remote;
306 static void
307 refreshmain(char *net)
309 int fd;
310 char file[128];
312 snprint(file, sizeof(file), "%s/dns", net);
313 if(debug)
314 syslog(0, logfile, "refreshing %s", file);
315 fd = open(file, ORDWR);
316 if(fd < 0){
317 syslog(0, logfile, "can't refresh %s", file);
318 return;
320 fprint(fd, "refresh");
321 close(fd);
324 /*
325 * the following varies between dnsdebug and dns
326 */
327 void
328 logreply(int id, uchar *addr, DNSmsg *mp)
330 RR *rp;
332 syslog(0, LOG, "%d: rcvd %I flags:%s%s%s%s%s", id, addr,
333 mp->flags & Fauth ? " auth" : "",
334 mp->flags & Ftrunc ? " trunc" : "",
335 mp->flags & Frecurse ? " rd" : "",
336 mp->flags & Fcanrec ? " ra" : "",
337 mp->flags & (Fauth|Rname) == (Fauth|Rname) ?
338 " nx" : "");
339 for(rp = mp->qd; rp != nil; rp = rp->next)
340 syslog(0, LOG, "%d: rcvd %I qd %s", id, addr, rp->owner->name);
341 for(rp = mp->an; rp != nil; rp = rp->next)
342 syslog(0, LOG, "%d: rcvd %I an %R", id, addr, rp);
343 for(rp = mp->ns; rp != nil; rp = rp->next)
344 syslog(0, LOG, "%d: rcvd %I ns %R", id, addr, rp);
345 for(rp = mp->ar; rp != nil; rp = rp->next)
346 syslog(0, LOG, "%d: rcvd %I ar %R", id, addr, rp);
349 void
350 logsend(int id, int subid, uchar *addr, char *sname, char *rname, int type)
352 char buf[12];
354 syslog(0, LOG, "%d.%d: sending to %I/%s %s %s",
355 id, subid, addr, sname, rname, rrname(type, buf, sizeof buf));
358 RR*
359 getdnsservers(int class)
361 return dnsservers(class);