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 { 11 Maxrequest= 128, 12 Ncache= 8, 13 Maxpath= 128, 14 Maxreply= 512, 15 Maxrrr= 16, 16 }; 17 18 static char *servername; 19 static RR *serverrr; 20 static RR *serveraddrs; 21 22 int debug; 23 int cachedb; 24 ulong now; 25 int testing; 26 int traceactivity; 27 char *trace; 28 int needrefresh; 29 int resolver; 30 uchar ipaddr[IPaddrlen]; /* my ip address */ 31 int maxage; 32 char *logfile = "dns"; 33 char *dbfile; 34 char mntpt[Maxpath]; 35 char *zonerefreshprogram; 36 37 int prettyrrfmt(Fmt*); 38 void preloadserveraddrs(void); 39 void squirrelserveraddrs(void); 40 int setserver(char*); 41 void doquery(char*, char*); 42 void docmd(int, char**); 43 44 void 45 main(int argc, char *argv[]) 46 { 47 int n; 48 Biobuf in; 49 char *p; 50 char *f[4]; 51 52 strcpy(mntpt, "/net"); 53 54 ARGBEGIN{ 55 case 'r': 56 resolver = 1; 57 break; 58 case 'x': 59 dbfile = "/lib/ndb/external"; 60 strcpy(mntpt, "/net.alt"); 61 break; 62 case 'f': 63 dbfile = ARGF(); 64 break; 65 }ARGEND 66 67 now = time(0); 68 dninit(); 69 fmtinstall('R', prettyrrfmt); 70 if(myipaddr(ipaddr, mntpt) < 0) 71 sysfatal("can't read my ip address"); 72 opendatabase(); 73 74 if(resolver) 75 squirrelserveraddrs(); 76 77 debug = 1; 78 79 if(argc > 0){ 80 docmd(argc, argv); 81 exits(0); 82 } 83 84 Binit(&in, 0, OREAD); 85 for(print("> "); p = Brdline(&in, '\n'); print("> ")){ 86 p[Blinelen(&in)-1] = 0; 87 n = tokenize(p, f, 3); 88 if(n<1) 89 continue; 90 91 /* flush the cache */ 92 dnpurge(); 93 94 docmd(n, f); 95 96 } 97 exits(0); 98 } 99 100 static char* 101 longtime(long t) 102 { 103 int d, h, m, n; 104 static char x[128]; 105 106 for(d = 0; t >= 24*60*60; t -= 24*60*60) 107 d++; 108 for(h = 0; t >= 60*60; t -= 60*60) 109 h++; 110 for(m = 0; t >= 60; t -= 60) 111 m++; 112 n = 0; 113 if(d) 114 n += sprint(x, "%d day ", d); 115 if(h) 116 n += sprint(x+n, "%d hr ", h); 117 if(m) 118 n += sprint(x+n, "%d min ", m); 119 if(t || n == 0) 120 sprint(x+n, "%ld sec", t); 121 return x; 122 } 123 124 int 125 prettyrrfmt(Fmt *f) 126 { 127 RR *rp; 128 char buf[3*Domlen]; 129 char *p, *e; 130 Txt *t; 131 132 rp = va_arg(f->args, RR*); 133 if(rp == 0){ 134 strcpy(buf, "<null>"); 135 goto out; 136 } 137 138 p = buf; 139 e = buf + sizeof(buf); 140 p = seprint(p, e, "%-32.32s %-15.15s %-5.5s", rp->owner->name, 141 longtime(rp->db ? rp->ttl : (rp->ttl-now)), 142 rrname(rp->type, buf, sizeof buf)); 143 144 if(rp->negative){ 145 seprint(p, e, "negative rcode %d\n", rp->negrcode); 146 goto out; 147 } 148 149 switch(rp->type){ 150 case Thinfo: 151 seprint(p, e, "\t%s %s", rp->cpu->name, rp->os->name); 152 break; 153 case Tcname: 154 case Tmb: 155 case Tmd: 156 case Tmf: 157 case Tns: 158 seprint(p, e, "\t%s", rp->host->name); 159 break; 160 case Tmg: 161 case Tmr: 162 seprint(p, e, "\t%s", rp->mb->name); 163 break; 164 case Tminfo: 165 seprint(p, e, "\t%s %s", rp->mb->name, rp->rmb->name); 166 break; 167 case Tmx: 168 seprint(p, e, "\t%lud %s", rp->pref, rp->host->name); 169 break; 170 case Ta: 171 case Taaaa: 172 seprint(p, e, "\t%s", rp->ip->name); 173 break; 174 case Tptr: 175 seprint(p, e, "\t%s", rp->ptr->name); 176 break; 177 case Tsoa: 178 seprint(p, e, "\t%s %s %lud %lud %lud %lud %lud", rp->host->name, 179 rp->rmb->name, rp->soa->serial, rp->soa->refresh, rp->soa->retry, 180 rp->soa->expire, rp->soa->minttl); 181 break; 182 case Tnull: 183 seprint(p, e, "\t%.*H", rp->null->dlen, rp->null->data); 184 break; 185 case Ttxt: 186 p = seprint(p, e, "\t"); 187 for(t = rp->txt; t != nil; t = t->next) 188 p = seprint(p, e, "%s", t->p); 189 break; 190 case Trp: 191 seprint(p, e, "\t%s %s", rp->rmb->name, rp->rp->name); 192 break; 193 case Tkey: 194 seprint(p, e, "\t%d %d %d", rp->key->flags, rp->key->proto, 195 rp->key->alg); 196 break; 197 case Tsig: 198 seprint(p, e, "\t%d %d %d %lud %lud %lud %d %s", 199 rp->sig->type, rp->sig->alg, rp->sig->labels, rp->sig->ttl, 200 rp->sig->exp, rp->sig->incep, rp->sig->tag, rp->sig->signer->name); 201 break; 202 case Tcert: 203 seprint(p, e, "\t%d %d %d", 204 rp->sig->type, rp->sig->tag, rp->sig->alg); 205 break; 206 default: 207 break; 208 } 209 out: 210 return fmtstrcpy(f, buf); 211 } 212 213 void 214 logsection(char *flag, RR *rp) 215 { 216 if(rp == nil) 217 return; 218 print("\t%s%R\n", flag, rp); 219 for(rp = rp->next; rp != nil; rp = rp->next) 220 print("\t %R\n", rp); 221 } 222 223 void 224 logreply(int id, uchar *addr, DNSmsg *mp) 225 { 226 RR *rp; 227 char buf[12]; 228 char resp[32]; 229 230 switch(mp->flags & Rmask){ 231 case Rok: 232 strcpy(resp, "OK"); 233 break; 234 case Rformat: 235 strcpy(resp, "Format error"); 236 break; 237 case Rserver: 238 strcpy(resp, "Server failed"); 239 break; 240 case Rname: 241 strcpy(resp, "Nonexistent"); 242 break; 243 case Runimplimented: 244 strcpy(resp, "Unimplemented"); 245 break; 246 case Rrefused: 247 strcpy(resp, "Refused"); 248 break; 249 default: 250 sprint(resp, "%d", mp->flags & Rmask); 251 break; 252 } 253 254 print("%d: rcvd %s from %I (%s%s%s%s%s)\n", id, resp, addr, 255 mp->flags & Fauth ? "authoritative" : "", 256 mp->flags & Ftrunc ? " truncated" : "", 257 mp->flags & Frecurse ? " recurse" : "", 258 mp->flags & Fcanrec ? " can_recurse" : "", 259 mp->flags & (Fauth|Rname) == (Fauth|Rname) ? 260 " nx" : ""); 261 for(rp = mp->qd; rp != nil; rp = rp->next) 262 print("\tQ: %s %s\n", rp->owner->name, rrname(rp->type, buf, sizeof buf)); 263 logsection("Ans: ", mp->an); 264 logsection("Auth: ", mp->ns); 265 logsection("Hint: ", mp->ar); 266 } 267 268 void 269 logsend(int id, int subid, uchar *addr, char *sname, char *rname, int type) 270 { 271 char buf[12]; 272 273 print("%d.%d: sending to %I/%s %s %s\n", id, subid, 274 addr, sname, rname, rrname(type, buf, sizeof buf)); 275 } 276 277 RR* 278 getdnsservers(int class) 279 { 280 RR *rr; 281 282 if(servername == nil) 283 return dnsservers(class); 284 285 rr = rralloc(Tns); 286 rr->owner = dnlookup("local#dns#servers", class, 1); 287 rr->host = dnlookup(servername, class, 1); 288 289 return rr; 290 } 291 292 void 293 squirrelserveraddrs(void) 294 { 295 RR *rr, *rp, **l; 296 Request req; 297 298 /* look up the resolver address first */ 299 resolver = 0; 300 debug = 0; 301 if(serveraddrs) 302 rrfreelist(serveraddrs); 303 serveraddrs = nil; 304 rr = getdnsservers(Cin); 305 l = &serveraddrs; 306 for(rp = rr; rp != nil; rp = rp->next){ 307 if(strcmp(ipattr(rp->host->name), "ip") == 0){ 308 *l = rralloc(Ta); 309 (*l)->owner = rp->host; 310 (*l)->ip = rp->host; 311 l = &(*l)->next; 312 continue; 313 } 314 req.isslave = 1; 315 req.aborttime = now + 60; /* don't spend more than 60 seconds */ 316 *l = dnresolve(rp->host->name, Cin, Ta, &req, 0, 0, Recurse, 0, 0); 317 while(*l != nil) 318 l = &(*l)->next; 319 } 320 resolver = 1; 321 debug = 1; 322 } 323 324 void 325 preloadserveraddrs(void) 326 { 327 RR *rp, **l, *first; 328 329 l = &first; 330 for(rp = serveraddrs; rp != nil; rp = rp->next){ 331 rrcopy(rp, l); 332 rrattach(first, 1); 333 } 334 } 335 336 int 337 setserver(char *server) 338 { 339 if(servername != nil){ 340 free(servername); 341 servername = nil; 342 resolver = 0; 343 } 344 if(server == nil || *server == 0) 345 return 0; 346 servername = strdup(server); 347 squirrelserveraddrs(); 348 if(serveraddrs == nil){ 349 print("can't resolve %s\n", servername); 350 resolver = 0; 351 } else { 352 resolver = 1; 353 } 354 return resolver ? 0 : -1; 355 } 356 357 void 358 doquery(char *name, char *tstr) 359 { 360 Request req; 361 RR *rr, *rp; 362 int len, type; 363 char *p, *np; 364 int rooted; 365 char buf[1024]; 366 367 if(resolver) 368 preloadserveraddrs(); 369 370 /* default to an "ip" request if alpha, "ptr" if numeric */ 371 if(tstr == nil || *tstr == 0) { 372 if(strcmp(ipattr(name), "ip") == 0) 373 tstr = "ptr"; 374 else 375 tstr = "ip"; 376 } 377 378 /* if name end in '.', remove it */ 379 len = strlen(name); 380 if(len > 0 && name[len-1] == '.'){ 381 rooted = 1; 382 name[len-1] = 0; 383 } else 384 rooted = 0; 385 386 /* inverse queries may need to be permuted */ 387 strncpy(buf, name, sizeof buf); 388 if(strcmp("ptr", tstr) == 0 389 && strstr(name, "IN-ADDR") == 0 390 && strstr(name, "in-addr") == 0){ 391 for(p = name; *p; p++) 392 ; 393 *p = '.'; 394 np = buf; 395 len = 0; 396 while(p >= name){ 397 len++; 398 p--; 399 if(*p == '.'){ 400 memmove(np, p+1, len); 401 np += len; 402 len = 0; 403 } 404 } 405 memmove(np, p+1, len); 406 np += len; 407 strcpy(np, "in-addr.arpa"); 408 } 409 410 /* look it up */ 411 type = rrtype(tstr); 412 if(type < 0){ 413 print("!unknown type %s\n", tstr); 414 return; 415 } 416 417 getactivity(&req); 418 req.isslave = 1; 419 req.aborttime = now + 60; /* don't spend more than 60 seconds */ 420 rr = dnresolve(buf, Cin, type, &req, 0, 0, Recurse, rooted, 0); 421 if(rr){ 422 print("----------------------------\n"); 423 for(rp = rr; rp; rp = rp->next) 424 print("answer %R\n", rp); 425 print("----------------------------\n"); 426 } 427 rrfreelist(rr); 428 429 putactivity(); 430 } 431 432 void 433 docmd(int n, char **f) 434 { 435 int tmpsrv; 436 char *name, *type; 437 438 name = nil; 439 type = nil; 440 tmpsrv = 0; 441 442 if(*f[0] == '@') { 443 if(setserver(f[0]+1) < 0) 444 return; 445 446 switch(n){ 447 case 3: 448 type = f[2]; 449 /* fall through */ 450 case 2: 451 name = f[1]; 452 tmpsrv = 1; 453 break; 454 } 455 } else { 456 switch(n){ 457 case 2: 458 type = f[1]; 459 /* fall through */ 460 case 1: 461 name = f[0]; 462 break; 463 } 464 } 465 466 if(name == nil) 467 return; 468 469 doquery(name, type); 470 471 if(tmpsrv) 472 setserver(""); 473 } 474