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