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