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