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