1 /* 2 * $Id: info_hes.c,v 5.2.1.3 91/03/03 20:39:41 jsp Alpha $ 3 * 4 * Copyright (c) 1989 Jan-Simon Pendry 5 * Copyright (c) 1989 Imperial College of Science, Technology & Medicine 6 * Copyright (c) 1989 The Regents of the University of California. 7 * All rights reserved. 8 * 9 * This code is derived from software contributed to Berkeley by 10 * Jan-Simon Pendry at Imperial College, London. 11 * 12 * %sccs.include.redist.c% 13 * 14 * @(#)info_hes.c 5.2 (Berkeley) 03/17/91 15 */ 16 17 /* 18 * Get info from Hesiod 19 * 20 * Zone transfer code from Bruce Cole <cole@cs.wisc.edu> 21 */ 22 23 #include "am.h" 24 25 #ifdef HAS_HESIOD_MAPS 26 #include <hesiod.h> 27 28 #define HES_PREFIX "hesiod." 29 #define HES_PREFLEN 7 30 31 #ifdef HAS_HESIOD_RELOAD 32 #include <arpa/nameser.h> 33 #include <resolv.h> 34 #include <sys/uio.h> 35 #include <netdb.h> 36 37 /* 38 * Patch up broken system include files 39 */ 40 #ifndef C_HS 41 #define C_HS 4 42 #endif 43 #ifndef T_TXT 44 #define T_TXT 16 45 #endif 46 47 static int soacnt; 48 static struct timeval hs_timeout; 49 static int servernum; 50 #endif /* HAS_HESIOD_RELOAD */ 51 52 /* 53 * No easy way to probe the server - check the map name begins with "hesiod." 54 */ 55 int hesiod_init P((char *map, time_t *tp)); 56 int hesiod_init(map, tp) 57 char *map; 58 time_t *tp; 59 { 60 #ifdef DEBUG 61 dlog("hesiod_init(%s)", map); 62 #endif 63 *tp = 0; 64 return strncmp(map, HES_PREFIX, HES_PREFLEN) == 0 ? 0 : ENOENT; 65 } 66 67 68 /* 69 * Make Hesiod name. Skip past the "hesiod." 70 * at the start of the map name and append 71 * ".automount". The net effect is that a lookup 72 * of /defaults in hesiod.home will result in a 73 * call to hes_resolve("/defaults", "home.automount"); 74 */ 75 #define MAKE_HES_NAME(dest, src) sprintf(dest, "%s%s", src + HES_PREFLEN, ".automount") 76 77 /* 78 * Do a Hesiod nameserver call. 79 * Modify time is ignored by Hesiod - XXX 80 */ 81 int hesiod_search P((mnt_map *m, char *map, char **pval, time_t *tp)); 82 int hesiod_search(m, map, key, pval, tp) 83 mnt_map *m; 84 char *map; 85 char *key; 86 char **pval; 87 time_t *tp; 88 { 89 int error; 90 char hes_map[MAXPATHLEN]; 91 char **rvec; 92 #ifdef DEBUG 93 dlog("hesiod_search(m=%x, map=%s, key=%s, pval=%x tp=%x)", m, map, key, pval, tp); 94 #endif 95 MAKE_HES_NAME(hes_map, map); 96 97 /* 98 * Call the resolver 99 */ 100 #ifdef DEBUG 101 dlog("hesiod_search: hes_resolve(%s, %s)", key, hes_map); 102 #ifdef HAS_HESIOD_RELOAD 103 if (debug_flags & D_FULL) 104 _res.options |= RES_DEBUG; 105 #endif 106 #endif 107 rvec = hes_resolve(key, hes_map); 108 /* 109 * If a reply was forthcoming then return 110 * it (and free subsequent replies) 111 */ 112 if (rvec && *rvec) { 113 *pval = *rvec; 114 while (*++rvec) 115 free(*rvec); 116 return 0; 117 } 118 119 /* 120 * Otherwise reflect the hesiod error into a Un*x error 121 */ 122 #ifdef DEBUG 123 dlog("hesiod_search: Error: %d", hes_error()); 124 #endif 125 switch (hes_error()) { 126 case HES_ER_NOTFOUND: error = ENOENT; break; 127 case HES_ER_CONFIG: error = EIO; break; 128 case HES_ER_NET: error = ETIMEDOUT; break; 129 default: error = EINVAL; break; 130 } 131 #ifdef DEBUG 132 dlog("hesiod_search: Returning: %d", error); 133 #endif 134 return error; 135 } 136 137 #ifdef HAS_HESIOD_RELOAD 138 /* 139 * Zone transfer... 140 */ 141 142 #define MAXHSNS 8 143 #define MAX_NSADDR 16 144 145 static char *hs_domain; 146 static mnt_map *hs_map; 147 static int hs_nscount; 148 static char nsaddr_list[MAX_NSADDR][sizeof(struct in_addr)]; 149 150 int hesiod_reload P((mnt_map *m, char *map, void (*fn)())); 151 int hesiod_reload(m, map, fn) 152 mnt_map *m; 153 char *map; 154 void (*fn)(); 155 { 156 char hes_map[MAXPATHLEN]; 157 char *zone_name, *cp; 158 short domainlen; 159 int status; 160 161 #ifdef DEBUG 162 dlog("hesiod_reload (%x %s %x)", m, map, fn); 163 #endif DEBUG 164 if (status = res_init()) { 165 #ifdef DEBUG 166 dlog("hesiod_reload: res_init failed with %d", status); 167 #endif 168 return(status); 169 } 170 _res.retrans = 90; 171 hs_map = m; 172 domainlen = strlen(hostdomain); 173 MAKE_HES_NAME(hes_map, map); 174 zone_name = hes_to_bind("", hes_map); 175 if (*zone_name == '.') 176 zone_name++; 177 hs_domain = zone_name; 178 /* Traverse the DNS tree until we find an SOA we can transfer from. 179 (Our initial zone_name is likely to just be a subtree of a 180 real zone). */ 181 do { 182 /* If we can't find any NS records, go up a level in the 183 DNS tree */ 184 if (hs_get_ns_list(zone_name) == 0 && 185 hs_zone_transfer(zone_name) == 0) 186 return(0); 187 /* Move up DNS tree by one component */ 188 if (cp = strchr(zone_name, '.')) 189 zone_name = ++cp; 190 else 191 break; 192 } while (strlen(zone_name) >= domainlen); 193 #ifdef DEBUG 194 dlog("hesiod_reload: Giving up on %s", hs_domain); 195 #endif 196 return(-1); 197 } 198 199 hs_zone_transfer(domain) 200 char *domain; 201 { 202 int status, len; 203 char buf[PACKETSZ]; 204 /* Want to make sure ansbuf is well alligned */ 205 long ansbuf[PACKETSZ/sizeof(long)]; 206 207 #ifdef DEBUG 208 dlog("hs_zone_transfer (%s)", domain); 209 #endif 210 if ((len = res_mkquery(QUERY, domain, C_HS, T_AXFR, 211 (char *)NULL, 0, NULL, buf, PACKETSZ)) == -1) { 212 #ifdef DEBUG 213 dlog("hs_zone_transfer: res_mkquery failed"); 214 #endif 215 errno = 0; 216 return(-1); 217 } 218 if ((status = hs_res_send(buf, len, (char *)ansbuf, PACKETSZ)) == -1) { 219 #ifdef DEBUG 220 dlog("hs_zone_transfer: hs_res_send failed. status %d errno %d", 221 status, errno); 222 #endif 223 errno = 0; 224 return(-1); 225 } 226 return(0); 227 } 228 229 #define hs_server_addr(ns) ((struct in_addr *) nsaddr_list[ns]) 230 231 hs_res_send(buf, buflen, answer, anslen) 232 char *buf; 233 int buflen; 234 char *answer; 235 int anslen; 236 { 237 int retry, ns; 238 u_short id, len; 239 HEADER *hp = (HEADER *) buf; 240 struct iovec iov[2]; 241 static int s = -1; 242 int status; 243 struct sockaddr_in server; 244 245 soacnt = 0; 246 id = hp->id; 247 /* 248 * Send request, RETRY times, or until successful 249 */ 250 for (retry = _res.retry; retry > 0; retry--) { 251 for (ns = 0; ns < hs_nscount; ns++) { 252 hs_timeout.tv_sec = 253 (_res.retrans << (_res.retry - retry)) 254 / hs_nscount; 255 if (hs_timeout.tv_sec <= 0) 256 hs_timeout.tv_sec = 1; 257 hs_timeout.tv_usec = 0; 258 if (s < 0) { 259 s = socket(AF_INET, SOCK_STREAM, 0); 260 if (s < 0) { 261 continue; 262 } 263 servernum = ns; 264 bcopy(hs_server_addr(ns), &server.sin_addr, 265 sizeof(struct in_addr)); 266 server.sin_family = AF_INET; 267 server.sin_port = htons(NAMESERVER_PORT); 268 269 if (connect(s, &server, 270 sizeof(struct sockaddr)) < 0) { 271 (void) close(s); 272 s = -1; 273 continue; 274 } 275 } 276 /* 277 * Send length & message 278 */ 279 len = htons((u_short)buflen); 280 iov[0].iov_base = (caddr_t)&len; 281 iov[0].iov_len = sizeof(len); 282 iov[1].iov_base = buf; 283 iov[1].iov_len = buflen; 284 if (writev(s, iov, 2) != sizeof(len) + buflen) { 285 (void) close(s); 286 s = -1; 287 continue; 288 } 289 status = 0; 290 while (s != -1 && soacnt < 2 && status != -2) { 291 if ((status = 292 hs_readresp(s, answer, anslen)) == -1) { 293 (void) close(s); 294 s = -1; 295 continue; 296 } 297 } 298 if (status == -2) { 299 /* There was a permanent error transfering this 300 zone. Give up. */ 301 if (s != -1) { 302 (void) close(s); 303 s = -1; 304 } 305 return(-1); 306 } 307 if (s == -1) 308 continue; 309 return (0); 310 } 311 } 312 if (errno == 0) 313 errno = ETIMEDOUT; 314 return (-1); 315 } 316 317 /* Returns: 318 0: Success 319 -1: Error 320 -2: Permanent failure 321 */ 322 hs_readresp(s, answer, anslen) 323 int s; 324 char *answer; 325 int anslen; 326 { 327 register int len, n; 328 char *cp; 329 330 cp = answer; 331 len = sizeof(short); 332 while (len != 0 && 333 (n = hs_res_vcread(s, (char *)cp, (int)len, &hs_timeout)) > 0) { 334 cp += n; 335 len -= n; 336 } 337 if (n <= 0) 338 return(-1); 339 cp = answer; 340 if ((len = _getshort(cp)) > anslen) { 341 #ifdef DEBUG 342 dlog("hs_readresp: response too long: %d", len); 343 #endif 344 return(-1); 345 } 346 while (len != 0 && 347 (n = hs_res_vcread(s, (char *)cp, (int)len, &hs_timeout)) > 0) { 348 cp += n; 349 len -= n; 350 } 351 if (n <= 0) 352 return(-1); 353 return(hs_parse(answer, answer+PACKETSZ)); 354 } 355 356 hs_res_vcread(sock, buf, buflen, timeout) 357 int sock, buflen; 358 char *buf; 359 struct timeval *timeout; 360 { 361 register int n; 362 363 if ((n = hs_res_selwait(sock, timeout)) > 0) 364 return(read(sock, buf, buflen)); 365 else 366 return(n); 367 } 368 369 hs_res_selwait(sock, timeout) 370 int sock; 371 struct timeval *timeout; 372 { 373 fd_set dsmask; 374 register int n; 375 376 /* 377 * Wait for reply 378 */ 379 FD_ZERO(&dsmask); 380 FD_SET(sock, &dsmask); 381 n = select(sock+1, &dsmask, (fd_set *)NULL, 382 (fd_set *)NULL, timeout); 383 return(n); 384 } 385 386 /* Returns: 387 0: Success 388 -1: Error 389 -2: Permanent failure 390 */ 391 hs_parse(msg, eom) 392 char *msg, *eom; 393 { 394 register char *cp; 395 register HEADER *hp; 396 register int n, len; 397 int qdcount, ancount; 398 char key[PACKETSZ]; 399 char *key_cpy, *value, *hs_make_value(); 400 short type; 401 402 hp = (HEADER *)msg; 403 if (hp->rcode != NOERROR || hp->opcode != QUERY) { 404 char dq[20]; 405 #ifdef DEBUG 406 dlog("Bad response (%d) from nameserver %s", hp->rcode, inet_dquad(dq, hs_server_addr(servernum)->s_addr)); 407 #endif DEBUG 408 return(-1); 409 } 410 cp = msg + sizeof(HEADER); 411 ancount = ntohs(hp->ancount); 412 qdcount = ntohs(hp->qdcount); 413 while (qdcount-- > 0) 414 cp += dn_skipname(cp, eom) + QFIXEDSZ; 415 if (soacnt == 0 && ancount == 0) { 416 /* XXX We should look for NS records to find SOA */ 417 #ifdef DEBUG 418 dlog("No SOA found"); 419 #endif 420 return(-2); 421 } 422 while (ancount-- > 0 && cp < eom) { 423 if ((n = dn_expand(msg, eom, cp, key, PACKETSZ)) < 0) 424 break; 425 cp += n; 426 if ((type = _getshort(cp)) == T_SOA) { 427 soacnt++; 428 } 429 cp += 2*sizeof(u_short) + sizeof(u_long); 430 len = _getshort(cp); 431 cp += sizeof(u_short); 432 /* Check to see if key is in our domain */ 433 if (type == T_TXT && hs_strip_our_domain(key)) { 434 value = hs_make_value(cp, len); 435 if (value == NULL) 436 return(-1); 437 key_cpy = strdup(key); 438 #ifdef DEBUG 439 dlog("hs_parse: Parsed key: %s, value: %s", key, 440 value); 441 #endif 442 mapc_add_kv(hs_map, key_cpy, value); 443 } 444 cp += len; 445 errno = 0; 446 } 447 return(0); 448 } 449 450 /* Check to see if the domain name in the supplied argument matches 451 hs_domain. Strip hs_domain from supplied argument if so. */ 452 hs_strip_our_domain(name) 453 char *name; 454 { 455 char *end_pos; 456 short targ_len, cur_len; 457 458 targ_len = strlen(hs_domain); 459 cur_len = strlen(name); 460 if (cur_len <= targ_len) 461 return(0); 462 end_pos = &name[cur_len - targ_len]; 463 if (strcmp(end_pos, hs_domain) != 0) 464 return(0); 465 if (*--end_pos != '.') 466 return(0); 467 *end_pos = '\0'; 468 return(1); 469 } 470 471 #define MAXDATA 8*1024 472 473 char * 474 hs_make_value(cp, len) 475 char *cp; 476 int len; 477 { 478 char *value, *cpcpy, *valuep; 479 int cnt, nextcnt, totalcnt, lencpy; 480 #ifdef DEBUG 481 char *dbgname; 482 483 dbgname = &cp[1]; 484 #endif DEBUG 485 486 lencpy = len; 487 cpcpy = cp; 488 totalcnt = 0; 489 cnt = *cpcpy++; 490 while (cnt) { 491 totalcnt += cnt; 492 lencpy -= cnt+1; 493 if (lencpy == 0) 494 break; 495 nextcnt = cpcpy[cnt]; 496 cpcpy = &cpcpy[cnt+1]; 497 cnt = nextcnt; 498 } 499 if (totalcnt < 1 || totalcnt > MAXDATA || totalcnt > len) { 500 #ifdef DEBUG 501 dlog("TXT RR not of expected length (%d %d): %s", totalcnt, 502 len, dbgname); 503 #endif DEBUG 504 return(NULL); 505 } 506 /* Allocate null terminated string */ 507 value = (char *) xmalloc(totalcnt+1); 508 value[totalcnt] = '\0'; 509 cnt = *cp++; 510 valuep = value; 511 while (cnt) { 512 bcopy(cp, valuep, cnt); 513 len -= cnt+1; 514 if (len == 0) 515 break; 516 valuep = &valuep[cnt]; 517 nextcnt = cp[cnt]; 518 cp = &cp[cnt+1]; 519 cnt = nextcnt; 520 } 521 return(value); 522 } 523 524 hs_make_ns_query(domain, ansbuf) 525 char *domain; 526 char *ansbuf; 527 { 528 int status, len; 529 char buf[PACKETSZ]; 530 531 if ((len = res_mkquery(QUERY, domain, C_HS, T_NS, 532 (char *)NULL, 0, NULL, buf, PACKETSZ)) == -1) { 533 #ifdef DEBUG 534 dlog("hs_get_ns_list: res_mkquery failed"); 535 #endif 536 errno = 0; 537 return(-1); 538 } 539 if ((status = res_send(buf, len, (char *)ansbuf, PACKETSZ)) == -1) { 540 #ifdef DEBUG 541 dlog("hs_get_ns_list: res_send failed. status %d errno %d", 542 status, errno); 543 #endif 544 errno = 0; 545 return(-1); 546 } 547 return(0); 548 } 549 550 static void 551 add_address(addr) 552 struct in_addr *addr; 553 { 554 char dq[20]; 555 bcopy((char *)addr, nsaddr_list[hs_nscount++], sizeof(struct in_addr)); 556 #ifdef DEBUG 557 dlog("Adding NS address %s", inet_dquad(dq, addr->s_addr)); 558 #endif DEBUG 559 } 560 561 hs_get_ns_list(domain) 562 char *domain; 563 { 564 register HEADER *hp; 565 int qdcount, nscount; 566 register char *cp; 567 register int n, len; 568 char key[PACKETSZ], name[PACKETSZ], msg[PACKETSZ], *eom; 569 register long **hptr; 570 struct hostent *ghp; 571 int numns; 572 char nsname[MAXHSNS][MAXDATA]; 573 int nshaveaddr[MAXHSNS], i; 574 short type; 575 576 if (hs_make_ns_query(domain, msg) == -1) 577 return(-1); 578 numns = hs_nscount = 0; 579 eom = &msg[PACKETSZ]; 580 bzero(nsname, sizeof(nsname)); 581 hp = (HEADER *)msg; 582 if (hp->rcode != NOERROR || hp->opcode != QUERY) { 583 #ifdef DEBUG 584 dlog("Bad response (%d) from nameserver %#x", hp->rcode, 585 hs_server_addr(servernum)->s_addr); 586 #endif DEBUG 587 return(-1); 588 } 589 cp = msg + sizeof(HEADER); 590 qdcount = ntohs(hp->qdcount); 591 while (qdcount-- > 0) 592 cp += dn_skipname(cp, eom) + QFIXEDSZ; 593 nscount = ntohs(hp->ancount) + ntohs(hp->nscount) + ntohs(hp->arcount); 594 #ifdef DEBUG 595 dlog("hs_get_ns_list: Processing %d response records", nscount); 596 #endif 597 for (;nscount; nscount--) { 598 if ((n = dn_expand(msg, eom, cp, key, PACKETSZ)) < 0) 599 break; 600 cp += n; 601 type = _getshort(cp); 602 cp += 2*sizeof(u_short) + sizeof(u_long); 603 len = _getshort(cp); 604 cp += sizeof(u_short); 605 #ifdef DEBUG 606 dlog("hs_get_ns_list: Record type: %d", type); 607 #endif 608 switch (type) { 609 case T_NS: 610 if (numns >= MAXHSNS || strcasecmp(domain, key) != 0) 611 break; 612 if ((n = dn_expand(msg, eom, cp, name, PACKETSZ)) < 0) 613 break; 614 #ifdef DEBUG 615 dlog("hs_get_ns_list: NS name: %s", name); 616 #endif 617 for (i = 0; i < numns; i++) 618 if (strcasecmp(nsname[i], name) == 0) 619 break; 620 if (i == numns) { 621 #ifdef DEBUG 622 dlog("hs_get_ns_list: Saving name %s", name); 623 #endif 624 strncpy(nsname[numns], name, MAXDATA); 625 nshaveaddr[numns] = 0; 626 numns++; 627 } 628 break; 629 case T_A: 630 if (hs_nscount == MAX_NSADDR) 631 break; 632 for (i = 0; i < numns; i++) { 633 if (strcasecmp(nsname[i], domain) == 0) { 634 nshaveaddr[i]++; 635 add_address((struct in_addr *) cp); 636 break; 637 } 638 } 639 break; 640 default: 641 break; 642 } 643 if (hs_nscount == MAX_NSADDR) 644 break; 645 cp += len; 646 errno = 0; 647 } 648 #ifdef DEBUG 649 dlog("hs_get_ns_list: Found %d NS records", numns); 650 #endif 651 for (i = 0; i < numns; i++) { 652 if (nshaveaddr[i]) 653 continue; 654 if ((ghp = gethostbyname(nsname[i])) == 0) 655 continue; 656 for (hptr = (long **)ghp->h_addr_list; 657 *hptr && hs_nscount < MAX_NSADDR; hptr++) { 658 add_address((struct in_addr *) *hptr); 659 } 660 } 661 if (hs_nscount) 662 return(0); 663 #ifdef DEBUG 664 dlog("No NS records found for %s", domain); 665 return(-1); 666 #endif DEBUG 667 } 668 #endif /* HAS_HESIOD_RELOAD */ 669 #endif /* HAS_HESIOD_MAPS */ 670