2N/A/*
2N/A * Copyright (c) 1989, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A
2N/A/*
2N/A * Portions Copyright (C) 2004-2009 Internet Systems Consortium, Inc. ("ISC")
2N/A * Portions Copyright (C) 1996-2003 Internet Software Consortium.
2N/A *
2N/A * Permission to use, copy, modify, and/or distribute this software for any
2N/A * purpose with or without fee is hereby granted, provided that the above
2N/A * copyright notice and this permission notice appear in all copies.
2N/A *
2N/A * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
2N/A * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
2N/A * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
2N/A * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
2N/A * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
2N/A * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
2N/A * PERFORMANCE OF THIS SOFTWARE.
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 1985, 1989, 1993
2N/A * The Regents of the University of California. All rights reserved.
2N/A *
2N/A * Redistribution and use in source and binary forms, with or without
2N/A * modification, are permitted provided that the following conditions
2N/A * are met:
2N/A * 1. Redistributions of source code must retain the above copyright
2N/A * notice, this list of conditions and the following disclaimer.
2N/A * 2. Redistributions in binary form must reproduce the above copyright
2N/A * notice, this list of conditions and the following disclaimer in the
2N/A * documentation and/or other materials provided with the distribution.
2N/A * 3. All advertising materials mentioning features or use of this software
2N/A * must display the following acknowledgement:
2N/A * This product includes software developed by the University of
2N/A * California, Berkeley and its contributors.
2N/A * 4. Neither the name of the University nor the names of its contributors
2N/A * may be used to endorse or promote products derived from this software
2N/A * without specific prior written permission.
2N/A *
2N/A * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2N/A * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2N/A * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2N/A * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2N/A * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2N/A * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2N/A * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2N/A * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2N/A * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2N/A * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2N/A * SUCH DAMAGE.
2N/A */
2N/A
2N/A/*
2N/A * Portions Copyright (c) 1993 by Digital Equipment Corporation.
2N/A *
2N/A * Permission to use, copy, modify, and distribute this software for any
2N/A * purpose with or without fee is hereby granted, provided that the above
2N/A * copyright notice and this permission notice appear in all copies, and that
2N/A * the name of Digital Equipment Corporation not be used in advertising or
2N/A * publicity pertaining to distribution of the document or software without
2N/A * specific, written prior permission.
2N/A *
2N/A * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
2N/A * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
2N/A * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
2N/A * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
2N/A * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
2N/A * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
2N/A * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
2N/A * SOFTWARE.
2N/A */
2N/A
2N/A#if defined(LIBC_SCCS) && !defined(lint)
2N/Astatic const char sccsid[] = "@(#)res_send.c 8.1 (Berkeley) 6/4/93";
2N/Astatic const char rcsid[] = "$Id: res_send.c,v 1.22 2009/01/22 23:49:23 tbox Exp $";
2N/A#endif /* LIBC_SCCS and not lint */
2N/A
2N/A/*! \file
2N/A * \brief
2N/A * Send query to name server and wait for reply.
2N/A */
2N/A
2N/A#include "port_before.h"
2N/A#include "fd_setsize.h"
2N/A
2N/A#include <sys/types.h>
2N/A#include <sys/param.h>
2N/A#include <sys/time.h>
2N/A#include <sys/socket.h>
2N/A#include <sys/uio.h>
2N/A
2N/A#include <netinet/in.h>
2N/A#include <arpa/nameser.h>
2N/A#include <arpa/inet.h>
2N/A
2N/A#include <errno.h>
2N/A#include <netdb.h>
2N/A#include <resolv.h>
2N/A#include <signal.h>
2N/A#include <stdio.h>
2N/A#include <stdlib.h>
2N/A#include <string.h>
2N/A#include <unistd.h>
2N/A
2N/A#include <isc/eventlib.h>
2N/A
2N/A#include "port_after.h"
2N/A
2N/A#ifdef USE_POLL
2N/A#ifdef HAVE_STROPTS_H
2N/A#include <stropts.h>
2N/A#endif
2N/A#include <poll.h>
2N/A#endif /* USE_POLL */
2N/A
2N/A/* Options. Leave them on. */
2N/A#define DEBUG
2N/A#include "res_debug.h"
2N/A#include "res_private.h"
2N/A
2N/A#define EXT(res) ((res)->_u._ext)
2N/A
2N/A#ifndef USE_POLL
2N/Astatic const int highestFD = FD_SETSIZE - 1;
2N/A#else
2N/Astatic int highestFD = 0;
2N/A#endif
2N/A
2N/A/* Forward. */
2N/A
2N/Astatic int get_salen __P((const struct sockaddr *));
2N/Astatic struct sockaddr * get_nsaddr __P((res_state, size_t));
2N/Astatic int send_vc(res_state, const u_char *, int,
2N/A u_char *, int, int *, int);
2N/Astatic int send_dg(res_state, const u_char *, int,
2N/A u_char *, int, int *, int, int,
2N/A int *, int *);
2N/Astatic void Aerror(const res_state, FILE *, const char *, int,
2N/A const struct sockaddr *, int);
2N/Astatic void Perror(const res_state, FILE *, const char *, int);
2N/Astatic int sock_eq(struct sockaddr *, struct sockaddr *);
2N/A#if defined(NEED_PSELECT) && !defined(USE_POLL)
2N/Astatic int pselect(int, void *, void *, void *,
2N/A struct timespec *,
2N/A const sigset_t *);
2N/A#endif
2N/Avoid res_pquery(const res_state, const u_char *, int, FILE *);
2N/A
2N/A#ifndef ORIGINAL_ISC_CODE
2N/A#pragma weak __res_nameinquery = res_nameinquery
2N/A#pragma weak __res_queriesmatch = res_queriesmatch
2N/A#pragma weak res_nisourserver = res_ourserver_p
2N/A#endif /* ORIGINAL_ISC_CODE */
2N/A
2N/Astatic const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
2N/A
2N/A#ifdef SUNW_SUBSECOND_TIME
2N/A#ifndef BILLION
2N/A#define BILLION 1000000000
2N/A#endif
2N/A#endif /* SUNW_SUBSECOND_TIME */
2N/A
2N/A/* Public. */
2N/A
2N/A/*%
2N/A * looks up "ina" in _res.ns_addr_list[]
2N/A *
2N/A * returns:
2N/A *\li 0 : not found
2N/A *\li >0 : found
2N/A *
2N/A * author:
2N/A *\li paul vixie, 29may94
2N/A */
2N/Aint
2N/Ares_ourserver_p(const res_state statp, const struct sockaddr *sa) {
2N/A const struct sockaddr_in *inp, *srv;
2N/A const struct sockaddr_in6 *in6p, *srv6;
2N/A int ns;
2N/A
2N/A switch (sa->sa_family) {
2N/A case AF_INET:
2N/A inp = (const struct sockaddr_in *)sa;
2N/A for (ns = 0; ns < statp->nscount; ns++) {
2N/A srv = (struct sockaddr_in *)get_nsaddr(statp, ns);
2N/A if (srv->sin_family == inp->sin_family &&
2N/A srv->sin_port == inp->sin_port &&
2N/A (srv->sin_addr.s_addr == INADDR_ANY ||
2N/A srv->sin_addr.s_addr == inp->sin_addr.s_addr))
2N/A return (1);
2N/A }
2N/A break;
2N/A case AF_INET6:
2N/A if (EXT(statp).ext == NULL)
2N/A break;
2N/A in6p = (const struct sockaddr_in6 *)sa;
2N/A for (ns = 0; ns < statp->nscount; ns++) {
2N/A srv6 = (struct sockaddr_in6 *)get_nsaddr(statp, ns);
2N/A if (srv6->sin6_family == in6p->sin6_family &&
2N/A srv6->sin6_port == in6p->sin6_port &&
2N/A#ifdef HAVE_SIN6_SCOPE_ID
2N/A (srv6->sin6_scope_id == 0 ||
2N/A srv6->sin6_scope_id == in6p->sin6_scope_id) &&
2N/A#endif
2N/A (IN6_IS_ADDR_UNSPECIFIED(&srv6->sin6_addr) ||
2N/A IN6_ARE_ADDR_EQUAL(&srv6->sin6_addr, &in6p->sin6_addr)))
2N/A return (1);
2N/A }
2N/A break;
2N/A default:
2N/A break;
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/A/*%
2N/A * look for (name,type,class) in the query section of packet (buf,eom)
2N/A *
2N/A * requires:
2N/A *\li buf + HFIXEDSZ <= eom
2N/A *
2N/A * returns:
2N/A *\li -1 : format error
2N/A *\li 0 : not found
2N/A *\li >0 : found
2N/A *
2N/A * author:
2N/A *\li paul vixie, 29may94
2N/A */
2N/Aint
2N/Ares_nameinquery(const char *name, int type, int class,
2N/A const u_char *buf, const u_char *eom)
2N/A{
2N/A const u_char *cp = buf + HFIXEDSZ;
2N/A int qdcount = ntohs(((const HEADER*)buf)->qdcount);
2N/A
2N/A while (qdcount-- > 0) {
2N/A char tname[MAXDNAME+1];
2N/A int n, ttype, tclass;
2N/A
2N/A n = dn_expand(buf, eom, cp, tname, sizeof tname);
2N/A if (n < 0)
2N/A return (-1);
2N/A cp += n;
2N/A if (cp + 2 * INT16SZ > eom)
2N/A return (-1);
2N/A ttype = ns_get16(cp); cp += INT16SZ;
2N/A tclass = ns_get16(cp); cp += INT16SZ;
2N/A if (ttype == type && tclass == class &&
2N/A ns_samename(tname, name) == 1)
2N/A return (1);
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/A/*%
2N/A * is there a 1:1 mapping of (name,type,class)
2N/A * in (buf1,eom1) and (buf2,eom2)?
2N/A *
2N/A * returns:
2N/A *\li -1 : format error
2N/A *\li 0 : not a 1:1 mapping
2N/A *\li >0 : is a 1:1 mapping
2N/A *
2N/A * author:
2N/A *\li paul vixie, 29may94
2N/A */
2N/Aint
2N/Ares_queriesmatch(const u_char *buf1, const u_char *eom1,
2N/A const u_char *buf2, const u_char *eom2)
2N/A{
2N/A const u_char *cp = buf1 + HFIXEDSZ;
2N/A int qdcount = ntohs(((const HEADER*)buf1)->qdcount);
2N/A
2N/A if (buf1 + HFIXEDSZ > eom1 || buf2 + HFIXEDSZ > eom2)
2N/A return (-1);
2N/A
2N/A /*
2N/A * Only header section present in replies to
2N/A * dynamic update packets.
2N/A */
2N/A if ((((const HEADER *)buf1)->opcode == ns_o_update) &&
2N/A (((const HEADER *)buf2)->opcode == ns_o_update))
2N/A return (1);
2N/A
2N/A if (qdcount != ntohs(((const HEADER*)buf2)->qdcount))
2N/A return (0);
2N/A while (qdcount-- > 0) {
2N/A char tname[MAXDNAME+1];
2N/A int n, ttype, tclass;
2N/A
2N/A n = dn_expand(buf1, eom1, cp, tname, sizeof tname);
2N/A if (n < 0)
2N/A return (-1);
2N/A cp += n;
2N/A if (cp + 2 * INT16SZ > eom1)
2N/A return (-1);
2N/A ttype = ns_get16(cp); cp += INT16SZ;
2N/A tclass = ns_get16(cp); cp += INT16SZ;
2N/A if (!res_nameinquery(tname, ttype, tclass, buf2, eom2))
2N/A return (0);
2N/A }
2N/A return (1);
2N/A}
2N/A
2N/Aint
2N/Ares_nsend(res_state statp,
2N/A const u_char *buf, int buflen, u_char *ans, int anssiz)
2N/A{
2N/A int gotsomewhere, terrno, tries, v_circuit, resplen, ns, n;
2N/A char abuf[NI_MAXHOST];
2N/A#ifdef SUNW_DEFER_ON_FAIL
2N/A res_defer_state_t defer_state;
2N/A boolean_t defer_is_allowed;
2N/A boolean_t query_is_sent;
2N/A#endif /* SUNW_DEFER_ON_FAIL */
2N/A
2N/A#ifdef USE_POLL
2N/A highestFD = sysconf(_SC_OPEN_MAX) - 1;
2N/A#endif
2N/A
2N/A /* No name servers or res_init() failure */
2N/A if (statp->nscount == 0 || EXT(statp).ext == NULL) {
2N/A errno = ESRCH;
2N/A return (-1);
2N/A }
2N/A if (anssiz < HFIXEDSZ) {
2N/A errno = EINVAL;
2N/A return (-1);
2N/A }
2N/A DprintQ((statp->options & RES_DEBUG) || (statp->pfcode & RES_PRF_QUERY),
2N/A (stdout, ";; res_send()\n"), buf, buflen);
2N/A v_circuit = (statp->options & RES_USEVC) || buflen > PACKETSZ;
2N/A gotsomewhere = 0;
2N/A terrno = ETIMEDOUT;
2N/A
2N/A /*
2N/A * If the ns_addr_list in the resolver context has changed, then
2N/A * invalidate our cached copy and the associated timing data.
2N/A */
2N/A if (EXT(statp).nscount != 0) {
2N/A int needclose = 0;
2N/A struct sockaddr_storage peer;
2N/A ISC_SOCKLEN_T peerlen;
2N/A
2N/A if (EXT(statp).nscount != statp->nscount)
2N/A needclose++;
2N/A else
2N/A for (ns = 0; ns < statp->nscount; ns++) {
2N/A if (statp->nsaddr_list[ns].sin_family &&
2N/A !sock_eq((struct sockaddr *)&statp->nsaddr_list[ns],
2N/A (struct sockaddr *)&EXT(statp).ext->nsaddrs[ns])) {
2N/A needclose++;
2N/A break;
2N/A }
2N/A
2N/A if (EXT(statp).nssocks[ns] == -1)
2N/A continue;
2N/A peerlen = sizeof(peer);
2N/A if (getpeername(EXT(statp).nssocks[ns],
2N/A (struct sockaddr *)&peer, &peerlen) < 0) {
2N/A needclose++;
2N/A break;
2N/A }
2N/A if (!sock_eq((struct sockaddr *)&peer,
2N/A get_nsaddr(statp, ns))) {
2N/A needclose++;
2N/A break;
2N/A }
2N/A }
2N/A if (needclose) {
2N/A res_nclose(statp);
2N/A EXT(statp).nscount = 0;
2N/A }
2N/A }
2N/A
2N/A#ifdef SUNW_SUBSECOND_TIME
2N/A /*
2N/A * If the retrans time in the public portion of the resolver context
2N/A * (an integer) has changed, update the corresponding retrans time
2N/A * in the private area of the resolver context (a struct timespec).
2N/A */
2N/A if (EXT(statp).ext != NULL)
2N/A if (EXT(statp).ext->retrans.tv_sec != statp->retrans)
2N/A EXT(statp).ext->retrans = evConsTime(statp->retrans, 0);
2N/A#endif /* SUNW_SUBSECOND_TIME */
2N/A
2N/A /*
2N/A * Maybe initialize our private copy of the ns_addr_list.
2N/A */
2N/A if (EXT(statp).nscount == 0) {
2N/A for (ns = 0; ns < statp->nscount; ns++) {
2N/A EXT(statp).nstimes[ns] = RES_MAXTIME;
2N/A EXT(statp).nssocks[ns] = -1;
2N/A if (!statp->nsaddr_list[ns].sin_family)
2N/A continue;
2N/A EXT(statp).ext->nsaddrs[ns].sin =
2N/A statp->nsaddr_list[ns];
2N/A#ifdef SUNW_DEFER_ON_FAIL
2N/A res_set_defer_state(statp, ns, RES_DEFER_ONLINE);
2N/A#endif /* SUNW_DEFER_ON_FAIL */
2N/A }
2N/A EXT(statp).nscount = statp->nscount;
2N/A }
2N/A
2N/A /*
2N/A * Some resolvers want to even out the load on their nameservers.
2N/A * Note that RES_BLAST overrides RES_ROTATE.
2N/A */
2N/A if ((statp->options & RES_ROTATE) != 0U &&
2N/A (statp->options & RES_BLAST) == 0U) {
2N/A union res_sockaddr_union inu;
2N/A struct sockaddr_in ina;
2N/A int lastns = statp->nscount - 1;
2N/A int fd;
2N/A u_int16_t nstime;
2N/A#ifdef SUNW_DEFER_ON_FAIL
2N/A struct timespec nsdefer;
2N/A#endif /* SUNW_DEFER_ON_FAIL */
2N/A
2N/A if (EXT(statp).ext != NULL) {
2N/A inu = EXT(statp).ext->nsaddrs[0];
2N/A#ifdef SUNW_DEFER_ON_FAIL
2N/A nsdefer = EXT(statp).ext->nsdefer[0];
2N/A#endif /* SUNW_DEFER_ON_FAIL */
2N/A }
2N/A ina = statp->nsaddr_list[0];
2N/A fd = EXT(statp).nssocks[0];
2N/A nstime = EXT(statp).nstimes[0];
2N/A for (ns = 0; ns < lastns; ns++) {
2N/A if (EXT(statp).ext != NULL) {
2N/A EXT(statp).ext->nsaddrs[ns] =
2N/A EXT(statp).ext->nsaddrs[ns + 1];
2N/A#ifdef SUNW_DEFER_ON_FAIL
2N/A EXT(statp).ext->nsdefer[ns] =
2N/A EXT(statp).ext->nsdefer[ns + 1];
2N/A#endif /* SUNW_DEFER_ON_FAIL */
2N/A }
2N/A statp->nsaddr_list[ns] = statp->nsaddr_list[ns + 1];
2N/A EXT(statp).nssocks[ns] = EXT(statp).nssocks[ns + 1];
2N/A EXT(statp).nstimes[ns] = EXT(statp).nstimes[ns + 1];
2N/A }
2N/A if (EXT(statp).ext != NULL) {
2N/A EXT(statp).ext->nsaddrs[lastns] = inu;
2N/A#ifdef SUNW_DEFER_ON_FAIL
2N/A EXT(statp).ext->nsdefer[lastns] = nsdefer;
2N/A#endif /* SUNW_DEFER_ON_FAIL */
2N/A }
2N/A statp->nsaddr_list[lastns] = ina;
2N/A EXT(statp).nssocks[lastns] = fd;
2N/A EXT(statp).nstimes[lastns] = nstime;
2N/A }
2N/A
2N/A /*
2N/A * Send request, RETRY times, or until successful.
2N/A */
2N/A#ifdef SUNW_DEFER_ON_FAIL
2N/A defer_is_allowed = B_TRUE;
2N/A query_is_sent = B_FALSE;
2N/A#endif /* SUNW_DEFER_ON_FAIL */
2N/A for (tries = 0; tries < statp->retry; tries++) {
2N/A for (ns = 0; ns < statp->nscount; ns++) {
2N/A struct sockaddr *nsap;
2N/A int nsaplen;
2N/A nsap = get_nsaddr(statp, ns);
2N/A nsaplen = get_salen(nsap);
2N/A#ifdef SUNW_DEFER_ON_FAIL
2N/A if (tries == 0 && defer_is_allowed) {
2N/A res_get_defer_state(statp, ns, &defer_state);
2N/A if (defer_state == RES_DEFER_OFFLINE) {
2N/A Dprint(((statp->options & RES_DEBUG) &&
2N/A getnameinfo(nsap, nsaplen, abuf,
2N/A sizeof(abuf), NULL, 0, niflags) == 0),
2N/A (stdout,
2N/A ";; Deferred server (# %d) address = %s\n",
2N/A ns + 1, abuf));
2N/A continue;
2N/A }
2N/A }
2N/A /*
2N/A * Although the query has not yet actually been sent,
2N/A * the following assignment is made here, because:
2N/A * 1) The query will be attempted below, before this boolean
2N/A * is next referenced, unless the qhook returns res_nextns.
2N/A * 2) Even if the qhook returns res_nextns, the server should
2N/A * still be considered to have been queried, so that this
2N/A * condition cannot cause the qhook to run a second time
2N/A * on this server when statp->retry == 1.
2N/A */
2N/A query_is_sent = B_TRUE;
2N/A#endif /* SUNW_DEFER_ON_FAIL */
2N/A statp->_flags &= ~RES_F_LASTMASK;
2N/A statp->_flags |= (ns << RES_F_LASTSHIFT);
2N/A same_ns:
2N/A if (statp->qhook) {
2N/A int done = 0, loops = 0;
2N/A
2N/A do {
2N/A res_sendhookact act;
2N/A
2N/A act = (*statp->qhook)(&nsap, &buf, &buflen,
2N/A ans, anssiz, &resplen);
2N/A switch (act) {
2N/A case res_goahead:
2N/A done = 1;
2N/A break;
2N/A case res_nextns:
2N/A res_nclose(statp);
2N/A goto next_ns;
2N/A case res_done:
2N/A return (resplen);
2N/A case res_modified:
2N/A /* give the hook another try */
2N/A if (++loops < 42) /*doug adams*/
2N/A break;
2N/A /*FALLTHROUGH*/
2N/A case res_error:
2N/A /*FALLTHROUGH*/
2N/A default:
2N/A goto fail;
2N/A }
2N/A } while (!done);
2N/A }
2N/A
2N/A Dprint(((statp->options & RES_DEBUG) &&
2N/A getnameinfo(nsap, nsaplen, abuf, sizeof(abuf),
2N/A NULL, 0, niflags) == 0),
2N/A (stdout, ";; Querying server (# %d) address = %s\n",
2N/A ns + 1, abuf));
2N/A
2N/A
2N/A if (v_circuit) {
2N/A /* Use VC; at most one attempt per server. */
2N/A tries = statp->retry;
2N/A n = send_vc(statp, buf, buflen, ans, anssiz, &terrno,
2N/A ns);
2N/A#ifdef SUNW_DEFER_ON_FAIL
2N/A res_set_defer_state(statp, ns,
2N/A n == 0 ? RES_DEFER_OFFLINE : RES_DEFER_ONLINE);
2N/A#endif /* SUNW_DEFER_ON_FAIL */
2N/A if (n < 0)
2N/A goto fail;
2N/A if (n == 0)
2N/A goto next_ns;
2N/A resplen = n;
2N/A } else {
2N/A /* Use datagrams. */
2N/A n = send_dg(statp, buf, buflen, ans, anssiz, &terrno,
2N/A ns, tries, &v_circuit, &gotsomewhere);
2N/A#ifdef SUNW_DEFER_ON_FAIL
2N/A res_set_defer_state(statp, ns,
2N/A n == 0 ? RES_DEFER_OFFLINE : RES_DEFER_ONLINE);
2N/A#endif /* SUNW_DEFER_ON_FAIL */
2N/A if (n < 0)
2N/A goto fail;
2N/A if (n == 0)
2N/A goto next_ns;
2N/A if (v_circuit)
2N/A goto same_ns;
2N/A resplen = n;
2N/A }
2N/A
2N/A Dprint((statp->options & RES_DEBUG) ||
2N/A ((statp->pfcode & RES_PRF_REPLY) &&
2N/A (statp->pfcode & RES_PRF_HEAD1)),
2N/A (stdout, ";; got answer:\n"));
2N/A
2N/A DprintQ((statp->options & RES_DEBUG) ||
2N/A (statp->pfcode & RES_PRF_REPLY),
2N/A (stdout, "%s", ""),
2N/A ans, (resplen > anssiz) ? anssiz : resplen);
2N/A
2N/A /*
2N/A * If we have temporarily opened a virtual circuit,
2N/A * or if we haven't been asked to keep a socket open,
2N/A * close the socket.
2N/A */
2N/A if ((v_circuit && (statp->options & RES_USEVC) == 0U) ||
2N/A (statp->options & RES_STAYOPEN) == 0U) {
2N/A res_nclose(statp);
2N/A }
2N/A if (statp->rhook) {
2N/A int done = 0, loops = 0;
2N/A
2N/A do {
2N/A res_sendhookact act;
2N/A
2N/A act = (*statp->rhook)(nsap, buf, buflen,
2N/A ans, anssiz, &resplen);
2N/A switch (act) {
2N/A case res_goahead:
2N/A case res_done:
2N/A done = 1;
2N/A break;
2N/A case res_nextns:
2N/A res_nclose(statp);
2N/A goto next_ns;
2N/A case res_modified:
2N/A /* give the hook another try */
2N/A if (++loops < 42) /*doug adams*/
2N/A break;
2N/A /*FALLTHROUGH*/
2N/A case res_error:
2N/A /*FALLTHROUGH*/
2N/A default:
2N/A goto fail;
2N/A }
2N/A } while (!done);
2N/A
2N/A }
2N/A return (resplen);
2N/A next_ns: ;
2N/A } /*foreach ns*/
2N/A#ifdef SUNW_DEFER_ON_FAIL
2N/A defer_is_allowed = B_FALSE;
2N/A if (tries == 0 && statp->retry == 1 && !query_is_sent) {
2N/A Dprint((statp->options & RES_DEBUG), (stdout,
2N/A ";; All servers deferred, forcing retry\n"));
2N/A tries--;
2N/A }
2N/A#endif /* SUNW_DEFER_ON_FAIL */
2N/A } /*foreach retry*/
2N/A res_nclose(statp);
2N/A if (!v_circuit) {
2N/A if (!gotsomewhere)
2N/A errno = ECONNREFUSED; /*%< no nameservers found */
2N/A else
2N/A errno = ETIMEDOUT; /*%< no answer obtained */
2N/A } else
2N/A errno = terrno;
2N/A return (-1);
2N/A fail:
2N/A res_nclose(statp);
2N/A return (-1);
2N/A}
2N/A
2N/A/* Private */
2N/A
2N/Astatic int
2N/Aget_salen(sa)
2N/A const struct sockaddr *sa;
2N/A{
2N/A
2N/A#ifdef HAVE_SA_LEN
2N/A /* There are people do not set sa_len. Be forgiving to them. */
2N/A if (sa->sa_len)
2N/A return (sa->sa_len);
2N/A#endif
2N/A
2N/A if (sa->sa_family == AF_INET)
2N/A return (sizeof(struct sockaddr_in));
2N/A else if (sa->sa_family == AF_INET6)
2N/A return (sizeof(struct sockaddr_in6));
2N/A else
2N/A return (0); /*%< unknown, die on connect */
2N/A}
2N/A
2N/A/*%
2N/A * pick appropriate nsaddr_list for use. see res_init() for initialization.
2N/A */
2N/Astatic struct sockaddr *
2N/Aget_nsaddr(statp, n)
2N/A res_state statp;
2N/A size_t n;
2N/A{
2N/A
2N/A if (!statp->nsaddr_list[n].sin_family && EXT(statp).ext) {
2N/A /*
2N/A * - EXT(statp).ext->nsaddrs[n] holds an address that is larger
2N/A * than struct sockaddr, and
2N/A * - user code did not update statp->nsaddr_list[n].
2N/A */
2N/A return (struct sockaddr *)(void *)&EXT(statp).ext->nsaddrs[n];
2N/A } else {
2N/A /*
2N/A * - user code updated statp->nsaddr_list[n], or
2N/A * - statp->nsaddr_list[n] has the same content as
2N/A * EXT(statp).ext->nsaddrs[n].
2N/A */
2N/A return (struct sockaddr *)(void *)&statp->nsaddr_list[n];
2N/A }
2N/A}
2N/A
2N/Astatic int
2N/Asend_vc(res_state statp,
2N/A const u_char *buf, int buflen, u_char *ans, int anssiz,
2N/A int *terrno, int ns)
2N/A{
2N/A const HEADER *hp = (const HEADER *) buf;
2N/A HEADER *anhp = (HEADER *) ans;
2N/A struct sockaddr *nsap;
2N/A int nsaplen;
2N/A int truncating, connreset, resplen, n;
2N/A struct iovec iov[2];
2N/A u_short len;
2N/A u_char *cp;
2N/A void *tmp;
2N/A#ifdef SO_NOSIGPIPE
2N/A int on = 1;
2N/A#endif
2N/A
2N/A nsap = get_nsaddr(statp, ns);
2N/A nsaplen = get_salen(nsap);
2N/A
2N/A connreset = 0;
2N/A same_ns:
2N/A truncating = 0;
2N/A
2N/A /* Are we still talking to whom we want to talk to? */
2N/A if (statp->_vcsock >= 0 && (statp->_flags & RES_F_VC) != 0) {
2N/A struct sockaddr_storage peer;
2N/A ISC_SOCKLEN_T size = sizeof peer;
2N/A
2N/A if (getpeername(statp->_vcsock,
2N/A (struct sockaddr *)&peer, &size) < 0 ||
2N/A !sock_eq((struct sockaddr *)&peer, nsap)) {
2N/A res_nclose(statp);
2N/A statp->_flags &= ~RES_F_VC;
2N/A }
2N/A }
2N/A
2N/A if (statp->_vcsock < 0 || (statp->_flags & RES_F_VC) == 0) {
2N/A if (statp->_vcsock >= 0)
2N/A res_nclose(statp);
2N/A
2N/A statp->_vcsock = socket(nsap->sa_family, SOCK_STREAM, 0);
2N/A if (statp->_vcsock > highestFD) {
2N/A res_nclose(statp);
2N/A errno = ENOTSOCK;
2N/A }
2N/A if (statp->_vcsock < 0) {
2N/A switch (errno) {
2N/A case EPROTONOSUPPORT:
2N/A#ifdef EPFNOSUPPORT
2N/A case EPFNOSUPPORT:
2N/A#endif
2N/A case EAFNOSUPPORT:
2N/A Perror(statp, stderr, "socket(vc)", errno);
2N/A return (0);
2N/A default:
2N/A *terrno = errno;
2N/A Perror(statp, stderr, "socket(vc)", errno);
2N/A return (-1);
2N/A }
2N/A }
2N/A#ifdef SO_NOSIGPIPE
2N/A /*
2N/A * Disable generation of SIGPIPE when writing to a closed
2N/A * socket. Write should return -1 and set errno to EPIPE
2N/A * instead.
2N/A *
2N/A * Push on even if setsockopt(SO_NOSIGPIPE) fails.
2N/A */
2N/A (void)setsockopt(statp->_vcsock, SOL_SOCKET, SO_NOSIGPIPE, &on,
2N/A sizeof(on));
2N/A#endif
2N/A errno = 0;
2N/A if (connect(statp->_vcsock, nsap, nsaplen) < 0) {
2N/A *terrno = errno;
2N/A Aerror(statp, stderr, "connect/vc", errno, nsap,
2N/A nsaplen);
2N/A res_nclose(statp);
2N/A return (0);
2N/A }
2N/A statp->_flags |= RES_F_VC;
2N/A }
2N/A
2N/A /*
2N/A * Send length & message
2N/A */
2N/A ns_put16((u_short)buflen, (u_char*)&len);
2N/A iov[0] = evConsIovec(&len, INT16SZ);
2N/A DE_CONST(buf, tmp);
2N/A iov[1] = evConsIovec(tmp, buflen);
2N/A if (writev(statp->_vcsock, iov, 2) != (INT16SZ + buflen)) {
2N/A *terrno = errno;
2N/A Perror(statp, stderr, "write failed", errno);
2N/A res_nclose(statp);
2N/A return (0);
2N/A }
2N/A /*
2N/A * Receive length & response
2N/A */
2N/A read_len:
2N/A cp = ans;
2N/A len = INT16SZ;
2N/A while ((n = read(statp->_vcsock, (char *)cp, (int)len)) > 0) {
2N/A cp += n;
2N/A if ((len -= n) == 0)
2N/A break;
2N/A }
2N/A if (n <= 0) {
2N/A *terrno = errno;
2N/A Perror(statp, stderr, "read failed", errno);
2N/A res_nclose(statp);
2N/A /*
2N/A * A long running process might get its TCP
2N/A * connection reset if the remote server was
2N/A * restarted. Requery the server instead of
2N/A * trying a new one. When there is only one
2N/A * server, this means that a query might work
2N/A * instead of failing. We only allow one reset
2N/A * per query to prevent looping.
2N/A */
2N/A if (*terrno == ECONNRESET && !connreset) {
2N/A connreset = 1;
2N/A res_nclose(statp);
2N/A goto same_ns;
2N/A }
2N/A res_nclose(statp);
2N/A return (0);
2N/A }
2N/A resplen = ns_get16(ans);
2N/A if (resplen > anssiz) {
2N/A Dprint(statp->options & RES_DEBUG,
2N/A (stdout, ";; response truncated\n")
2N/A );
2N/A truncating = 1;
2N/A len = anssiz;
2N/A } else
2N/A len = resplen;
2N/A if (len < HFIXEDSZ) {
2N/A /*
2N/A * Undersized message.
2N/A */
2N/A Dprint(statp->options & RES_DEBUG,
2N/A (stdout, ";; undersized: %d\n", len));
2N/A *terrno = EMSGSIZE;
2N/A res_nclose(statp);
2N/A return (0);
2N/A }
2N/A cp = ans;
2N/A while (len != 0 && (n = read(statp->_vcsock, (char *)cp, (int)len)) > 0){
2N/A cp += n;
2N/A len -= n;
2N/A }
2N/A if (n <= 0) {
2N/A *terrno = errno;
2N/A Perror(statp, stderr, "read(vc)", errno);
2N/A res_nclose(statp);
2N/A return (0);
2N/A }
2N/A if (truncating) {
2N/A /*
2N/A * Flush rest of answer so connection stays in synch.
2N/A */
2N/A anhp->tc = 1;
2N/A len = resplen - anssiz;
2N/A while (len != 0) {
2N/A char junk[PACKETSZ];
2N/A
2N/A n = read(statp->_vcsock, junk,
2N/A (len > sizeof junk) ? sizeof junk : len);
2N/A if (n > 0)
2N/A len -= n;
2N/A else
2N/A break;
2N/A }
2N/A }
2N/A /*
2N/A * If the calling applicating has bailed out of
2N/A * a previous call and failed to arrange to have
2N/A * the circuit closed or the server has got
2N/A * itself confused, then drop the packet and
2N/A * wait for the correct one.
2N/A */
2N/A if (hp->id != anhp->id) {
2N/A DprintQ((statp->options & RES_DEBUG) ||
2N/A (statp->pfcode & RES_PRF_REPLY),
2N/A (stdout, ";; old answer (unexpected):\n"),
2N/A ans, (resplen > anssiz) ? anssiz: resplen);
2N/A goto read_len;
2N/A }
2N/A
2N/A /*
2N/A * All is well, or the error is fatal. Signal that the
2N/A * next nameserver ought not be tried.
2N/A */
2N/A return (resplen);
2N/A}
2N/A
2N/Astatic int
2N/Asend_dg(res_state statp, const u_char *buf, int buflen, u_char *ans,
2N/A int anssiz, int *terrno, int ns, int tries, int *v_circuit,
2N/A int *gotsomewhere)
2N/A{
2N/A const HEADER *hp = (const HEADER *) buf;
2N/A HEADER *anhp = (HEADER *) ans;
2N/A const struct sockaddr *nsap;
2N/A int nsaplen;
2N/A struct timespec now, timeout, finish;
2N/A struct sockaddr_storage from;
2N/A ISC_SOCKLEN_T fromlen;
2N/A int resplen, seconds, n, s;
2N/A#ifdef SUNW_SUBSECOND_TIME
2N/A long long nanoseconds;
2N/A#endif /* SUNW_SUBSECOND_TIME */
2N/A#ifdef USE_POLL
2N/A int polltimeout;
2N/A struct pollfd pollfd;
2N/A#else
2N/A fd_set dsmask;
2N/A#endif
2N/A
2N/A nsap = get_nsaddr(statp, ns);
2N/A nsaplen = get_salen(nsap);
2N/A if (EXT(statp).nssocks[ns] == -1) {
2N/A EXT(statp).nssocks[ns] = socket(nsap->sa_family, SOCK_DGRAM, 0);
2N/A if (EXT(statp).nssocks[ns] > highestFD) {
2N/A res_nclose(statp);
2N/A errno = ENOTSOCK;
2N/A }
2N/A if (EXT(statp).nssocks[ns] < 0) {
2N/A switch (errno) {
2N/A case EPROTONOSUPPORT:
2N/A#ifdef EPFNOSUPPORT
2N/A case EPFNOSUPPORT:
2N/A#endif
2N/A case EAFNOSUPPORT:
2N/A Perror(statp, stderr, "socket(dg)", errno);
2N/A return (0);
2N/A default:
2N/A *terrno = errno;
2N/A Perror(statp, stderr, "socket(dg)", errno);
2N/A return (-1);
2N/A }
2N/A }
2N/A#ifndef CANNOT_CONNECT_DGRAM
2N/A /*
2N/A * On a 4.3BSD+ machine (client and server,
2N/A * actually), sending to a nameserver datagram
2N/A * port with no nameserver will cause an
2N/A * ICMP port unreachable message to be returned.
2N/A * If our datagram socket is "connected" to the
2N/A * server, we get an ECONNREFUSED error on the next
2N/A * socket operation, and select returns if the
2N/A * error message is received. We can thus detect
2N/A * the absence of a nameserver without timing out.
2N/A */
2N/A if (connect(EXT(statp).nssocks[ns], nsap, nsaplen) < 0) {
2N/A Aerror(statp, stderr, "connect(dg)", errno, nsap,
2N/A nsaplen);
2N/A res_nclose(statp);
2N/A return (0);
2N/A }
2N/A#endif /* !CANNOT_CONNECT_DGRAM */
2N/A Dprint(statp->options & RES_DEBUG,
2N/A (stdout, ";; new DG socket\n"))
2N/A }
2N/A s = EXT(statp).nssocks[ns];
2N/A#ifndef CANNOT_CONNECT_DGRAM
2N/A if (send(s, (const char*)buf, buflen, 0) != buflen) {
2N/A Perror(statp, stderr, "send", errno);
2N/A res_nclose(statp);
2N/A return (0);
2N/A }
2N/A#else /* !CANNOT_CONNECT_DGRAM */
2N/A if (sendto(s, (const char*)buf, buflen, 0, nsap, nsaplen) != buflen)
2N/A {
2N/A Aerror(statp, stderr, "sendto", errno, nsap, nsaplen);
2N/A res_nclose(statp);
2N/A return (0);
2N/A }
2N/A#endif /* !CANNOT_CONNECT_DGRAM */
2N/A
2N/A /*
2N/A * Wait for reply.
2N/A */
2N/A#ifdef SUNW_SUBSECOND_TIME
2N/A if (EXT(statp).ext != NULL) {
2N/A nanoseconds = EXT(statp).ext->retrans.tv_sec;
2N/A nanoseconds *= BILLION;
2N/A nanoseconds += EXT(statp).ext->retrans.tv_nsec;
2N/A } else {
2N/A nanoseconds = statp->retrans;
2N/A nanoseconds *= BILLION;
2N/A }
2N/A nanoseconds <<= tries;
2N/A if (ns > 0)
2N/A nanoseconds /= statp->nscount;
2N/A if (nanoseconds < RES_MINRETRANS)
2N/A nanoseconds = RES_MINRETRANS;
2N/A seconds = nanoseconds / BILLION;
2N/A nanoseconds = nanoseconds % BILLION;
2N/A#else /* SUNW_SUBSECOND_TIME */
2N/A seconds = (statp->retrans << tries);
2N/A if (ns > 0)
2N/A seconds /= statp->nscount;
2N/A if (seconds <= 0)
2N/A seconds = 1;
2N/A#endif /* SUNW_SUBSECOND_TIME */
2N/A now = evNowTime();
2N/A#ifdef SUNW_SUBSECOND_TIME
2N/A timeout = evConsTime(seconds, nanoseconds);
2N/A#else /* SUNW_SUBSECOND_TIME */
2N/A timeout = evConsTime(seconds, 0);
2N/A#endif /* SUNW_SUBSECOND_TIME */
2N/A finish = evAddTime(now, timeout);
2N/A goto nonow;
2N/A wait:
2N/A now = evNowTime();
2N/A nonow:
2N/A#ifndef USE_POLL
2N/A FD_ZERO(&dsmask);
2N/A FD_SET(s, &dsmask);
2N/A if (evCmpTime(finish, now) > 0)
2N/A timeout = evSubTime(finish, now);
2N/A else
2N/A timeout = evConsTime(0, 0);
2N/A n = pselect(s + 1, &dsmask, NULL, NULL, &timeout, NULL);
2N/A#else
2N/A timeout = evSubTime(finish, now);
2N/A if (timeout.tv_sec < 0)
2N/A timeout = evConsTime(0, 0);
2N/A polltimeout = 1000*timeout.tv_sec +
2N/A timeout.tv_nsec/1000000;
2N/A pollfd.fd = s;
2N/A pollfd.events = POLLRDNORM;
2N/A n = poll(&pollfd, 1, polltimeout);
2N/A#endif /* USE_POLL */
2N/A
2N/A if (n == 0) {
2N/A Dprint(statp->options & RES_DEBUG, (stdout, ";; timeout\n"));
2N/A *gotsomewhere = 1;
2N/A return (0);
2N/A }
2N/A if (n < 0) {
2N/A if (errno == EINTR)
2N/A goto wait;
2N/A#ifndef USE_POLL
2N/A Perror(statp, stderr, "select", errno);
2N/A#else
2N/A Perror(statp, stderr, "poll", errno);
2N/A#endif /* USE_POLL */
2N/A res_nclose(statp);
2N/A return (0);
2N/A }
2N/A errno = 0;
2N/A fromlen = sizeof(from);
2N/A resplen = recvfrom(s, (char*)ans, anssiz,0,
2N/A (struct sockaddr *)&from, &fromlen);
2N/A if (resplen <= 0) {
2N/A Perror(statp, stderr, "recvfrom", errno);
2N/A res_nclose(statp);
2N/A return (0);
2N/A }
2N/A *gotsomewhere = 1;
2N/A if (resplen < HFIXEDSZ) {
2N/A /*
2N/A * Undersized message.
2N/A */
2N/A Dprint(statp->options & RES_DEBUG,
2N/A (stdout, ";; undersized: %d\n",
2N/A resplen));
2N/A *terrno = EMSGSIZE;
2N/A res_nclose(statp);
2N/A return (0);
2N/A }
2N/A if (hp->id != anhp->id) {
2N/A /*
2N/A * response from old query, ignore it.
2N/A * XXX - potential security hazard could
2N/A * be detected here.
2N/A */
2N/A DprintQ((statp->options & RES_DEBUG) ||
2N/A (statp->pfcode & RES_PRF_REPLY),
2N/A (stdout, ";; old answer:\n"),
2N/A ans, (resplen > anssiz) ? anssiz : resplen);
2N/A goto wait;
2N/A }
2N/A if (!(statp->options & RES_INSECURE1) &&
2N/A !res_ourserver_p(statp, (struct sockaddr *)&from)) {
2N/A /*
2N/A * response from wrong server? ignore it.
2N/A * XXX - potential security hazard could
2N/A * be detected here.
2N/A */
2N/A DprintQ((statp->options & RES_DEBUG) ||
2N/A (statp->pfcode & RES_PRF_REPLY),
2N/A (stdout, ";; not our server:\n"),
2N/A ans, (resplen > anssiz) ? anssiz : resplen);
2N/A goto wait;
2N/A }
2N/A#ifdef RES_USE_EDNS0
2N/A if (anhp->rcode == FORMERR && (statp->options & RES_USE_EDNS0) != 0U) {
2N/A /*
2N/A * Do not retry if the server do not understand EDNS0.
2N/A * The case has to be captured here, as FORMERR packet do not
2N/A * carry query section, hence res_queriesmatch() returns 0.
2N/A */
2N/A DprintQ(statp->options & RES_DEBUG,
2N/A (stdout, "server rejected query with EDNS0:\n"),
2N/A ans, (resplen > anssiz) ? anssiz : resplen);
2N/A /* record the error */
2N/A statp->_flags |= RES_F_EDNS0ERR;
2N/A res_nclose(statp);
2N/A return (0);
2N/A }
2N/A#endif
2N/A if (!(statp->options & RES_INSECURE2) &&
2N/A !res_queriesmatch(buf, buf + buflen,
2N/A ans, ans + anssiz)) {
2N/A /*
2N/A * response contains wrong query? ignore it.
2N/A * XXX - potential security hazard could
2N/A * be detected here.
2N/A */
2N/A DprintQ((statp->options & RES_DEBUG) ||
2N/A (statp->pfcode & RES_PRF_REPLY),
2N/A (stdout, ";; wrong query name:\n"),
2N/A ans, (resplen > anssiz) ? anssiz : resplen);
2N/A goto wait;
2N/A }
2N/A if (anhp->rcode == SERVFAIL ||
2N/A anhp->rcode == NOTIMP ||
2N/A anhp->rcode == REFUSED) {
2N/A DprintQ(statp->options & RES_DEBUG,
2N/A (stdout, "server rejected query:\n"),
2N/A ans, (resplen > anssiz) ? anssiz : resplen);
2N/A res_nclose(statp);
2N/A /* don't retry if called from dig */
2N/A if (!statp->pfcode)
2N/A return (0);
2N/A }
2N/A if (!(statp->options & RES_IGNTC) && anhp->tc) {
2N/A /*
2N/A * To get the rest of answer,
2N/A * use TCP with same server.
2N/A */
2N/A Dprint(statp->options & RES_DEBUG,
2N/A (stdout, ";; truncated answer\n"));
2N/A *v_circuit = 1;
2N/A res_nclose(statp);
2N/A return (1);
2N/A }
2N/A /*
2N/A * All is well, or the error is fatal. Signal that the
2N/A * next nameserver ought not be tried.
2N/A */
2N/A return (resplen);
2N/A}
2N/A
2N/Astatic void
2N/AAerror(const res_state statp, FILE *file, const char *string, int error,
2N/A const struct sockaddr *address, int alen)
2N/A{
2N/A int save = errno;
2N/A char hbuf[NI_MAXHOST];
2N/A char sbuf[NI_MAXSERV];
2N/A
2N/A alen = alen;
2N/A
2N/A if ((statp->options & RES_DEBUG) != 0U) {
2N/A if (getnameinfo(address, alen, hbuf, sizeof(hbuf),
2N/A sbuf, sizeof(sbuf), niflags)) {
2N/A strncpy(hbuf, "?", sizeof(hbuf) - 1);
2N/A hbuf[sizeof(hbuf) - 1] = '\0';
2N/A strncpy(sbuf, "?", sizeof(sbuf) - 1);
2N/A sbuf[sizeof(sbuf) - 1] = '\0';
2N/A }
2N/A fprintf(file, "res_send: %s ([%s].%s): %s\n",
2N/A string, hbuf, sbuf, strerror(error));
2N/A }
2N/A errno = save;
2N/A}
2N/A
2N/Astatic void
2N/APerror(const res_state statp, FILE *file, const char *string, int error) {
2N/A int save = errno;
2N/A
2N/A if ((statp->options & RES_DEBUG) != 0U)
2N/A fprintf(file, "res_send: %s: %s\n",
2N/A string, strerror(error));
2N/A errno = save;
2N/A}
2N/A
2N/Astatic int
2N/Asock_eq(struct sockaddr *a, struct sockaddr *b) {
2N/A struct sockaddr_in *a4, *b4;
2N/A struct sockaddr_in6 *a6, *b6;
2N/A
2N/A if (a->sa_family != b->sa_family)
2N/A return 0;
2N/A switch (a->sa_family) {
2N/A case AF_INET:
2N/A a4 = (struct sockaddr_in *)a;
2N/A b4 = (struct sockaddr_in *)b;
2N/A return a4->sin_port == b4->sin_port &&
2N/A a4->sin_addr.s_addr == b4->sin_addr.s_addr;
2N/A case AF_INET6:
2N/A a6 = (struct sockaddr_in6 *)a;
2N/A b6 = (struct sockaddr_in6 *)b;
2N/A return a6->sin6_port == b6->sin6_port &&
2N/A#ifdef HAVE_SIN6_SCOPE_ID
2N/A a6->sin6_scope_id == b6->sin6_scope_id &&
2N/A#endif
2N/A IN6_ARE_ADDR_EQUAL(&a6->sin6_addr, &b6->sin6_addr);
2N/A default:
2N/A return 0;
2N/A }
2N/A}
2N/A
2N/A#if defined(NEED_PSELECT) && !defined(USE_POLL)
2N/A/* XXX needs to move to the porting library. */
2N/Astatic int
2N/Apselect(int nfds, void *rfds, void *wfds, void *efds,
2N/A struct timespec *tsp, const sigset_t *sigmask)
2N/A{
2N/A struct timeval tv, *tvp;
2N/A sigset_t sigs;
2N/A int n;
2N/A
2N/A if (tsp) {
2N/A tvp = &tv;
2N/A tv = evTimeVal(*tsp);
2N/A } else
2N/A tvp = NULL;
2N/A if (sigmask)
2N/A sigprocmask(SIG_SETMASK, sigmask, &sigs);
2N/A n = select(nfds, rfds, wfds, efds, tvp);
2N/A if (sigmask)
2N/A sigprocmask(SIG_SETMASK, &sigs, NULL);
2N/A if (tsp)
2N/A *tsp = evTimeSpec(tv);
2N/A return (n);
2N/A}
2N/A#endif
2N/A
2N/A#ifdef SUNW_DEFER_ON_FAIL
2N/A
2N/A/*
2N/A * Set the current defer status for a nameserver.
2N/A */
2N/Avoid
2N/Ares_set_defer_state(res_state statp, int ns, res_defer_state_t defer_state)
2N/A{
2N/A struct timespec ts_zero, ts_now;
2N/A
2N/A /* if statp extension data is unavailable, do nothing */
2N/A if (statp->_u._ext.ext == NULL)
2N/A return;
2N/A
2N/A ts_zero = evConsTime(0, 0);
2N/A
2N/A /* always set status to online if defer-on-fail option is disabled */
2N/A if (evCmpTime(ts_zero, statp->_u._ext.ext->defer_on_fail) == 0)
2N/A defer_state = RES_DEFER_ONLINE;
2N/A
2N/A switch (defer_state) {
2N/A case RES_DEFER_ONLINE:
2N/A /*
2N/A * Caller is reporting a successful query.
2N/A * Mark the server as online.
2N/A * Set it's defer time to 0, as if it has always been online.
2N/A */
2N/A statp->_u._ext.ext->nsdefer[ns] = ts_zero;
2N/A break;
2N/A case RES_DEFER_OFFLINE:
2N/A /*
2N/A * Caller is reporting an unsuccessful query.
2N/A * Mark the server as offline.
2N/A * Set the retry time to a "defer-on-fail" time in the future.
2N/A * However, do not extend an unexpired retry time.
2N/A */
2N/A ts_now = evNowTimeMonotonic();
2N/A if (evCmpTime(ts_now, statp->_u._ext.ext->nsdefer[ns]) >= 0) {
2N/A statp->_u._ext.ext->nsdefer[ns] = evAddTime(ts_now,
2N/A statp->_u._ext.ext->defer_on_fail);
2N/A }
2N/A break;
2N/A default:
2N/A break;
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Get status code indicating whether or not the nameserver
2N/A * is expected to be responsive to queries.
2N/A */
2N/Avoid
2N/Ares_get_defer_state(res_state statp, int ns, res_defer_state_t *defer_state)
2N/A{
2N/A struct timespec ts_zero, ts_now;
2N/A
2N/A /* if statp extension data is unavailable, always return "online" */
2N/A if (statp->_u._ext.ext == NULL) {
2N/A *defer_state = RES_DEFER_ONLINE;
2N/A return;
2N/A }
2N/A
2N/A ts_zero = evConsTime(0, 0);
2N/A
2N/A /* quick return when defer-on-fail option is disabled */
2N/A if (evCmpTime(ts_zero, statp->_u._ext.ext->defer_on_fail) == 0) {
2N/A *defer_state = RES_DEFER_ONLINE;
2N/A return;
2N/A }
2N/A
2N/A /* quick return for "online" servers */
2N/A if (evCmpTime(ts_zero, statp->_u._ext.ext->nsdefer[ns]) == 0) {
2N/A *defer_state = RES_DEFER_ONLINE;
2N/A return;
2N/A }
2N/A
2N/A /*
2N/A * if "offline" server is now ready for retry,
2N/A * mark it "online" and return new status
2N/A */
2N/A ts_now = evNowTimeMonotonic();
2N/A if (evCmpTime(ts_now, statp->_u._ext.ext->nsdefer[ns]) >= 0) {
2N/A statp->_u._ext.ext->nsdefer[ns] = ts_zero;
2N/A *defer_state = RES_DEFER_ONLINE;
2N/A return;
2N/A }
2N/A
2N/A /* server is still considered "offline" */
2N/A *defer_state = RES_DEFER_OFFLINE;
2N/A}
2N/A
2N/A#endif /* SUNW_DEFER_ON_FAIL */