/*-1* SPDX-License-Identifier: (ISC AND BSD-3-Clause)2*3* Portions Copyright (C) 2004, 2005, 2008 Internet Systems Consortium, Inc. ("ISC")4* Portions Copyright (C) 1996-2001, 2003 Internet Software Consortium.5*6* Permission to use, copy, modify, and/or distribute this software for any7* purpose with or without fee is hereby granted, provided that the above8* copyright notice and this permission notice appear in all copies.9*10* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH11* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY12* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,13* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM14* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE15* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR16* PERFORMANCE OF THIS SOFTWARE.17*/1819/*20* Copyright (c) 1988, 199321* The Regents of the University of California. All rights reserved.22*23* Redistribution and use in source and binary forms, with or without24* modification, are permitted provided that the following conditions25* are met:26* 1. Redistributions of source code must retain the above copyright27* notice, this list of conditions and the following disclaimer.28* 2. Redistributions in binary form must reproduce the above copyright29* notice, this list of conditions and the following disclaimer in the30* documentation and/or other materials provided with the distribution.31* 3. Neither the name of the University nor the names of its contributors32* may be used to endorse or promote products derived from this software33* without specific prior written permission.34*35* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND36* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE37* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE38* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE39* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL40* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS41* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)42* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT43* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY44* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF45* SUCH DAMAGE.46*/4748/*49* Portions Copyright (c) 1993 by Digital Equipment Corporation.50*51* Permission to use, copy, modify, and distribute this software for any52* purpose with or without fee is hereby granted, provided that the above53* copyright notice and this permission notice appear in all copies, and that54* the name of Digital Equipment Corporation not be used in advertising or55* publicity pertaining to distribution of the document or software without56* specific, written prior permission.57*58* THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL59* WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES60* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT61* CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL62* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR63* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS64* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS65* SOFTWARE.66*/6768#include "port_before.h"69#include <sys/param.h>70#include <netinet/in.h>71#include <arpa/inet.h>72#include <arpa/nameser.h>73#include <ctype.h>74#include <errno.h>75#include <netdb.h>76#include <resolv.h>77#include <stdio.h>78#include <stdlib.h>79#include <string.h>80#include <unistd.h>81#include "port_after.h"8283#if PACKETSZ > 102484#define MAXPACKET PACKETSZ85#else86#define MAXPACKET 102487#endif8889/*%90* Formulate a normal query, send, and await answer.91* Returned answer is placed in supplied buffer "answer".92* Perform preliminary check of answer, returning success only93* if no error is indicated and the answer count is nonzero.94* Return the size of the response on success, -1 on error.95* Error number is left in H_ERRNO.96*97* Caller must parse answer and determine whether it answers the question.98*/99int100res_nquery(res_state statp,101const char *name, /*%< domain name */102int class, int type, /*%< class and type of query */103u_char *answer, /*%< buffer to put answer */104int anslen) /*%< size of answer buffer */105{106u_char buf[MAXPACKET];107HEADER *hp = (HEADER *) answer;108u_int oflags;109u_char *rdata;110int n;111112oflags = statp->_flags;113114again:115hp->rcode = NOERROR; /*%< default */116#ifdef DEBUG117if (statp->options & RES_DEBUG)118printf(";; res_query(%s, %d, %d)\n", name, class, type);119#endif120121n = res_nmkquery(statp, QUERY, name, class, type, NULL, 0, NULL,122buf, sizeof(buf));123#ifdef RES_USE_EDNS0124if (n > 0 && (statp->_flags & RES_F_EDNS0ERR) == 0 &&125(statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC|RES_NSID))) {126n = res_nopt(statp, n, buf, sizeof(buf), anslen);127if (n > 0 && (statp->options & RES_NSID) != 0U) {128rdata = &buf[n];129n = res_nopt_rdata(statp, n, buf, sizeof(buf), rdata,130NS_OPT_NSID, 0, NULL);131}132}133#endif134if (n <= 0) {135#ifdef DEBUG136if (statp->options & RES_DEBUG)137printf(";; res_query: mkquery failed\n");138#endif139RES_SET_H_ERRNO(statp, NO_RECOVERY);140return (n);141}142143n = res_nsend(statp, buf, n, answer, anslen);144if (n < 0) {145#ifdef RES_USE_EDNS0146/* if the query choked with EDNS0, retry without EDNS0 */147if ((statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0U &&148((oflags ^ statp->_flags) & RES_F_EDNS0ERR) != 0) {149statp->_flags |= RES_F_EDNS0ERR;150if (statp->options & RES_DEBUG)151printf(";; res_nquery: retry without EDNS0\n");152goto again;153}154#endif155#ifdef DEBUG156if (statp->options & RES_DEBUG)157printf(";; res_query: send error\n");158#endif159RES_SET_H_ERRNO(statp, TRY_AGAIN);160return (n);161}162163if (hp->rcode != NOERROR || ntohs(hp->ancount) == 0) {164#ifdef DEBUG165if (statp->options & RES_DEBUG)166printf(";; rcode = (%s), counts = an:%d ns:%d ar:%d\n",167p_rcode(hp->rcode),168ntohs(hp->ancount),169ntohs(hp->nscount),170ntohs(hp->arcount));171#endif172switch (hp->rcode) {173case NXDOMAIN:174RES_SET_H_ERRNO(statp, HOST_NOT_FOUND);175break;176case SERVFAIL:177RES_SET_H_ERRNO(statp, TRY_AGAIN);178break;179case NOERROR:180RES_SET_H_ERRNO(statp, NO_DATA);181break;182case FORMERR:183case NOTIMP:184case REFUSED:185default:186RES_SET_H_ERRNO(statp, NO_RECOVERY);187break;188}189return (-1);190}191return (n);192}193194/*%195* Formulate a normal query, send, and retrieve answer in supplied buffer.196* Return the size of the response on success, -1 on error.197* If enabled, implement search rules until answer or unrecoverable failure198* is detected. Error code, if any, is left in H_ERRNO.199*/200int201res_nsearch(res_state statp,202const char *name, /*%< domain name */203int class, int type, /*%< class and type of query */204u_char *answer, /*%< buffer to put answer */205int anslen) /*%< size of answer */206{207const char *cp, * const *domain;208HEADER *hp = (HEADER *) answer;209char tmp[NS_MAXDNAME];210u_int dots;211int trailing_dot, ret, saved_herrno;212int got_nodata = 0, got_servfail = 0, root_on_list = 0;213int tried_as_is = 0;214int searched = 0;215216errno = 0;217RES_SET_H_ERRNO(statp, HOST_NOT_FOUND); /*%< True if we never query. */218dots = 0;219for (cp = name; *cp != '\0'; cp++)220dots += (*cp == '.');221trailing_dot = 0;222if (cp > name && *--cp == '.')223trailing_dot++;224225/* If there aren't any dots, it could be a user-level alias. */226if (!dots && (cp = res_hostalias(statp, name, tmp, sizeof tmp))!= NULL)227return (res_nquery(statp, cp, class, type, answer, anslen));228229/*230* If there are enough dots in the name, let's just give it a231* try 'as is'. The threshold can be set with the "ndots" option.232* Also, query 'as is', if there is a trailing dot in the name.233*/234saved_herrno = -1;235if (dots >= statp->ndots || trailing_dot) {236ret = res_nquerydomain(statp, name, NULL, class, type,237answer, anslen);238if (ret > 0 || trailing_dot)239return (ret);240if (errno == ECONNREFUSED) {241RES_SET_H_ERRNO(statp, TRY_AGAIN);242return (-1);243}244switch (statp->res_h_errno) {245case NO_DATA:246case HOST_NOT_FOUND:247break;248case TRY_AGAIN:249if (hp->rcode == SERVFAIL)250break;251/* FALLTHROUGH */252default:253return (-1);254}255saved_herrno = statp->res_h_errno;256tried_as_is++;257}258259/*260* We do at least one level of search if261* - there is no dot and RES_DEFNAME is set, or262* - there is at least one dot, there is no trailing dot,263* and RES_DNSRCH is set.264*/265if ((!dots && (statp->options & RES_DEFNAMES) != 0U) ||266(dots && !trailing_dot && (statp->options & RES_DNSRCH) != 0U)) {267int done = 0;268269for (domain = (const char * const *)statp->dnsrch;270*domain && !done;271domain++) {272searched = 1;273274if (domain[0][0] == '\0' ||275(domain[0][0] == '.' && domain[0][1] == '\0'))276root_on_list++;277278if (root_on_list && tried_as_is)279continue;280281ret = res_nquerydomain(statp, name, *domain,282class, type,283answer, anslen);284if (ret > 0)285return (ret);286287/*288* If no server present, give up.289* If name isn't found in this domain,290* keep trying higher domains in the search list291* (if that's enabled).292* On a NO_DATA error, keep trying, otherwise293* a wildcard entry of another type could keep us294* from finding this entry higher in the domain.295* If we get some other error (negative answer or296* server failure), then stop searching up,297* but try the input name below in case it's298* fully-qualified.299*/300if (errno == ECONNREFUSED) {301RES_SET_H_ERRNO(statp, TRY_AGAIN);302return (-1);303}304305switch (statp->res_h_errno) {306case NO_DATA:307got_nodata++;308/* FALLTHROUGH */309case HOST_NOT_FOUND:310/* keep trying */311break;312case TRY_AGAIN:313/*314* This can occur due to a server failure315* (that is, all listed servers have failed),316* or all listed servers have timed out.317* ((HEADER *)answer)->rcode may not be set318* to SERVFAIL in the case of a timeout.319*320* Either way we must return TRY_AGAIN in321* order to avoid non-deterministic322* return codes.323* For example, loaded name servers or races324* against network startup/validation (dhcp,325* ppp, etc) can cause the search to timeout326* on one search element, e.g. 'fu.bar.com',327* and return a definitive failure on the328* next search element, e.g. 'fu.'.329*/330got_servfail++;331if (hp->rcode == SERVFAIL) {332/* try next search element, if any */333break;334}335/* FALLTHROUGH */336default:337/* anything else implies that we're done */338done++;339}340341/* if we got here for some reason other than DNSRCH,342* we only wanted one iteration of the loop, so stop.343*/344if ((statp->options & RES_DNSRCH) == 0U)345done++;346}347}348349switch (statp->res_h_errno) {350case NO_DATA:351case HOST_NOT_FOUND:352break;353case TRY_AGAIN:354if (hp->rcode == SERVFAIL)355break;356/* FALLTHROUGH */357default:358goto giveup;359}360361/*362* If the query has not already been tried as is then try it363* unless RES_NOTLDQUERY is set and there were no dots.364*/365if ((dots || !searched || (statp->options & RES_NOTLDQUERY) == 0U) &&366!(tried_as_is || root_on_list)) {367ret = res_nquerydomain(statp, name, NULL, class, type,368answer, anslen);369if (ret > 0)370return (ret);371}372373/* if we got here, we didn't satisfy the search.374* if we did an initial full query, return that query's H_ERRNO375* (note that we wouldn't be here if that query had succeeded).376* else if we ever got a nodata, send that back as the reason.377* else send back meaningless H_ERRNO, that being the one from378* the last DNSRCH we did.379*/380giveup:381if (saved_herrno != -1)382RES_SET_H_ERRNO(statp, saved_herrno);383else if (got_nodata)384RES_SET_H_ERRNO(statp, NO_DATA);385else if (got_servfail)386RES_SET_H_ERRNO(statp, TRY_AGAIN);387return (-1);388}389390/*%391* Perform a call on res_query on the concatenation of name and domain,392* removing a trailing dot from name if domain is NULL.393*/394int395res_nquerydomain(res_state statp,396const char *name,397const char *domain,398int class, int type, /*%< class and type of query */399u_char *answer, /*%< buffer to put answer */400int anslen) /*%< size of answer */401{402char nbuf[MAXDNAME];403const char *longname = nbuf;404int n, d;405406#ifdef DEBUG407if (statp->options & RES_DEBUG)408printf(";; res_nquerydomain(%s, %s, %d, %d)\n",409name, domain?domain:"<Nil>", class, type);410#endif411if (domain == NULL) {412/*413* Check for trailing '.';414* copy without '.' if present.415*/416n = strlen(name);417if (n >= MAXDNAME) {418RES_SET_H_ERRNO(statp, NO_RECOVERY);419return (-1);420}421n--;422if (n >= 0 && name[n] == '.') {423strncpy(nbuf, name, n);424nbuf[n] = '\0';425} else426longname = name;427} else {428n = strlen(name);429d = strlen(domain);430if (n + d + 1 >= MAXDNAME) {431RES_SET_H_ERRNO(statp, NO_RECOVERY);432return (-1);433}434sprintf(nbuf, "%s.%s", name, domain);435}436return (res_nquery(statp, longname, class, type, answer, anslen));437}438439const char *440res_hostalias(const res_state statp, const char *name, char *dst, size_t siz) {441char *file, *cp1, *cp2;442char buf[BUFSIZ];443FILE *fp;444445if (statp->options & RES_NOALIASES)446return (NULL);447file = secure_getenv("HOSTALIASES");448if (file == NULL || (fp = fopen(file, "re")) == NULL)449return (NULL);450setbuf(fp, NULL);451buf[sizeof(buf) - 1] = '\0';452while (fgets(buf, sizeof(buf), fp)) {453for (cp1 = buf; *cp1 && !isspace((unsigned char)*cp1); ++cp1)454;455if (!*cp1)456break;457*cp1 = '\0';458if (ns_samename(buf, name) == 1) {459while (isspace((unsigned char)*++cp1))460;461if (!*cp1)462break;463for (cp2 = cp1 + 1; *cp2 &&464!isspace((unsigned char)*cp2); ++cp2)465;466*cp2 = '\0';467strncpy(dst, cp1, siz - 1);468dst[siz - 1] = '\0';469fclose(fp);470return (dst);471}472}473fclose(fp);474return (NULL);475}476477/*! \file */478479480