1  /*
       2   * Copyright (c) 1988, 1993
       3   *    The Regents of the University of California.  All rights reserved.
       4   *
       5   * Redistribution and use in source and binary forms, with or without
       6   * modification, are permitted provided that the following conditions
       7   * are met:
       8   * 1. Redistributions of source code must retain the above copyright
       9   *    notice, this list of conditions and the following disclaimer.
      10   * 2. Redistributions in binary form must reproduce the above copyright
      11   *    notice, this list of conditions and the following disclaimer in the
      12   *    documentation and/or other materials provided with the distribution.
      13   * 4. Neither the name of the University nor the names of its contributors
      14   *    may be used to endorse or promote products derived from this software
      15   *    without specific prior written permission.
      16   *
      17   * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
      18   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
      19   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
      20   * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
      21   * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
      22   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
      23   * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
      24   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
      25   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
      26   * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
      27   * SUCH DAMAGE.
      28   */
      29  
      30  /*
      31   * Portions Copyright (c) 1993 by Digital Equipment Corporation.
      32   *
      33   * Permission to use, copy, modify, and distribute this software for any
      34   * purpose with or without fee is hereby granted, provided that the above
      35   * copyright notice and this permission notice appear in all copies, and that
      36   * the name of Digital Equipment Corporation not be used in advertising or
      37   * publicity pertaining to distribution of the document or software without
      38   * specific, written prior permission.
      39   *
      40   * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
      41   * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
      42   * OF MERCHANTABILITY AND FITNESS.   IN NO EVENT SHALL DIGITAL EQUIPMENT
      43   * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
      44   * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
      45   * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
      46   * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
      47   * SOFTWARE.
      48   */
      49  
      50  /*
      51   * Portions Copyright (c) 1996-1999 by Internet Software Consortium.
      52   *
      53   * Permission to use, copy, modify, and distribute this software for any
      54   * purpose with or without fee is hereby granted, provided that the above
      55   * copyright notice and this permission notice appear in all copies.
      56   *
      57   * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
      58   * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
      59   * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
      60   * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
      61   * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
      62   * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
      63   * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
      64   * SOFTWARE.
      65   */
      66  
      67  #include <assert.h>
      68  #include <sys/types.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 <resolv/resolv-internal.h>
      78  #include <resolv/resolv_context.h>
      79  #include <stdio.h>
      80  #include <stdlib.h>
      81  #include <string.h>
      82  #include <shlib-compat.h>
      83  
      84  #if PACKETSZ > 65536
      85  #define MAXPACKET	PACKETSZ
      86  #else
      87  #define MAXPACKET	65536
      88  #endif
      89  
      90  #define QUERYSIZE	(HFIXEDSZ + QFIXEDSZ + MAXCDNAME + 1)
      91  
      92  static int
      93  __res_context_querydomain (struct resolv_context *,
      94  			   const char *name, const char *domain,
      95  			   int class, int type, unsigned char *answer, int anslen,
      96  			   unsigned char **answerp, unsigned char **answerp2, int *nanswerp2,
      97  			   int *resplen2, int *answerp2_malloced);
      98  
      99  /* Formulate a normal query, send, and await answer.  Returned answer
     100     is placed in supplied buffer ANSWER.  Perform preliminary check of
     101     answer, returning success only if no error is indicated and the
     102     answer count is nonzero.  Return the size of the response on
     103     success, -1 on error.  Error number is left in h_errno.
     104  
     105     Caller must parse answer and determine whether it answers the
     106     question.  */
     107  int
     108  __res_context_query (struct resolv_context *ctx, const char *name,
     109  		     int class, int type,
     110  		     unsigned char *answer, int anslen,
     111  		     unsigned char **answerp, unsigned char **answerp2,
     112  		     int *nanswerp2, int *resplen2, int *answerp2_malloced)
     113  {
     114  	struct __res_state *statp = ctx->resp;
     115  	UHEADER *hp = (UHEADER *) answer;
     116  	UHEADER *hp2;
     117  	int n, use_malloc = 0;
     118  
     119  	size_t bufsize = (type == T_QUERY_A_AND_AAAA ? 2 : 1) * QUERYSIZE;
     120  	u_char *buf = alloca (bufsize);
     121  	u_char *query1 = buf;
     122  	int nquery1 = -1;
     123  	u_char *query2 = NULL;
     124  	int nquery2 = 0;
     125  
     126   again:
     127  	hp->rcode = NOERROR;	/* default */
     128  
     129  	if (type == T_QUERY_A_AND_AAAA)
     130  	  {
     131  	    n = __res_context_mkquery (ctx, QUERY, name, class, T_A, NULL,
     132  				       query1, bufsize);
     133  	    if (n > 0)
     134  	      {
     135  		if ((statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0)
     136  		  {
     137  		    /* Use RESOLV_EDNS_BUFFER_SIZE because the receive
     138  		       buffer can be reallocated.  */
     139  		    n = __res_nopt (ctx, n, query1, bufsize,
     140  				    RESOLV_EDNS_BUFFER_SIZE);
     141  		    if (n < 0)
     142  		      goto unspec_nomem;
     143  		  }
     144  
     145  		nquery1 = n;
     146  		/* Align the buffer.  */
     147  		int npad = ((nquery1 + __alignof__ (HEADER) - 1)
     148  			    & ~(__alignof__ (HEADER) - 1)) - nquery1;
     149  		if (n > bufsize - npad)
     150  		  {
     151  		    n = -1;
     152  		    goto unspec_nomem;
     153  		  }
     154  		int nused = n + npad;
     155  		query2 = buf + nused;
     156  		n = __res_context_mkquery (ctx, QUERY, name, class, T_AAAA,
     157  					   NULL, query2, bufsize - nused);
     158  		if (n > 0
     159  		    && (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0)
     160  		  /* Use RESOLV_EDNS_BUFFER_SIZE because the receive
     161  		     buffer can be reallocated.  */
     162  		  n = __res_nopt (ctx, n, query2, bufsize,
     163  				  RESOLV_EDNS_BUFFER_SIZE);
     164  		nquery2 = n;
     165  	      }
     166  
     167  	  unspec_nomem:;
     168  	  }
     169  	else
     170  	  {
     171  	    n = __res_context_mkquery (ctx, QUERY, name, class, type, NULL,
     172  				       query1, bufsize);
     173  
     174  	    if (n > 0
     175  		&& (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0)
     176  	      {
     177  		/* Use RESOLV_EDNS_BUFFER_SIZE if the receive buffer
     178  		   can be reallocated.  */
     179  		size_t advertise;
     180  		if (answerp == NULL)
     181  		  advertise = anslen;
     182  		else
     183  		  advertise = RESOLV_EDNS_BUFFER_SIZE;
     184  		n = __res_nopt (ctx, n, query1, bufsize, advertise);
     185  	      }
     186  
     187  	    nquery1 = n;
     188  	  }
     189  
     190  	if (__glibc_unlikely (n <= 0) && !use_malloc) {
     191  		/* Retry just in case res_nmkquery failed because of too
     192  		   short buffer.  Shouldn't happen.  */
     193  		bufsize = (type == T_QUERY_A_AND_AAAA ? 2 : 1) * MAXPACKET;
     194  		buf = malloc (bufsize);
     195  		if (buf != NULL) {
     196  			query1 = buf;
     197  			use_malloc = 1;
     198  			goto again;
     199  		}
     200  	}
     201  	if (__glibc_unlikely (n <= 0))       {
     202  		RES_SET_H_ERRNO(statp, NO_RECOVERY);
     203  		if (use_malloc)
     204  			free (buf);
     205  		return (n);
     206  	}
     207  
     208  	/* Suppress AAAA lookups if required.  __res_handle_no_aaaa
     209  	   checks RES_NOAAAA first, so avoids parsing the
     210  	   just-generated query packet in most cases.  nss_dns avoids
     211  	   using T_QUERY_A_AND_AAAA in RES_NOAAAA mode, so there is no
     212  	   need to handle it here.  */
     213  	if (type == T_AAAA && __res_handle_no_aaaa (ctx, query1, nquery1,
     214  						    answer, anslen, &n))
     215  	  /* There must be no second query for AAAA queries.  The code
     216  	     below is still needed to translate NODATA responses.  */
     217  	  assert (query2 == NULL);
     218  	else
     219  	  {
     220  	    assert (answerp == NULL || (void *) *answerp == (void *) answer);
     221  	    n = __res_context_send (ctx, query1, nquery1, query2, nquery2,
     222  				    answer, anslen,
     223  				    answerp, answerp2, nanswerp2, resplen2,
     224  				    answerp2_malloced);
     225  	  }
     226  
     227  	if (use_malloc)
     228  		free (buf);
     229  	if (n < 0) {
     230  		RES_SET_H_ERRNO(statp, TRY_AGAIN);
     231  		return (n);
     232  	}
     233  
     234  	if (answerp != NULL)
     235  	  /* __res_context_send might have reallocated the buffer.  */
     236  	  hp = (UHEADER *) *answerp;
     237  
     238  	/* We simplify the following tests by assigning HP to HP2 or
     239  	   vice versa.  It is easy to verify that this is the same as
     240  	   ignoring all tests of HP or HP2.  */
     241  	if (answerp2 == NULL || *resplen2 < (int) sizeof (HEADER))
     242  	  {
     243  	    hp2 = hp;
     244  	  }
     245  	else
     246  	  {
     247  	    hp2 = (UHEADER *) *answerp2;
     248  	    if (n < (int) sizeof (HEADER))
     249  	      {
     250  	        hp = hp2;
     251  	      }
     252  	  }
     253  
     254  	/* Make sure both hp and hp2 are defined */
     255  	assert((hp != NULL) && (hp2 != NULL));
     256  
     257  	if ((hp->rcode != NOERROR || ntohs(hp->ancount) == 0)
     258  	    && (hp2->rcode != NOERROR || ntohs(hp2->ancount) == 0)) {
     259  		switch (hp->rcode == NOERROR ? hp2->rcode : hp->rcode) {
     260  		case NXDOMAIN:
     261  			if ((hp->rcode == NOERROR && ntohs (hp->ancount) != 0)
     262  			    || (hp2->rcode == NOERROR
     263  				&& ntohs (hp2->ancount) != 0))
     264  				goto success;
     265  			RES_SET_H_ERRNO(statp, HOST_NOT_FOUND);
     266  			break;
     267  		case SERVFAIL:
     268  			RES_SET_H_ERRNO(statp, TRY_AGAIN);
     269  			break;
     270  		case NOERROR:
     271  			if (ntohs (hp->ancount) != 0
     272  			    || ntohs (hp2->ancount) != 0)
     273  				goto success;
     274  			RES_SET_H_ERRNO(statp, NO_DATA);
     275  			break;
     276  		case FORMERR:
     277  		case NOTIMP:
     278  			/* Servers must not reply to AAAA queries with
     279  			   NOTIMP etc but some of them do.  */
     280  			if ((hp->rcode == NOERROR && ntohs (hp->ancount) != 0)
     281  			    || (hp2->rcode == NOERROR
     282  				&& ntohs (hp2->ancount) != 0))
     283  				goto success;
     284  			/* FALLTHROUGH */
     285  		case REFUSED:
     286  		default:
     287  			RES_SET_H_ERRNO(statp, NO_RECOVERY);
     288  			break;
     289  		}
     290  		return (-1);
     291  	}
     292   success:
     293  	return (n);
     294  }
     295  libc_hidden_def (__res_context_query)
     296  
     297  /* Common part of res_nquery and res_query.  */
     298  static int
     299  context_query_common (struct resolv_context *ctx,
     300  		      const char *name, int class, int type,
     301  		      unsigned char *answer, int anslen)
     302  {
     303    if (ctx == NULL)
     304      {
     305        RES_SET_H_ERRNO (&_res, NETDB_INTERNAL);
     306        return -1;
     307      }
     308    int result = __res_context_query (ctx, name, class, type, answer, anslen,
     309  				    NULL, NULL, NULL, NULL, NULL);
     310    __resolv_context_put (ctx);
     311    return result;
     312  }
     313  
     314  int
     315  ___res_nquery (res_state statp,
     316  	       const char *name,      /* Domain name.  */
     317  	       int class, int type,   /* Class and type of query.  */
     318  	       unsigned char *answer, /* Buffer to put answer.  */
     319  	       int anslen)	      /* Size of answer buffer.  */
     320  {
     321    return context_query_common
     322      (__resolv_context_get_override (statp), name, class, type, answer, anslen);
     323  }
     324  versioned_symbol (libc, ___res_nquery, res_nquery, GLIBC_2_34);
     325  #if OTHER_SHLIB_COMPAT (libresolv, GLIBC_2_2, GLIBC_2_34)
     326  compat_symbol (libresolv, ___res_nquery, __res_nquery, GLIBC_2_2);
     327  #endif
     328  
     329  int
     330  ___res_query (const char *name, int class, int type,
     331  	      unsigned char *answer, int anslen)
     332  {
     333    return context_query_common
     334      (__resolv_context_get (), name, class, type, answer, anslen);
     335  }
     336  versioned_symbol (libc, ___res_query, res_query, GLIBC_2_34);
     337  #if OTHER_SHLIB_COMPAT (libresolv, GLIBC_2_0, GLIBC_2_2)
     338  compat_symbol (libresolv, ___res_query, res_query, GLIBC_2_0);
     339  #endif
     340  #if OTHER_SHLIB_COMPAT (libresolv, GLIBC_2_2, GLIBC_2_34)
     341  compat_symbol (libresolv, ___res_query, __res_query, GLIBC_2_2);
     342  #endif
     343  
     344  /* Formulate a normal query, send, and retrieve answer in supplied
     345     buffer.  Return the size of the response on success, -1 on error.
     346     If enabled, implement search rules until answer or unrecoverable
     347     failure is detected.  Error code, if any, is left in h_errno.  */
     348  int
     349  __res_context_search (struct resolv_context *ctx,
     350  		      const char *name, int class, int type,
     351  		      unsigned char *answer, int anslen,
     352  		      unsigned char **answerp, unsigned char **answerp2,
     353  		      int *nanswerp2, int *resplen2, int *answerp2_malloced)
     354  {
     355  	struct __res_state *statp = ctx->resp;
     356  	const char *cp;
     357  	UHEADER *hp = (UHEADER *) answer;
     358  	char tmp[NS_MAXDNAME];
     359  	u_int dots;
     360  	int trailing_dot, ret, saved_herrno;
     361  	int got_nodata = 0, got_servfail = 0, root_on_list = 0;
     362  	int tried_as_is = 0;
     363  	int searched = 0;
     364  
     365  	__set_errno (0);
     366  	RES_SET_H_ERRNO(statp, HOST_NOT_FOUND);  /* True if we never query. */
     367  
     368  	dots = 0;
     369  	for (cp = name; *cp != '\0'; cp++)
     370  		dots += (*cp == '.');
     371  	trailing_dot = 0;
     372  	if (cp > name && *--cp == '.')
     373  		trailing_dot++;
     374  
     375  	/* If there aren't any dots, it could be a user-level alias. */
     376  	if (!dots && (cp = __res_context_hostalias
     377  		      (ctx, name, tmp, sizeof tmp))!= NULL)
     378  	  return __res_context_query (ctx, cp, class, type, answer,
     379  				      anslen, answerp, answerp2,
     380  				      nanswerp2, resplen2, answerp2_malloced);
     381  
     382  	/*
     383  	 * If there are enough dots in the name, let's just give it a
     384  	 * try 'as is'. The threshold can be set with the "ndots" option.
     385  	 * Also, query 'as is', if there is a trailing dot in the name.
     386  	 */
     387  	saved_herrno = -1;
     388  	if (dots >= statp->ndots || trailing_dot) {
     389  		ret = __res_context_querydomain (ctx, name, NULL, class, type,
     390  						 answer, anslen, answerp,
     391  						 answerp2, nanswerp2, resplen2,
     392  						 answerp2_malloced);
     393  		if (ret > 0 || trailing_dot
     394  		    /* If the second response is valid then we use that.  */
     395  		    || (ret == 0 && resplen2 != NULL && *resplen2 > 0))
     396  			return (ret);
     397  		saved_herrno = h_errno;
     398  		tried_as_is++;
     399  		if (answerp && *answerp != answer) {
     400  			answer = *answerp;
     401  			anslen = MAXPACKET;
     402  		}
     403  		if (answerp2 && *answerp2_malloced)
     404  		  {
     405  		    free (*answerp2);
     406  		    *answerp2 = NULL;
     407  		    *nanswerp2 = 0;
     408  		    *answerp2_malloced = 0;
     409  		  }
     410  	}
     411  
     412  	/*
     413  	 * We do at least one level of search if
     414  	 *	- there is no dot and RES_DEFNAME is set, or
     415  	 *	- there is at least one dot, there is no trailing dot,
     416  	 *	  and RES_DNSRCH is set.
     417  	 */
     418  	if ((!dots && (statp->options & RES_DEFNAMES) != 0) ||
     419  	    (dots && !trailing_dot && (statp->options & RES_DNSRCH) != 0)) {
     420  		int done = 0;
     421  
     422  		for (size_t domain_index = 0; !done; ++domain_index) {
     423  			const char *dname = __resolv_context_search_list
     424  			  (ctx, domain_index);
     425  			if (dname == NULL)
     426  			  break;
     427  			searched = 1;
     428  
     429  			/* __res_context_querydoman concatenates name
     430  			   with dname with a "." in between.  If we
     431  			   pass it in dname the "." we got from the
     432  			   configured default search path, we'll end
     433  			   up with "name..", which won't resolve.
     434  			   OTOH, passing it "" will result in "name.",
     435  			   which has the intended effect for both
     436  			   possible representations of the root
     437  			   domain.  */
     438  			if (dname[0] == '.')
     439  				dname++;
     440  			if (dname[0] == '\0')
     441  				root_on_list++;
     442  
     443  			ret = __res_context_querydomain
     444  			  (ctx, name, dname, class, type,
     445  			   answer, anslen, answerp, answerp2, nanswerp2,
     446  			   resplen2, answerp2_malloced);
     447  			if (ret > 0 || (ret == 0 && resplen2 != NULL
     448  					&& *resplen2 > 0))
     449  				return (ret);
     450  
     451  			if (answerp && *answerp != answer) {
     452  				answer = *answerp;
     453  				anslen = MAXPACKET;
     454  			}
     455  			if (answerp2 && *answerp2_malloced)
     456  			  {
     457  			    free (*answerp2);
     458  			    *answerp2 = NULL;
     459  			    *nanswerp2 = 0;
     460  			    *answerp2_malloced = 0;
     461  			  }
     462  
     463  			/*
     464  			 * If no server present, give up.
     465  			 * If name isn't found in this domain,
     466  			 * keep trying higher domains in the search list
     467  			 * (if that's enabled).
     468  			 * On a NO_DATA error, keep trying, otherwise
     469  			 * a wildcard entry of another type could keep us
     470  			 * from finding this entry higher in the domain.
     471  			 * If we get some other error (negative answer or
     472  			 * server failure), then stop searching up,
     473  			 * but try the input name below in case it's
     474  			 * fully-qualified.
     475  			 */
     476  			if (errno == ECONNREFUSED) {
     477  				RES_SET_H_ERRNO(statp, TRY_AGAIN);
     478  				return (-1);
     479  			}
     480  
     481  			switch (statp->res_h_errno) {
     482  			case NO_DATA:
     483  				got_nodata++;
     484  				/* FALLTHROUGH */
     485  			case HOST_NOT_FOUND:
     486  				/* keep trying */
     487  				break;
     488  			case TRY_AGAIN:
     489  				if (hp->rcode == SERVFAIL) {
     490  					/* try next search element, if any */
     491  					got_servfail++;
     492  					break;
     493  				}
     494  				/* FALLTHROUGH */
     495  			default:
     496  				/* anything else implies that we're done */
     497  				done++;
     498  			}
     499  
     500  			/* if we got here for some reason other than DNSRCH,
     501  			 * we only wanted one iteration of the loop, so stop.
     502  			 */
     503  			if ((statp->options & RES_DNSRCH) == 0)
     504  				done++;
     505  		}
     506  	}
     507  
     508  	/*
     509  	 * If the query has not already been tried as is then try it
     510  	 * unless RES_NOTLDQUERY is set and there were no dots.
     511  	 */
     512  	if ((dots || !searched || (statp->options & RES_NOTLDQUERY) == 0)
     513  	    && !(tried_as_is || root_on_list)) {
     514  		ret = __res_context_querydomain
     515  		  (ctx, name, NULL, class, type,
     516  		   answer, anslen, answerp, answerp2, nanswerp2,
     517  		   resplen2, answerp2_malloced);
     518  		if (ret > 0 || (ret == 0 && resplen2 != NULL
     519  				&& *resplen2 > 0))
     520  			return (ret);
     521  	}
     522  
     523  	/* if we got here, we didn't satisfy the search.
     524  	 * if we did an initial full query, return that query's H_ERRNO
     525  	 * (note that we wouldn't be here if that query had succeeded).
     526  	 * else if we ever got a nodata, send that back as the reason.
     527  	 * else send back meaningless H_ERRNO, that being the one from
     528  	 * the last DNSRCH we did.
     529  	 */
     530  	if (answerp2 && *answerp2_malloced)
     531  	  {
     532  	    free (*answerp2);
     533  	    *answerp2 = NULL;
     534  	    *nanswerp2 = 0;
     535  	    *answerp2_malloced = 0;
     536  	  }
     537  	if (saved_herrno != -1)
     538  		RES_SET_H_ERRNO(statp, saved_herrno);
     539  	else if (got_nodata)
     540  		RES_SET_H_ERRNO(statp, NO_DATA);
     541  	else if (got_servfail)
     542  		RES_SET_H_ERRNO(statp, TRY_AGAIN);
     543  	return (-1);
     544  }
     545  libc_hidden_def (__res_context_search)
     546  
     547  /* Common part of res_nsearch and res_search.  */
     548  static int
     549  context_search_common (struct resolv_context *ctx,
     550  		       const char *name, int class, int type,
     551  		       unsigned char *answer, int anslen)
     552  {
     553    if (ctx == NULL)
     554      {
     555        RES_SET_H_ERRNO (&_res, NETDB_INTERNAL);
     556        return -1;
     557      }
     558    int result = __res_context_search (ctx, name, class, type, answer, anslen,
     559  				     NULL, NULL, NULL, NULL, NULL);
     560    __resolv_context_put (ctx);
     561    return result;
     562  }
     563  
     564  int
     565  ___res_nsearch (res_state statp,
     566  		const char *name,      /* Domain name.  */
     567  		int class, int type,   /* Class and type of query.  */
     568  		unsigned char *answer, /* Buffer to put answer.  */
     569  		int anslen)	       /* Size of answer.  */
     570  {
     571    return context_search_common
     572      (__resolv_context_get_override (statp), name, class, type, answer, anslen);
     573  }
     574  versioned_symbol (libc, ___res_nsearch, res_nsearch, GLIBC_2_34);
     575  #if OTHER_SHLIB_COMPAT (libresolv, GLIBC_2_2, GLIBC_2_34)
     576  compat_symbol (libresolv, ___res_nsearch, __res_nsearch, GLIBC_2_2);
     577  #endif
     578  
     579  int
     580  ___res_search (const char *name, int class, int type,
     581  	       unsigned char *answer, int anslen)
     582  {
     583    return context_search_common
     584      (__resolv_context_get (), name, class, type, answer, anslen);
     585  }
     586  versioned_symbol (libc, ___res_search, res_search, GLIBC_2_34);
     587  #if OTHER_SHLIB_COMPAT (libresolv, GLIBC_2_0, GLIBC_2_2)
     588  compat_symbol (libresolv, ___res_search, res_search, GLIBC_2_0);
     589  #endif
     590  #if OTHER_SHLIB_COMPAT (libresolv, GLIBC_2_2, GLIBC_2_34)
     591  compat_symbol (libresolv, ___res_search, __res_search, GLIBC_2_2);
     592  #endif
     593  
     594  /*  Perform a call on res_query on the concatenation of name and
     595      domain.  */
     596  static int
     597  __res_context_querydomain (struct resolv_context *ctx,
     598  			   const char *name, const char *domain,
     599  			   int class, int type,
     600  			   unsigned char *answer, int anslen,
     601  			   unsigned char **answerp, unsigned char **answerp2,
     602  			   int *nanswerp2, int *resplen2,
     603  			   int *answerp2_malloced)
     604  {
     605  	struct __res_state *statp = ctx->resp;
     606  	char nbuf[MAXDNAME];
     607  	const char *longname = nbuf;
     608  	size_t n, d;
     609  
     610  	if (domain == NULL) {
     611  		n = strlen(name);
     612  
     613  		/* Decrement N prior to checking it against MAXDNAME
     614  		   so that we detect a wrap to SIZE_MAX and return
     615  		   a reasonable error.  */
     616  		n--;
     617  		if (n >= MAXDNAME - 1) {
     618  			RES_SET_H_ERRNO(statp, NO_RECOVERY);
     619  			return (-1);
     620  		}
     621  		longname = name;
     622  	} else {
     623  		n = strlen(name);
     624  		d = strlen(domain);
     625  		if (n + d + 1 >= MAXDNAME) {
     626  			RES_SET_H_ERRNO(statp, NO_RECOVERY);
     627  			return (-1);
     628  		}
     629  		char *p = __stpcpy (nbuf, name);
     630  		*p++ = '.';
     631  		strcpy (p, domain);
     632  	}
     633  	return __res_context_query (ctx, longname, class, type, answer,
     634  				    anslen, answerp, answerp2, nanswerp2,
     635  				    resplen2, answerp2_malloced);
     636  }
     637  
     638  /* Common part of res_nquerydomain and res_querydomain.  */
     639  static int
     640  context_querydomain_common (struct resolv_context *ctx,
     641  			    const char *name, const char *domain,
     642  			    int class, int type,
     643  			    unsigned char *answer, int anslen)
     644  {
     645    if (ctx == NULL)
     646      {
     647        RES_SET_H_ERRNO (&_res, NETDB_INTERNAL);
     648        return -1;
     649      }
     650    int result = __res_context_querydomain (ctx, name, domain, class, type,
     651  					  answer, anslen,
     652  					  NULL, NULL, NULL, NULL, NULL);
     653    __resolv_context_put (ctx);
     654    return result;
     655  }
     656  
     657  int
     658  ___res_nquerydomain (res_state statp,
     659  		     const char *name,
     660  		     const char *domain,
     661  		     int class, int type, /* Class and type of query.  */
     662  		     unsigned char *answer, /* Buffer to put answer.  */
     663  		     int anslen)	    /* Size of answer.  */
     664  {
     665    return context_querydomain_common
     666      (__resolv_context_get_override (statp),
     667       name, domain, class, type, answer, anslen);
     668  }
     669  versioned_symbol (libc, ___res_nquerydomain, res_nquerydomain, GLIBC_2_34);
     670  #if OTHER_SHLIB_COMPAT (libresolv, GLIBC_2_2, GLIBC_2_34)
     671  compat_symbol (libresolv, ___res_nquerydomain, __res_nquerydomain, GLIBC_2_2);
     672  #endif
     673  
     674  int
     675  ___res_querydomain (const char *name, const char *domain, int class, int type,
     676  		    unsigned char *answer, int anslen)
     677  {
     678    return context_querydomain_common
     679      (__resolv_context_get (), name, domain, class, type, answer, anslen);
     680  }
     681  versioned_symbol (libc, ___res_querydomain, res_querydomain, GLIBC_2_34);
     682  #if OTHER_SHLIB_COMPAT (libresolv, GLIBC_2_0, GLIBC_2_2)
     683  compat_symbol (libresolv, ___res_querydomain, res_querydomain, GLIBC_2_0);
     684  #endif
     685  #if OTHER_SHLIB_COMPAT (libresolv, GLIBC_2_2, GLIBC_2_34)
     686  compat_symbol (libresolv, ___res_querydomain, __res_querydomain, GLIBC_2_2);
     687  #endif