xref: /plan9/sys/src/cmd/ndb/dnsdebug.c (revision a84536681645e23c630ce4ef2e5c3b284d4c590b)
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 int traceactivity;
27 char	*trace;
28 int	needrefresh;
29 int	resolver;
30 uchar	ipaddr[IPaddrlen];	/* my ip address */
31 int	maxage;
32 char	*logfile = "dns";
33 char	*dbfile;
34 char	mntpt[Maxpath];
35 char	*zonerefreshprogram;
36 
37 int prettyrrfmt(Fmt*);
38 void preloadserveraddrs(void);
39 void squirrelserveraddrs(void);
40 int setserver(char*);
41 void doquery(char*, char*);
42 void docmd(int, char**);
43 
44 void
45 main(int argc, char *argv[])
46 {
47 	int n;
48 	Biobuf in;
49 	char *p;
50 	char *f[4];
51 
52 	strcpy(mntpt, "/net");
53 
54 	ARGBEGIN{
55 	case 'r':
56 		resolver = 1;
57 		break;
58 	case 'x':
59 		dbfile = "/lib/ndb/external";
60 		strcpy(mntpt, "/net.alt");
61 		break;
62 	case 'f':
63 		dbfile = ARGF();
64 		break;
65 	}ARGEND
66 
67 	now = time(0);
68 	dninit();
69 	fmtinstall('R', prettyrrfmt);
70 	if(myipaddr(ipaddr, mntpt) < 0)
71 		sysfatal("can't read my ip address");
72 	opendatabase();
73 
74 	if(resolver)
75 		squirrelserveraddrs();
76 
77 	debug = 1;
78 
79 	if(argc > 0){
80 		docmd(argc, argv);
81 		exits(0);
82 	}
83 
84 	Binit(&in, 0, OREAD);
85 	for(print("> "); p = Brdline(&in, '\n'); print("> ")){
86 		p[Blinelen(&in)-1] = 0;
87 		n = tokenize(p, f, 3);
88 		if(n<1)
89 			continue;
90 
91 		/* flush the cache */
92 		dnpurge();
93 
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->name);
159 		break;
160 	case Tmg:
161 	case Tmr:
162 		seprint(p, e, "\t%s", rp->mb->name);
163 		break;
164 	case Tminfo:
165 		seprint(p, e, "\t%s %s", rp->mb->name, rp->rmb->name);
166 		break;
167 	case Tmx:
168 		seprint(p, e, "\t%lud %s", rp->pref, rp->host->name);
169 		break;
170 	case Ta:
171 	case Taaaa:
172 		seprint(p, e, "\t%s", rp->ip->name);
173 		break;
174 	case Tptr:
175 		seprint(p, e, "\t%s", rp->ptr->name);
176 		break;
177 	case Tsoa:
178 		seprint(p, e, "\t%s %s %lud %lud %lud %lud %lud", rp->host->name,
179 			rp->rmb->name, rp->soa->serial, rp->soa->refresh, rp->soa->retry,
180 			rp->soa->expire, rp->soa->minttl);
181 		break;
182 	case Tnull:
183 		seprint(p, e, "\t%.*H", rp->null->dlen, rp->null->data);
184 		break;
185 	case Ttxt:
186 		p = seprint(p, e, "\t");
187 		for(t = rp->txt; t != nil; t = t->next)
188 			p = seprint(p, e, "%s", t->p);
189 		break;
190 	case Trp:
191 		seprint(p, e, "\t%s %s", rp->rmb->name, rp->rp->name);
192 		break;
193 	case Tkey:
194 		seprint(p, e, "\t%d %d %d", rp->key->flags, rp->key->proto,
195 			rp->key->alg);
196 		break;
197 	case Tsig:
198 		seprint(p, e, "\t%d %d %d %lud %lud %lud %d %s",
199 			rp->sig->type, rp->sig->alg, rp->sig->labels, rp->sig->ttl,
200 			rp->sig->exp, rp->sig->incep, rp->sig->tag, rp->sig->signer->name);
201 		break;
202 	case Tcert:
203 		seprint(p, e, "\t%d %d %d",
204 			rp->sig->type, rp->sig->tag, rp->sig->alg);
205 		break;
206 	default:
207 		break;
208 	}
209 out:
210 	return fmtstrcpy(f, buf);
211 }
212 
213 void
214 logsection(char *flag, RR *rp)
215 {
216 	if(rp == nil)
217 		return;
218 	print("\t%s%R\n", flag, rp);
219 	for(rp = rp->next; rp != nil; rp = rp->next)
220 		print("\t      %R\n", rp);
221 }
222 
223 void
224 logreply(int id, uchar *addr, DNSmsg *mp)
225 {
226 	RR *rp;
227 	char buf[12];
228 	char resp[32];
229 
230 	switch(mp->flags & Rmask){
231 	case Rok:
232 		strcpy(resp, "OK");
233 		break;
234 	case Rformat:
235 		strcpy(resp, "Format error");
236 		break;
237 	case Rserver:
238 		strcpy(resp, "Server failed");
239 		break;
240 	case Rname:
241 		strcpy(resp, "Nonexistent");
242 		break;
243 	case Runimplimented:
244 		strcpy(resp, "Unimplemented");
245 		break;
246 	case Rrefused:
247 		strcpy(resp, "Refused");
248 		break;
249 	default:
250 		sprint(resp, "%d", mp->flags & Rmask);
251 		break;
252 	}
253 
254 	print("%d: rcvd %s from %I (%s%s%s%s%s)\n", id, resp, addr,
255 		mp->flags & Fauth ? "authoritative" : "",
256 		mp->flags & Ftrunc ? " truncated" : "",
257 		mp->flags & Frecurse ? " recurse" : "",
258 		mp->flags & Fcanrec ? " can_recurse" : "",
259 		mp->flags & (Fauth|Rname) == (Fauth|Rname) ?
260 		" nx" : "");
261 	for(rp = mp->qd; rp != nil; rp = rp->next)
262 		print("\tQ:    %s %s\n", rp->owner->name, rrname(rp->type, buf, sizeof buf));
263 	logsection("Ans:  ", mp->an);
264 	logsection("Auth: ", mp->ns);
265 	logsection("Hint: ", mp->ar);
266 }
267 
268 void
269 logsend(int id, int subid, uchar *addr, char *sname, char *rname, int type)
270 {
271 	char buf[12];
272 
273 	print("%d.%d: sending to %I/%s %s %s\n", id, subid,
274 		addr, sname, rname, rrname(type, buf, sizeof buf));
275 }
276 
277 RR*
278 getdnsservers(int class)
279 {
280 	RR *rr;
281 
282 	if(servername == nil)
283 		return dnsservers(class);
284 
285 	rr = rralloc(Tns);
286 	rr->owner = dnlookup("local#dns#servers", class, 1);
287 	rr->host = dnlookup(servername, class, 1);
288 
289 	return rr;
290 }
291 
292 void
293 squirrelserveraddrs(void)
294 {
295 	RR *rr, *rp, **l;
296 	Request req;
297 
298 	/* look up the resolver address first */
299 	resolver = 0;
300 	debug = 0;
301 	if(serveraddrs)
302 		rrfreelist(serveraddrs);
303 	serveraddrs = nil;
304 	rr = getdnsservers(Cin);
305 	l = &serveraddrs;
306 	for(rp = rr; rp != nil; rp = rp->next){
307 		if(strcmp(ipattr(rp->host->name), "ip") == 0){
308 			*l = rralloc(Ta);
309 			(*l)->owner = rp->host;
310 			(*l)->ip = rp->host;
311 			l = &(*l)->next;
312 			continue;
313 		}
314 		req.isslave = 1;
315 		req.aborttime = now + 60;	/* don't spend more than 60 seconds */
316 		*l = dnresolve(rp->host->name, Cin, Ta, &req, 0, 0, Recurse, 0, 0);
317 		while(*l != nil)
318 			l = &(*l)->next;
319 	}
320 	resolver = 1;
321 	debug = 1;
322 }
323 
324 void
325 preloadserveraddrs(void)
326 {
327 	RR *rp, **l, *first;
328 
329 	l = &first;
330 	for(rp = serveraddrs; rp != nil; rp = rp->next){
331 		rrcopy(rp, l);
332 		rrattach(first, 1);
333 	}
334 }
335 
336 int
337 setserver(char *server)
338 {
339 	if(servername != nil){
340 		free(servername);
341 		servername = nil;
342 		resolver = 0;
343 	}
344 	if(server == nil || *server == 0)
345 		return 0;
346 	servername = strdup(server);
347 	squirrelserveraddrs();
348 	if(serveraddrs == nil){
349 		print("can't resolve %s\n", servername);
350 		resolver = 0;
351 	} else {
352 		resolver = 1;
353 	}
354 	return resolver ? 0 : -1;
355 }
356 
357 void
358 doquery(char *name, char *tstr)
359 {
360 	Request req;
361 	RR *rr, *rp;
362 	int len, type;
363 	char *p, *np;
364 	int rooted;
365 	char buf[1024];
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 
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 	getactivity(&req);
418 	req.isslave = 1;
419 	req.aborttime = now + 60;	/* don't spend more than 60 seconds */
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();
430 }
431 
432 void
433 docmd(int n, char **f)
434 {
435 	int tmpsrv;
436 	char *name, *type;
437 
438 	name = nil;
439 	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