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