]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ndb/dnsdebug.c
Import sources from 2011-03-30 iso image - lib
[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 *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
41 usage(void)
42 {
43         fprint(2, "%s: [-rx] [-f db-file] [[@server] domain [type]]\n", argv0);
44         exits("usage");
45 }
46
47 void
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*
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
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
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
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
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*
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
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                 req.isslave = 1;
327                 req.aborttime = NS2MS(nowns) + Maxreqtm;
328                 *l = dnresolve(rp->host->name, Cin, Ta, &req, 0, 0, Recurse, 0, 0);
329                 if(*l == nil)
330                         *l = dnresolve(rp->host->name, Cin, Taaaa, &req,
331                                 0, 0, Recurse, 0, 0);
332                 while(*l != nil)
333                         l = &(*l)->next;
334         }
335         cfg.resolver = 1;
336         debug = 1;
337 }
338
339 void
340 preloadserveraddrs(void)
341 {
342         RR *rp, **l, *first;
343
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 }