xref: /plan9/sys/src/cmd/ndb/dnsdebug.c (revision b2ebcd4d1aeacb3fc8c3d85492d03278b32bed3f)
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
usage(void)41 usage(void)
42 {
43 	fprint(2, "%s: [-rx] [-f db-file] [[@server] domain [type]]\n", argv0);
44 	exits("usage");
45 }
46 
47 void
main(int argc,char * argv[])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 	nowns = nsec();
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(cfg.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*
longtime(long t)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
prettyrrfmt(Fmt * f)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", 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 Tsrv:
189 		seprint(p, e, "\t%ud %ud %ud %s",
190 			rp->srv->pri, rp->srv->weight, rp->port, rp->host->name);
191 		break;
192 	case Tnull:
193 		seprint(p, e, "\t%.*H", rp->null->dlen, rp->null->data);
194 		break;
195 	case Ttxt:
196 		p = seprint(p, e, "\t");
197 		for(t = rp->txt; t != nil; t = t->next)
198 			p = seprint(p, e, "%s", t->p);
199 		break;
200 	case Trp:
201 		seprint(p, e, "\t%s %s", rp->rmb->name, rp->rp->name);
202 		break;
203 	case Tkey:
204 		seprint(p, e, "\t%d %d %d", rp->key->flags, rp->key->proto,
205 			rp->key->alg);
206 		break;
207 	case Tsig:
208 		seprint(p, e, "\t%d %d %d %lud %lud %lud %d %s",
209 			rp->sig->type, rp->sig->alg, rp->sig->labels,
210 			rp->sig->ttl, rp->sig->exp, rp->sig->incep,
211 			rp->sig->tag, rp->sig->signer->name);
212 		break;
213 	case Tcert:
214 		seprint(p, e, "\t%d %d %d",
215 			rp->sig->type, rp->sig->tag, rp->sig->alg);
216 		break;
217 	}
218 out:
219 	return fmtstrcpy(f, buf);
220 }
221 
222 void
logsection(char * flag,RR * rp)223 logsection(char *flag, RR *rp)
224 {
225 	if(rp == nil)
226 		return;
227 	print("\t%s%R\n", flag, rp);
228 	for(rp = rp->next; rp != nil; rp = rp->next)
229 		print("\t      %R\n", rp);
230 }
231 
232 void
logreply(int id,uchar * addr,DNSmsg * mp)233 logreply(int id, uchar *addr, DNSmsg *mp)
234 {
235 	RR *rp;
236 	char buf[12], resp[32];
237 
238 	switch(mp->flags & Rmask){
239 	case Rok:
240 		strcpy(resp, "OK");
241 		break;
242 	case Rformat:
243 		strcpy(resp, "Format error");
244 		break;
245 	case Rserver:
246 		strcpy(resp, "Server failed");
247 		break;
248 	case Rname:
249 		strcpy(resp, "Nonexistent");
250 		break;
251 	case Runimplimented:
252 		strcpy(resp, "Unimplemented");
253 		break;
254 	case Rrefused:
255 		strcpy(resp, "Refused");
256 		break;
257 	default:
258 		sprint(resp, "%d", mp->flags & Rmask);
259 		break;
260 	}
261 
262 	print("%d: rcvd %s from %I (%s%s%s%s%s)\n", id, resp, addr,
263 		mp->flags & Fauth? "authoritative": "",
264 		mp->flags & Ftrunc? " truncated": "",
265 		mp->flags & Frecurse? " recurse": "",
266 		mp->flags & Fcanrec? " can_recurse": "",
267 		(mp->flags & (Fauth|Rmask)) == (Fauth|Rname)? " nx": "");
268 	for(rp = mp->qd; rp != nil; rp = rp->next)
269 		print("\tQ:    %s %s\n", rp->owner->name,
270 			rrname(rp->type, buf, sizeof buf));
271 	logsection("Ans:  ", mp->an);
272 	logsection("Auth: ", mp->ns);
273 	logsection("Hint: ", mp->ar);
274 }
275 
276 void
logsend(int id,int subid,uchar * addr,char * sname,char * rname,int type)277 logsend(int id, int subid, uchar *addr, char *sname, char *rname, int type)
278 {
279 	char buf[12];
280 
281 	print("%d.%d: sending to %I/%s %s %s\n", id, subid,
282 		addr, sname, rname, rrname(type, buf, sizeof buf));
283 }
284 
285 RR*
getdnsservers(int class)286 getdnsservers(int class)
287 {
288 	RR *rr;
289 
290 	if(servername == nil)
291 		return dnsservers(class);
292 
293 	rr = rralloc(Tns);
294 	rr->owner = dnlookup("local#dns#servers", class, 1);
295 	rr->host = dnlookup(servername, class, 1);
296 
297 	return rr;
298 }
299 
300 void
squirrelserveraddrs(void)301 squirrelserveraddrs(void)
302 {
303 	int v4;
304 	char *attr;
305 	RR *rr, *rp, **l;
306 	Request req;
307 
308 	/* look up the resolver address first */
309 	cfg.resolver = 0;
310 	debug = 0;
311 	if(serveraddrs)
312 		rrfreelist(serveraddrs);
313 	serveraddrs = nil;
314 	rr = getdnsservers(Cin);
315 	l = &serveraddrs;
316 	for(rp = rr; rp != nil; rp = rp->next){
317 		attr = ipattr(rp->host->name);
318 		v4 = strcmp(attr, "ip") == 0;
319 		if(v4 || strcmp(attr, "ipv6") == 0){
320 			*l = rralloc(v4? Ta: Taaaa);
321 			(*l)->owner = rp->host;
322 			(*l)->ip = rp->host;
323 			l = &(*l)->next;
324 			continue;
325 		}
326 		memset(&req, 0, sizeof req);
327 		req.isslave = 1;
328 		req.aborttime = NS2MS(nowns) + Maxreqtm;
329 		*l = dnresolve(rp->host->name, Cin, Ta, &req, 0, 0, Recurse, 0, 0);
330 		if(*l == nil)
331 			*l = dnresolve(rp->host->name, Cin, Taaaa, &req,
332 				0, 0, Recurse, 0, 0);
333 		while(*l != nil)
334 			l = &(*l)->next;
335 	}
336 	cfg.resolver = 1;
337 	debug = 1;
338 }
339 
340 void
preloadserveraddrs(void)341 preloadserveraddrs(void)
342 {
343 	RR *rp, **l, *first;
344 
345 	l = &first;
346 	for(rp = serveraddrs; rp != nil; rp = rp->next){
347 		lock(&dnlock);
348 		rrcopy(rp, l);
349 		unlock(&dnlock);
350 		rrattach(first, Authoritative);
351 	}
352 }
353 
354 int
setserver(char * server)355 setserver(char *server)
356 {
357 	if(servername != nil){
358 		free(servername);
359 		servername = nil;
360 		cfg.resolver = 0;
361 	}
362 	if(server == nil || *server == 0)
363 		return 0;
364 	servername = strdup(server);
365 	squirrelserveraddrs();
366 	if(serveraddrs == nil){
367 		print("can't resolve %s\n", servername);
368 		cfg.resolver = 0;
369 	} else
370 		cfg.resolver = 1;
371 	return cfg.resolver? 0: -1;
372 }
373 
374 void
doquery(char * name,char * tstr)375 doquery(char *name, char *tstr)
376 {
377 	int len, type, rooted;
378 	char *p, *np;
379 	char buf[1024];
380 	RR *rr, *rp;
381 	Request req;
382 
383 	if(cfg.resolver)
384 		preloadserveraddrs();
385 
386 	/* default to an "ip" request if alpha, "ptr" if numeric */
387 	if(tstr == nil || *tstr == 0)
388 		if(strcmp(ipattr(name), "ip") == 0)
389 			tstr = "ptr";
390 		else
391 			tstr = "ip";
392 
393 	/* if name end in '.', remove it */
394 	len = strlen(name);
395 	if(len > 0 && name[len-1] == '.'){
396 		rooted = 1;
397 		name[len-1] = 0;
398 	} else
399 		rooted = 0;
400 
401 	/* inverse queries may need to be permuted */
402 	strncpy(buf, name, sizeof buf);
403 	if(strcmp("ptr", tstr) == 0 && cistrstr(name, ".arpa") == nil){
404 		/* TODO: reversing v6 addrs is harder */
405 		for(p = name; *p; p++)
406 			;
407 		*p = '.';
408 		np = buf;
409 		len = 0;
410 		while(p >= name){
411 			len++;
412 			p--;
413 			if(*p == '.'){
414 				memmove(np, p+1, len);
415 				np += len;
416 				len = 0;
417 			}
418 		}
419 		memmove(np, p+1, len);
420 		np += len;
421 		strcpy(np, "in-addr.arpa");	/* TODO: ip6.arpa for v6 */
422 	}
423 
424 	/* look it up */
425 	type = rrtype(tstr);
426 	if(type < 0){
427 		print("!unknown type %s\n", tstr);
428 		return;
429 	}
430 
431 	memset(&req, 0, sizeof req);
432 	getactivity(&req, 0);
433 	req.isslave = 1;
434 	req.aborttime = NS2MS(nowns) + Maxreqtm;
435 	rr = dnresolve(buf, Cin, type, &req, 0, 0, Recurse, rooted, 0);
436 	if(rr){
437 		print("----------------------------\n");
438 		for(rp = rr; rp; rp = rp->next)
439 			print("answer %R\n", rp);
440 		print("----------------------------\n");
441 	}
442 	rrfreelist(rr);
443 
444 	putactivity(0);
445 }
446 
447 void
docmd(int n,char ** f)448 docmd(int n, char **f)
449 {
450 	int tmpsrv;
451 	char *name, *type;
452 
453 	name = type = nil;
454 	tmpsrv = 0;
455 
456 	if(*f[0] == '@') {
457 		if(setserver(f[0]+1) < 0)
458 			return;
459 
460 		switch(n){
461 		case 3:
462 			type = f[2];
463 			/* fall through */
464 		case 2:
465 			name = f[1];
466 			tmpsrv = 1;
467 			break;
468 		}
469 	} else
470 		switch(n){
471 		case 2:
472 			type = f[1];
473 			/* fall through */
474 		case 1:
475 			name = f[0];
476 			break;
477 		}
478 
479 	if(name == nil)
480 		return;
481 
482 	doquery(name, type);
483 
484 	if(tmpsrv)
485 		setserver("");
486 }
487