]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ndb/dnsdebug.c
b2f3cebf272a544157082ecedc328d6d2431c12b
[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 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->ttl),
142                 rrname(rp->type, buf, sizeof buf));
143
144         if(rp->negative){
145                 seprint(p, e, "negative rcode %d", 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? rp->host->name: ""));
159                 break;
160         case Tmg:
161         case Tmr:
162                 seprint(p, e, "\t%s", (rp->mb? rp->mb->name: ""));
163                 break;
164         case Tminfo:
165                 seprint(p, e, "\t%s %s", (rp->mb? rp->mb->name: ""),
166                         (rp->rmb? rp->rmb->name: ""));
167                 break;
168         case Tmx:
169                 seprint(p, e, "\t%lud %s", rp->pref,
170                         (rp->host? rp->host->name: ""));
171                 break;
172         case Ta:
173         case Taaaa:
174                 seprint(p, e, "\t%s", (rp->ip? rp->ip->name: ""));
175                 break;
176         case Tptr:
177                 seprint(p, e, "\t%s", (rp->ptr? rp->ptr->name: ""));
178                 break;
179         case Tsoa:
180                 seprint(p, e, "\t%s %s %lud %lud %lud %lud %lud",
181                         rp->host->name, rp->rmb->name, rp->soa->serial,
182                         rp->soa->refresh, rp->soa->retry,
183                         rp->soa->expire, rp->soa->minttl);
184                 break;
185         case Tsrv:
186                 seprint(p, e, "\t%ud %ud %ud %s",
187                         rp->srv->pri, rp->srv->weight, rp->port, rp->host->name);
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,
207                         rp->sig->ttl, rp->sig->exp, rp->sig->incep,
208                         rp->sig->tag, rp->sig->signer->name);
209                 break;
210         case Tcert:
211                 seprint(p, e, "\t%d %d %d",
212                         rp->sig->type, rp->sig->tag, rp->sig->alg);
213                 break;
214         }
215 out:
216         return fmtstrcpy(f, buf);
217 }
218
219 void
220 logsection(char *flag, RR *rp)
221 {
222         if(rp == nil)
223                 return;
224         print("\t%s%R\n", flag, rp);
225         for(rp = rp->next; rp != nil; rp = rp->next)
226                 print("\t      %R\n", rp);
227 }
228
229 void
230 logreply(int id, uchar *addr, DNSmsg *mp)
231 {
232         RR *rp;
233         char buf[12], resp[32];
234
235         switch(mp->flags & Rmask){
236         case Rok:
237                 strcpy(resp, "OK");
238                 break;
239         case Rformat:
240                 strcpy(resp, "Format error");
241                 break;
242         case Rserver:
243                 strcpy(resp, "Server failed");
244                 break;
245         case Rname:
246                 strcpy(resp, "Nonexistent");
247                 break;
248         case Runimplimented:
249                 strcpy(resp, "Unimplemented");
250                 break;
251         case Rrefused:
252                 strcpy(resp, "Refused");
253                 break;
254         default:
255                 sprint(resp, "%d", mp->flags & Rmask);
256                 break;
257         }
258
259         print("%d: rcvd %s from %I (%s%s%s%s%s)\n", id, resp, addr,
260                 mp->flags & Fauth? "authoritative": "",
261                 mp->flags & Ftrunc? " truncated": "",
262                 mp->flags & Frecurse? " recurse": "",
263                 mp->flags & Fcanrec? " can_recurse": "",
264                 (mp->flags & (Fauth|Rmask)) == (Fauth|Rname)? " nx": "");
265         for(rp = mp->qd; rp != nil; rp = rp->next)
266                 print("\tQ:    %s %s\n", rp->owner->name,
267                         rrname(rp->type, buf, sizeof buf));
268         logsection("Ans:  ", mp->an);
269         logsection("Auth: ", mp->ns);
270         logsection("Hint: ", mp->ar);
271 }
272
273 void
274 logsend(int id, int subid, uchar *addr, char *sname, char *rname, int type)
275 {
276         char buf[12];
277
278         print("%d.%d: sending to %I/%s %s %s\n", id, subid,
279                 addr, sname, rname, rrname(type, buf, sizeof buf));
280 }
281
282 RR*
283 getdnsservers(int class)
284 {
285         RR *rr;
286
287         if(servername == nil)
288                 return dnsservers(class);
289
290         rr = rralloc(Tns);
291         rr->owner = dnlookup("local#dns#servers", class, 1);
292         rr->host = idnlookup(servername, class, 1);
293
294         return rr;
295 }
296
297 void
298 squirrelserveraddrs(void)
299 {
300         int v4;
301         char *attr;
302         RR *rr, *rp, **l;
303         Request req;
304
305         /* look up the resolver address first */
306         cfg.resolver = 0;
307         debug = 0;
308         if(serveraddrs){
309                 rrfreelist(serveraddrs);
310                 serveraddrs = nil;
311         }
312         rr = getdnsservers(Cin);
313         l = &serveraddrs;
314         for(rp = rr; rp != nil; rp = rp->next){
315                 attr = ipattr(rp->host->name);
316                 v4 = strcmp(attr, "ip") == 0;
317                 if(v4 || strcmp(attr, "ipv6") == 0){
318                         *l = rralloc(v4? Ta: Taaaa);
319                         (*l)->owner = rp->host;
320                         (*l)->ip = rp->host;
321                         l = &(*l)->next;
322                         continue;
323                 }
324                 memset(&req, 0, sizeof req);
325                 req.isslave = 1;
326                 req.aborttime = NS2MS(nowns) + Maxreqtm;
327                 *l = dnresolve(rp->host->name, Cin, Ta, &req, 0, 0, Recurse, 0, 0);
328                 if(*l == nil)
329                         *l = dnresolve(rp->host->name, Cin, Taaaa, &req,
330                                 0, 0, Recurse, 0, 0);
331                 while(*l != nil)
332                         l = &(*l)->next;
333         }
334         cfg.resolver = 1;
335         debug = 1;
336 }
337
338 void
339 preloadserveraddrs(void)
340 {
341         RR *rp, **l, *first;
342
343         first = nil;
344         l = &first;
345         for(rp = serveraddrs; rp != nil; rp = rp->next){
346                 rrcopy(rp, l);
347                 rrattach(first, Authoritative);
348         }
349 }
350
351 int
352 setserver(char *server)
353 {
354         if(servername != nil){
355                 free(servername);
356                 servername = nil;
357                 cfg.resolver = 0;
358         }
359         if(server == nil || *server == 0)
360                 return 0;
361         servername = strdup(server);
362         squirrelserveraddrs();
363         if(serveraddrs == nil){
364                 print("can't resolve %s\n", servername);
365                 cfg.resolver = 0;
366         } else
367                 cfg.resolver = 1;
368         return cfg.resolver? 0: -1;
369 }
370
371 void
372 doquery(char *name, char *tstr)
373 {
374         int len, type, rooted;
375         char *p, *np;
376         char buf[1024];
377         RR *rr, *rp;
378         Request req;
379
380         if(cfg.resolver)
381                 preloadserveraddrs();
382
383         /* default to an "ip" request if alpha, "ptr" if numeric */
384         if(tstr == nil || *tstr == 0)
385                 if(strcmp(ipattr(name), "ip") == 0)
386                         tstr = "ptr";
387                 else
388                         tstr = "ip";
389
390         /* if name end in '.', remove it */
391         len = strlen(name);
392         if(len > 0 && name[len-1] == '.'){
393                 rooted = 1;
394                 name[len-1] = 0;
395         } else
396                 rooted = 0;
397
398         /* inverse queries may need to be permuted */
399         strncpy(buf, name, sizeof buf);
400         if(strcmp("ptr", tstr) == 0 && cistrstr(name, ".arpa") == nil){
401                 /* TODO: reversing v6 addrs is harder */
402                 for(p = name; *p; p++)
403                         ;
404                 *p = '.';
405                 np = buf;
406                 len = 0;
407                 while(p >= name){
408                         len++;
409                         p--;
410                         if(*p == '.'){
411                                 memmove(np, p+1, len);
412                                 np += len;
413                                 len = 0;
414                         }
415                 }
416                 memmove(np, p+1, len);
417                 np += len;
418                 strcpy(np, "in-addr.arpa");     /* TODO: ip6.arpa for v6 */
419         }
420
421         /* look it up */
422         type = rrtype(tstr);
423         if(type < 0){
424                 print("!unknown type %s\n", tstr);
425                 return;
426         }
427
428         memset(&req, 0, sizeof req);
429         getactivity(&req, 0);
430         req.isslave = 1;
431         req.aborttime = NS2MS(nowns) + Maxreqtm;
432         rr = dnresolve(buf, Cin, type, &req, 0, 0, Recurse, rooted, 0);
433         if(rr){
434                 print("----------------------------\n");
435                 for(rp = rr; rp; rp = rp->next)
436                         print("answer %R\n", rp);
437                 print("----------------------------\n");
438         }
439         rrfreelist(rr);
440
441         putactivity(0);
442 }
443
444 void
445 docmd(int n, char **f)
446 {
447         int tmpsrv;
448         char *name, *type;
449
450         name = type = nil;
451         tmpsrv = 0;
452
453         if(*f[0] == '@') {
454                 if(setserver(f[0]+1) < 0)
455                         return;
456
457                 switch(n){
458                 case 3:
459                         type = f[2];
460                         /* fall through */
461                 case 2:
462                         name = f[1];
463                         tmpsrv = 1;
464                         break;
465                 }
466         } else
467                 switch(n){
468                 case 2:
469                         type = f[1];
470                         /* fall through */
471                 case 1:
472                         name = f[0];
473                         break;
474                 }
475
476         if(name == nil)
477                 return;
478
479         doquery(name, type);
480
481         if(tmpsrv)
482                 setserver("");
483 }