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