]> git.saurik.com Git - apple/libresolv.git/blob - res_query.c
libresolv-19.tar.gz
[apple/libresolv.git] / res_query.c
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 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34 /*
35 * Portions Copyright (c) 1993 by Digital Equipment Corporation.
36 *
37 * Permission to use, copy, modify, and distribute this software for any
38 * purpose with or without fee is hereby granted, provided that the above
39 * copyright notice and this permission notice appear in all copies, and that
40 * the name of Digital Equipment Corporation not be used in advertising or
41 * publicity pertaining to distribution of the document or software without
42 * specific, written prior permission.
43 *
44 * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
45 * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
46 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
47 * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
48 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
49 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
50 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
51 * SOFTWARE.
52 */
53
54 /*
55 * Portions Copyright (c) 1996-1999 by Internet Software Consortium.
56 *
57 * Permission to use, copy, modify, and distribute this software for any
58 * purpose with or without fee is hereby granted, provided that the above
59 * copyright notice and this permission notice appear in all copies.
60 *
61 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
62 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
63 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
64 * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
65 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
66 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
67 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
68 * SOFTWARE.
69 */
70
71 #if defined(LIBC_SCCS) && !defined(lint)
72 static const char sccsid[] = "@(#)res_query.c 8.1 (Berkeley) 6/4/93";
73 static const char rcsid[] = "$Id: res_query.c,v 1.1 2006/03/01 19:01:38 majka Exp $";
74 #endif /* LIBC_SCCS and not lint */
75
76 #ifndef __APPLE__
77 #include "port_before.h"
78 #endif
79 #include <sys/types.h>
80 #include <sys/param.h>
81 #include <netinet/in.h>
82 #include <arpa/inet.h>
83 #include <arpa/nameser.h>
84 #include <ctype.h>
85 #include <errno.h>
86 #include <netdb.h>
87 #include <resolv.h>
88 #include <stdio.h>
89 #include <stdlib.h>
90 #include <string.h>
91 #include "res_private.h"
92 #include <dns_sd.h>
93 #include <sys/event.h>
94 #include <sys/time.h>
95 #include <time.h>
96 #include <unistd.h>
97 #ifndef __APPLE__
98 #include "port_after.h"
99 #endif
100
101 /* Options. Leave them on. */
102 #define DEBUG
103
104 #if PACKETSZ > 1024
105 #define MAXPACKET PACKETSZ
106 #else
107 #define MAXPACKET 1024
108 #endif
109
110 #define BILLION 1000000000
111
112 /* length of a reverse DNS IPv6 address query name, e.g. "9.4.a.f.c.e.e.f.e.e.1.5.4.1.4.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.e.f.ip6.arpa" */
113 #define IPv6_REVERSE_LEN 72
114
115 /* index of the trailing char that must be "8", "9", "A", "a", "b", or "B" */
116 #define IPv6_REVERSE_LINK_LOCAL_TRAILING_CHAR 58
117
118 /* index of low-order nibble of embedded scope id */
119 #define IPv6_REVERSE_LINK_LOCAL_SCOPE_ID_LOW 48
120
121 const static uint8_t hexval[128] =
122 {
123 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0 - 15 */
124 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16 - 31 */
125 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 32 - 47 */
126 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, /* 48 - 63 */
127 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 64 - 79 */
128 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 95 */
129 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 96 - 111 */
130 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 112 - 127 */
131 };
132
133 struct res_query_context
134 {
135 u_char *answer;
136 size_t anslen;
137 size_t ansmaxlen;
138 uint32_t ifnum;
139 DNSServiceFlags flags;
140 DNSServiceErrorType error;
141 };
142
143 static void
144 res_query_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode, const char *fullname, uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, const void *rdata, uint32_t ttl, void *ctx)
145 {
146 struct res_query_context *context;
147 int n;
148 size_t buflen;
149 u_char *dnlist[2], *cp;
150 HEADER *ans;
151 struct in6_addr a6;
152
153 context = (struct res_query_context *)ctx;
154
155 context->flags = flags;
156 context->error = errorCode;
157
158 if (errorCode != kDNSServiceErr_NoError) return;
159
160 buflen = context->ansmaxlen - context->anslen;
161 if (buflen < NS_HFIXEDSZ) return;
162
163 dnlist[0] = context->answer + NS_HFIXEDSZ;
164 dnlist[1] = NULL;
165
166 cp = context->answer + context->anslen;
167
168 n = dn_comp((char *)fullname, cp, buflen, dnlist, &dnlist[1]);
169 if (n < 0) return;
170
171 /*
172 * Check that there is enough space in the buffer for the resource name (n),
173 * the resource record data (rdlen) and the resource record header (10).
174 */
175 if (buflen < n + rdlen + 10) return;
176
177 cp += n;
178 buflen -= n;
179
180 putshort(rrtype, cp);
181 cp += sizeof(uint16_t);
182
183 putshort(rrclass, cp);
184 cp += sizeof(uint16_t);
185
186 putlong(ttl, cp);
187 cp += sizeof(uint32_t);
188
189 putshort(rdlen, cp);
190 cp += sizeof(uint16_t);
191
192 memcpy(cp, rdata, rdlen);
193 cp += rdlen;
194
195 ans = (HEADER *)context->answer;
196 ans->ancount = htons(ntohs(ans->ancount) + 1);
197
198 context->anslen = (size_t)(cp - context->answer);
199
200 /*
201 * Save the interface number for the first AAAA record for link-local addresses.
202 * It's used by getaddrinfo to set the scope id.
203 */
204 if ((context->ifnum == 0) && (rrtype == ns_t_aaaa))
205 {
206 memset(&a6, 0, sizeof(struct in6_addr));
207 memcpy(&a6, rdata, sizeof(struct in6_addr));
208 if (IN6_IS_ADDR_LINKLOCAL(&a6)) context->ifnum = ifIndex;
209 }
210 }
211
212 static void
213 h_errno_for_dnssd_err(DNSServiceErrorType dnssd_err, int *h_errno_err)
214 {
215 switch (dnssd_err)
216 {
217 case kDNSServiceErr_NoError:
218 *h_errno_err = NETDB_SUCCESS;
219 break;
220 case kDNSServiceErr_Unknown:
221 *h_errno_err = NO_RECOVERY;
222 break;
223 case kDNSServiceErr_NoSuchRecord:
224 *h_errno_err = NO_DATA;
225 break;
226 case kDNSServiceErr_NoSuchName:
227 *h_errno_err = HOST_NOT_FOUND;
228 break;
229 case kDNSServiceErr_NoMemory:
230 default:
231 *h_errno_err = NETDB_INTERNAL;
232 break;
233 }
234 }
235
236 static int
237 _is_rev_link_local(const char *name)
238 {
239 int len, i;
240
241 if (name == NULL) return 0;
242
243 len = strlen(name);
244 if (len == 0) return 0;
245
246 /* check for trailing '.' */
247 if (name[len - 1] == '.') len--;
248
249 if (len != IPv6_REVERSE_LEN) return 0;
250
251 i = IPv6_REVERSE_LINK_LOCAL_TRAILING_CHAR;
252 if ((name[i] != '8') && (name[i] != '9') && (name[i] != 'A') && (name[i] != 'a') && (name[i] != 'B') && (name[i] != 'b')) return 0;
253
254 i = IPv6_REVERSE_LINK_LOCAL_TRAILING_CHAR + 1;
255 if (strncasecmp(name + i, ".e.f.ip6.arpa", 13)) return 0;
256
257 for (i = 0; i < IPv6_REVERSE_LINK_LOCAL_TRAILING_CHAR; i += 2)
258 {
259 if (name[i] < '0') return 0;
260 if ((name[i] > '9') && (name[i] < 'A')) return 0;
261 if ((name[i] > 'F') && (name[i] < 'a')) return 0;
262 if (name[i] > 'f') return 0;
263 if (name[i + 1] != '.') return 0;
264 }
265
266 return 1;
267 }
268
269 __private_extern__ int
270 res_query_mDNSResponder(res_state statp, const char *name, int class, int type, u_char *answer, int anslen, struct sockaddr *from, uint32_t *fromlen)
271 {
272 DNSServiceRef sdRef;
273 DNSServiceErrorType result;
274 struct res_query_context context;
275 int i, kq, n, wait;
276 struct kevent kv;
277 struct timeval ctv;
278 struct timespec now, finish, timeout;
279 HEADER *ans;
280 uint32_t iface;
281 uint16_t nibble;
282 char *qname;
283 result = 0;
284 kq = -1;
285 ans = (HEADER *)answer;
286
287 ans->rcode = 0;
288
289 memset(&context, 0, sizeof(struct res_query_context));
290
291 /* Build a dummy DNS header with question for the answer */
292 context.answer = answer;
293 context.ansmaxlen = anslen;
294 context.anslen = res_nmkquery(statp, ns_o_query, name, class, type, NULL, 0, NULL, answer, anslen);
295 if (context.anslen <= 0) return 0;
296
297 /* Mark DNS packet as a response */
298 ans->qr = 1;
299 ans->qr = htons(ans->qr);
300
301 /* Pull out Scope ID in link-local reverse queries */
302 qname = (char *)name;
303 iface = 0;
304 if (_is_rev_link_local(name))
305 {
306 /* _is_rev_link_local rejects chars > 127 so it's safe to index into hexval */
307 i = IPv6_REVERSE_LINK_LOCAL_SCOPE_ID_LOW;
308 nibble = hexval[(uint32_t)name[i]];
309 iface = nibble;
310
311 i += 2;
312 nibble = hexval[(uint32_t)name[i]];
313 iface += (nibble << 4);
314
315 i += 2;
316 nibble = hexval[(uint32_t)name[i]];
317 iface += (nibble << 8);
318
319 i += 2;
320 nibble = hexval[(uint32_t)name[i]];
321 iface += (nibble << 12);
322
323 if (iface != 0)
324 {
325 qname = strdup(name);
326 if (qname == NULL)
327 {
328 h_errno = NO_RECOVERY;
329 errno = ENOMEM;
330 return -1;
331 }
332
333 i = IPv6_REVERSE_LINK_LOCAL_SCOPE_ID_LOW;
334 qname[i] = '0';
335 qname[i + 2] = '0';
336 qname[i + 4] = '0';
337 qname[i + 6] = '0';
338 }
339 }
340
341 result = DNSServiceQueryRecord(&sdRef, kDNSServiceFlagsReturnIntermediates, iface, qname, type, class, res_query_callback, &context);
342 if (iface != 0) free(qname);
343
344 if (result != 0) return 0;
345
346 /* Use a kqueue to wait for a response from mDNSResponder */
347 kq = kqueue();
348
349 /* determine the maximum time to wait for a result */
350 gettimeofday(&ctv, NULL);
351
352 /* N.B. statp->retrans is actually the total timeount in seconds */
353 timeout.tv_sec = statp->retrans;
354 timeout.tv_nsec = 0;
355
356 finish.tv_sec = ctv.tv_sec + statp->retrans;
357 finish.tv_nsec = ctv.tv_usec * 1000;
358
359 EV_SET(&kv, DNSServiceRefSockFD(sdRef), EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, 0);
360
361 wait = 1;
362 while (wait == 1)
363 {
364 n = kevent(kq, &kv, 1, &kv, 1, &timeout);
365
366 if (n < 0)
367 {
368 if (errno == EINTR) goto keep_waiting;
369 h_errno = NO_RECOVERY;
370 wait = 0;
371 }
372 else if ((n == 0) && (ans->ancount == 0))
373 {
374 h_errno = TRY_AGAIN;
375 wait = 0;
376 }
377 else
378 {
379 result = DNSServiceProcessResult(sdRef);
380 if ((result != 0) || (context.error != 0))
381 {
382 if (result == 0) result = context.error;
383 h_errno_for_dnssd_err(result, &h_errno);
384 wait = 0;
385 }
386
387 if ((ans->ancount > 0) && ((context.flags & kDNSServiceFlagsMoreComing) == 0)) wait = 0;
388 }
389
390 keep_waiting:
391
392 if (wait == 1)
393 {
394 /* calculate remaining timeout */
395 gettimeofday(&ctv, NULL);
396
397 now.tv_sec = ctv.tv_sec;
398 now.tv_nsec = ctv.tv_usec * 1000;
399
400 timeout.tv_sec = finish.tv_sec - now.tv_sec;
401 if (finish.tv_nsec >= now.tv_nsec)
402 {
403 timeout.tv_nsec = finish.tv_nsec - now.tv_nsec;
404 }
405 else
406 {
407 timeout.tv_nsec = BILLION - now.tv_nsec + finish.tv_nsec;
408 timeout.tv_sec--;
409 }
410 }
411 }
412
413 DNSServiceRefDeallocate(sdRef);
414 close(kq);
415
416 if (ans->ancount == 0) context.anslen = -1;
417
418 if ((from != NULL) && (fromlen != NULL) && (context.ifnum != 0))
419 {
420 ((struct sockaddr_in6 *)from)->sin6_len = sizeof(struct sockaddr_in6);
421 ((struct sockaddr_in6 *)from)->sin6_family = AF_INET6;
422 ((struct sockaddr_in6 *)from)->sin6_addr.__u6_addr.__u6_addr8[15] = 1;
423 ((struct sockaddr_in6 *)from)->sin6_scope_id = context.ifnum;
424 *fromlen = sizeof(struct sockaddr_in6);
425 }
426
427 return context.anslen;
428 }
429
430 static int
431 res_soa_minimum(const u_char *msg, int len)
432 {
433 ns_msg handle;
434 const u_char *eom;
435 uint32_t i, b;
436 int min, soa_min;
437
438 eom = msg + len;
439
440 handle._msg = msg;
441 handle._eom = eom;
442
443 if (msg + NS_INT16SZ > eom) return -1;
444 NS_GET16(handle._id, msg);
445
446 if (msg + NS_INT16SZ > eom) return -1;
447 NS_GET16(handle._flags, msg);
448
449 for (i = 0; i < ns_s_max; i++)
450 {
451 if (msg + NS_INT16SZ > eom) return -1;
452 NS_GET16(handle._counts[i], msg);
453 }
454
455 if (handle._counts[ns_s_ns] == 0) return -1;
456
457 /* Skip forward to nameserver section */
458 for (i = 0; i < ns_s_ns; i++)
459 {
460 if (handle._counts[i] == 0) handle._sections[i] = NULL;
461 else
462 {
463 b = ns_skiprr(msg, eom, (ns_sect)i, handle._counts[i]);
464 if (b < 0) return -1;
465
466 handle._sections[i] = msg;
467 msg += b;
468 }
469 }
470
471 min = -1;
472 for (i = 0; i < handle._counts[ns_s_ns]; i++)
473 {
474 b = ns_skiprr(msg, eom, ns_s_ns, 1);
475 if (b < 0) return -1;
476
477 memcpy(&soa_min, msg + b - sizeof(int32_t), sizeof(int32_t));
478 soa_min = ntohl(soa_min);
479 if ((i == 0) || (soa_min < min)) min = soa_min;
480 msg += b;
481 }
482
483 return min;
484 }
485
486 /*
487 * Formulate a normal query, send, and await answer.
488 * Returned answer is placed in supplied buffer "answer".
489 * Perform preliminary check of answer, returning success only
490 * if no error is indicated and the answer count is nonzero.
491 * Return the size of the response on success, -1 on error.
492 * Error number is left in H_ERRNO.
493 *
494 * Caller must parse answer and determine whether it answers the question.
495 */
496 __private_extern__ int
497 res_nquery_soa_min(res_state statp, const char *name, int class, int type, u_char *answer, int anslen, struct sockaddr *from, int *fromlen, int *min)
498 {
499 u_char buf[MAXPACKET];
500 HEADER *hp = (HEADER *) answer;
501 int n;
502 u_int oflags;
503
504 if (min != NULL) *min = -1;
505
506 oflags = statp->_flags;
507
508 again:
509 __h_errno_set(statp, 0);
510 hp->rcode = ns_r_noerror; /* default */
511
512 #ifdef DEBUG
513 if (statp->options & RES_DEBUG) printf(";; res_query(%s, %d, %d)\n", name, class, type);
514 #endif
515
516 n = res_nmkquery(statp, ns_o_query, name, class, type, NULL, 0, NULL, buf, sizeof(buf));
517 #ifdef RES_USE_EDNS0
518 if (n > 0 && (statp->_flags & RES_F_EDNS0ERR) == 0 && (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0)
519 n = res_nopt(statp, n, buf, sizeof(buf), anslen);
520 #endif
521 if (n <= 0)
522 {
523 #ifdef DEBUG
524 if (statp->options & RES_DEBUG) printf(";; res_query: mkquery failed\n");
525 #endif
526 __h_errno_set(statp, NO_RECOVERY);
527 return (n);
528 }
529
530 n = res_nsend_2(statp, buf, n, answer, anslen, from, fromlen);
531 if (n < 0)
532 {
533 #ifdef RES_USE_EDNS0
534 /* if the query choked with EDNS0, retry without EDNS0 */
535 if ((statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0 && ((oflags ^ statp->_flags) & RES_F_EDNS0ERR) != 0)
536 {
537 statp->_flags |= RES_F_EDNS0ERR;
538 if (statp->options & RES_DEBUG) printf(";; res_nquery: retry without EDNS0\n");
539 goto again;
540 }
541 #endif
542 #ifdef DEBUG
543 if (statp->options & RES_DEBUG) printf(";; res_query: send error\n");
544 #endif
545 __h_errno_set(statp, TRY_AGAIN);
546 return (n);
547 }
548
549 if (hp->rcode != ns_r_noerror || ntohs(hp->ancount) == 0)
550 {
551 if (min != NULL)
552 {
553 *min = res_soa_minimum(answer, anslen);
554 if (statp->options & RES_DEBUG) printf(";; res_nquery: SOA minimum = %d\n", *min);
555 }
556
557 #ifdef DEBUG
558 if (statp->options & RES_DEBUG) printf(";; rcode = %d, ancount=%d\n", hp->rcode, ntohs(hp->ancount));
559 #endif
560 switch (hp->rcode)
561 {
562 case ns_r_nxdomain:
563 __h_errno_set(statp, HOST_NOT_FOUND);
564 break;
565 case ns_r_servfail:
566 __h_errno_set(statp, TRY_AGAIN);
567 break;
568 case ns_r_noerror:
569 __h_errno_set(statp, NO_DATA);
570 break;
571 case ns_r_formerr:
572 case ns_r_notimpl:
573 case ns_r_refused:
574 default:
575 __h_errno_set(statp, NO_RECOVERY);
576 break;
577 }
578
579 return (-1);
580 }
581
582 return (n);
583 }
584
585 int
586 res_nquery_2(res_state statp, const char *name, int class, int type, u_char *answer, int anslen, struct sockaddr *from, int *fromlen)
587 {
588 int unused = 0;
589
590 return res_nquery_soa_min(statp, name, class, type, answer, anslen, from, fromlen, &unused);
591 }
592
593 int
594 res_nquery(res_state statp, const char *name, int class, int type, u_char *answer, int anslen)
595 {
596 struct sockaddr_storage f;
597 int l;
598
599 l = sizeof(struct sockaddr_storage);
600
601 return res_nquery_2(statp, name, class, type, answer, anslen, (struct sockaddr *)&f, &l);
602 }
603
604 /*
605 * Perform a call on res_query on the concatenation of name and domain,
606 * removing a trailing dot from name if domain is NULL.
607 */
608 int
609 res_nquerydomain_2(res_state statp, const char *name, const char *domain, int class, int type, u_char *answer, int anslen, struct sockaddr *from, int *fromlen)
610 {
611 char nbuf[NS_MAXDNAME];
612 const char *longname = nbuf;
613 int n, d;
614
615 #ifdef DEBUG
616 if (statp->options & RES_DEBUG) printf(";; res_nquerydomain(%s, %s, %d, %d)\n", name, domain?domain:"<Nil>", class, type);
617 #endif
618 if (domain == NULL)
619 {
620 /*
621 * Check for trailing '.';
622 * copy without '.' if present.
623 */
624 n = strlen(name);
625 if (n >= NS_MAXDNAME)
626 {
627 __h_errno_set(statp, NO_RECOVERY);
628 return (-1);
629 }
630
631 n--;
632 if (n >= 0 && name[n] == '.')
633 {
634 strncpy(nbuf, name, n);
635 nbuf[n] = '\0';
636 }
637 else
638 {
639 longname = name;
640 }
641 }
642 else
643 {
644 n = strlen(name);
645 d = strlen(domain);
646 if (n + d + 1 >= NS_MAXDNAME)
647 {
648 __h_errno_set(statp, NO_RECOVERY);
649 return (-1);
650 }
651
652 sprintf(nbuf, "%s.%s", name, domain);
653 }
654
655 return (res_nquery_2(statp, longname, class, type, answer, anslen, from, fromlen));
656 }
657
658 int
659 res_nquerydomain(res_state statp, const char *name, const char *domain, int class, int type, u_char *answer, int anslen)
660 {
661 struct sockaddr_storage f;
662 int l;
663
664 l = sizeof(struct sockaddr_storage);
665
666 return res_nquerydomain_2(statp, name, domain, class, type, answer, anslen, (struct sockaddr *)&f, &l);
667 }
668
669 /*
670 * Formulate a normal query, send, and retrieve answer in supplied buffer.
671 * Return the size of the response on success, -1 on error.
672 * If enabled, implement search rules until answer or unrecoverable failure
673 * is detected. Error code, if any, is left in H_ERRNO.
674 */
675 int
676 res_nsearch_2(res_state statp, const char *name, int class, int type, u_char *answer, int anslen, struct sockaddr *from, int *fromlen)
677 {
678 const char *cp, * const *domain;
679 HEADER *hp = (HEADER *) answer;
680 char tmp[NS_MAXDNAME];
681 u_int dots;
682 int trailing_dot, ret, saved_herrno;
683 int got_nodata = 0, got_servfail = 0, root_on_list = 0;
684 int tried_as_is = 0;
685 int searched = 0;
686
687 errno = 0;
688 __h_errno_set(statp, HOST_NOT_FOUND); /* True if we never query. */
689
690 dots = 0;
691 for (cp = name; *cp != '\0'; cp++) dots += (*cp == '.');
692
693 trailing_dot = 0;
694 if (cp > name && *--cp == '.') trailing_dot++;
695
696 /* If there aren't any dots, it could be a user-level alias. */
697 if (!dots && (cp = res_hostalias(statp, name, tmp, sizeof tmp))!= NULL)
698 return (res_nquery(statp, cp, class, type, answer, anslen));
699
700 /*
701 * If there are enough dots in the name, let's just give it a
702 * try 'as is'. The threshold can be set with the "ndots" option.
703 * Also, query 'as is', if there is a trailing dot in the name.
704 */
705 saved_herrno = -1;
706 if (dots >= statp->ndots || trailing_dot)
707 {
708 ret = res_nquerydomain_2(statp, name, NULL, class, type, answer, anslen, from, fromlen);
709 if (ret > 0 || trailing_dot) return (ret);
710 saved_herrno = h_errno;
711 tried_as_is++;
712 }
713
714 /*
715 * We do at least one level of search if
716 * - there is no dot and RES_DEFNAME is set, or
717 * - there is at least one dot, there is no trailing dot,
718 * and RES_DNSRCH is set.
719 */
720 if ((!dots && (statp->options & RES_DEFNAMES) != 0) || (dots && !trailing_dot && (statp->options & RES_DNSRCH) != 0))
721 {
722 int done = 0;
723
724 for (domain = (const char * const *)statp->dnsrch; *domain && !done; domain++)
725 {
726 searched = 1;
727
728 if (domain[0][0] == '\0' || (domain[0][0] == '.' && domain[0][1] == '\0')) root_on_list++;
729
730 ret = res_nquerydomain_2(statp, name, *domain, class, type, answer, anslen, from, fromlen);
731 if (ret > 0) return (ret);
732
733 /*
734 * If no server present, give up.
735 * If name isn't found in this domain,
736 * keep trying higher domains in the search list
737 * (if that's enabled).
738 * On a NO_DATA error, keep trying, otherwise
739 * a wildcard entry of another type could keep us
740 * from finding this entry higher in the domain.
741 * If we get some other error (negative answer or
742 * server failure), then stop searching up,
743 * but try the input name below in case it's
744 * fully-qualified.
745 */
746 if (errno == ECONNREFUSED)
747 {
748 __h_errno_set(statp, TRY_AGAIN);
749 return (-1);
750 }
751
752 switch (statp->res_h_errno)
753 {
754 case NO_DATA:
755 got_nodata++;
756 /* FALLTHROUGH */
757 case HOST_NOT_FOUND:
758 /* keep trying */
759 break;
760 case TRY_AGAIN:
761 if (hp->rcode == ns_r_refused)
762 {
763 /* try next search element, if any */
764 got_servfail++;
765 break;
766 }
767 /* FALLTHROUGH */
768 default:
769 /* anything else implies that we're done */
770 done++;
771 }
772
773 /* if we got here for some reason other than DNSRCH,
774 * we only wanted one iteration of the loop, so stop.
775 */
776 if ((statp->options & RES_DNSRCH) == 0) done++;
777 }
778 }
779
780 /*
781 * If the query has not already been tried as is then try it
782 * unless RES_NOTLDQUERY is set and there were no dots.
783 */
784 if ((dots || !searched || (statp->options & RES_NOTLDQUERY) == 0) && !(tried_as_is || root_on_list))
785 {
786 ret = res_nquerydomain_2(statp, name, NULL, class, type, answer, anslen, from, fromlen);
787 if (ret > 0) return (ret);
788 }
789
790 /* if we got here, we didn't satisfy the search.
791 * if we did an initial full query, return that query's H_ERRNO
792 * (note that we wouldn't be here if that query had succeeded).
793 * else if we ever got a nodata, send that back as the reason.
794 * else send back meaningless H_ERRNO, that being the one from
795 * the last DNSRCH we did.
796 */
797 if (saved_herrno != -1)
798 __h_errno_set(statp, saved_herrno);
799 else if (got_nodata)
800 __h_errno_set(statp, NO_DATA);
801 else if (got_servfail)
802 __h_errno_set(statp, TRY_AGAIN);
803 return (-1);
804 }
805
806 int
807 __res_nsearch_list_2(res_state statp, const char *name, int class, int type, u_char *answer, int anslen, struct sockaddr *from, int *fromlen, int nsearch, char **search)
808 {
809 const char *cp, *domain;
810 HEADER *hp = (HEADER *) answer;
811 char tmp[NS_MAXDNAME];
812 u_int dots;
813 int trailing_dot, ret, saved_herrno, i;
814 int got_nodata = 0, got_servfail = 0, root_on_list = 0;
815 int tried_as_is = 0;
816 int searched = 0;
817
818 errno = 0;
819 __h_errno_set(statp, HOST_NOT_FOUND); /* True if we never query. */
820
821 dots = 0;
822 for (cp = name; *cp != '\0'; cp++) dots += (*cp == '.');
823
824 trailing_dot = 0;
825 if (cp > name && *--cp == '.') trailing_dot++;
826
827 /* If there aren't any dots, it could be a user-level alias. */
828 if (!dots && (cp = res_hostalias(statp, name, tmp, sizeof tmp)) != NULL)
829 return (res_nquery(statp, cp, class, type, answer, anslen));
830
831 /*
832 * If there are enough dots in the name, let's just give it a
833 * try 'as is'. The threshold can be set with the "ndots" option.
834 * Also, query 'as is', if there is a trailing dot in the name.
835 */
836 saved_herrno = -1;
837 if (dots >= statp->ndots || trailing_dot)
838 {
839 ret = res_nquerydomain(statp, name, NULL, class, type, answer, anslen);
840 if (ret > 0 || trailing_dot) return ret;
841 saved_herrno = h_errno;
842 tried_as_is++;
843 }
844
845 /*
846 * We do at least one level of search if
847 * - there is no dot and RES_DEFNAME is set, or
848 * - there is at least one dot, there is no trailing dot,
849 * and RES_DNSRCH is set.
850 */
851 if ((!dots && (statp->options & RES_DEFNAMES) != 0) || (dots && !trailing_dot && (statp->options & RES_DNSRCH) != 0))
852 {
853 int done = 0;
854
855 for (i = 0; i < nsearch; i++)
856 {
857 domain = search[i];
858 searched = 1;
859
860 if (domain[0] == '\0' || (domain[0] == '.' && domain[1] == '\0')) root_on_list++;
861
862 ret = res_nquerydomain_2(statp, name, domain, class, type, answer, anslen, from, fromlen);
863 if (ret > 0) return ret;
864
865 /*
866 * If no server present, give up.
867 * If name isn't found in this domain,
868 * keep trying higher domains in the search list
869 * (if that's enabled).
870 * On a NO_DATA error, keep trying, otherwise
871 * a wildcard entry of another type could keep us
872 * from finding this entry higher in the domain.
873 * If we get some other error (negative answer or
874 * server failure), then stop searching up,
875 * but try the input name below in case it's
876 * fully-qualified.
877 */
878 if (errno == ECONNREFUSED)
879 {
880 __h_errno_set(statp, TRY_AGAIN);
881 return -1;
882 }
883
884 switch (statp->res_h_errno)
885 {
886 case NO_DATA:
887 got_nodata++;
888 /* FALLTHROUGH */
889 case HOST_NOT_FOUND:
890 /* keep trying */
891 break;
892 case TRY_AGAIN:
893 if (hp->rcode == ns_r_refused)
894 {
895 /* try next search element, if any */
896 got_servfail++;
897 break;
898 }
899 /* FALLTHROUGH */
900 default:
901 /* anything else implies that we're done */
902 done++;
903 }
904
905 /*
906 * if we got here for some reason other than DNSRCH,
907 * we only wanted one iteration of the loop, so stop.
908 */
909 if ((statp->options & RES_DNSRCH) == 0) done++;
910 }
911 }
912
913 /*
914 * If the query has not already been tried as is then try it
915 * unless RES_NOTLDQUERY is set and there were no dots.
916 */
917 if ((dots || !searched || (statp->options & RES_NOTLDQUERY) == 0) && !(tried_as_is || root_on_list))
918 {
919 ret = res_nquerydomain_2(statp, name, NULL, class, type, answer, anslen, from, fromlen);
920 if (ret > 0) return ret;
921 }
922
923 /*
924 * we got here, we didn't satisfy the search.
925 * if we did an initial full query, return that query's H_ERRNO
926 * (note that we wouldn't be here if that query had succeeded).
927 * else if we ever got a nodata, send that back as the reason.
928 * else send back meaningless H_ERRNO, that being the one from
929 * the last DNSRCH we did.
930 */
931 if (saved_herrno != -1) __h_errno_set(statp, saved_herrno);
932 else if (got_nodata) __h_errno_set(statp, NO_DATA);
933 else if (got_servfail) __h_errno_set(statp, TRY_AGAIN);
934 return -1;
935 }
936
937 int
938 res_nsearch(res_state statp, const char *name, int class, int type, u_char *answer, int anslen)
939 {
940 struct sockaddr_storage f;
941 int l;
942
943 l = sizeof(struct sockaddr_storage);
944
945 return res_nsearch_2(statp, name, class, type, answer, anslen, (struct sockaddr *)&f, &l);
946 }
947
948 const char *
949 res_hostalias(const res_state statp, const char *name, char *dst, size_t siz)
950 {
951 char *file, *cp1, *cp2;
952 char buf[BUFSIZ];
953 FILE *fp;
954
955 if (statp->options & RES_NOALIASES) return (NULL);
956
957 file = getenv("HOSTALIASES");
958 if (file == NULL || (fp = fopen(file, "r")) == NULL) return (NULL);
959
960 setbuf(fp, NULL);
961 buf[sizeof(buf) - 1] = '\0';
962 while (fgets(buf, sizeof(buf), fp))
963 {
964 for (cp1 = buf; *cp1 && !isspace((unsigned char)*cp1); ++cp1) ;
965
966 if (!*cp1) break;
967 *cp1 = '\0';
968
969 if (ns_samename(buf, name) == 1)
970 {
971 while (isspace((unsigned char)*++cp1)) ;
972
973 if (!*cp1) break;
974
975 for (cp2 = cp1 + 1; *cp2 && !isspace((unsigned char)*cp2); ++cp2) ;
976
977 *cp2 = '\0';
978 strncpy(dst, cp1, siz - 1);
979 dst[siz - 1] = '\0';
980 fclose(fp);
981 return (dst);
982 }
983 }
984
985 fclose(fp);
986 return (NULL);
987 }