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