xref: /plan9/sys/src/cmd/ndb/dnsdebug.c (revision 96cbc34f1b36a29efdcfd47b10e70703a690febc)
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 Cfg cfg;
14 
15 static char *servername;
16 static RR *serverrr;
17 static RR *serveraddrs;
18 
19 char	*dbfile;
20 int	debug;
21 uchar	ipaddr[IPaddrlen];	/* my ip address */
22 char	*logfile = "dnsdebug";
23 int	maxage  = 60*60;
24 char	mntpt[Maxpath];
25 int	needrefresh;
26 ulong	now;
27 vlong	nowns;
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 	cfg.inside = 1;
57 
58 	ARGBEGIN{
59 	case 'f':
60 		dbfile = EARGF(usage());
61 		break;
62 	case 'r':
63 		cfg.resolver = 1;
64 		break;
65 	case 'x':
66 		dbfile = "/lib/ndb/external";
67 		strcpy(mntpt, "/net.alt");
68 		break;
69 	default:
70 		usage();
71 	}ARGEND
72 
73 	now = time(nil);
74 	dninit();
75 	fmtinstall('R', prettyrrfmt);
76 	if(myipaddr(ipaddr, mntpt) < 0)
77 		sysfatal("can't read my ip address");
78 	opendatabase();
79 
80 	if(cfg.resolver)
81 		squirrelserveraddrs();
82 
83 	debug = 1;
84 
85 	if(argc > 0){
86 		docmd(argc, argv);
87 		exits(0);
88 	}
89 
90 	Binit(&in, 0, OREAD);
91 	for(print("> "); p = Brdline(&in, '\n'); print("> ")){
92 		p[Blinelen(&in)-1] = 0;
93 		n = tokenize(p, f, 3);
94 		if(n>=1) {
95 			dnpurge();		/* flush the cache */
96 			docmd(n, f);
97 		}
98 	}
99 	exits(0);
100 }
101 
102 static char*
103 longtime(long t)
104 {
105 	int d, h, m, n;
106 	static char x[128];
107 
108 	for(d = 0; t >= 24*60*60; t -= 24*60*60)
109 		d++;
110 	for(h = 0; t >= 60*60; t -= 60*60)
111 		h++;
112 	for(m = 0; t >= 60; t -= 60)
113 		m++;
114 	n = 0;
115 	if(d)
116 		n += sprint(x, "%d day ", d);
117 	if(h)
118 		n += sprint(x+n, "%d hr ", h);
119 	if(m)
120 		n += sprint(x+n, "%d min ", m);
121 	if(t || n == 0)
122 		sprint(x+n, "%ld sec", t);
123 	return x;
124 }
125 
126 int
127 prettyrrfmt(Fmt *f)
128 {
129 	RR *rp;
130 	char buf[3*Domlen];
131 	char *p, *e;
132 	Txt *t;
133 
134 	rp = va_arg(f->args, RR*);
135 	if(rp == 0){
136 		strcpy(buf, "<null>");
137 		goto out;
138 	}
139 
140 	p = buf;
141 	e = buf + sizeof(buf);
142 	p = seprint(p, e, "%-32.32s %-15.15s %-5.5s", rp->owner->name,
143 		longtime(rp->db? rp->ttl: (rp->ttl - now)),
144 		rrname(rp->type, buf, sizeof buf));
145 
146 	if(rp->negative){
147 		seprint(p, e, "negative rcode %d\n", rp->negrcode);
148 		goto out;
149 	}
150 
151 	switch(rp->type){
152 	case Thinfo:
153 		seprint(p, e, "\t%s %s", rp->cpu->name, rp->os->name);
154 		break;
155 	case Tcname:
156 	case Tmb:
157 	case Tmd:
158 	case Tmf:
159 	case Tns:
160 		seprint(p, e, "\t%s", (rp->host? rp->host->name: ""));
161 		break;
162 	case Tmg:
163 	case Tmr:
164 		seprint(p, e, "\t%s", (rp->mb? rp->mb->name: ""));
165 		break;
166 	case Tminfo:
167 		seprint(p, e, "\t%s %s", (rp->mb? rp->mb->name: ""),
168 			(rp->rmb? rp->rmb->name: ""));
169 		break;
170 	case Tmx:
171 		seprint(p, e, "\t%lud %s", rp->pref,
172 			(rp->host? rp->host->name: ""));
173 		break;
174 	case Ta:
175 	case Taaaa:
176 		seprint(p, e, "\t%s", (rp->ip? rp->ip->name: ""));
177 		break;
178 	case Tptr:
179 		seprint(p, e, "\t%s", (rp->ptr? rp->ptr->name: ""));
180 		break;
181 	case Tsoa:
182 		seprint(p, e, "\t%s %s %lud %lud %lud %lud %lud",
183 			rp->host->name, rp->rmb->name, rp->soa->serial,
184 			rp->soa->refresh, rp->soa->retry,
185 			rp->soa->expire, rp->soa->minttl);
186 		break;
187 	case Tsrv:
188 		seprint(p, e, "\t%ud %ud %ud %s",
189 			rp->srv->pri, rp->srv->weight, rp->port, rp->host->name);
190 		break;
191 	case Tnull:
192 		seprint(p, e, "\t%.*H", rp->null->dlen, rp->null->data);
193 		break;
194 	case Ttxt:
195 		p = seprint(p, e, "\t");
196 		for(t = rp->txt; t != nil; t = t->next)
197 			p = seprint(p, e, "%s", t->p);
198 		break;
199 	case Trp:
200 		seprint(p, e, "\t%s %s", rp->rmb->name, rp->rp->name);
201 		break;
202 	case Tkey:
203 		seprint(p, e, "\t%d %d %d", rp->key->flags, rp->key->proto,
204 			rp->key->alg);
205 		break;
206 	case Tsig:
207 		seprint(p, e, "\t%d %d %d %lud %lud %lud %d %s",
208 			rp->sig->type, rp->sig->alg, rp->sig->labels,
209 			rp->sig->ttl, rp->sig->exp, rp->sig->incep,
210 			rp->sig->tag, rp->sig->signer->name);
211 		break;
212 	case Tcert:
213 		seprint(p, e, "\t%d %d %d",
214 			rp->sig->type, rp->sig->tag, rp->sig->alg);
215 		break;
216 	}
217 out:
218 	return fmtstrcpy(f, buf);
219 }
220 
221 void
222 logsection(char *flag, RR *rp)
223 {
224 	if(rp == nil)
225 		return;
226 	print("\t%s%R\n", flag, rp);
227 	for(rp = rp->next; rp != nil; rp = rp->next)
228 		print("\t      %R\n", rp);
229 }
230 
231 void
232 logreply(int id, uchar *addr, DNSmsg *mp)
233 {
234 	RR *rp;
235 	char buf[12], 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|Rmask)) == (Fauth|Rname)? " nx": "");
267 	for(rp = mp->qd; rp != nil; rp = rp->next)
268 		print("\tQ:    %s %s\n", rp->owner->name,
269 			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
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*
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
300 squirrelserveraddrs(void)
301 {
302 	RR *rr, *rp, **l;
303 	Request req;
304 
305 	/* look up the resolver address first */
306 	cfg.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.isslave = 1;
322 		req.aborttime = now + Maxreqtm;
323 		*l = dnresolve(rp->host->name, Cin, Ta, &req, 0, 0, Recurse, 0, 0);
324 		while(*l != nil)
325 			l = &(*l)->next;
326 	}
327 	cfg.resolver = 1;
328 	debug = 1;
329 }
330 
331 void
332 preloadserveraddrs(void)
333 {
334 	RR *rp, **l, *first;
335 
336 	l = &first;
337 	for(rp = serveraddrs; rp != nil; rp = rp->next){
338 		rrcopy(rp, l);
339 		rrattach(first, Authoritative);
340 	}
341 }
342 
343 int
344 setserver(char *server)
345 {
346 	if(servername != nil){
347 		free(servername);
348 		servername = nil;
349 		cfg.resolver = 0;
350 	}
351 	if(server == nil || *server == 0)
352 		return 0;
353 	servername = strdup(server);
354 	squirrelserveraddrs();
355 	if(serveraddrs == nil){
356 		print("can't resolve %s\n", servername);
357 		cfg.resolver = 0;
358 	} else
359 		cfg.resolver = 1;
360 	return cfg.resolver? 0: -1;
361 }
362 
363 void
364 doquery(char *name, char *tstr)
365 {
366 	int len, type, rooted;
367 	char *p, *np;
368 	char buf[1024];
369 	RR *rr, *rp;
370 	Request req;
371 
372 	if(cfg.resolver)
373 		preloadserveraddrs();
374 
375 	/* default to an "ip" request if alpha, "ptr" if numeric */
376 	if(tstr == nil || *tstr == 0)
377 		if(strcmp(ipattr(name), "ip") == 0)
378 			tstr = "ptr";
379 		else
380 			tstr = "ip";
381 
382 	/* if name end in '.', remove it */
383 	len = strlen(name);
384 	if(len > 0 && name[len-1] == '.'){
385 		rooted = 1;
386 		name[len-1] = 0;
387 	} else
388 		rooted = 0;
389 
390 	/* inverse queries may need to be permuted */
391 	strncpy(buf, name, sizeof buf);
392 	if(strcmp("ptr", tstr) == 0 && cistrstr(name, ".arpa") == nil){
393 		/* TODO: reversing v6 addrs is harder */
394 		for(p = name; *p; p++)
395 			;
396 		*p = '.';
397 		np = buf;
398 		len = 0;
399 		while(p >= name){
400 			len++;
401 			p--;
402 			if(*p == '.'){
403 				memmove(np, p+1, len);
404 				np += len;
405 				len = 0;
406 			}
407 		}
408 		memmove(np, p+1, len);
409 		np += len;
410 		strcpy(np, "in-addr.arpa");	/* TODO: ip6.arpa for v6 */
411 	}
412 
413 	/* look it up */
414 	type = rrtype(tstr);
415 	if(type < 0){
416 		print("!unknown type %s\n", tstr);
417 		return;
418 	}
419 
420 	memset(&req, 0, sizeof req);
421 	getactivity(&req, 0);
422 	req.isslave = 1;
423 	req.aborttime = now + Maxreqtm;
424 	rr = dnresolve(buf, Cin, type, &req, 0, 0, Recurse, rooted, 0);
425 	if(rr){
426 		print("----------------------------\n");
427 		for(rp = rr; rp; rp = rp->next)
428 			print("answer %R\n", rp);
429 		print("----------------------------\n");
430 	}
431 	rrfreelist(rr);
432 
433 	putactivity(0);
434 }
435 
436 void
437 docmd(int n, char **f)
438 {
439 	int tmpsrv;
440 	char *name, *type;
441 
442 	name = type = nil;
443 	tmpsrv = 0;
444 
445 	if(*f[0] == '@') {
446 		if(setserver(f[0]+1) < 0)
447 			return;
448 
449 		switch(n){
450 		case 3:
451 			type = f[2];
452 			/* fall through */
453 		case 2:
454 			name = f[1];
455 			tmpsrv = 1;
456 			break;
457 		}
458 	} else
459 		switch(n){
460 		case 2:
461 			type = f[1];
462 			/* fall through */
463 		case 1:
464 			name = f[0];
465 			break;
466 		}
467 
468 	if(name == nil)
469 		return;
470 
471 	doquery(name, type);
472 
473 	if(tmpsrv)
474 		setserver("");
475 }
476