#include <u.h>#include <libc.h>#include <bio.h>#include <ctype.h>#include <ip.h>#include <ndb.h>#include "dns.h" enum { Maxrequest= 128,}; Cfg cfg; static char *servername;static RR *serverrr;static RR *serveraddrs; char *dbfile;int useedns0 = 1;int debug;uchar ipaddr[IPaddrlen]; /* my ip address */char *logfile = "dnsdebug";int maxage = 60*60;char mntpt[Maxpath];int needrefresh;ulong now;vlong nowns;int testing;char *trace;int traceactivity;char *zonerefreshprogram; void docmd(int, char**);void doquery(char*, char*);void preloadserveraddrs(void);int prettyrrfmt(Fmt*);int setserver(char*);void squirrelserveraddrs(void); voidusage(void){ fprint(2, "%s: [-erx] [-f db-file] [[@server] domain [type]]\n", argv0); exits("usage");} voidmain(int argc, char *argv[]){ int n; Biobuf in; char *p; char *f[4]; strcpy(mntpt, "/net"); cfg.inside = 1; ARGBEGIN{ case 'e': useedns0 ^= 1; break; case 'f': dbfile = EARGF(usage()); break; case 'r': cfg.resolver = 1; break; case 'x': dbfile = "/lib/ndb/external"; strcpy(mntpt, "/net.alt"); break; default: usage(); }ARGEND now = time(nil); nowns = nsec(); dninit(); fmtinstall('R', prettyrrfmt); if(myipaddr(ipaddr, mntpt) < 0) sysfatal("can't read my ip address"); opendatabase(); if(cfg.resolver) squirrelserveraddrs(); debug = 1; if(argc > 0){ docmd(argc, argv); exits(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) { dnpurge(); /* flush the cache */ docmd(n, f); } } exits(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;} intprettyrrfmt(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", 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? rp->host->name: "")); break; case Tmg: case Tmr: seprint(p, e, "\t%s", (rp->mb? rp->mb->name: "")); break; case Tminfo: seprint(p, e, "\t%s %s", (rp->mb? rp->mb->name: ""), (rp->rmb? rp->rmb->name: "")); break; case Tmx: seprint(p, e, "\t%lud %s", rp->pref, (rp->host? rp->host->name: "")); break; case Ta: case Taaaa: seprint(p, e, "\t%s", (rp->ip? rp->ip->name: "")); break; case Tptr: seprint(p, e, "\t%s", (rp->ptr? 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 Tsrv: seprint(p, e, "\t%ud %ud %ud %s", rp->srv->pri, rp->srv->weight, rp->port, rp->host->name); break; case Tnull: seprint(p, e, "\t%.*H", rp->null->dlen, rp->null->data); break; case Ttxt: case Tspf: 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; }out: return fmtstrcpy(f, buf);} voidlogsection(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);} voidlogreply(char *net, int id, uchar *addr, DNSmsg *mp){ RR *rp; char buf[12], resp[32]; print("%d: rcvd %s from %s %I (%s%s%s%s%s) %.2ux\n", id, respname(mp->flags & Rmask, resp, sizeof resp), net, addr, mp->flags & Fauth? "authoritative": "", mp->flags & Ftrunc? " truncated": "", mp->flags & Frecurse? " recurse": "", mp->flags & Fcanrec? " can_recurse": "", (mp->flags & (Fauth|Rmask)) == (Fauth|Rname)? " nx": "", mp->flags); 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);} voidlogsend(char *proto, int id, int subid, uchar *addr, char *sname, char *rname, int type){ char buf[12]; print("%d.%d: sending to %s %I/%s %s %s\n", id, subid, proto, addr, sname, rname, rrname(type, buf, sizeof buf));} voidlogtrunc(char *oldproto, char *newproto, int id, int subid, uchar *addr, char *sname, char *rname, int type){ char buf[12]; USED(addr, sname); print("%d.%d: %s: truncated retry %s: question: %s %s\n", id, subid, oldproto, newproto, 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;} voidsquirrelserveraddrs(void){ int v4; char *attr; RR *rr, *rp, **l; Request req; /* look up the resolver address first */ cfg.resolver = 0; debug = 0; if(serveraddrs) rrfreelist(serveraddrs); serveraddrs = nil; rr = getdnsservers(Cin); l = &serveraddrs; for(rp = rr; rp != nil; rp = rp->next){ attr = ipattr(rp->host->name); v4 = strcmp(attr, "ip") == 0; if(v4 || strcmp(attr, "ipv6") == 0){ *l = rralloc(v4? Ta: Taaaa); (*l)->owner = rp->host; (*l)->ip = rp->host; l = &(*l)->next; continue; } memset(&req, 0, sizeof req); req.isslave = 1; req.aborttime = NS2MS(nowns) + Maxreqtm; *l = dnresolve(rp->host->name, Cin, Ta, &req, 0, 0, Recurse, 0, 0); if(*l == nil) *l = dnresolve(rp->host->name, Cin, Taaaa, &req, 0, 0, Recurse, 0, 0); while(*l != nil) l = &(*l)->next; } cfg.resolver = 1; debug = 1;} voidpreloadserveraddrs(void){ RR *rp, **l, *first; l = &first; for(rp = serveraddrs; rp != nil; rp = rp->next){ lock(&dnlock); rrcopy(rp, l); unlock(&dnlock); rrattach(first, Authoritative); }} intsetserver(char *server){ if(servername != nil){ free(servername); servername = nil; cfg.resolver = 0; } if(server == nil || *server == 0) return 0; servername = strdup(server); squirrelserveraddrs(); if(serveraddrs == nil){ print("can't resolve %s\n", servername); cfg.resolver = 0; } else cfg.resolver = 1; return cfg.resolver? 0: -1;} voiddoquery(char *name, char *tstr){ int len, type, rooted; char *p, *np; char buf[1024]; RR *rr, *rp; Request req; if(cfg.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 && cistrstr(name, ".arpa") == nil){ /* TODO: reversing v6 addrs is harder */ 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"); /* TODO: ip6.arpa for v6 */ } /* look it up */ type = rrtype(tstr); if(type < 0){ print("!unknown type %s\n", tstr); return; } memset(&req, 0, sizeof req); getactivity(&req, 0); req.isslave = 1; req.aborttime = NS2MS(nowns) + Maxreqtm; 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(0);} voiddocmd(int n, char **f){ int tmpsrv; char *name, *type; name = 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("");}
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <ctype.h>
#include <ip.h>
#include <ndb.h>
#include "dns.h"
enum {
Maxrequest= 128,
};
Cfg cfg;
static char *servername;
static RR *serverrr;
static RR *serveraddrs;
char *dbfile;
int useedns0 = 1;
int debug;
uchar ipaddr[IPaddrlen]; /* my ip address */
char *logfile = "dnsdebug";
int maxage = 60*60;
char mntpt[Maxpath];
int needrefresh;
ulong now;
vlong nowns;
int testing;
char *trace;
int traceactivity;
char *zonerefreshprogram;
void docmd(int, char**);
void doquery(char*, char*);
void preloadserveraddrs(void);
int prettyrrfmt(Fmt*);
int setserver(char*);
void squirrelserveraddrs(void);
void
usage(void)
{
fprint(2, "%s: [-erx] [-f db-file] [[@server] domain [type]]\n", argv0);
exits("usage");
}
void
main(int argc, char *argv[])
{
int n;
Biobuf in;
char *p;
char *f[4];
strcpy(mntpt, "/net");
cfg.inside = 1;
ARGBEGIN{
case 'e':
useedns0 ^= 1;
break;
case 'f':
dbfile = EARGF(usage());
break;
case 'r':
cfg.resolver = 1;
break;
case 'x':
dbfile = "/lib/ndb/external";
strcpy(mntpt, "/net.alt");
break;
default:
usage();
}ARGEND
now = time(nil);
nowns = nsec();
dninit();
fmtinstall('R', prettyrrfmt);
if(myipaddr(ipaddr, mntpt) < 0)
sysfatal("can't read my ip address");
opendatabase();
if(cfg.resolver)
squirrelserveraddrs();
debug = 1;
if(argc > 0){
docmd(argc, argv);
exits(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) {
dnpurge(); /* flush the cache */
docmd(n, f);
}
}
exits(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", 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? rp->host->name: ""));
break;
case Tmg:
case Tmr:
seprint(p, e, "\t%s", (rp->mb? rp->mb->name: ""));
break;
case Tminfo:
seprint(p, e, "\t%s %s", (rp->mb? rp->mb->name: ""),
(rp->rmb? rp->rmb->name: ""));
break;
case Tmx:
seprint(p, e, "\t%lud %s", rp->pref,
(rp->host? rp->host->name: ""));
break;
case Ta:
case Taaaa:
seprint(p, e, "\t%s", (rp->ip? rp->ip->name: ""));
break;
case Tptr:
seprint(p, e, "\t%s", (rp->ptr? 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 Tsrv:
seprint(p, e, "\t%ud %ud %ud %s",
rp->srv->pri, rp->srv->weight, rp->port, rp->host->name);
break;
case Tnull:
seprint(p, e, "\t%.*H", rp->null->dlen, rp->null->data);
break;
case Ttxt:
case Tspf:
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;
}
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(char *net, int id, uchar *addr, DNSmsg *mp)
{
RR *rp;
char buf[12], resp[32];
print("%d: rcvd %s from %s %I (%s%s%s%s%s) %.2ux\n",
id, respname(mp->flags & Rmask, resp, sizeof resp), net, addr,
mp->flags & Fauth? "authoritative": "",
mp->flags & Ftrunc? " truncated": "",
mp->flags & Frecurse? " recurse": "",
mp->flags & Fcanrec? " can_recurse": "",
(mp->flags & (Fauth|Rmask)) == (Fauth|Rname)? " nx": "", mp->flags);
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(char *proto, int id, int subid, uchar *addr, char *sname, char *rname, int type)
{
char buf[12];
print("%d.%d: sending to %s %I/%s %s %s\n", id, subid,
proto, addr, sname, rname, rrname(type, buf, sizeof buf));
}
void
logtrunc(char *oldproto, char *newproto, int id, int subid, uchar *addr, char *sname, char *rname, int type)
{
char buf[12];
USED(addr, sname);
print("%d.%d: %s: truncated retry %s: question: %s %s\n", id, subid,
oldproto, newproto, 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)
{
int v4;
char *attr;
RR *rr, *rp, **l;
Request req;
/* look up the resolver address first */
cfg.resolver = 0;
debug = 0;
if(serveraddrs)
rrfreelist(serveraddrs);
serveraddrs = nil;
rr = getdnsservers(Cin);
l = &serveraddrs;
for(rp = rr; rp != nil; rp = rp->next){
attr = ipattr(rp->host->name);
v4 = strcmp(attr, "ip") == 0;
if(v4 || strcmp(attr, "ipv6") == 0){
*l = rralloc(v4? Ta: Taaaa);
(*l)->owner = rp->host;
(*l)->ip = rp->host;
l = &(*l)->next;
continue;
}
memset(&req, 0, sizeof req);
req.isslave = 1;
req.aborttime = NS2MS(nowns) + Maxreqtm;
*l = dnresolve(rp->host->name, Cin, Ta, &req, 0, 0, Recurse, 0, 0);
if(*l == nil)
*l = dnresolve(rp->host->name, Cin, Taaaa, &req,
0, 0, Recurse, 0, 0);
while(*l != nil)
l = &(*l)->next;
}
cfg.resolver = 1;
debug = 1;
}
void
preloadserveraddrs(void)
{
RR *rp, **l, *first;
l = &first;
for(rp = serveraddrs; rp != nil; rp = rp->next){
lock(&dnlock);
rrcopy(rp, l);
unlock(&dnlock);
rrattach(first, Authoritative);
}
}
int
setserver(char *server)
{
if(servername != nil){
free(servername);
servername = nil;
cfg.resolver = 0;
}
if(server == nil || *server == 0)
return 0;
servername = strdup(server);
squirrelserveraddrs();
if(serveraddrs == nil){
print("can't resolve %s\n", servername);
cfg.resolver = 0;
} else
cfg.resolver = 1;
return cfg.resolver? 0: -1;
}
void
doquery(char *name, char *tstr)
{
int len, type, rooted;
char *p, *np;
char buf[1024];
RR *rr, *rp;
Request req;
if(cfg.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 && cistrstr(name, ".arpa") == nil){
/* TODO: reversing v6 addrs is harder */
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"); /* TODO: ip6.arpa for v6 */
}
/* look it up */
type = rrtype(tstr);
if(type < 0){
print("!unknown type %s\n", tstr);
return;
}
memset(&req, 0, sizeof req);
getactivity(&req, 0);
req.isslave = 1;
req.aborttime = NS2MS(nowns) + Maxreqtm;
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(0);
}
void
docmd(int n, char **f)
{
int tmpsrv;
char *name, *type;
name = 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("");
}