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 	HEADER *hp = (HEADER *) answer;
116 	HEADER *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 	assert (answerp == NULL || (void *) *answerp == (void *) answer);
208 	n = __res_context_send (ctx, query1, nquery1, query2, nquery2, answer,
209 				anslen, answerp, answerp2, nanswerp2, resplen2,
210 				answerp2_malloced);
211 	if (use_malloc)
212 		free (buf);
213 	if (n < 0) {
214 		RES_SET_H_ERRNO(statp, TRY_AGAIN);
215 		return (n);
216 	}
217 
218 	if (answerp != NULL)
219 	  /* __res_context_send might have reallocated the buffer.  */
220 	  hp = (HEADER *) *answerp;
221 
222 	/* We simplify the following tests by assigning HP to HP2 or
223 	   vice versa.  It is easy to verify that this is the same as
224 	   ignoring all tests of HP or HP2.  */
225 	if (answerp2 == NULL || *resplen2 < (int) sizeof (HEADER))
226 	  {
227 	    hp2 = hp;
228 	  }
229 	else
230 	  {
231 	    hp2 = (HEADER *) *answerp2;
232 	    if (n < (int) sizeof (HEADER))
233 	      {
234 	        hp = hp2;
235 	      }
236 	  }
237 
238 	/* Make sure both hp and hp2 are defined */
239 	assert((hp != NULL) && (hp2 != NULL));
240 
241 	if ((hp->rcode != NOERROR || ntohs(hp->ancount) == 0)
242 	    && (hp2->rcode != NOERROR || ntohs(hp2->ancount) == 0)) {
243 		switch (hp->rcode == NOERROR ? hp2->rcode : hp->rcode) {
244 		case NXDOMAIN:
245 			if ((hp->rcode == NOERROR && ntohs (hp->ancount) != 0)
246 			    || (hp2->rcode == NOERROR
247 				&& ntohs (hp2->ancount) != 0))
248 				goto success;
249 			RES_SET_H_ERRNO(statp, HOST_NOT_FOUND);
250 			break;
251 		case SERVFAIL:
252 			RES_SET_H_ERRNO(statp, TRY_AGAIN);
253 			break;
254 		case NOERROR:
255 			if (ntohs (hp->ancount) != 0
256 			    || ntohs (hp2->ancount) != 0)
257 				goto success;
258 			RES_SET_H_ERRNO(statp, NO_DATA);
259 			break;
260 		case FORMERR:
261 		case NOTIMP:
262 			/* Servers must not reply to AAAA queries with
263 			   NOTIMP etc but some of them do.  */
264 			if ((hp->rcode == NOERROR && ntohs (hp->ancount) != 0)
265 			    || (hp2->rcode == NOERROR
266 				&& ntohs (hp2->ancount) != 0))
267 				goto success;
268 			/* FALLTHROUGH */
269 		case REFUSED:
270 		default:
271 			RES_SET_H_ERRNO(statp, NO_RECOVERY);
272 			break;
273 		}
274 		return (-1);
275 	}
276  success:
277 	return (n);
278 }
libc_hidden_def(__res_context_query)279 libc_hidden_def (__res_context_query)
280 
281 /* Common part of res_nquery and res_query.  */
282 static int
283 context_query_common (struct resolv_context *ctx,
284 		      const char *name, int class, int type,
285 		      unsigned char *answer, int anslen)
286 {
287   if (ctx == NULL)
288     {
289       RES_SET_H_ERRNO (&_res, NETDB_INTERNAL);
290       return -1;
291     }
292   int result = __res_context_query (ctx, name, class, type, answer, anslen,
293 				    NULL, NULL, NULL, NULL, NULL);
294   __resolv_context_put (ctx);
295   return result;
296 }
297 
298 int
___res_nquery(res_state statp,const char * name,int class,int type,unsigned char * answer,int anslen)299 ___res_nquery (res_state statp,
300 	       const char *name,      /* Domain name.  */
301 	       int class, int type,   /* Class and type of query.  */
302 	       unsigned char *answer, /* Buffer to put answer.  */
303 	       int anslen)	      /* Size of answer buffer.  */
304 {
305   return context_query_common
306     (__resolv_context_get_override (statp), name, class, type, answer, anslen);
307 }
308 versioned_symbol (libc, ___res_nquery, res_nquery, GLIBC_2_34);
309 #if OTHER_SHLIB_COMPAT (libresolv, GLIBC_2_2, GLIBC_2_34)
310 compat_symbol (libresolv, ___res_nquery, __res_nquery, GLIBC_2_2);
311 #endif
312 
313 int
___res_query(const char * name,int class,int type,unsigned char * answer,int anslen)314 ___res_query (const char *name, int class, int type,
315 	      unsigned char *answer, int anslen)
316 {
317   return context_query_common
318     (__resolv_context_get (), name, class, type, answer, anslen);
319 }
320 versioned_symbol (libc, ___res_query, res_query, GLIBC_2_34);
321 #if OTHER_SHLIB_COMPAT (libresolv, GLIBC_2_0, GLIBC_2_2)
322 compat_symbol (libresolv, ___res_query, res_query, GLIBC_2_0);
323 #endif
324 #if OTHER_SHLIB_COMPAT (libresolv, GLIBC_2_2, GLIBC_2_34)
325 compat_symbol (libresolv, ___res_query, __res_query, GLIBC_2_2);
326 #endif
327 
328 /* Formulate a normal query, send, and retrieve answer in supplied
329    buffer.  Return the size of the response on success, -1 on error.
330    If enabled, implement search rules until answer or unrecoverable
331    failure is detected.  Error code, if any, is left in h_errno.  */
332 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)333 __res_context_search (struct resolv_context *ctx,
334 		      const char *name, int class, int type,
335 		      unsigned char *answer, int anslen,
336 		      unsigned char **answerp, unsigned char **answerp2,
337 		      int *nanswerp2, int *resplen2, int *answerp2_malloced)
338 {
339 	struct __res_state *statp = ctx->resp;
340 	const char *cp;
341 	HEADER *hp = (HEADER *) answer;
342 	char tmp[NS_MAXDNAME];
343 	u_int dots;
344 	int trailing_dot, ret, saved_herrno;
345 	int got_nodata = 0, got_servfail = 0, root_on_list = 0;
346 	int tried_as_is = 0;
347 	int searched = 0;
348 
349 	__set_errno (0);
350 	RES_SET_H_ERRNO(statp, HOST_NOT_FOUND);  /* True if we never query. */
351 
352 	dots = 0;
353 	for (cp = name; *cp != '\0'; cp++)
354 		dots += (*cp == '.');
355 	trailing_dot = 0;
356 	if (cp > name && *--cp == '.')
357 		trailing_dot++;
358 
359 	/* If there aren't any dots, it could be a user-level alias. */
360 	if (!dots && (cp = __res_context_hostalias
361 		      (ctx, name, tmp, sizeof tmp))!= NULL)
362 	  return __res_context_query (ctx, cp, class, type, answer,
363 				      anslen, answerp, answerp2,
364 				      nanswerp2, resplen2, answerp2_malloced);
365 
366 	/*
367 	 * If there are enough dots in the name, let's just give it a
368 	 * try 'as is'. The threshold can be set with the "ndots" option.
369 	 * Also, query 'as is', if there is a trailing dot in the name.
370 	 */
371 	saved_herrno = -1;
372 	if (dots >= statp->ndots || trailing_dot) {
373 		ret = __res_context_querydomain (ctx, name, NULL, class, type,
374 						 answer, anslen, answerp,
375 						 answerp2, nanswerp2, resplen2,
376 						 answerp2_malloced);
377 		if (ret > 0 || trailing_dot
378 		    /* If the second response is valid then we use that.  */
379 		    || (ret == 0 && resplen2 != NULL && *resplen2 > 0))
380 			return (ret);
381 		saved_herrno = h_errno;
382 		tried_as_is++;
383 		if (answerp && *answerp != answer) {
384 			answer = *answerp;
385 			anslen = MAXPACKET;
386 		}
387 		if (answerp2 && *answerp2_malloced)
388 		  {
389 		    free (*answerp2);
390 		    *answerp2 = NULL;
391 		    *nanswerp2 = 0;
392 		    *answerp2_malloced = 0;
393 		  }
394 	}
395 
396 	/*
397 	 * We do at least one level of search if
398 	 *	- there is no dot and RES_DEFNAME is set, or
399 	 *	- there is at least one dot, there is no trailing dot,
400 	 *	  and RES_DNSRCH is set.
401 	 */
402 	if ((!dots && (statp->options & RES_DEFNAMES) != 0) ||
403 	    (dots && !trailing_dot && (statp->options & RES_DNSRCH) != 0)) {
404 		int done = 0;
405 
406 		for (size_t domain_index = 0; !done; ++domain_index) {
407 			const char *dname = __resolv_context_search_list
408 			  (ctx, domain_index);
409 			if (dname == NULL)
410 			  break;
411 			searched = 1;
412 
413 			/* __res_context_querydoman concatenates name
414 			   with dname with a "." in between.  If we
415 			   pass it in dname the "." we got from the
416 			   configured default search path, we'll end
417 			   up with "name..", which won't resolve.
418 			   OTOH, passing it "" will result in "name.",
419 			   which has the intended effect for both
420 			   possible representations of the root
421 			   domain.  */
422 			if (dname[0] == '.')
423 				dname++;
424 			if (dname[0] == '\0')
425 				root_on_list++;
426 
427 			ret = __res_context_querydomain
428 			  (ctx, name, dname, class, type,
429 			   answer, anslen, answerp, answerp2, nanswerp2,
430 			   resplen2, answerp2_malloced);
431 			if (ret > 0 || (ret == 0 && resplen2 != NULL
432 					&& *resplen2 > 0))
433 				return (ret);
434 
435 			if (answerp && *answerp != answer) {
436 				answer = *answerp;
437 				anslen = MAXPACKET;
438 			}
439 			if (answerp2 && *answerp2_malloced)
440 			  {
441 			    free (*answerp2);
442 			    *answerp2 = NULL;
443 			    *nanswerp2 = 0;
444 			    *answerp2_malloced = 0;
445 			  }
446 
447 			/*
448 			 * If no server present, give up.
449 			 * If name isn't found in this domain,
450 			 * keep trying higher domains in the search list
451 			 * (if that's enabled).
452 			 * On a NO_DATA error, keep trying, otherwise
453 			 * a wildcard entry of another type could keep us
454 			 * from finding this entry higher in the domain.
455 			 * If we get some other error (negative answer or
456 			 * server failure), then stop searching up,
457 			 * but try the input name below in case it's
458 			 * fully-qualified.
459 			 */
460 			if (errno == ECONNREFUSED) {
461 				RES_SET_H_ERRNO(statp, TRY_AGAIN);
462 				return (-1);
463 			}
464 
465 			switch (statp->res_h_errno) {
466 			case NO_DATA:
467 				got_nodata++;
468 				/* FALLTHROUGH */
469 			case HOST_NOT_FOUND:
470 				/* keep trying */
471 				break;
472 			case TRY_AGAIN:
473 				if (hp->rcode == SERVFAIL) {
474 					/* try next search element, if any */
475 					got_servfail++;
476 					break;
477 				}
478 				/* FALLTHROUGH */
479 			default:
480 				/* anything else implies that we're done */
481 				done++;
482 			}
483 
484 			/* if we got here for some reason other than DNSRCH,
485 			 * we only wanted one iteration of the loop, so stop.
486 			 */
487 			if ((statp->options & RES_DNSRCH) == 0)
488 				done++;
489 		}
490 	}
491 
492 	/*
493 	 * If the query has not already been tried as is then try it
494 	 * unless RES_NOTLDQUERY is set and there were no dots.
495 	 */
496 	if ((dots || !searched || (statp->options & RES_NOTLDQUERY) == 0)
497 	    && !(tried_as_is || root_on_list)) {
498 		ret = __res_context_querydomain
499 		  (ctx, name, NULL, class, type,
500 		   answer, anslen, answerp, answerp2, nanswerp2,
501 		   resplen2, answerp2_malloced);
502 		if (ret > 0 || (ret == 0 && resplen2 != NULL
503 				&& *resplen2 > 0))
504 			return (ret);
505 	}
506 
507 	/* if we got here, we didn't satisfy the search.
508 	 * if we did an initial full query, return that query's H_ERRNO
509 	 * (note that we wouldn't be here if that query had succeeded).
510 	 * else if we ever got a nodata, send that back as the reason.
511 	 * else send back meaningless H_ERRNO, that being the one from
512 	 * the last DNSRCH we did.
513 	 */
514 	if (answerp2 && *answerp2_malloced)
515 	  {
516 	    free (*answerp2);
517 	    *answerp2 = NULL;
518 	    *nanswerp2 = 0;
519 	    *answerp2_malloced = 0;
520 	  }
521 	if (saved_herrno != -1)
522 		RES_SET_H_ERRNO(statp, saved_herrno);
523 	else if (got_nodata)
524 		RES_SET_H_ERRNO(statp, NO_DATA);
525 	else if (got_servfail)
526 		RES_SET_H_ERRNO(statp, TRY_AGAIN);
527 	return (-1);
528 }
libc_hidden_def(__res_context_search)529 libc_hidden_def (__res_context_search)
530 
531 /* Common part of res_nsearch and res_search.  */
532 static int
533 context_search_common (struct resolv_context *ctx,
534 		       const char *name, int class, int type,
535 		       unsigned char *answer, int anslen)
536 {
537   if (ctx == NULL)
538     {
539       RES_SET_H_ERRNO (&_res, NETDB_INTERNAL);
540       return -1;
541     }
542   int result = __res_context_search (ctx, name, class, type, answer, anslen,
543 				     NULL, NULL, NULL, NULL, NULL);
544   __resolv_context_put (ctx);
545   return result;
546 }
547 
548 int
___res_nsearch(res_state statp,const char * name,int class,int type,unsigned char * answer,int anslen)549 ___res_nsearch (res_state statp,
550 		const char *name,      /* Domain name.  */
551 		int class, int type,   /* Class and type of query.  */
552 		unsigned char *answer, /* Buffer to put answer.  */
553 		int anslen)	       /* Size of answer.  */
554 {
555   return context_search_common
556     (__resolv_context_get_override (statp), name, class, type, answer, anslen);
557 }
558 versioned_symbol (libc, ___res_nsearch, res_nsearch, GLIBC_2_34);
559 #if OTHER_SHLIB_COMPAT (libresolv, GLIBC_2_2, GLIBC_2_34)
560 compat_symbol (libresolv, ___res_nsearch, __res_nsearch, GLIBC_2_2);
561 #endif
562 
563 int
___res_search(const char * name,int class,int type,unsigned char * answer,int anslen)564 ___res_search (const char *name, int class, int type,
565 	       unsigned char *answer, int anslen)
566 {
567   return context_search_common
568     (__resolv_context_get (), name, class, type, answer, anslen);
569 }
570 versioned_symbol (libc, ___res_search, res_search, GLIBC_2_34);
571 #if OTHER_SHLIB_COMPAT (libresolv, GLIBC_2_0, GLIBC_2_2)
572 compat_symbol (libresolv, ___res_search, res_search, GLIBC_2_0);
573 #endif
574 #if OTHER_SHLIB_COMPAT (libresolv, GLIBC_2_2, GLIBC_2_34)
575 compat_symbol (libresolv, ___res_search, __res_search, GLIBC_2_2);
576 #endif
577 
578 /*  Perform a call on res_query on the concatenation of name and
579     domain.  */
580 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)581 __res_context_querydomain (struct resolv_context *ctx,
582 			   const char *name, const char *domain,
583 			   int class, int type,
584 			   unsigned char *answer, int anslen,
585 			   unsigned char **answerp, unsigned char **answerp2,
586 			   int *nanswerp2, int *resplen2,
587 			   int *answerp2_malloced)
588 {
589 	struct __res_state *statp = ctx->resp;
590 	char nbuf[MAXDNAME];
591 	const char *longname = nbuf;
592 	size_t n, d;
593 
594 	if (domain == NULL) {
595 		n = strlen(name);
596 
597 		/* Decrement N prior to checking it against MAXDNAME
598 		   so that we detect a wrap to SIZE_MAX and return
599 		   a reasonable error.  */
600 		n--;
601 		if (n >= MAXDNAME - 1) {
602 			RES_SET_H_ERRNO(statp, NO_RECOVERY);
603 			return (-1);
604 		}
605 		longname = name;
606 	} else {
607 		n = strlen(name);
608 		d = strlen(domain);
609 		if (n + d + 1 >= MAXDNAME) {
610 			RES_SET_H_ERRNO(statp, NO_RECOVERY);
611 			return (-1);
612 		}
613 		char *p = __stpcpy (nbuf, name);
614 		*p++ = '.';
615 		strcpy (p, domain);
616 	}
617 	return __res_context_query (ctx, longname, class, type, answer,
618 				    anslen, answerp, answerp2, nanswerp2,
619 				    resplen2, answerp2_malloced);
620 }
621 
622 /* Common part of res_nquerydomain and res_querydomain.  */
623 static int
context_querydomain_common(struct resolv_context * ctx,const char * name,const char * domain,int class,int type,unsigned char * answer,int anslen)624 context_querydomain_common (struct resolv_context *ctx,
625 			    const char *name, const char *domain,
626 			    int class, int type,
627 			    unsigned char *answer, int anslen)
628 {
629   if (ctx == NULL)
630     {
631       RES_SET_H_ERRNO (&_res, NETDB_INTERNAL);
632       return -1;
633     }
634   int result = __res_context_querydomain (ctx, name, domain, class, type,
635 					  answer, anslen,
636 					  NULL, NULL, NULL, NULL, NULL);
637   __resolv_context_put (ctx);
638   return result;
639 }
640 
641 int
___res_nquerydomain(res_state statp,const char * name,const char * domain,int class,int type,unsigned char * answer,int anslen)642 ___res_nquerydomain (res_state statp,
643 		     const char *name,
644 		     const char *domain,
645 		     int class, int type, /* Class and type of query.  */
646 		     unsigned char *answer, /* Buffer to put answer.  */
647 		     int anslen)	    /* Size of answer.  */
648 {
649   return context_querydomain_common
650     (__resolv_context_get_override (statp),
651      name, domain, class, type, answer, anslen);
652 }
653 versioned_symbol (libc, ___res_nquerydomain, res_nquerydomain, GLIBC_2_34);
654 #if OTHER_SHLIB_COMPAT (libresolv, GLIBC_2_2, GLIBC_2_34)
655 compat_symbol (libresolv, ___res_nquerydomain, __res_nquerydomain, GLIBC_2_2);
656 #endif
657 
658 int
___res_querydomain(const char * name,const char * domain,int class,int type,unsigned char * answer,int anslen)659 ___res_querydomain (const char *name, const char *domain, int class, int type,
660 		    unsigned char *answer, int anslen)
661 {
662   return context_querydomain_common
663     (__resolv_context_get (), name, domain, class, type, answer, anslen);
664 }
665 versioned_symbol (libc, ___res_querydomain, res_querydomain, GLIBC_2_34);
666 #if OTHER_SHLIB_COMPAT (libresolv, GLIBC_2_0, GLIBC_2_2)
667 compat_symbol (libresolv, ___res_querydomain, res_querydomain, GLIBC_2_0);
668 #endif
669 #if OTHER_SHLIB_COMPAT (libresolv, GLIBC_2_2, GLIBC_2_34)
670 compat_symbol (libresolv, ___res_querydomain, __res_querydomain, GLIBC_2_2);
671 #endif
672