2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
2N/A * Use is subject to license terms.
2N/A */
2N/A
2N/A#pragma ident "%Z%%M% %I% %E% SMI"
2N/A
2N/A#include <thread.h>
2N/A#include <stdlib.h>
2N/A#include <netdb.h>
2N/A#include <strings.h>
2N/A#include <alloca.h>
2N/A#include <sys/socket.h>
2N/A#include <netinet/in.h>
2N/A#include <arpa/inet.h>
2N/A#include <arpa/nameser.h>
2N/A#include "res_update.h"
2N/A#include <stdio.h>
2N/A#include <errno.h>
2N/A#include <resolv.h>
2N/A#include <assert.h>
2N/A#include <stdarg.h>
2N/A#include <libnvpair.h>
2N/A
2N/A#define MAX_RETRIES 5 /* times to loop on TRY_AGAIN errors */
2N/A#define LEASEMIN 3600 /* minimum lease time allowed by RFC 1531 */
2N/A
2N/Astatic boolean_t getNS(char *, struct in_addr *);
2N/Astatic void cacheNS(char *, struct in_addr *, int);
2N/Astatic boolean_t lookupNS(char *, struct in_addr *);
2N/Astatic boolean_t send_update(struct hostent *, struct in_addr *);
2N/Astatic unsigned short parse_ushort(const char **);
2N/Astatic unsigned int parse_uint(const char **);
2N/Astatic void freeupdrecs(ns_updque);
2N/Astatic void freehost(struct hostent *);
2N/Astatic boolean_t delA(struct __res_state *, char *);
2N/Astatic boolean_t delPTR(struct __res_state *, char *, char *);
2N/Astatic boolean_t addA(struct __res_state *, char *, struct in_addr);
2N/Astatic boolean_t addPTR(struct __res_state *, char *, char *);
2N/Astatic boolean_t retry_update(struct __res_state *, ns_updrec *);
2N/A
2N/Aextern char *inet_ntoa_r(struct in_addr, char *);
2N/A
2N/A/*
2N/A * The parent (calling) thread and the child thread it spawns to do an
2N/A * update use this structure to rendezvous. The child thread sets the
2N/A * ``done'' variable to B_TRUE when it's completed its work. The nusers
2N/A * variable lets us arbitrate to see who has to clean up (via the
2N/A * provided childstat_cleanup() function) the dynamically-allocated
2N/A * structure - last one to wake up loses, and has to do the work.
2N/A */
2N/Astruct childstat {
2N/A mutex_t m;
2N/A cond_t cv;
2N/A struct hostent *hp;
2N/A boolean_t synchflag;
2N/A boolean_t done;
2N/A int ret;
2N/A int nusers;
2N/A};
2N/Astatic void childstat_cleanup(struct childstat *);
2N/A
2N/Astatic void update_thread(void *);
2N/A
2N/A/*
2N/A * The given environment variable, if present, will contain the name
2N/A * of a file (or the distinguished values "stdout" and "stderr") into
2N/A * which we should place the debugging output from this shared object.
2N/A *
2N/A * The debugging output is basically free-form but uses the dprint()
2N/A * function to ensure that each message is tagged with its thread ID,
2N/A * so we have some hope of sorting out later what actually happened.
2N/A */
2N/Astatic char env_filetoken[] = "DHCP_DNS_OUTPUT";
2N/Astatic void dprint(char *, ...);
2N/Astatic FILE *debug_fp;
2N/A
2N/Astatic boolean_t dns_config_ok; /* did res_ninit() work? */
2N/A
2N/Astatic nvlist_t *nvl;
2N/A
2N/A/* CSTYLED */
2N/A#pragma init (init)
2N/A
2N/A/*
2N/A * This is the shared object startup function, called once when we
2N/A * are dlopen()ed.
2N/A */
2N/Astatic void
2N/Ainit(void)
2N/A{
2N/A char *cp;
2N/A struct __res_state res;
2N/A
2N/A if (cp = getenv(env_filetoken)) {
2N/A if (strcmp(cp, "stdout") == 0)
2N/A debug_fp = stdout;
2N/A else if (strcmp(cp, "stderr") == 0)
2N/A debug_fp = stderr;
2N/A else {
2N/A debug_fp = fopen(cp, "a");
2N/A }
2N/A if (debug_fp)
2N/A (void) setvbuf(debug_fp, NULL, _IOLBF, BUFSIZ);
2N/A }
2N/A
2N/A /*
2N/A * Use res_ninit(3RESOLV) to see whether DNS has been configured
2N/A * on the host running this code. In practice, life must be very
2N/A * bad for res_ninit() to fail.
2N/A */
2N/A (void) memset(&res, 0, sizeof (res));
2N/A if (res_ninit(&res) == -1) {
2N/A dprint("res_ninit() failed - dns_config_ok FALSE\n");
2N/A dns_config_ok = B_FALSE;
2N/A } else {
2N/A dprint("res_ninit() succeeded\n");
2N/A dns_config_ok = B_TRUE;
2N/A }
2N/A res_ndestroy(&res);
2N/A}
2N/A
2N/A/*
2N/A * This is the interface exported to the outside world. Control over
2N/A * the hostent structure is assumed to pass to dns_puthostent(); it will
2N/A * free the associated space when done.
2N/A */
2N/Aint
2N/Adns_puthostent(struct hostent *hp, time_t timeout)
2N/A{
2N/A struct childstat *sp;
2N/A timestruc_t t;
2N/A int ret;
2N/A thread_t tid;
2N/A
2N/A
2N/A /*
2N/A * Check the consistency of the hostent structure:
2N/A * both the name and address fields should be valid,
2N/A * h_addrtype must be AF_INET, and h_length must be
2N/A * sizeof (struct in_addr);
2N/A */
2N/A if (hp == NULL) {
2N/A dprint("hp is NULL - return -1\n");
2N/A return (-1);
2N/A }
2N/A if (hp->h_addr_list == NULL) {
2N/A dprint("h_addr_list is NULL - return -1\n");
2N/A freehost(hp);
2N/A return (-1);
2N/A }
2N/A if (hp->h_addr_list[0] == NULL) {
2N/A dprint("h_addr_list is zero-length - return -1\n");
2N/A freehost(hp);
2N/A return (-1);
2N/A }
2N/A if (hp->h_name == NULL) {
2N/A dprint("h_name is NULL - return -1\n");
2N/A freehost(hp);
2N/A return (-1);
2N/A }
2N/A if (hp->h_name[0] == '\0') {
2N/A dprint("h_name[0] is NUL - return -1\n");
2N/A freehost(hp);
2N/A return (-1);
2N/A }
2N/A if (hp->h_addrtype != AF_INET) {
2N/A dprint("h_addrtype (%d) != AF_INET - return -1\n",
2N/A hp->h_addrtype);
2N/A freehost(hp);
2N/A return (-1);
2N/A }
2N/A if (hp->h_length != sizeof (struct in_addr)) {
2N/A dprint("h_length (%d) != sizeof (struct in_addr) - return -1\n",
2N/A hp->h_length);
2N/A freehost(hp);
2N/A return (-1);
2N/A }
2N/A
2N/A dprint("dns_puthostent(%s, %d)\n", hp->h_name, (int)timeout);
2N/A
2N/A if (dns_config_ok == B_FALSE) {
2N/A dprint("dns_config_ok FALSE - return -1\n");
2N/A freehost(hp);
2N/A return (-1);
2N/A }
2N/A
2N/A if ((sp = malloc(sizeof (struct childstat))) == NULL) {
2N/A dprint("malloc (sizeof struct childstat) failed\n");
2N/A freehost(hp);
2N/A return (-1);
2N/A }
2N/A
2N/A /*
2N/A * From this point on, both hp and sp are cleaned up and freed via
2N/A * childstat_cleanup(), with bookkeeping done to see whether the
2N/A * parent thread or the child one should be the one in charge of
2N/A * cleaning up.
2N/A */
2N/A sp->hp = hp;
2N/A
2N/A if (timeout > 0)
2N/A sp->synchflag = B_TRUE;
2N/A else
2N/A sp->synchflag = B_FALSE;
2N/A sp->done = B_FALSE;
2N/A sp->ret = 0;
2N/A sp->nusers = 1;
2N/A (void) mutex_init(&sp->m, USYNC_THREAD, 0);
2N/A (void) cond_init(&sp->cv, USYNC_THREAD, 0);
2N/A (void) time(&t.tv_sec);
2N/A t.tv_sec += timeout;
2N/A t.tv_nsec = 0;
2N/A
2N/A if (thr_create(NULL, NULL, (void *(*)(void *))update_thread,
2N/A (void *) sp, THR_DAEMON|THR_DETACHED, &tid)) {
2N/A dprint("thr_create failed (errno %d) - return -1\n", errno);
2N/A childstat_cleanup(sp);
2N/A return (-1);
2N/A }
2N/A else
2N/A dprint("thread %u created\n", tid);
2N/A
2N/A if (!sp->done) { /* we might already have finished */
2N/A (void) mutex_lock(&sp->m);
2N/A
2N/A /* if asynchronous, and child still working, just return; */
2N/A if ((!sp->done) && (timeout == 0)) {
2N/A sp->nusers--;
2N/A (void) mutex_unlock(&sp->m);
2N/A dprint("done 0, timeout 0\n");
2N/A return (0);
2N/A }
2N/A
2N/A /* otherwise, wait for child to finish or time to expire */
2N/A while (!sp->done)
2N/A if (cond_timedwait(&sp->cv, &sp->m, &t) == ETIME) {
2N/A /*
2N/A * Child thread did not return before the
2N/A * timeout. One might think we could
2N/A * assert(sp->nusers > 1);
2N/A * here, but we can't: we must protect
2N/A * against this sequence of events:
2N/A * cond_timedwait() times out
2N/A *
2N/A * child finishes, grabs mutex,
2N/A * decrements nusers, sets done,
2N/A * and exits.
2N/A *
2N/A * cond_timedwait() reacquires the
2N/A * mutex and returns ETIME
2N/A *
2N/A * If this happens, nusers will now be 1,
2N/A * even though cond_timedwait() returned
2N/A * ETIME.
2N/A */
2N/A if (sp->nusers == 1)
2N/A /* child must have also set done */
2N/A break;
2N/A else
2N/A /* child thread has not returned */
2N/A sp->nusers--;
2N/A (void) mutex_unlock(&sp->m);
2N/A dprint("update for %s timed out\n", hp->h_name);
2N/A return (0);
2N/A }
2N/A assert(sp->done);
2N/A ret = sp->ret;
2N/A }
2N/A
2N/A childstat_cleanup(sp);
2N/A return (ret);
2N/A}
2N/A
2N/A/*
2N/A * This worker thread, spawned by dns_puthostent(), is responsible for
2N/A * seeing that the update work gets done and cleaning up afterward
2N/A * if necessary.
2N/A */
2N/Astatic void
2N/Aupdate_thread(void *arg)
2N/A{
2N/A char *p;
2N/A int num_updated = 0;
2N/A struct in_addr ia;
2N/A struct hostent *hp;
2N/A struct childstat *sp;
2N/A
2N/A dprint("update_thread running\n");
2N/A
2N/A sp = (struct childstat *)arg;
2N/A
2N/A (void) mutex_lock(&sp->m);
2N/A /*
2N/A * Paranoia: if nusers was 0 and we were asked to do a
2N/A * synchronous update, our parent must have incremented
2N/A * it, called cond_timedwait(), timed out, and decremented it,
2N/A * all before we got this far. In this case, we do nothing
2N/A * except clean up and exit.
2N/A */
2N/A if ((++sp->nusers == 1) && sp->synchflag) {
2N/A childstat_cleanup(sp);
2N/A thr_exit(0);
2N/A }
2N/A
2N/A (void) mutex_unlock(&sp->m);
2N/A
2N/A hp = sp->hp;
2N/A
2N/A /*
2N/A * h_name should be full-qualified; find the name servers for
2N/A * its domain ...
2N/A */
2N/A for (p = hp->h_name; *p != NULL; p++)
2N/A if (*p == '.') {
2N/A if (getNS(++p, &ia)) {
2N/A char ntoab[INET_ADDRSTRLEN];
2N/A
2N/A (void) inet_ntoa_r(ia, ntoab);
2N/A dprint("update for %s goes to %s\n",
2N/A hp->h_name, ntoab);
2N/A /* ... and send the update to one of them. */
2N/A if (send_update(hp, &ia)) {
2N/A dprint("send_update succeeded\n");
2N/A num_updated = 1;
2N/A } else {
2N/A dprint("send_update failed\n");
2N/A num_updated = 0;
2N/A }
2N/A } else {
2N/A dprint("getNS failed\n");
2N/A num_updated = -1;
2N/A }
2N/A break;
2N/A }
2N/A dprint("update for %s returning %d\n", hp->h_name, num_updated);
2N/A
2N/A (void) mutex_lock(&sp->m);
2N/A if (--sp->nusers == 0) {
2N/A /* parent timed out and abandoned us - our turn to clean up */
2N/A childstat_cleanup(sp);
2N/A } else {
2N/A sp->done = B_TRUE;
2N/A sp->ret = num_updated;
2N/A (void) cond_signal(&sp->cv);
2N/A (void) mutex_unlock(&sp->m);
2N/A }
2N/A
2N/A thr_exit(0);
2N/A}
2N/A
2N/A/*
2N/A * Find a name server for the supplied domain and return its IP address.
2N/A * Sadly, in order to do this we have to parse the actual DNS reply
2N/A * packet - no functions are provided for doing this work for us.
2N/A */
2N/Astatic boolean_t
2N/AgetNS(char *domain, struct in_addr *iap)
2N/A{
2N/A HEADER *hp;
2N/A union {
2N/A HEADER h;
2N/A char buf[NS_PACKETSZ];
2N/A } abuf;
2N/A int alen;
2N/A int count;
2N/A int retries;
2N/A unsigned char name[MAXDNAME];
2N/A int qdcount, ancount, nscount, arcount;
2N/A unsigned char *data;
2N/A unsigned char *m_bound;
2N/A int type, class, ttl, dlen;
2N/A struct hostent *ep;
2N/A unsigned char *NS_data;
2N/A boolean_t found_NS = B_FALSE;
2N/A struct __res_state res;
2N/A extern struct hostent *res_gethostbyname(const char *);
2N/A
2N/A if (lookupNS(domain, iap)) {
2N/A dprint("getNS: found cached IP address for domain %s\n",
2N/A domain);
2N/A return (B_TRUE);
2N/A }
2N/A (void) memset(&res, 0, sizeof (res));
2N/A if (res_ninit(&res) == -1) {
2N/A dprint("getNS(\"%s\"): res_ninit failed\n", domain);
2N/A return (B_FALSE);
2N/A }
2N/A for (retries = 0; retries < MAX_RETRIES; retries++) {
2N/A alen = res_nquery(&res, domain, C_IN, T_NS, (uchar_t *)&abuf,
2N/A sizeof (abuf));
2N/A
2N/A if (alen <= 0) {
2N/A /*
2N/A * Look for indicators from libresolv:res_nsend()
2N/A * that we should retry a request.
2N/A */
2N/A if ((errno == ECONNREFUSED) ||
2N/A ((h_errno == TRY_AGAIN) && (errno == ETIMEDOUT))) {
2N/A dprint("getNS retry: errno %d, h_errno %d\n",
2N/A errno, h_errno);
2N/A continue;
2N/A } else {
2N/A dprint("getNS(\"%s\"): res_nquery failed "
2N/A "(h_errno %d)\n", domain, h_errno);
2N/A res_ndestroy(&res);
2N/A return (B_FALSE);
2N/A }
2N/A }
2N/A }
2N/A if (alen <= 0) {
2N/A dprint("getNS(\"%s\"): res_nquery failed " "(h_errno %d)\n",
2N/A domain, h_errno);
2N/A res_ndestroy(&res);
2N/A return (B_FALSE);
2N/A }
2N/A
2N/A m_bound = ((unsigned char *)&abuf) + alen;
2N/A
2N/A hp = (HEADER *)&abuf;
2N/A data = (unsigned char *)&hp[1]; /* a DNS paradigm - actually abuf.buf */
2N/A
2N/A qdcount = ntohs(hp->qdcount);
2N/A ancount = ntohs(hp->ancount);
2N/A nscount = ntohs(hp->nscount);
2N/A arcount = ntohs(hp->arcount);
2N/A
2N/A dprint("getNS(\"%s\"):\n", domain);
2N/A dprint("\tqdcount %d\n", qdcount);
2N/A dprint("\tancount %d\n", ancount);
2N/A dprint("\tnscount %d\n", nscount);
2N/A dprint("\tarcount %d\n", arcount);
2N/A
2N/A while (--qdcount >= 0) {
2N/A dlen = dn_skipname(data, m_bound);
2N/A if (dlen < 0) {
2N/A dprint("dn_skipname returned < 0\n");
2N/A res_ndestroy(&res);
2N/A return (B_FALSE);
2N/A }
2N/A data += dlen + QFIXEDSZ;
2N/A }
2N/A
2N/A count = ancount;
2N/A count += arcount;
2N/A while (--count >= 0 && data < m_bound) {
2N/A if ((dlen = dn_expand((unsigned char *) &abuf, m_bound,
2N/A data, (char *)name, sizeof (name))) < 0) {
2N/A dprint("dn_expand() dom failed\n");
2N/A res_ndestroy(&res);
2N/A return (B_FALSE);
2N/A }
2N/A data += dlen;
2N/A type = parse_ushort((const char **)&data);
2N/A class = parse_ushort((const char **)&data);
2N/A ttl = parse_uint((const char **)&data);
2N/A dlen = parse_ushort((const char **)&data);
2N/A
2N/A switch (type) {
2N/A case T_NS:
2N/A dprint("\ttype T_NS\n");
2N/A break;
2N/A case T_CNAME:
2N/A dprint("\ttype T_CNAME\n");
2N/A break;
2N/A case T_A:
2N/A dprint("\ttype T_A\n");
2N/A break;
2N/A case T_SOA:
2N/A dprint("\ttype T_SOA\n");
2N/A break;
2N/A case T_MX:
2N/A dprint("\ttype T_MX\n");
2N/A break;
2N/A case T_TXT:
2N/A dprint("\ttype T_TXT\n");
2N/A break;
2N/A default:
2N/A dprint("\ttype %d\n", type);
2N/A }
2N/A if (class == C_IN)
2N/A dprint("\tclass C_IN\n");
2N/A else
2N/A dprint("\tclass %d\n", class);
2N/A dprint("\tttl %d secs\n", ttl);
2N/A dprint("\tlen %d bytes\n", dlen);
2N/A
2N/A switch (type) {
2N/A case T_A:
2N/A (void) memcpy(iap, data, sizeof (struct in_addr));
2N/A cacheNS(domain, iap, ttl);
2N/A res_ndestroy(&res);
2N/A return (B_TRUE);
2N/A
2N/A case T_NS:
2N/A found_NS = B_TRUE;
2N/A NS_data = data; /* we may need this name below */
2N/A if (dn_expand((unsigned char *) &abuf, m_bound, data,
2N/A (char *)name, sizeof (name)) < 0) {
2N/A dprint("\tdn_expand() T_NS failed\n");
2N/A res_ndestroy(&res);
2N/A return (B_FALSE);
2N/A }
2N/A dprint("\tname %s\n", name);
2N/A break;
2N/A }
2N/A data += dlen;
2N/A }
2N/A dprint("getNS: fell through res_nquery results - no A records\n");
2N/A
2N/A /*
2N/A * The reply contained NS records, but no A records. Use
2N/A * res_gethostbyname() to get the name server's address
2N/A * via DNS.
2N/A */
2N/A if (found_NS) {
2N/A if (dn_expand((unsigned char *) &abuf, m_bound, NS_data,
2N/A (char *)name, sizeof (name)) < 0) {
2N/A dprint("\tdn_expand() T_NS failed\n");
2N/A res_ndestroy(&res);
2N/A return (B_FALSE);
2N/A }
2N/A
2N/A if (ep = res_gethostbyname((const char *)name)) {
2N/A (void) memcpy(iap, ep->h_addr, sizeof (struct in_addr));
2N/A cacheNS(domain, iap, ttl);
2N/A res_ndestroy(&res);
2N/A return (B_TRUE);
2N/A } else
2N/A dprint("getNS: res_gethostbyname(%s) failed\n", name);
2N/A } else {
2N/A dprint("getNS: reply contained no NS records\n");
2N/A }
2N/A
2N/A res_ndestroy(&res);
2N/A return (B_FALSE);
2N/A}
2N/A
2N/A/*
2N/A * Cache the <domain, IP address> tuple (which is assumed to not already
2N/A * be cached) for ttl seconds.
2N/A */
2N/Astatic void
2N/AcacheNS(char *domain, struct in_addr *iap, int ttl)
2N/A{
2N/A if (ttl > 0) {
2N/A time_t now;
2N/A
2N/A if (nvl == NULL &&
2N/A nvlist_alloc(&nvl, NV_UNIQUE_NAME_TYPE, 0) != 0) {
2N/A dprint("cacheNS: nvlist_alloc failed\n");
2N/A return;
2N/A }
2N/A
2N/A (void) time(&now);
2N/A now += ttl;
2N/A if ((nvlist_add_int32(nvl, domain, iap->s_addr) != 0) ||
2N/A (nvlist_add_byte_array(nvl, domain, (uchar_t *)&now,
2N/A sizeof (now)) != 0)) {
2N/A dprint("cacheNS: nvlist_add failed\n");
2N/A nvlist_free(nvl);
2N/A nvl = NULL;
2N/A }
2N/A } else
2N/A dprint("cacheNS: ttl 0 - nothing to cache\n");
2N/A}
2N/A
2N/A/*
2N/A * See whether the <domain, IP address> tuple has been cached.
2N/A */
2N/Astatic boolean_t
2N/AlookupNS(char *domain, struct in_addr *iap)
2N/A{
2N/A int32_t i;
2N/A
2N/A if (nvlist_lookup_int32(nvl, domain, &i) == 0) {
2N/A time_t *ttlptr;
2N/A uint_t nelem = sizeof (*ttlptr);
2N/A
2N/A if (nvlist_lookup_byte_array(nvl, domain, (uchar_t **)&ttlptr,
2N/A &nelem) != 0)
2N/A return (B_FALSE);
2N/A
2N/A if (*ttlptr >= time(0)) { /* still OK to use */
2N/A iap->s_addr = i;
2N/A return (B_TRUE);
2N/A } else {
2N/A (void) nvlist_remove_all(nvl, domain);
2N/A }
2N/A }
2N/A
2N/A return (B_FALSE);
2N/A}
2N/A
2N/A/*
2N/A * Do the work of updating DNS to have the <hp->h_name <-> hp->h_addr>
2N/A * pairing.
2N/A */
2N/Astatic boolean_t
2N/Asend_update(struct hostent *hp, struct in_addr *to_server)
2N/A{
2N/A char *forfqhost;
2N/A struct __res_state res;
2N/A struct in_addr netaddr;
2N/A char revnamebuf[MAXDNAME];
2N/A
2N/A (void) memset(&res, 0, sizeof (res));
2N/A if (res_ninit(&res) == -1) {
2N/A dprint("send_updated res_ninit failed!");
2N/A return (B_FALSE);
2N/A }
2N/A res.nscount = 1;
2N/A res.nsaddr.sin_family = AF_INET;
2N/A res.nsaddr.sin_port = htons(NAMESERVER_PORT);
2N/A res.nsaddr.sin_addr.s_addr = to_server->s_addr;
2N/A
2N/A /* If debugging output desired, then ask resolver to do it, too */
2N/A if (debug_fp != NULL)
2N/A res.options |= RES_DEBUG;
2N/A
2N/A if (strchr(hp->h_name, '.') == NULL) {
2N/A dprint("send_update handed non-FQDN: %s\n", hp->h_name);
2N/A res_ndestroy(&res);
2N/A return (B_FALSE);
2N/A }
2N/A forfqhost = hp->h_name;
2N/A
2N/A /* Construct the fully-qualified name for PTR record updates */
2N/A /* LINTED - alignment */
2N/A netaddr.s_addr = ((struct in_addr *)hp->h_addr)->s_addr;
2N/A (void) snprintf(revnamebuf, sizeof (revnamebuf),
2N/A "%u.%u.%u.%u.in-addr.ARPA",
2N/A netaddr.S_un.S_un_b.s_b4, netaddr.S_un.S_un_b.s_b3,
2N/A netaddr.S_un.S_un_b.s_b2, netaddr.S_un.S_un_b.s_b1);
2N/A dprint("send_update %s: revname %s\n", hp->h_name, revnamebuf);
2N/A
2N/A /*
2N/A * The steps in doing an update:
2N/A * - delete any A records
2N/A * - delete any PTR records
2N/A * - add an A record
2N/A * - add a PTR record
2N/A */
2N/A
2N/A if (!delA(&res, forfqhost) ||
2N/A !delPTR(&res, forfqhost, revnamebuf) ||
2N/A !addA(&res, forfqhost, netaddr) ||
2N/A !addPTR(&res, forfqhost, revnamebuf)) {
2N/A res_ndestroy(&res);
2N/A return (B_FALSE);
2N/A }
2N/A res_ndestroy(&res);
2N/A return (B_TRUE);
2N/A}
2N/A
2N/A/* delete A records for this fully-qualified name */
2N/Astatic boolean_t
2N/AdelA(struct __res_state *resp, char *fqdn)
2N/A{
2N/A ns_updque q;
2N/A ns_updrec *updreqp;
2N/A
2N/A INIT_LIST(q);
2N/A updreqp = res_mkupdrec(S_UPDATE, fqdn, C_IN, T_A, 0);
2N/A if (updreqp == NULL) {
2N/A dprint("res_mkupdrec (del A) failed\n");
2N/A return (B_FALSE);
2N/A }
2N/A updreqp->r_opcode = DELETE;
2N/A updreqp->r_data = NULL;
2N/A updreqp->r_size = 0;
2N/A APPEND(q, updreqp, r_link);
2N/A if (retry_update(resp, HEAD(q)) != 1) {
2N/A dprint("res_nupdate (del A) failed - errno %d, h_errno %d\n",
2N/A errno, h_errno);
2N/A freeupdrecs(q);
2N/A return (B_FALSE);
2N/A }
2N/A freeupdrecs(q);
2N/A return (B_TRUE);
2N/A}
2N/A
2N/A/* delete PTR records for this address */
2N/Astatic boolean_t
2N/AdelPTR(struct __res_state *resp, char *fqdn, char *revname)
2N/A{
2N/A ns_updque q;
2N/A ns_updrec *updreqp;
2N/A
2N/A INIT_LIST(q);
2N/A updreqp = res_mkupdrec(S_UPDATE, revname, C_IN, T_PTR, 0);
2N/A if (updreqp == NULL) {
2N/A dprint("res_mkupdrec (del PTR) failed\n");
2N/A return (B_FALSE);
2N/A }
2N/A updreqp->r_opcode = DELETE;
2N/A updreqp->r_data = (unsigned char *)fqdn;
2N/A updreqp->r_size = strlen(fqdn);
2N/A APPEND(q, updreqp, r_link);
2N/A if (retry_update(resp, HEAD(q)) != 1) {
2N/A dprint("res_nupdate (del PTR) failed - errno %d, h_errno %d\n",
2N/A errno, h_errno);
2N/A freeupdrecs(q);
2N/A return (B_FALSE);
2N/A }
2N/A freeupdrecs(q);
2N/A return (B_TRUE);
2N/A}
2N/A
2N/A/* add an A record for this fqdn <-> addr pair */
2N/Astatic boolean_t
2N/AaddA(struct __res_state *resp, char *fqdn, struct in_addr na)
2N/A{
2N/A ns_updque q;
2N/A ns_updrec *prereqp, *updreqp;
2N/A int ttl = LEASEMIN;
2N/A char ntoab[INET_ADDRSTRLEN];
2N/A
2N/A INIT_LIST(q);
2N/A prereqp = res_mkupdrec(S_PREREQ, fqdn, C_IN, T_A, 0);
2N/A if (prereqp == NULL) {
2N/A dprint("res_mkupdrec (add A PREREQ) failed\n");
2N/A return (B_FALSE);
2N/A }
2N/A prereqp->r_opcode = NXRRSET;
2N/A prereqp->r_data = NULL;
2N/A prereqp->r_size = 0;
2N/A APPEND(q, prereqp, r_link);
2N/A updreqp = res_mkupdrec(S_UPDATE, fqdn, C_IN, T_A, ttl);
2N/A if (updreqp == NULL) {
2N/A dprint("res_mkupdrec (add A UPDATE) failed\n");
2N/A freeupdrecs(q);
2N/A return (B_FALSE);
2N/A }
2N/A
2N/A (void) inet_ntoa_r(na, ntoab);
2N/A updreqp->r_opcode = ADD;
2N/A updreqp->r_data = (unsigned char *)ntoab;
2N/A updreqp->r_size = strlen(ntoab);
2N/A APPEND(q, updreqp, r_link);
2N/A if (retry_update(resp, HEAD(q)) != 1) {
2N/A dprint("res_nupdate (ADD A) failed - errno %d, h_errno %d\n",
2N/A errno, h_errno);
2N/A freeupdrecs(q);
2N/A return (B_FALSE);
2N/A }
2N/A freeupdrecs(q);
2N/A return (B_TRUE);
2N/A}
2N/A
2N/A/* add a PTR record for this fqdn <-> address pair */
2N/Astatic boolean_t
2N/AaddPTR(struct __res_state *resp, char *fqdn, char *revname)
2N/A{
2N/A ns_updque q;
2N/A ns_updrec *prereqp, *updreqp;
2N/A int ttl = LEASEMIN;
2N/A
2N/A INIT_LIST(q);
2N/A prereqp = res_mkupdrec(S_UPDATE, revname, C_IN, T_PTR, 0);
2N/A if (prereqp == NULL) {
2N/A dprint("res_mkupdrec (add PTR DELETE) failed\n");
2N/A return (B_FALSE);
2N/A }
2N/A prereqp->r_opcode = DELETE;
2N/A prereqp->r_data = NULL;
2N/A prereqp->r_size = 0;
2N/A APPEND(q, prereqp, r_link);
2N/A updreqp = res_mkupdrec(S_UPDATE, revname, C_IN, T_PTR, ttl);
2N/A if (updreqp == NULL) {
2N/A dprint("res_mkupdrec (add PTR ADD) failed\n");
2N/A freeupdrecs(q);
2N/A return (B_FALSE);
2N/A }
2N/A updreqp->r_opcode = ADD;
2N/A updreqp->r_data = (unsigned char *)fqdn;
2N/A updreqp->r_size = strlen(fqdn);
2N/A APPEND(q, updreqp, r_link);
2N/A if (retry_update(resp, HEAD(q)) != 1) {
2N/A dprint("res_nupdate (ADD PTR) failed - errno %d, h_errno %d\n",
2N/A errno, h_errno);
2N/A freeupdrecs(q);
2N/A return (B_FALSE);
2N/A }
2N/A freeupdrecs(q);
2N/A
2N/A return (B_TRUE);
2N/A}
2N/A
2N/A/* retry an update request when appropriate */
2N/Astatic boolean_t
2N/Aretry_update(struct __res_state *resp, ns_updrec *h)
2N/A{
2N/A int retries;
2N/A
2N/A for (retries = 0; retries < MAX_RETRIES; retries++)
2N/A if (res_nupdate(resp, h, NULL) == 1) {
2N/A return (B_TRUE);
2N/A } else {
2N/A /*
2N/A * Look for indicators from libresolv:res_nsend()
2N/A * that we should retry a request.
2N/A */
2N/A if ((errno == ECONNREFUSED) ||
2N/A ((h_errno == TRY_AGAIN) && (errno == ETIMEDOUT))) {
2N/A dprint("retry_update - errno %d, h_errno %d\n",
2N/A errno, h_errno);
2N/A continue;
2N/A } else
2N/A return (B_FALSE);
2N/A }
2N/A
2N/A return (B_FALSE);
2N/A}
2N/A
2N/Astatic void
2N/Afreeupdrecs(ns_updque q)
2N/A{
2N/A while (!EMPTY(q)) {
2N/A ns_updrec *tmp;
2N/A
2N/A tmp = HEAD(q);
2N/A UNLINK(q, tmp, r_link);
2N/A res_freeupdrec(tmp);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Parse a 16-bit quantity from a DNS reply packet.
2N/A */
2N/Astatic unsigned short
2N/Aparse_ushort(const char **pp)
2N/A{
2N/A const uchar_t *p = (const uchar_t *)*pp;
2N/A unsigned short val;
2N/A
2N/A val = (p[0] << 8) | p[1];
2N/A *pp += 2;
2N/A return (val);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Parse a 32-bit quantity from a DNS reply packet.
2N/A */
2N/Astatic unsigned int
2N/Aparse_uint(const char **pp)
2N/A{
2N/A const uchar_t *p = (const uchar_t *)*pp;
2N/A unsigned int val;
2N/A
2N/A val = ((uint_t)p[0] << 24) | ((uint_t)p[1] << 16) |
2N/A ((uint_t)p[2] << 8) | (uint_t)p[3];
2N/A *pp += 4;
2N/A return (val);
2N/A}
2N/A
2N/A/*
2N/A * Clean up a childstat structure's synchronization variables and free
2N/A * the allocated memory.
2N/A */
2N/Astatic void
2N/Achildstat_cleanup(struct childstat *sp)
2N/A{
2N/A (void) cond_destroy(&sp->cv);
2N/A (void) mutex_destroy(&sp->m);
2N/A freehost(sp->hp);
2N/A free(sp);
2N/A}
2N/A
2N/A/*
2N/A * Format and print a debug message, prepending the thread ID of the
2N/A * thread logging the message.
2N/A */
2N/A/* PRINTFLIKE1 */
2N/Astatic void
2N/Adprint(char *format, ...)
2N/A{
2N/A va_list ap;
2N/A
2N/A va_start(ap, format);
2N/A if (debug_fp) {
2N/A (void) fprintf(debug_fp, "%u: ", thr_self());
2N/A (void) vfprintf(debug_fp, format, ap);
2N/A va_end(ap);
2N/A }
2N/A}
2N/A
2N/Astatic void
2N/Afreehost(struct hostent *hp)
2N/A{
2N/A free(hp->h_addr);
2N/A free(hp->h_addr_list);
2N/A free(hp->h_name);
2N/A free(hp);
2N/A}