]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ndb/dnsdebug.c
ndb/dns: double Maxretries for long cname redirection chains
[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 uchar   ipaddr[IPaddrlen];      /* my ip address */
21 char    *logfile = "dnsdebug";
22 int     maxage  = 60*60;
23 char    mntpt[Maxpath];
24 int     needrefresh;
25 ulong   now;
26 vlong   nowns;
27 int     testing;
28 char    *trace;
29 int     traceactivity;
30 char    *zonerefreshprogram;
31
32 void    docmd(int, char**);
33 void    doquery(char*, char*);
34 void    preloadserveraddrs(void);
35 int     prettyrrfmt(Fmt*);
36 int     setserver(char*);
37 void    squirrelserveraddrs(void);
38
39 void
40 usage(void)
41 {
42         fprint(2, "%s: [-rx] [-f db-file] [[@server] domain [type]]\n", argv0);
43         exits("usage");
44 }
45
46 void
47 main(int argc, char *argv[])
48 {
49         int n;
50         Biobuf in;
51         char *p;
52         char *f[4];
53
54         strcpy(mntpt, "/net");
55         cfg.inside = 1;
56
57         ARGBEGIN{
58         case 'f':
59                 dbfile = EARGF(usage());
60                 break;
61         case 'r':
62                 cfg.resolver = 1;
63                 break;
64         case 'x':
65                 dbfile = "/lib/ndb/external";
66                 strcpy(mntpt, "/net.alt");
67                 break;
68         default:
69                 usage();
70         }ARGEND
71
72         now = time(nil);
73         nowns = nsec();
74         dninit();
75         fmtinstall('R', prettyrrfmt);
76         if(myipaddr(ipaddr, mntpt) < 0)
77                 sysfatal("can't read my ip address");
78         opendatabase();
79
80         if(cfg.resolver)
81                 squirrelserveraddrs();
82
83         debug = 1;
84
85         if(argc > 0){
86                 docmd(argc, argv);
87                 exits(0);
88         }
89
90         Binit(&in, 0, OREAD);
91         for(print("> "); p = Brdline(&in, '\n'); print("> ")){
92                 p[Blinelen(&in)-1] = 0;
93                 n = tokenize(p, f, 3);
94                 if(n>=1) {
95                         dnpurge();              /* flush the cache */
96                         docmd(n, f);
97                 }
98         }
99         exits(0);
100 }
101
102 static char*
103 longtime(long t)
104 {
105         int d, h, m, n;
106         static char x[128];
107
108         for(d = 0; t >= 24*60*60; t -= 24*60*60)
109                 d++;
110         for(h = 0; t >= 60*60; t -= 60*60)
111                 h++;
112         for(m = 0; t >= 60; t -= 60)
113                 m++;
114         n = 0;
115         if(d)
116                 n += sprint(x, "%d day ", d);
117         if(h)
118                 n += sprint(x+n, "%d hr ", h);
119         if(m)
120                 n += sprint(x+n, "%d min ", m);
121         if(t || n == 0)
122                 sprint(x+n, "%ld sec", t);
123         return x;
124 }
125
126 int
127 prettyrrfmt(Fmt *f)
128 {
129         RR *rp;
130         char buf[3*Domlen];
131         char *p, *e;
132         Txt *t;
133
134         rp = va_arg(f->args, RR*);
135         if(rp == 0){
136                 strcpy(buf, "<null>");
137                 goto out;
138         }
139
140         p = buf;
141         e = buf + sizeof(buf);
142         p = seprint(p, e, "%-32.32s %-15.15s %-5.5s", rp->owner->name,
143                 longtime(rp->ttl),
144                 rrname(rp->type, buf, sizeof buf));
145
146         if(rp->negative){
147                 seprint(p, e, "negative rcode %d", rp->negrcode);
148                 goto out;
149         }
150
151         switch(rp->type){
152         case Thinfo:
153                 seprint(p, e, "\t%s %s", rp->cpu->name, rp->os->name);
154                 break;
155         case Tcname:
156         case Tmb:
157         case Tmd:
158         case Tmf:
159         case Tns:
160                 seprint(p, e, "\t%s", (rp->host? rp->host->name: ""));
161                 break;
162         case Tmg:
163         case Tmr:
164                 seprint(p, e, "\t%s", (rp->mb? rp->mb->name: ""));
165                 break;
166         case Tminfo:
167                 seprint(p, e, "\t%s %s", (rp->mb? rp->mb->name: ""),
168                         (rp->rmb? rp->rmb->name: ""));
169                 break;
170         case Tmx:
171                 seprint(p, e, "\t%lud %s", rp->pref,
172                         (rp->host? rp->host->name: ""));
173                 break;
174         case Ta:
175         case Taaaa:
176                 seprint(p, e, "\t%s", (rp->ip? rp->ip->name: ""));
177                 break;
178         case Tptr:
179                 seprint(p, e, "\t%s", (rp->ptr? rp->ptr->name: ""));
180                 break;
181         case Tsoa:
182                 seprint(p, e, "\t%s %s %lud %lud %lud %lud %lud",
183                         rp->host->name, rp->rmb->name, rp->soa->serial,
184                         rp->soa->refresh, rp->soa->retry,
185                         rp->soa->expire, rp->soa->minttl);
186                 break;
187         case Tsrv:
188                 seprint(p, e, "\t%ud %ud %ud %s",
189                         rp->srv->pri, rp->srv->weight, rp->port, rp->host->name);
190                 break;
191         case Tnull:
192                 seprint(p, e, "\t%.*H", rp->null->dlen, rp->null->data);
193                 break;
194         case Ttxt:
195                 p = seprint(p, e, "\t");
196                 for(t = rp->txt; t != nil; t = t->next)
197                         p = seprint(p, e, "%s", t->p);
198                 break;
199         case Trp:
200                 seprint(p, e, "\t%s %s", rp->rmb->name, rp->rp->name);
201                 break;
202         case Tkey:
203                 seprint(p, e, "\t%d %d %d", rp->key->flags, rp->key->proto,
204                         rp->key->alg);
205                 break;
206         case Tsig:
207                 seprint(p, e, "\t%d %d %d %lud %lud %lud %d %s",
208                         rp->sig->type, rp->sig->alg, rp->sig->labels,
209                         rp->sig->ttl, rp->sig->exp, rp->sig->incep,
210                         rp->sig->tag, rp->sig->signer->name);
211                 break;
212         case Tcert:
213                 seprint(p, e, "\t%d %d %d",
214                         rp->sig->type, rp->sig->tag, rp->sig->alg);
215                 break;
216         }
217 out:
218         return fmtstrcpy(f, buf);
219 }
220
221 void
222 logsection(char *flag, RR *rp)
223 {
224         if(rp == nil)
225                 return;
226         print("\t%s%R\n", flag, rp);
227         for(rp = rp->next; rp != nil; rp = rp->next)
228                 print("\t      %R\n", rp);
229 }
230
231 void
232 logreply(int id, uchar *addr, DNSmsg *mp)
233 {
234         RR *rp;
235         char buf[12], resp[32];
236
237         switch(mp->flags & Rmask){
238         case Rok:
239                 strcpy(resp, "OK");
240                 break;
241         case Rformat:
242                 strcpy(resp, "Format error");
243                 break;
244         case Rserver:
245                 strcpy(resp, "Server failed");
246                 break;
247         case Rname:
248                 strcpy(resp, "Nonexistent");
249                 break;
250         case Runimplimented:
251                 strcpy(resp, "Unimplemented");
252                 break;
253         case Rrefused:
254                 strcpy(resp, "Refused");
255                 break;
256         default:
257                 sprint(resp, "%d", mp->flags & Rmask);
258                 break;
259         }
260
261         print("%d: rcvd %s from %I (%s%s%s%s%s)\n", id, resp, addr,
262                 mp->flags & Fauth? "authoritative": "",
263                 mp->flags & Ftrunc? " truncated": "",
264                 mp->flags & Frecurse? " recurse": "",
265                 mp->flags & Fcanrec? " can_recurse": "",
266                 (mp->flags & (Fauth|Rmask)) == (Fauth|Rname)? " nx": "");
267         for(rp = mp->qd; rp != nil; rp = rp->next)
268                 print("\tQ:    %s %s\n", rp->owner->name,
269                         rrname(rp->type, buf, sizeof buf));
270         logsection("Ans:  ", mp->an);
271         logsection("Auth: ", mp->ns);
272         logsection("Hint: ", mp->ar);
273 }
274
275 void
276 logsend(int id, int subid, uchar *addr, char *sname, char *rname, int type)
277 {
278         char buf[12];
279
280         print("%d.%d: sending to %I/%s %s %s\n", id, subid,
281                 addr, sname, rname, rrname(type, buf, sizeof buf));
282 }
283
284 RR*
285 getdnsservers(int class)
286 {
287         RR *rr;
288
289         if(servername == nil)
290                 return dnsservers(class);
291
292         rr = rralloc(Tns);
293         rr->owner = dnlookup("local#dns#servers", class, 1);
294         rr->host = idnlookup(servername, class, 1);
295
296         return rr;
297 }
298
299 void
300 squirrelserveraddrs(void)
301 {
302         int v4;
303         char *attr;
304         RR *rr, *rp, **l;
305         Request req;
306
307         /* look up the resolver address first */
308         cfg.resolver = 0;
309         debug = 0;
310         if(serveraddrs){
311                 rrfreelist(serveraddrs);
312                 serveraddrs = nil;
313         }
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 }