1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <ctype.h>
5 #include <ip.h>
6 #include <ndb.h>
7 #include <thread.h>
8 #include "dns.h"
9
10 enum
11 {
12 Maxrequest= 128,
13 Ncache= 8,
14 Maxpath= 128,
15 Maxreply= 512,
16 Maxrrr= 16
17 };
18
19 static char *servername;
20 static RR *serveraddrs;
21
22 int debug;
23 int cachedb;
24 ulong now;
25 int testing;
26 int traceactivity;
27 char *trace;
28 int needrefresh;
29 int resolver;
30 uchar ipaddr[IPaddrlen]; /* my ip address */
31 int maxage;
32 char *logfile = "dns";
33 char *dbfile;
34 char mntpt[Maxpath];
35 char *zonerefreshprogram;
36 char *tcpaddr;
37 char *udpaddr;
38
39 int prettyrrfmt(Fmt*);
40 void preloadserveraddrs(void);
41 void squirrelserveraddrs(void);
42 int setserver(char*);
43 void doquery(char*, char*);
44 void docmd(int, char**);
45
46 void
usage(void)47 usage(void)
48 {
49 fprint(2, "usage: dnsdebug [-fr] [query ...]\n");
50 threadexitsall("usage");
51 }
52
53 void
threadmain(int argc,char * argv[])54 threadmain(int argc, char *argv[])
55 {
56 int n;
57 Biobuf in;
58 char *p;
59 char *f[4];
60
61 strcpy(mntpt, "/net");
62
63 ARGBEGIN{
64 case 'r':
65 resolver = 1;
66 break;
67 case 'f':
68 dbfile = EARGF(usage());
69 break;
70 default:
71 usage();
72 }ARGEND
73
74 now = time(0);
75 dninit();
76 fmtinstall('R', prettyrrfmt);
77 if(myipaddr(ipaddr, mntpt) < 0)
78 sysfatal("can't read my ip address");
79 opendatabase();
80
81 if(resolver)
82 squirrelserveraddrs();
83
84 debug = 1;
85
86 if(argc > 0){
87 docmd(argc, argv);
88 threadexitsall(0);
89 }
90
91 Binit(&in, 0, OREAD);
92 for(print("> "); p = Brdline(&in, '\n'); print("> ")){
93 p[Blinelen(&in)-1] = 0;
94 n = tokenize(p, f, 3);
95 if(n<1)
96 continue;
97
98 /* flush the cache */
99 dnpurge();
100
101 docmd(n, f);
102
103 }
104 threadexitsall(0);
105 }
106
107 static char*
longtime(long t)108 longtime(long t)
109 {
110 int d, h, m, n;
111 static char x[128];
112
113 for(d = 0; t >= 24*60*60; t -= 24*60*60)
114 d++;
115 for(h = 0; t >= 60*60; t -= 60*60)
116 h++;
117 for(m = 0; t >= 60; t -= 60)
118 m++;
119 n = 0;
120 if(d)
121 n += sprint(x, "%d day ", d);
122 if(h)
123 n += sprint(x+n, "%d hr ", h);
124 if(m)
125 n += sprint(x+n, "%d min ", m);
126 if(t || n == 0)
127 sprint(x+n, "%ld sec", t);
128 return x;
129 }
130
131 int
prettyrrfmt(Fmt * f)132 prettyrrfmt(Fmt *f)
133 {
134 RR *rp;
135 char buf[3*Domlen];
136 char *p, *e;
137 Txt *t;
138
139 rp = va_arg(f->args, RR*);
140 if(rp == 0){
141 strcpy(buf, "<null>");
142 goto out;
143 }
144
145 p = buf;
146 e = buf + sizeof(buf);
147 p = seprint(p, e, "%-32.32s %-15.15s %-5.5s", rp->owner->name,
148 longtime(rp->db ? rp->ttl : (rp->ttl-now)),
149 rrname(rp->type, buf, sizeof buf));
150
151 if(rp->negative){
152 seprint(p, e, "negative rcode %d\n", rp->negrcode);
153 goto out;
154 }
155
156 switch(rp->type){
157 case Thinfo:
158 seprint(p, e, "\t%s %s", rp->cpu->name, rp->os->name);
159 break;
160 case Tcname:
161 case Tmb:
162 case Tmd:
163 case Tmf:
164 case Tns:
165 seprint(p, e, "\t%s", rp->host->name);
166 break;
167 case Tmg:
168 case Tmr:
169 seprint(p, e, "\t%s", rp->mb->name);
170 break;
171 case Tminfo:
172 seprint(p, e, "\t%s %s", rp->mb->name, rp->rmb->name);
173 break;
174 case Tmx:
175 seprint(p, e, "\t%lud %s", rp->pref, rp->host->name);
176 break;
177 case Ta:
178 case Taaaa:
179 seprint(p, e, "\t%s", rp->ip->name);
180 break;
181 case Tptr:
182 seprint(p, e, "\t%s", rp->ptr->name);
183 break;
184 case Tsoa:
185 seprint(p, e, "\t%s %s %lud %lud %lud %lud %lud", rp->host->name,
186 rp->rmb->name, rp->soa->serial, rp->soa->refresh, rp->soa->retry,
187 rp->soa->expire, rp->soa->minttl);
188 break;
189 case Tnull:
190 seprint(p, e, "\t%.*H", rp->null->dlen, rp->null->data);
191 break;
192 case Ttxt:
193 p = seprint(p, e, "\t");
194 for(t = rp->txt; t != nil; t = t->next)
195 p = seprint(p, e, "%s", t->p);
196 break;
197 case Trp:
198 seprint(p, e, "\t%s %s", rp->rmb->name, rp->rp->name);
199 break;
200 case Tkey:
201 seprint(p, e, "\t%d %d %d", rp->key->flags, rp->key->proto,
202 rp->key->alg);
203 break;
204 case Tsig:
205 seprint(p, e, "\t%d %d %d %lud %lud %lud %d %s",
206 rp->sig->type, rp->sig->alg, rp->sig->labels, rp->sig->ttl,
207 rp->sig->exp, rp->sig->incep, rp->sig->tag, rp->sig->signer->name);
208 break;
209 case Tcert:
210 seprint(p, e, "\t%d %d %d",
211 rp->sig->type, rp->sig->tag, rp->sig->alg);
212 break;
213 default:
214 break;
215 }
216 out:
217 return fmtstrcpy(f, buf);
218 }
219
220 void
logsection(char * flag,RR * rp)221 logsection(char *flag, RR *rp)
222 {
223 if(rp == nil)
224 return;
225 print("\t%s%R\n", flag, rp);
226 for(rp = rp->next; rp != nil; rp = rp->next)
227 print("\t %R\n", rp);
228 }
229
230 void
logreply(int id,uchar * addr,DNSmsg * mp)231 logreply(int id, uchar *addr, DNSmsg *mp)
232 {
233 RR *rp;
234 char buf[12];
235 char resp[32];
236
237 switch(mp->flags & Rmask){
238 case Rok:
239 strcpy(resp, "OK");
240 break;
241 case Rformat:
242 strcpy(resp, "Format error");
243 break;
244 case Rserver:
245 strcpy(resp, "Server failed");
246 break;
247 case Rname:
248 strcpy(resp, "Nonexistent");
249 break;
250 case Runimplimented:
251 strcpy(resp, "Unimplemented");
252 break;
253 case Rrefused:
254 strcpy(resp, "Refused");
255 break;
256 default:
257 sprint(resp, "%d", mp->flags & Rmask);
258 break;
259 }
260
261 print("%d: rcvd %s from %I (%s%s%s%s%s)\n", id, resp, addr,
262 mp->flags & Fauth ? "authoritative" : "",
263 mp->flags & Ftrunc ? " truncated" : "",
264 mp->flags & Frecurse ? " recurse" : "",
265 mp->flags & Fcanrec ? " can_recurse" : "",
266 mp->flags & (Fauth|Rname) == (Fauth|Rname) ?
267 " nx" : "");
268 for(rp = mp->qd; rp != nil; rp = rp->next)
269 print("\tQ: %s %s\n", rp->owner->name, rrname(rp->type, buf, sizeof buf));
270 logsection("Ans: ", mp->an);
271 logsection("Auth: ", mp->ns);
272 logsection("Hint: ", mp->ar);
273 }
274
275 void
logsend(int id,int subid,uchar * addr,char * sname,char * rname,int type)276 logsend(int id, int subid, uchar *addr, char *sname, char *rname, int type)
277 {
278 char buf[12];
279
280 print("%d.%d: sending to %I/%s %s %s\n", id, subid,
281 addr, sname, rname, rrname(type, buf, sizeof buf));
282 }
283
284 RR*
getdnsservers(int class)285 getdnsservers(int class)
286 {
287 RR *rr;
288
289 if(servername == nil)
290 return dnsservers(class);
291
292 rr = rralloc(Tns);
293 rr->owner = dnlookup("local#dns#servers", class, 1);
294 rr->host = dnlookup(servername, class, 1);
295
296 return rr;
297 }
298
299 void
squirrelserveraddrs(void)300 squirrelserveraddrs(void)
301 {
302 RR *rr, *rp, **l;
303 Request req;
304
305 /* look up the resolver address first */
306 resolver = 0;
307 debug = 0;
308 if(serveraddrs)
309 rrfreelist(serveraddrs);
310 serveraddrs = nil;
311 rr = getdnsservers(Cin);
312 l = &serveraddrs;
313 for(rp = rr; rp != nil; rp = rp->next){
314 if(strcmp(ipattr(rp->host->name), "ip") == 0){
315 *l = rralloc(Ta);
316 (*l)->owner = rp->host;
317 (*l)->ip = rp->host;
318 l = &(*l)->next;
319 continue;
320 }
321 req.aborttime = now + 60; /* don't spend more than 60 seconds */
322 *l = dnresolve(rp->host->name, Cin, Ta, &req, 0, 0, Recurse, 0, 0);
323 while(*l != nil)
324 l = &(*l)->next;
325 }
326 resolver = 1;
327 debug = 1;
328 }
329
330 void
preloadserveraddrs(void)331 preloadserveraddrs(void)
332 {
333 RR *rp, **l, *first;
334
335 l = &first;
336 for(rp = serveraddrs; rp != nil; rp = rp->next){
337 rrcopy(rp, l);
338 rrattach(first, 1);
339 }
340 }
341
342 int
setserver(char * server)343 setserver(char *server)
344 {
345 if(servername != nil){
346 free(servername);
347 servername = nil;
348 resolver = 0;
349 }
350 if(server == nil || *server == 0)
351 return 0;
352 servername = strdup(server);
353 squirrelserveraddrs();
354 if(serveraddrs == nil){
355 print("can't resolve %s\n", servername);
356 resolver = 0;
357 } else {
358 resolver = 1;
359 }
360 return resolver ? 0 : -1;
361 }
362
363 void
doquery(char * name,char * tstr)364 doquery(char *name, char *tstr)
365 {
366 Request req;
367 RR *rr, *rp;
368 int len, type;
369 char *p, *np;
370 int rooted;
371 char buf[1024];
372
373 if(resolver)
374 preloadserveraddrs();
375
376 /* default to an "ip" request if alpha, "ptr" if numeric */
377 if(tstr == nil || *tstr == 0) {
378 if(strcmp(ipattr(name), "ip") == 0)
379 tstr = "ptr";
380 else
381 tstr = "ip";
382 }
383
384 /* if name end in '.', remove it */
385 len = strlen(name);
386 if(len > 0 && name[len-1] == '.'){
387 rooted = 1;
388 name[len-1] = 0;
389 } else
390 rooted = 0;
391
392 /* inverse queries may need to be permuted */
393 strncpy(buf, name, sizeof buf);
394 if(strcmp("ptr", tstr) == 0
395 && strstr(name, "IN-ADDR") == 0
396 && strstr(name, "in-addr") == 0){
397 for(p = name; *p; p++)
398 ;
399 *p = '.';
400 np = buf;
401 len = 0;
402 while(p >= name){
403 len++;
404 p--;
405 if(*p == '.'){
406 memmove(np, p+1, len);
407 np += len;
408 len = 0;
409 }
410 }
411 memmove(np, p+1, len);
412 np += len;
413 strcpy(np, "in-addr.arpa");
414 }
415
416 /* look it up */
417 type = rrtype(tstr);
418 if(type < 0){
419 print("!unknown type %s\n", tstr);
420 return;
421 }
422
423 getactivity(&req);
424 req.aborttime = now + 60; /* don't spend more than 60 seconds */
425 rr = dnresolve(buf, Cin, type, &req, 0, 0, Recurse, rooted, 0);
426 if(rr){
427 print("----------------------------\n");
428 for(rp = rr; rp; rp = rp->next)
429 print("answer %R\n", rp);
430 print("----------------------------\n");
431 }
432 rrfreelist(rr);
433
434 putactivity();
435 }
436
437 void
docmd(int n,char ** f)438 docmd(int n, char **f)
439 {
440 int tmpsrv;
441 char *name, *type;
442
443 name = nil;
444 type = nil;
445 tmpsrv = 0;
446
447 if(*f[0] == '@') {
448 if(setserver(f[0]+1) < 0)
449 return;
450
451 switch(n){
452 case 3:
453 type = f[2];
454 /* fall through */
455 case 2:
456 name = f[1];
457 tmpsrv = 1;
458 break;
459 }
460 } else {
461 switch(n){
462 case 2:
463 type = f[1];
464 /* fall through */
465 case 1:
466 name = f[0];
467 break;
468 }
469 }
470
471 if(name == nil)
472 return;
473
474 doquery(name, type);
475
476 if(tmpsrv)
477 setserver("");
478 }
479