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