1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <thread.h> 29 #include <stdlib.h> 30 #include <netdb.h> 31 #include <strings.h> 32 #include <alloca.h> 33 #include <sys/socket.h> 34 #include <netinet/in.h> 35 #include <arpa/inet.h> 36 #include <arpa/nameser.h> 37 #include "res_update.h" 38 #include <stdio.h> 39 #include <errno.h> 40 #include <resolv.h> 41 #include <assert.h> 42 #include <stdarg.h> 43 #include <libnvpair.h> 44 45 #define MAX_RETRIES 5 /* times to loop on TRY_AGAIN errors */ 46 #define LEASEMIN 3600 /* minimum lease time allowed by RFC 1531 */ 47 48 static boolean_t getNS(char *, struct in_addr *); 49 static void cacheNS(char *, struct in_addr *, int); 50 static boolean_t lookupNS(char *, struct in_addr *); 51 static boolean_t send_update(struct hostent *, struct in_addr *); 52 static unsigned short parse_ushort(const char **); 53 static unsigned int parse_uint(const char **); 54 static void freeupdrecs(ns_updque); 55 static void freehost(struct hostent *); 56 static boolean_t delA(struct __res_state *, char *); 57 static boolean_t delPTR(struct __res_state *, char *, char *); 58 static boolean_t addA(struct __res_state *, char *, struct in_addr); 59 static boolean_t addPTR(struct __res_state *, char *, char *); 60 static boolean_t retry_update(struct __res_state *, ns_updrec *); 61 62 extern char *inet_ntoa_r(struct in_addr, char *); 63 64 /* 65 * The parent (calling) thread and the child thread it spawns to do an 66 * update use this structure to rendezvous. The child thread sets the 67 * ``done'' variable to B_TRUE when it's completed its work. The nusers 68 * variable lets us arbitrate to see who has to clean up (via the 69 * provided childstat_cleanup() function) the dynamically-allocated 70 * structure - last one to wake up loses, and has to do the work. 71 */ 72 struct childstat { 73 mutex_t m; 74 cond_t cv; 75 struct hostent *hp; 76 boolean_t synchflag; 77 boolean_t done; 78 int ret; 79 int nusers; 80 }; 81 static void childstat_cleanup(struct childstat *); 82 83 static void update_thread(void *); 84 85 /* 86 * The given environment variable, if present, will contain the name 87 * of a file (or the distinguished values "stdout" and "stderr") into 88 * which we should place the debugging output from this shared object. 89 * 90 * The debugging output is basically free-form but uses the dprint() 91 * function to ensure that each message is tagged with its thread ID, 92 * so we have some hope of sorting out later what actually happened. 93 */ 94 static char env_filetoken[] = "DHCP_DNS_OUTPUT"; 95 static void dprint(char *, ...); 96 static FILE *debug_fp; 97 98 static boolean_t dns_config_ok; /* did res_ninit() work? */ 99 100 static nvlist_t *nvl; 101 102 /* CSTYLED */ 103 #pragma init (init) 104 105 /* 106 * This is the shared object startup function, called once when we 107 * are dlopen()ed. 108 */ 109 static void 110 init(void) 111 { 112 char *cp; 113 struct __res_state res; 114 115 if (cp = getenv(env_filetoken)) { 116 if (strcmp(cp, "stdout") == 0) 117 debug_fp = stdout; 118 else if (strcmp(cp, "stderr") == 0) 119 debug_fp = stderr; 120 else { 121 debug_fp = fopen(cp, "a"); 122 } 123 if (debug_fp) 124 (void) setvbuf(debug_fp, NULL, _IOLBF, BUFSIZ); 125 } 126 127 /* 128 * Use res_ninit(3RESOLV) to see whether DNS has been configured 129 * on the host running this code. In practice, life must be very 130 * bad for res_ninit() to fail. 131 */ 132 (void) memset(&res, 0, sizeof (res)); 133 if (res_ninit(&res) == -1) { 134 dprint("res_ninit() failed - dns_config_ok FALSE\n"); 135 dns_config_ok = B_FALSE; 136 } else { 137 dprint("res_ninit() succeeded\n"); 138 dns_config_ok = B_TRUE; 139 } 140 res_ndestroy(&res); 141 } 142 143 /* 144 * This is the interface exported to the outside world. Control over 145 * the hostent structure is assumed to pass to dns_puthostent(); it will 146 * free the associated space when done. 147 */ 148 int 149 dns_puthostent(struct hostent *hp, time_t timeout) 150 { 151 struct childstat *sp; 152 timestruc_t t; 153 int ret; 154 thread_t tid; 155 156 157 /* 158 * Check the consistency of the hostent structure: 159 * both the name and address fields should be valid, 160 * h_addrtype must be AF_INET, and h_length must be 161 * sizeof (struct in_addr); 162 */ 163 if (hp == NULL) { 164 dprint("hp is NULL - return -1\n"); 165 return (-1); 166 } 167 if (hp->h_addr_list == NULL) { 168 dprint("h_addr_list is NULL - return -1\n"); 169 freehost(hp); 170 return (-1); 171 } 172 if (hp->h_addr_list[0] == NULL) { 173 dprint("h_addr_list is zero-length - return -1\n"); 174 freehost(hp); 175 return (-1); 176 } 177 if (hp->h_name == NULL) { 178 dprint("h_name is NULL - return -1\n"); 179 freehost(hp); 180 return (-1); 181 } 182 if (hp->h_name[0] == '\0') { 183 dprint("h_name[0] is NUL - return -1\n"); 184 freehost(hp); 185 return (-1); 186 } 187 if (hp->h_addrtype != AF_INET) { 188 dprint("h_addrtype (%d) != AF_INET - return -1\n", 189 hp->h_addrtype); 190 freehost(hp); 191 return (-1); 192 } 193 if (hp->h_length != sizeof (struct in_addr)) { 194 dprint("h_length (%d) != sizeof (struct in_addr) - return -1\n", 195 hp->h_length); 196 freehost(hp); 197 return (-1); 198 } 199 200 dprint("dns_puthostent(%s, %d)\n", hp->h_name, (int)timeout); 201 202 if (dns_config_ok == B_FALSE) { 203 dprint("dns_config_ok FALSE - return -1\n"); 204 freehost(hp); 205 return (-1); 206 } 207 208 if ((sp = malloc(sizeof (struct childstat))) == NULL) { 209 dprint("malloc (sizeof struct childstat) failed\n"); 210 freehost(hp); 211 return (-1); 212 } 213 214 /* 215 * From this point on, both hp and sp are cleaned up and freed via 216 * childstat_cleanup(), with bookkeeping done to see whether the 217 * parent thread or the child one should be the one in charge of 218 * cleaning up. 219 */ 220 sp->hp = hp; 221 222 if (timeout > 0) 223 sp->synchflag = B_TRUE; 224 else 225 sp->synchflag = B_FALSE; 226 sp->done = B_FALSE; 227 sp->ret = 0; 228 sp->nusers = 1; 229 (void) mutex_init(&sp->m, USYNC_THREAD, 0); 230 (void) cond_init(&sp->cv, USYNC_THREAD, 0); 231 (void) time(&t.tv_sec); 232 t.tv_sec += timeout; 233 t.tv_nsec = 0; 234 235 if (thr_create(NULL, NULL, (void *(*)(void *))update_thread, 236 (void *) sp, THR_DAEMON|THR_DETACHED, &tid)) { 237 dprint("thr_create failed (errno %d) - return -1\n", errno); 238 childstat_cleanup(sp); 239 return (-1); 240 } 241 else 242 dprint("thread %u created\n", tid); 243 244 if (!sp->done) { /* we might already have finished */ 245 (void) mutex_lock(&sp->m); 246 247 /* if asynchronous, and child still working, just return; */ 248 if ((!sp->done) && (timeout == 0)) { 249 sp->nusers--; 250 (void) mutex_unlock(&sp->m); 251 dprint("done 0, timeout 0\n"); 252 return (0); 253 } 254 255 /* otherwise, wait for child to finish or time to expire */ 256 while (!sp->done) 257 if (cond_timedwait(&sp->cv, &sp->m, &t) == ETIME) { 258 /* 259 * Child thread did not return before the 260 * timeout. One might think we could 261 * assert(sp->nusers > 1); 262 * here, but we can't: we must protect 263 * against this sequence of events: 264 * cond_timedwait() times out 265 * 266 * child finishes, grabs mutex, 267 * decrements nusers, sets done, 268 * and exits. 269 * 270 * cond_timedwait() reacquires the 271 * mutex and returns ETIME 272 * 273 * If this happens, nusers will now be 1, 274 * even though cond_timedwait() returned 275 * ETIME. 276 */ 277 if (sp->nusers == 1) 278 /* child must have also set done */ 279 break; 280 else 281 /* child thread has not returned */ 282 sp->nusers--; 283 (void) mutex_unlock(&sp->m); 284 dprint("update for %s timed out\n", hp->h_name); 285 return (0); 286 } 287 assert(sp->done); 288 ret = sp->ret; 289 } 290 291 childstat_cleanup(sp); 292 return (ret); 293 } 294 295 /* 296 * This worker thread, spawned by dns_puthostent(), is responsible for 297 * seeing that the update work gets done and cleaning up afterward 298 * if necessary. 299 */ 300 static void 301 update_thread(void *arg) 302 { 303 char *p; 304 int num_updated = 0; 305 struct in_addr ia; 306 struct hostent *hp; 307 struct childstat *sp; 308 309 dprint("update_thread running\n"); 310 311 sp = (struct childstat *)arg; 312 313 (void) mutex_lock(&sp->m); 314 /* 315 * Paranoia: if nusers was 0 and we were asked to do a 316 * synchronous update, our parent must have incremented 317 * it, called cond_timedwait(), timed out, and decremented it, 318 * all before we got this far. In this case, we do nothing 319 * except clean up and exit. 320 */ 321 if ((++sp->nusers == 1) && sp->synchflag) { 322 childstat_cleanup(sp); 323 thr_exit(0); 324 } 325 326 (void) mutex_unlock(&sp->m); 327 328 hp = sp->hp; 329 330 /* 331 * h_name should be full-qualified; find the name servers for 332 * its domain ... 333 */ 334 for (p = hp->h_name; *p != NULL; p++) 335 if (*p == '.') { 336 if (getNS(++p, &ia)) { 337 char ntoab[INET_ADDRSTRLEN]; 338 339 (void) inet_ntoa_r(ia, ntoab); 340 dprint("update for %s goes to %s\n", 341 hp->h_name, ntoab); 342 /* ... and send the update to one of them. */ 343 if (send_update(hp, &ia)) { 344 dprint("send_update succeeded\n"); 345 num_updated = 1; 346 } else { 347 dprint("send_update failed\n"); 348 num_updated = 0; 349 } 350 } else { 351 dprint("getNS failed\n"); 352 num_updated = -1; 353 } 354 break; 355 } 356 dprint("update for %s returning %d\n", hp->h_name, num_updated); 357 358 (void) mutex_lock(&sp->m); 359 if (--sp->nusers == 0) { 360 /* parent timed out and abandoned us - our turn to clean up */ 361 childstat_cleanup(sp); 362 } else { 363 sp->done = B_TRUE; 364 sp->ret = num_updated; 365 (void) cond_signal(&sp->cv); 366 (void) mutex_unlock(&sp->m); 367 } 368 369 thr_exit(0); 370 } 371 372 /* 373 * Find a name server for the supplied domain and return its IP address. 374 * Sadly, in order to do this we have to parse the actual DNS reply 375 * packet - no functions are provided for doing this work for us. 376 */ 377 static boolean_t 378 getNS(char *domain, struct in_addr *iap) 379 { 380 HEADER *hp; 381 union { 382 HEADER h; 383 char buf[NS_PACKETSZ]; 384 } abuf; 385 int alen; 386 int count; 387 int retries; 388 unsigned char name[MAXDNAME]; 389 int qdcount, ancount, nscount, arcount; 390 unsigned char *data; 391 unsigned char *m_bound; 392 int type, class, ttl, dlen; 393 struct hostent *ep; 394 unsigned char *NS_data; 395 boolean_t found_NS = B_FALSE; 396 struct __res_state res; 397 extern struct hostent *res_gethostbyname(const char *); 398 399 if (lookupNS(domain, iap)) { 400 dprint("getNS: found cached IP address for domain %s\n", 401 domain); 402 return (B_TRUE); 403 } 404 (void) memset(&res, 0, sizeof (res)); 405 if (res_ninit(&res) == -1) { 406 dprint("getNS(\"%s\"): res_ninit failed\n", domain); 407 return (B_FALSE); 408 } 409 for (retries = 0; retries < MAX_RETRIES; retries++) { 410 alen = res_nquery(&res, domain, C_IN, T_NS, (uchar_t *)&abuf, 411 sizeof (abuf)); 412 413 if (alen <= 0) { 414 /* 415 * Look for indicators from libresolv:res_nsend() 416 * that we should retry a request. 417 */ 418 if ((errno == ECONNREFUSED) || 419 ((h_errno == TRY_AGAIN) && (errno == ETIMEDOUT))) { 420 dprint("getNS retry: errno %d, h_errno %d\n", 421 errno, h_errno); 422 continue; 423 } else { 424 dprint("getNS(\"%s\"): res_nquery failed " 425 "(h_errno %d)\n", domain, h_errno); 426 res_ndestroy(&res); 427 return (B_FALSE); 428 } 429 } 430 } 431 if (alen <= 0) { 432 dprint("getNS(\"%s\"): res_nquery failed " "(h_errno %d)\n", 433 domain, h_errno); 434 res_ndestroy(&res); 435 return (B_FALSE); 436 } 437 438 m_bound = ((unsigned char *)&abuf) + alen; 439 440 hp = (HEADER *)&abuf; 441 data = (unsigned char *)&hp[1]; /* a DNS paradigm - actually abuf.buf */ 442 443 qdcount = ntohs(hp->qdcount); 444 ancount = ntohs(hp->ancount); 445 nscount = ntohs(hp->nscount); 446 arcount = ntohs(hp->arcount); 447 448 dprint("getNS(\"%s\"):\n", domain); 449 dprint("\tqdcount %d\n", qdcount); 450 dprint("\tancount %d\n", ancount); 451 dprint("\tnscount %d\n", nscount); 452 dprint("\tarcount %d\n", arcount); 453 454 while (--qdcount >= 0) { 455 dlen = dn_skipname(data, m_bound); 456 if (dlen < 0) { 457 dprint("dn_skipname returned < 0\n"); 458 res_ndestroy(&res); 459 return (B_FALSE); 460 } 461 data += dlen + QFIXEDSZ; 462 } 463 464 count = ancount; 465 count += arcount; 466 while (--count >= 0 && data < m_bound) { 467 if ((dlen = dn_expand((unsigned char *) &abuf, m_bound, 468 data, (char *)name, sizeof (name))) < 0) { 469 dprint("dn_expand() dom failed\n"); 470 res_ndestroy(&res); 471 return (B_FALSE); 472 } 473 data += dlen; 474 type = parse_ushort((const char **)&data); 475 class = parse_ushort((const char **)&data); 476 ttl = parse_uint((const char **)&data); 477 dlen = parse_ushort((const char **)&data); 478 479 switch (type) { 480 case T_NS: 481 dprint("\ttype T_NS\n"); 482 break; 483 case T_CNAME: 484 dprint("\ttype T_CNAME\n"); 485 break; 486 case T_A: 487 dprint("\ttype T_A\n"); 488 break; 489 case T_SOA: 490 dprint("\ttype T_SOA\n"); 491 break; 492 case T_MX: 493 dprint("\ttype T_MX\n"); 494 break; 495 case T_TXT: 496 dprint("\ttype T_TXT\n"); 497 break; 498 default: 499 dprint("\ttype %d\n", type); 500 } 501 if (class == C_IN) 502 dprint("\tclass C_IN\n"); 503 else 504 dprint("\tclass %d\n", class); 505 dprint("\tttl %d secs\n", ttl); 506 dprint("\tlen %d bytes\n", dlen); 507 508 switch (type) { 509 case T_A: 510 (void) memcpy(iap, data, sizeof (struct in_addr)); 511 cacheNS(domain, iap, ttl); 512 res_ndestroy(&res); 513 return (B_TRUE); 514 515 case T_NS: 516 found_NS = B_TRUE; 517 NS_data = data; /* we may need this name below */ 518 if (dn_expand((unsigned char *) &abuf, m_bound, data, 519 (char *)name, sizeof (name)) < 0) { 520 dprint("\tdn_expand() T_NS failed\n"); 521 res_ndestroy(&res); 522 return (B_FALSE); 523 } 524 dprint("\tname %s\n", name); 525 break; 526 } 527 data += dlen; 528 } 529 dprint("getNS: fell through res_nquery results - no A records\n"); 530 531 /* 532 * The reply contained NS records, but no A records. Use 533 * res_gethostbyname() to get the name server's address 534 * via DNS. 535 */ 536 if (found_NS) { 537 if (dn_expand((unsigned char *) &abuf, m_bound, NS_data, 538 (char *)name, sizeof (name)) < 0) { 539 dprint("\tdn_expand() T_NS failed\n"); 540 res_ndestroy(&res); 541 return (B_FALSE); 542 } 543 544 if (ep = res_gethostbyname((const char *)name)) { 545 (void) memcpy(iap, ep->h_addr, sizeof (struct in_addr)); 546 cacheNS(domain, iap, ttl); 547 res_ndestroy(&res); 548 return (B_TRUE); 549 } else 550 dprint("getNS: res_gethostbyname(%s) failed\n", name); 551 } else { 552 dprint("getNS: reply contained no NS records\n"); 553 } 554 555 res_ndestroy(&res); 556 return (B_FALSE); 557 } 558 559 /* 560 * Cache the <domain, IP address> tuple (which is assumed to not already 561 * be cached) for ttl seconds. 562 */ 563 static void 564 cacheNS(char *domain, struct in_addr *iap, int ttl) 565 { 566 if (ttl > 0) { 567 time_t now; 568 569 if (nvl == NULL && 570 nvlist_alloc(&nvl, NV_UNIQUE_NAME_TYPE, 0) != 0) { 571 dprint("cacheNS: nvlist_alloc failed\n"); 572 return; 573 } 574 575 (void) time(&now); 576 now += ttl; 577 if ((nvlist_add_int32(nvl, domain, iap->s_addr) != 0) || 578 (nvlist_add_byte_array(nvl, domain, (uchar_t *)&now, 579 sizeof (now)) != 0)) { 580 dprint("cacheNS: nvlist_add failed\n"); 581 nvlist_free(nvl); 582 nvl = NULL; 583 } 584 } else 585 dprint("cacheNS: ttl 0 - nothing to cache\n"); 586 } 587 588 /* 589 * See whether the <domain, IP address> tuple has been cached. 590 */ 591 static boolean_t 592 lookupNS(char *domain, struct in_addr *iap) 593 { 594 int32_t i; 595 596 if (nvlist_lookup_int32(nvl, domain, &i) == 0) { 597 time_t *ttlptr; 598 uint_t nelem = sizeof (*ttlptr); 599 600 if (nvlist_lookup_byte_array(nvl, domain, (uchar_t **)&ttlptr, 601 &nelem) != 0) 602 return (B_FALSE); 603 604 if (*ttlptr >= time(0)) { /* still OK to use */ 605 iap->s_addr = i; 606 return (B_TRUE); 607 } else { 608 (void) nvlist_remove_all(nvl, domain); 609 } 610 } 611 612 return (B_FALSE); 613 } 614 615 /* 616 * Do the work of updating DNS to have the <hp->h_name <-> hp->h_addr> 617 * pairing. 618 */ 619 static boolean_t 620 send_update(struct hostent *hp, struct in_addr *to_server) 621 { 622 char *forfqhost; 623 struct __res_state res; 624 struct in_addr netaddr; 625 char revnamebuf[MAXDNAME]; 626 627 (void) memset(&res, 0, sizeof (res)); 628 if (res_ninit(&res) == -1) { 629 dprint("send_updated res_ninit failed!"); 630 return (B_FALSE); 631 } 632 res.nscount = 1; 633 res.nsaddr.sin_family = AF_INET; 634 res.nsaddr.sin_port = htons(NAMESERVER_PORT); 635 res.nsaddr.sin_addr.s_addr = to_server->s_addr; 636 637 /* If debugging output desired, then ask resolver to do it, too */ 638 if (debug_fp != NULL) 639 res.options |= RES_DEBUG; 640 641 if (strchr(hp->h_name, '.') == NULL) { 642 dprint("send_update handed non-FQDN: %s\n", hp->h_name); 643 res_ndestroy(&res); 644 return (B_FALSE); 645 } 646 forfqhost = hp->h_name; 647 648 /* Construct the fully-qualified name for PTR record updates */ 649 /* LINTED - alignment */ 650 netaddr.s_addr = ((struct in_addr *)hp->h_addr)->s_addr; 651 (void) snprintf(revnamebuf, sizeof (revnamebuf), 652 "%u.%u.%u.%u.in-addr.ARPA", 653 netaddr.S_un.S_un_b.s_b4, netaddr.S_un.S_un_b.s_b3, 654 netaddr.S_un.S_un_b.s_b2, netaddr.S_un.S_un_b.s_b1); 655 dprint("send_update %s: revname %s\n", hp->h_name, revnamebuf); 656 657 /* 658 * The steps in doing an update: 659 * - delete any A records 660 * - delete any PTR records 661 * - add an A record 662 * - add a PTR record 663 */ 664 665 if (!delA(&res, forfqhost) || 666 !delPTR(&res, forfqhost, revnamebuf) || 667 !addA(&res, forfqhost, netaddr) || 668 !addPTR(&res, forfqhost, revnamebuf)) { 669 res_ndestroy(&res); 670 return (B_FALSE); 671 } 672 res_ndestroy(&res); 673 return (B_TRUE); 674 } 675 676 /* delete A records for this fully-qualified name */ 677 static boolean_t 678 delA(struct __res_state *resp, char *fqdn) 679 { 680 ns_updque q; 681 ns_updrec *updreqp; 682 683 INIT_LIST(q); 684 updreqp = res_mkupdrec(S_UPDATE, fqdn, C_IN, T_A, 0); 685 if (updreqp == NULL) { 686 dprint("res_mkupdrec (del A) failed\n"); 687 return (B_FALSE); 688 } 689 updreqp->r_opcode = DELETE; 690 updreqp->r_data = NULL; 691 updreqp->r_size = 0; 692 APPEND(q, updreqp, r_link); 693 if (retry_update(resp, HEAD(q)) != 1) { 694 dprint("res_nupdate (del A) failed - errno %d, h_errno %d\n", 695 errno, h_errno); 696 freeupdrecs(q); 697 return (B_FALSE); 698 } 699 freeupdrecs(q); 700 return (B_TRUE); 701 } 702 703 /* delete PTR records for this address */ 704 static boolean_t 705 delPTR(struct __res_state *resp, char *fqdn, char *revname) 706 { 707 ns_updque q; 708 ns_updrec *updreqp; 709 710 INIT_LIST(q); 711 updreqp = res_mkupdrec(S_UPDATE, revname, C_IN, T_PTR, 0); 712 if (updreqp == NULL) { 713 dprint("res_mkupdrec (del PTR) failed\n"); 714 return (B_FALSE); 715 } 716 updreqp->r_opcode = DELETE; 717 updreqp->r_data = (unsigned char *)fqdn; 718 updreqp->r_size = strlen(fqdn); 719 APPEND(q, updreqp, r_link); 720 if (retry_update(resp, HEAD(q)) != 1) { 721 dprint("res_nupdate (del PTR) failed - errno %d, h_errno %d\n", 722 errno, h_errno); 723 freeupdrecs(q); 724 return (B_FALSE); 725 } 726 freeupdrecs(q); 727 return (B_TRUE); 728 } 729 730 /* add an A record for this fqdn <-> addr pair */ 731 static boolean_t 732 addA(struct __res_state *resp, char *fqdn, struct in_addr na) 733 { 734 ns_updque q; 735 ns_updrec *prereqp, *updreqp; 736 int ttl = LEASEMIN; 737 char ntoab[INET_ADDRSTRLEN]; 738 739 INIT_LIST(q); 740 prereqp = res_mkupdrec(S_PREREQ, fqdn, C_IN, T_A, 0); 741 if (prereqp == NULL) { 742 dprint("res_mkupdrec (add A PREREQ) failed\n"); 743 return (B_FALSE); 744 } 745 prereqp->r_opcode = NXRRSET; 746 prereqp->r_data = NULL; 747 prereqp->r_size = 0; 748 APPEND(q, prereqp, r_link); 749 updreqp = res_mkupdrec(S_UPDATE, fqdn, C_IN, T_A, ttl); 750 if (updreqp == NULL) { 751 dprint("res_mkupdrec (add A UPDATE) failed\n"); 752 freeupdrecs(q); 753 return (B_FALSE); 754 } 755 756 (void) inet_ntoa_r(na, ntoab); 757 updreqp->r_opcode = ADD; 758 updreqp->r_data = (unsigned char *)ntoab; 759 updreqp->r_size = strlen(ntoab); 760 APPEND(q, updreqp, r_link); 761 if (retry_update(resp, HEAD(q)) != 1) { 762 dprint("res_nupdate (ADD A) failed - errno %d, h_errno %d\n", 763 errno, h_errno); 764 freeupdrecs(q); 765 return (B_FALSE); 766 } 767 freeupdrecs(q); 768 return (B_TRUE); 769 } 770 771 /* add a PTR record for this fqdn <-> address pair */ 772 static boolean_t 773 addPTR(struct __res_state *resp, char *fqdn, char *revname) 774 { 775 ns_updque q; 776 ns_updrec *prereqp, *updreqp; 777 int ttl = LEASEMIN; 778 779 INIT_LIST(q); 780 prereqp = res_mkupdrec(S_UPDATE, revname, C_IN, T_PTR, 0); 781 if (prereqp == NULL) { 782 dprint("res_mkupdrec (add PTR DELETE) failed\n"); 783 return (B_FALSE); 784 } 785 prereqp->r_opcode = DELETE; 786 prereqp->r_data = NULL; 787 prereqp->r_size = 0; 788 APPEND(q, prereqp, r_link); 789 updreqp = res_mkupdrec(S_UPDATE, revname, C_IN, T_PTR, ttl); 790 if (updreqp == NULL) { 791 dprint("res_mkupdrec (add PTR ADD) failed\n"); 792 freeupdrecs(q); 793 return (B_FALSE); 794 } 795 updreqp->r_opcode = ADD; 796 updreqp->r_data = (unsigned char *)fqdn; 797 updreqp->r_size = strlen(fqdn); 798 APPEND(q, updreqp, r_link); 799 if (retry_update(resp, HEAD(q)) != 1) { 800 dprint("res_nupdate (ADD PTR) failed - errno %d, h_errno %d\n", 801 errno, h_errno); 802 freeupdrecs(q); 803 return (B_FALSE); 804 } 805 freeupdrecs(q); 806 807 return (B_TRUE); 808 } 809 810 /* retry an update request when appropriate */ 811 static boolean_t 812 retry_update(struct __res_state *resp, ns_updrec *h) 813 { 814 int retries; 815 816 for (retries = 0; retries < MAX_RETRIES; retries++) 817 if (res_nupdate(resp, h, NULL) == 1) { 818 return (B_TRUE); 819 } else { 820 /* 821 * Look for indicators from libresolv:res_nsend() 822 * that we should retry a request. 823 */ 824 if ((errno == ECONNREFUSED) || 825 ((h_errno == TRY_AGAIN) && (errno == ETIMEDOUT))) { 826 dprint("retry_update - errno %d, h_errno %d\n", 827 errno, h_errno); 828 continue; 829 } else 830 return (B_FALSE); 831 } 832 833 return (B_FALSE); 834 } 835 836 static void 837 freeupdrecs(ns_updque q) 838 { 839 while (!EMPTY(q)) { 840 ns_updrec *tmp; 841 842 tmp = HEAD(q); 843 UNLINK(q, tmp, r_link); 844 res_freeupdrec(tmp); 845 } 846 } 847 848 /* 849 * Parse a 16-bit quantity from a DNS reply packet. 850 */ 851 static unsigned short 852 parse_ushort(const char **pp) 853 { 854 const uchar_t *p = (const uchar_t *)*pp; 855 unsigned short val; 856 857 val = (p[0] << 8) | p[1]; 858 *pp += 2; 859 return (val); 860 } 861 862 863 /* 864 * Parse a 32-bit quantity from a DNS reply packet. 865 */ 866 static unsigned int 867 parse_uint(const char **pp) 868 { 869 const uchar_t *p = (const uchar_t *)*pp; 870 unsigned int val; 871 872 val = ((uint_t)p[0] << 24) | ((uint_t)p[1] << 16) | 873 ((uint_t)p[2] << 8) | (uint_t)p[3]; 874 *pp += 4; 875 return (val); 876 } 877 878 /* 879 * Clean up a childstat structure's synchronization variables and free 880 * the allocated memory. 881 */ 882 static void 883 childstat_cleanup(struct childstat *sp) 884 { 885 (void) cond_destroy(&sp->cv); 886 (void) mutex_destroy(&sp->m); 887 freehost(sp->hp); 888 free(sp); 889 } 890 891 /* 892 * Format and print a debug message, prepending the thread ID of the 893 * thread logging the message. 894 */ 895 /* PRINTFLIKE1 */ 896 static void 897 dprint(char *format, ...) 898 { 899 va_list ap; 900 901 va_start(ap, format); 902 if (debug_fp) { 903 (void) fprintf(debug_fp, "%u: ", thr_self()); 904 (void) vfprintf(debug_fp, format, ap); 905 va_end(ap); 906 } 907 } 908 909 static void 910 freehost(struct hostent *hp) 911 { 912 free(hp->h_addr); 913 free(hp->h_addr_list); 914 free(hp->h_name); 915 free(hp); 916 }