| #include <u.h> |
| #include <libc.h> |
| #include <bio.h> |
| #include <ctype.h> |
| #include <ip.h> |
| #include <ndb.h> |
| #include <thread.h> |
| #include "dns.h" |
| |
| enum |
| { |
| Maxrequest= 128, |
| Ncache= 8, |
| Maxpath= 128, |
| Maxreply= 512, |
| Maxrrr= 16 |
| }; |
| |
| static char *servername; |
| static RR *serveraddrs; |
| |
| int debug; |
| int cachedb; |
| ulong now; |
| int testing; |
| int traceactivity; |
| char *trace; |
| int needrefresh; |
| int resolver; |
| uchar ipaddr[IPaddrlen]; /* my ip address */ |
| int maxage; |
| char *logfile = "dns"; |
| char *dbfile; |
| char mntpt[Maxpath]; |
| char *zonerefreshprogram; |
| char *tcpaddr; |
| char *udpaddr; |
| |
| int prettyrrfmt(Fmt*); |
| void preloadserveraddrs(void); |
| void squirrelserveraddrs(void); |
| int setserver(char*); |
| void doquery(char*, char*); |
| void docmd(int, char**); |
| |
| void |
| usage(void) |
| { |
| fprint(2, "usage: dnsdebug [-fr] [query ...]\n"); |
| threadexitsall("usage"); |
| } |
| |
| void |
| threadmain(int argc, char *argv[]) |
| { |
| int n; |
| Biobuf in; |
| char *p; |
| char *f[4]; |
| |
| strcpy(mntpt, "/net"); |
| |
| ARGBEGIN{ |
| case 'r': |
| resolver = 1; |
| break; |
| case 'f': |
| dbfile = EARGF(usage()); |
| break; |
| default: |
| usage(); |
| }ARGEND |
| |
| now = time(0); |
| dninit(); |
| fmtinstall('R', prettyrrfmt); |
| if(myipaddr(ipaddr, mntpt) < 0) |
| sysfatal("can't read my ip address"); |
| opendatabase(); |
| |
| if(resolver) |
| squirrelserveraddrs(); |
| |
| debug = 1; |
| |
| if(argc > 0){ |
| docmd(argc, argv); |
| threadexitsall(0); |
| } |
| |
| Binit(&in, 0, OREAD); |
| for(print("> "); p = Brdline(&in, '\n'); print("> ")){ |
| p[Blinelen(&in)-1] = 0; |
| n = tokenize(p, f, 3); |
| if(n<1) |
| continue; |
| |
| /* flush the cache */ |
| dnpurge(); |
| |
| docmd(n, f); |
| |
| } |
| threadexitsall(0); |
| } |
| |
| static char* |
| longtime(long t) |
| { |
| int d, h, m, n; |
| static char x[128]; |
| |
| for(d = 0; t >= 24*60*60; t -= 24*60*60) |
| d++; |
| for(h = 0; t >= 60*60; t -= 60*60) |
| h++; |
| for(m = 0; t >= 60; t -= 60) |
| m++; |
| n = 0; |
| if(d) |
| n += sprint(x, "%d day ", d); |
| if(h) |
| n += sprint(x+n, "%d hr ", h); |
| if(m) |
| n += sprint(x+n, "%d min ", m); |
| if(t || n == 0) |
| sprint(x+n, "%ld sec", t); |
| return x; |
| } |
| |
| int |
| prettyrrfmt(Fmt *f) |
| { |
| RR *rp; |
| char buf[3*Domlen]; |
| char *p, *e; |
| Txt *t; |
| |
| rp = va_arg(f->args, RR*); |
| if(rp == 0){ |
| strcpy(buf, "<null>"); |
| goto out; |
| } |
| |
| p = buf; |
| e = buf + sizeof(buf); |
| p = seprint(p, e, "%-32.32s %-15.15s %-5.5s", rp->owner->name, |
| longtime(rp->db ? rp->ttl : (rp->ttl-now)), |
| rrname(rp->type, buf, sizeof buf)); |
| |
| if(rp->negative){ |
| seprint(p, e, "negative rcode %d\n", rp->negrcode); |
| goto out; |
| } |
| |
| switch(rp->type){ |
| case Thinfo: |
| seprint(p, e, "\t%s %s", rp->cpu->name, rp->os->name); |
| break; |
| case Tcname: |
| case Tmb: |
| case Tmd: |
| case Tmf: |
| case Tns: |
| seprint(p, e, "\t%s", rp->host->name); |
| break; |
| case Tmg: |
| case Tmr: |
| seprint(p, e, "\t%s", rp->mb->name); |
| break; |
| case Tminfo: |
| seprint(p, e, "\t%s %s", rp->mb->name, rp->rmb->name); |
| break; |
| case Tmx: |
| seprint(p, e, "\t%lud %s", rp->pref, rp->host->name); |
| break; |
| case Ta: |
| case Taaaa: |
| seprint(p, e, "\t%s", rp->ip->name); |
| break; |
| case Tptr: |
| seprint(p, e, "\t%s", rp->ptr->name); |
| break; |
| case Tsoa: |
| seprint(p, e, "\t%s %s %lud %lud %lud %lud %lud", rp->host->name, |
| rp->rmb->name, rp->soa->serial, rp->soa->refresh, rp->soa->retry, |
| rp->soa->expire, rp->soa->minttl); |
| break; |
| case Tnull: |
| seprint(p, e, "\t%.*H", rp->null->dlen, rp->null->data); |
| break; |
| case Ttxt: |
| p = seprint(p, e, "\t"); |
| for(t = rp->txt; t != nil; t = t->next) |
| p = seprint(p, e, "%s", t->p); |
| break; |
| case Trp: |
| seprint(p, e, "\t%s %s", rp->rmb->name, rp->rp->name); |
| break; |
| case Tkey: |
| seprint(p, e, "\t%d %d %d", rp->key->flags, rp->key->proto, |
| rp->key->alg); |
| break; |
| case Tsig: |
| seprint(p, e, "\t%d %d %d %lud %lud %lud %d %s", |
| rp->sig->type, rp->sig->alg, rp->sig->labels, rp->sig->ttl, |
| rp->sig->exp, rp->sig->incep, rp->sig->tag, rp->sig->signer->name); |
| break; |
| case Tcert: |
| seprint(p, e, "\t%d %d %d", |
| rp->sig->type, rp->sig->tag, rp->sig->alg); |
| break; |
| default: |
| break; |
| } |
| out: |
| return fmtstrcpy(f, buf); |
| } |
| |
| void |
| logsection(char *flag, RR *rp) |
| { |
| if(rp == nil) |
| return; |
| print("\t%s%R\n", flag, rp); |
| for(rp = rp->next; rp != nil; rp = rp->next) |
| print("\t %R\n", rp); |
| } |
| |
| void |
| logreply(int id, uchar *addr, DNSmsg *mp) |
| { |
| RR *rp; |
| char buf[12]; |
| char resp[32]; |
| |
| switch(mp->flags & Rmask){ |
| case Rok: |
| strcpy(resp, "OK"); |
| break; |
| case Rformat: |
| strcpy(resp, "Format error"); |
| break; |
| case Rserver: |
| strcpy(resp, "Server failed"); |
| break; |
| case Rname: |
| strcpy(resp, "Nonexistent"); |
| break; |
| case Runimplimented: |
| strcpy(resp, "Unimplemented"); |
| break; |
| case Rrefused: |
| strcpy(resp, "Refused"); |
| break; |
| default: |
| sprint(resp, "%d", mp->flags & Rmask); |
| break; |
| } |
| |
| print("%d: rcvd %s from %I (%s%s%s%s%s)\n", id, resp, addr, |
| mp->flags & Fauth ? "authoritative" : "", |
| mp->flags & Ftrunc ? " truncated" : "", |
| mp->flags & Frecurse ? " recurse" : "", |
| mp->flags & Fcanrec ? " can_recurse" : "", |
| mp->flags & (Fauth|Rname) == (Fauth|Rname) ? |
| " nx" : ""); |
| for(rp = mp->qd; rp != nil; rp = rp->next) |
| print("\tQ: %s %s\n", rp->owner->name, rrname(rp->type, buf, sizeof buf)); |
| logsection("Ans: ", mp->an); |
| logsection("Auth: ", mp->ns); |
| logsection("Hint: ", mp->ar); |
| } |
| |
| void |
| logsend(int id, int subid, uchar *addr, char *sname, char *rname, int type) |
| { |
| char buf[12]; |
| |
| print("%d.%d: sending to %I/%s %s %s\n", id, subid, |
| addr, sname, rname, rrname(type, buf, sizeof buf)); |
| } |
| |
| RR* |
| getdnsservers(int class) |
| { |
| RR *rr; |
| |
| if(servername == nil) |
| return dnsservers(class); |
| |
| rr = rralloc(Tns); |
| rr->owner = dnlookup("local#dns#servers", class, 1); |
| rr->host = dnlookup(servername, class, 1); |
| |
| return rr; |
| } |
| |
| void |
| squirrelserveraddrs(void) |
| { |
| RR *rr, *rp, **l; |
| Request req; |
| |
| /* look up the resolver address first */ |
| resolver = 0; |
| debug = 0; |
| if(serveraddrs) |
| rrfreelist(serveraddrs); |
| serveraddrs = nil; |
| rr = getdnsservers(Cin); |
| l = &serveraddrs; |
| for(rp = rr; rp != nil; rp = rp->next){ |
| if(strcmp(ipattr(rp->host->name), "ip") == 0){ |
| *l = rralloc(Ta); |
| (*l)->owner = rp->host; |
| (*l)->ip = rp->host; |
| l = &(*l)->next; |
| continue; |
| } |
| req.aborttime = now + 60; /* don't spend more than 60 seconds */ |
| *l = dnresolve(rp->host->name, Cin, Ta, &req, 0, 0, Recurse, 0, 0); |
| while(*l != nil) |
| l = &(*l)->next; |
| } |
| resolver = 1; |
| debug = 1; |
| } |
| |
| void |
| preloadserveraddrs(void) |
| { |
| RR *rp, **l, *first; |
| |
| l = &first; |
| for(rp = serveraddrs; rp != nil; rp = rp->next){ |
| rrcopy(rp, l); |
| rrattach(first, 1); |
| } |
| } |
| |
| int |
| setserver(char *server) |
| { |
| if(servername != nil){ |
| free(servername); |
| servername = nil; |
| resolver = 0; |
| } |
| if(server == nil || *server == 0) |
| return 0; |
| servername = strdup(server); |
| squirrelserveraddrs(); |
| if(serveraddrs == nil){ |
| print("can't resolve %s\n", servername); |
| resolver = 0; |
| } else { |
| resolver = 1; |
| } |
| return resolver ? 0 : -1; |
| } |
| |
| void |
| doquery(char *name, char *tstr) |
| { |
| Request req; |
| RR *rr, *rp; |
| int len, type; |
| char *p, *np; |
| int rooted; |
| char buf[1024]; |
| |
| if(resolver) |
| preloadserveraddrs(); |
| |
| /* default to an "ip" request if alpha, "ptr" if numeric */ |
| if(tstr == nil || *tstr == 0) { |
| if(strcmp(ipattr(name), "ip") == 0) |
| tstr = "ptr"; |
| else |
| tstr = "ip"; |
| } |
| |
| /* if name end in '.', remove it */ |
| len = strlen(name); |
| if(len > 0 && name[len-1] == '.'){ |
| rooted = 1; |
| name[len-1] = 0; |
| } else |
| rooted = 0; |
| |
| /* inverse queries may need to be permuted */ |
| strncpy(buf, name, sizeof buf); |
| if(strcmp("ptr", tstr) == 0 |
| && strstr(name, "IN-ADDR") == 0 |
| && strstr(name, "in-addr") == 0){ |
| for(p = name; *p; p++) |
| ; |
| *p = '.'; |
| np = buf; |
| len = 0; |
| while(p >= name){ |
| len++; |
| p--; |
| if(*p == '.'){ |
| memmove(np, p+1, len); |
| np += len; |
| len = 0; |
| } |
| } |
| memmove(np, p+1, len); |
| np += len; |
| strcpy(np, "in-addr.arpa"); |
| } |
| |
| /* look it up */ |
| type = rrtype(tstr); |
| if(type < 0){ |
| print("!unknown type %s\n", tstr); |
| return; |
| } |
| |
| getactivity(&req); |
| req.aborttime = now + 60; /* don't spend more than 60 seconds */ |
| rr = dnresolve(buf, Cin, type, &req, 0, 0, Recurse, rooted, 0); |
| if(rr){ |
| print("----------------------------\n"); |
| for(rp = rr; rp; rp = rp->next) |
| print("answer %R\n", rp); |
| print("----------------------------\n"); |
| } |
| rrfreelist(rr); |
| |
| putactivity(); |
| } |
| |
| void |
| docmd(int n, char **f) |
| { |
| int tmpsrv; |
| char *name, *type; |
| |
| name = nil; |
| type = nil; |
| tmpsrv = 0; |
| |
| if(*f[0] == '@') { |
| if(setserver(f[0]+1) < 0) |
| return; |
| |
| switch(n){ |
| case 3: |
| type = f[2]; |
| /* fall through */ |
| case 2: |
| name = f[1]; |
| tmpsrv = 1; |
| break; |
| } |
| } else { |
| switch(n){ |
| case 2: |
| type = f[1]; |
| /* fall through */ |
| case 1: |
| name = f[0]; |
| break; |
| } |
| } |
| |
| if(name == nil) |
| return; |
| |
| doquery(name, type); |
| |
| if(tmpsrv) |
| setserver(""); |
| } |