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