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 }