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
__res_context_query(struct resolv_context * ctx,const char * name,int class,int type,unsigned char * answer,int anslen,unsigned char ** answerp,unsigned char ** answerp2,int * nanswerp2,int * resplen2,int * answerp2_malloced)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 }
libc_hidden_def(__res_context_query)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
___res_nquery(res_state statp,const char * name,int class,int type,unsigned char * answer,int anslen)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
___res_query(const char * name,int class,int type,unsigned char * answer,int anslen)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
__res_context_search(struct resolv_context * ctx,const char * name,int class,int type,unsigned char * answer,int anslen,unsigned char ** answerp,unsigned char ** answerp2,int * nanswerp2,int * resplen2,int * answerp2_malloced)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 }
libc_hidden_def(__res_context_search)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
___res_nsearch(res_state statp,const char * name,int class,int type,unsigned char * answer,int anslen)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
___res_search(const char * name,int class,int type,unsigned char * answer,int anslen)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
__res_context_querydomain(struct resolv_context * ctx,const char * name,const char * domain,int class,int type,unsigned char * answer,int anslen,unsigned char ** answerp,unsigned char ** answerp2,int * nanswerp2,int * resplen2,int * answerp2_malloced)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
context_querydomain_common(struct resolv_context * ctx,const char * name,const char * domain,int class,int type,unsigned char * answer,int anslen)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
___res_nquerydomain(res_state statp,const char * name,const char * domain,int class,int type,unsigned char * answer,int anslen)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
___res_querydomain(const char * name,const char * domain,int class,int type,unsigned char * answer,int anslen)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
688