]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ndb/dnsdebug.c
merge
[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                 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
341 preloadserveraddrs(void)
342 {
343         RR *rp, **l, *first;
344
345         first = nil;
346         l = &first;
347         for(rp = serveraddrs; rp != nil; rp = rp->next){
348                 rrcopy(rp, l);
349                 rrattach(first, Authoritative);
350         }
351 }
352
353 int
354 setserver(char *server)
355 {
356         if(servername != nil){
357                 free(servername);
358                 servername = nil;
359                 cfg.resolver = 0;
360         }
361         if(server == nil || *server == 0)
362                 return 0;
363         servername = strdup(server);
364         squirrelserveraddrs();
365         if(serveraddrs == nil){
366                 print("can't resolve %s\n", servername);
367                 cfg.resolver = 0;
368         } else
369                 cfg.resolver = 1;
370         return cfg.resolver? 0: -1;
371 }
372
373 void
374 doquery(char *name, char *tstr)
375 {
376         int len, type, rooted;
377         char *p, *np;
378         char buf[1024];
379         RR *rr, *rp;
380         Request req;
381
382         if(cfg.resolver)
383                 preloadserveraddrs();
384
385         /* default to an "ip" request if alpha, "ptr" if numeric */
386         if(tstr == nil || *tstr == 0)
387                 if(strcmp(ipattr(name), "ip") == 0)
388                         tstr = "ptr";
389                 else
390                         tstr = "ip";
391
392         /* if name end in '.', remove it */
393         len = strlen(name);
394         if(len > 0 && name[len-1] == '.'){
395                 rooted = 1;
396                 name[len-1] = 0;
397         } else
398                 rooted = 0;
399
400         /* inverse queries may need to be permuted */
401         strncpy(buf, name, sizeof buf);
402         if(strcmp("ptr", tstr) == 0 && cistrstr(name, ".arpa") == nil){
403                 /* TODO: reversing v6 addrs is harder */
404                 for(p = name; *p; p++)
405                         ;
406                 *p = '.';
407                 np = buf;
408                 len = 0;
409                 while(p >= name){
410                         len++;
411                         p--;
412                         if(*p == '.'){
413                                 memmove(np, p+1, len);
414                                 np += len;
415                                 len = 0;
416                         }
417                 }
418                 memmove(np, p+1, len);
419                 np += len;
420                 strcpy(np, "in-addr.arpa");     /* TODO: ip6.arpa for v6 */
421         }
422
423         /* look it up */
424         type = rrtype(tstr);
425         if(type < 0){
426                 print("!unknown type %s\n", tstr);
427                 return;
428         }
429
430         memset(&req, 0, sizeof req);
431         getactivity(&req, 0);
432         req.isslave = 1;
433         req.aborttime = NS2MS(nowns) + Maxreqtm;
434         rr = dnresolve(buf, Cin, type, &req, 0, 0, Recurse, rooted, 0);
435         if(rr){
436                 print("----------------------------\n");
437                 for(rp = rr; rp; rp = rp->next)
438                         print("answer %R\n", rp);
439                 print("----------------------------\n");
440         }
441         rrfreelist(rr);
442
443         putactivity(0);
444 }
445
446 void
447 docmd(int n, char **f)
448 {
449         int tmpsrv;
450         char *name, *type;
451
452         name = type = nil;
453         tmpsrv = 0;
454
455         if(*f[0] == '@') {
456                 if(setserver(f[0]+1) < 0)
457                         return;
458
459                 switch(n){
460                 case 3:
461                         type = f[2];
462                         /* fall through */
463                 case 2:
464                         name = f[1];
465                         tmpsrv = 1;
466                         break;
467                 }
468         } else
469                 switch(n){
470                 case 2:
471                         type = f[1];
472                         /* fall through */
473                 case 1:
474                         name = f[0];
475                         break;
476                 }
477
478         if(name == nil)
479                 return;
480
481         doquery(name, type);
482
483         if(tmpsrv)
484                 setserver("");
485 }