]> git.saurik.com Git - apple/libresolv.git/blob - res_query.c
libresolv-25.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_nxdomain) || ((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 TTL = %d\n", *min);
555 }
556 }
557
558 if (hp->rcode != ns_r_noerror || ntohs(hp->ancount) == 0)
559 {
560 #ifdef DEBUG
561 if (statp->options & RES_DEBUG) printf(";; rcode = %d, ancount=%d\n", hp->rcode, ntohs(hp->ancount));
562 #endif
563 switch (hp->rcode)
564 {
565 case ns_r_nxdomain:
566 __h_errno_set(statp, HOST_NOT_FOUND);
567 break;
568 case ns_r_servfail:
569 __h_errno_set(statp, TRY_AGAIN);
570 break;
571 case ns_r_noerror:
572 __h_errno_set(statp, NO_DATA);
573 break;
574 case ns_r_formerr:
575 case ns_r_notimpl:
576 case ns_r_refused:
577 default:
578 __h_errno_set(statp, NO_RECOVERY);
579 break;
580 }
581
582 return (-1);
583 }
584
585 return (n);
586 }
587
588 int
589 res_nquery_2(res_state statp, const char *name, int class, int type, u_char *answer, int anslen, struct sockaddr *from, int *fromlen)
590 {
591 int unused = 0;
592
593 return res_nquery_soa_min(statp, name, class, type, answer, anslen, from, fromlen, &unused);
594 }
595
596 int
597 res_nquery(res_state statp, const char *name, int class, int type, u_char *answer, int anslen)
598 {
599 struct sockaddr_storage f;
600 int l;
601
602 l = sizeof(struct sockaddr_storage);
603
604 return res_nquery_2(statp, name, class, type, answer, anslen, (struct sockaddr *)&f, &l);
605 }
606
607 /*
608 * Perform a call on res_query on the concatenation of name and domain,
609 * removing a trailing dot from name if domain is NULL.
610 */
611 int
612 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)
613 {
614 char nbuf[NS_MAXDNAME];
615 const char *longname = nbuf;
616 int n, d;
617
618 #ifdef DEBUG
619 if (statp->options & RES_DEBUG) printf(";; res_nquerydomain(%s, %s, %d, %d)\n", name, domain?domain:"<Nil>", class, type);
620 #endif
621 if (domain == NULL)
622 {
623 /*
624 * Check for trailing '.';
625 * copy without '.' if present.
626 */
627 n = strlen(name);
628 if (n >= NS_MAXDNAME)
629 {
630 __h_errno_set(statp, NO_RECOVERY);
631 return (-1);
632 }
633
634 n--;
635 if (n >= 0 && name[n] == '.')
636 {
637 strncpy(nbuf, name, n);
638 nbuf[n] = '\0';
639 }
640 else
641 {
642 longname = name;
643 }
644 }
645 else
646 {
647 n = strlen(name);
648 d = strlen(domain);
649 if (n + d + 1 >= NS_MAXDNAME)
650 {
651 __h_errno_set(statp, NO_RECOVERY);
652 return (-1);
653 }
654
655 sprintf(nbuf, "%s.%s", name, domain);
656 }
657
658 return (res_nquery_2(statp, longname, class, type, answer, anslen, from, fromlen));
659 }
660
661 int
662 res_nquerydomain(res_state statp, const char *name, const char *domain, int class, int type, u_char *answer, int anslen)
663 {
664 struct sockaddr_storage f;
665 int l;
666
667 l = sizeof(struct sockaddr_storage);
668
669 return res_nquerydomain_2(statp, name, domain, class, type, answer, anslen, (struct sockaddr *)&f, &l);
670 }
671
672 /*
673 * Formulate a normal query, send, and retrieve answer in supplied buffer.
674 * Return the size of the response on success, -1 on error.
675 * If enabled, implement search rules until answer or unrecoverable failure
676 * is detected. Error code, if any, is left in H_ERRNO.
677 */
678 int
679 res_nsearch_2(res_state statp, const char *name, int class, int type, u_char *answer, int anslen, struct sockaddr *from, int *fromlen)
680 {
681 const char *cp, * const *domain;
682 HEADER *hp = (HEADER *) answer;
683 char tmp[NS_MAXDNAME];
684 u_int dots;
685 int trailing_dot, ret, saved_herrno;
686 int got_nodata = 0, got_servfail = 0, root_on_list = 0;
687 int tried_as_is = 0;
688 int searched = 0;
689
690 errno = 0;
691 __h_errno_set(statp, HOST_NOT_FOUND); /* True if we never query. */
692
693 dots = 0;
694 for (cp = name; *cp != '\0'; cp++) dots += (*cp == '.');
695
696 trailing_dot = 0;
697 if (cp > name && *--cp == '.') trailing_dot++;
698
699 /* If there aren't any dots, it could be a user-level alias. */
700 if (!dots && (cp = res_hostalias(statp, name, tmp, sizeof tmp))!= NULL)
701 return (res_nquery(statp, cp, class, type, answer, anslen));
702
703 /*
704 * If there are enough dots in the name, let's just give it a
705 * try 'as is'. The threshold can be set with the "ndots" option.
706 * Also, query 'as is', if there is a trailing dot in the name.
707 */
708 saved_herrno = -1;
709 if (dots >= statp->ndots || trailing_dot)
710 {
711 ret = res_nquerydomain_2(statp, name, NULL, class, type, answer, anslen, from, fromlen);
712 if (ret > 0 || trailing_dot) return (ret);
713 saved_herrno = h_errno;
714 tried_as_is++;
715 }
716
717 /*
718 * We do at least one level of search if
719 * - there is no dot and RES_DEFNAME is set, or
720 * - there is at least one dot, there is no trailing dot,
721 * and RES_DNSRCH is set.
722 */
723 if ((!dots && (statp->options & RES_DEFNAMES) != 0) || (dots && !trailing_dot && (statp->options & RES_DNSRCH) != 0))
724 {
725 int done = 0;
726
727 for (domain = (const char * const *)statp->dnsrch; *domain && !done; domain++)
728 {
729 searched = 1;
730
731 if (domain[0][0] == '\0' || (domain[0][0] == '.' && domain[0][1] == '\0')) root_on_list++;
732
733 ret = res_nquerydomain_2(statp, name, *domain, class, type, answer, anslen, from, fromlen);
734 if (ret > 0) return (ret);
735
736 /*
737 * If no server present, give up.
738 * If name isn't found in this domain,
739 * keep trying higher domains in the search list
740 * (if that's enabled).
741 * On a NO_DATA error, keep trying, otherwise
742 * a wildcard entry of another type could keep us
743 * from finding this entry higher in the domain.
744 * If we get some other error (negative answer or
745 * server failure), then stop searching up,
746 * but try the input name below in case it's
747 * fully-qualified.
748 */
749 if (errno == ECONNREFUSED)
750 {
751 __h_errno_set(statp, TRY_AGAIN);
752 return (-1);
753 }
754
755 switch (statp->res_h_errno)
756 {
757 case NO_DATA:
758 got_nodata++;
759 /* FALLTHROUGH */
760 case HOST_NOT_FOUND:
761 /* keep trying */
762 break;
763 case TRY_AGAIN:
764 if (hp->rcode == ns_r_refused)
765 {
766 /* try next search element, if any */
767 got_servfail++;
768 break;
769 }
770 /* FALLTHROUGH */
771 default:
772 /* anything else implies that we're done */
773 done++;
774 }
775
776 /* if we got here for some reason other than DNSRCH,
777 * we only wanted one iteration of the loop, so stop.
778 */
779 if ((statp->options & RES_DNSRCH) == 0) done++;
780 }
781 }
782
783 /*
784 * If the query has not already been tried as is then try it
785 * unless RES_NOTLDQUERY is set and there were no dots.
786 */
787 if ((dots || !searched || (statp->options & RES_NOTLDQUERY) == 0) && !(tried_as_is || root_on_list))
788 {
789 ret = res_nquerydomain_2(statp, name, NULL, class, type, answer, anslen, from, fromlen);
790 if (ret > 0) return (ret);
791 }
792
793 /* if we got here, we didn't satisfy the search.
794 * if we did an initial full query, return that query's H_ERRNO
795 * (note that we wouldn't be here if that query had succeeded).
796 * else if we ever got a nodata, send that back as the reason.
797 * else send back meaningless H_ERRNO, that being the one from
798 * the last DNSRCH we did.
799 */
800 if (saved_herrno != -1)
801 __h_errno_set(statp, saved_herrno);
802 else if (got_nodata)
803 __h_errno_set(statp, NO_DATA);
804 else if (got_servfail)
805 __h_errno_set(statp, TRY_AGAIN);
806 return (-1);
807 }
808
809 int
810 __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)
811 {
812 const char *cp, *domain;
813 HEADER *hp = (HEADER *) answer;
814 char tmp[NS_MAXDNAME];
815 u_int dots;
816 int trailing_dot, ret, saved_herrno, i;
817 int got_nodata = 0, got_servfail = 0, root_on_list = 0;
818 int tried_as_is = 0;
819 int searched = 0;
820
821 errno = 0;
822 __h_errno_set(statp, HOST_NOT_FOUND); /* True if we never query. */
823
824 dots = 0;
825 for (cp = name; *cp != '\0'; cp++) dots += (*cp == '.');
826
827 trailing_dot = 0;
828 if (cp > name && *--cp == '.') trailing_dot++;
829
830 /* If there aren't any dots, it could be a user-level alias. */
831 if (!dots && (cp = res_hostalias(statp, name, tmp, sizeof tmp)) != NULL)
832 return (res_nquery(statp, cp, class, type, answer, anslen));
833
834 /*
835 * If there are enough dots in the name, let's just give it a
836 * try 'as is'. The threshold can be set with the "ndots" option.
837 * Also, query 'as is', if there is a trailing dot in the name.
838 */
839 saved_herrno = -1;
840 if (dots >= statp->ndots || trailing_dot)
841 {
842 ret = res_nquerydomain(statp, name, NULL, class, type, answer, anslen);
843 if (ret > 0 || trailing_dot) return ret;
844 saved_herrno = h_errno;
845 tried_as_is++;
846 }
847
848 /*
849 * We do at least one level of search if
850 * - there is no dot and RES_DEFNAME is set, or
851 * - there is at least one dot, there is no trailing dot,
852 * and RES_DNSRCH is set.
853 */
854 if ((!dots && (statp->options & RES_DEFNAMES) != 0) || (dots && !trailing_dot && (statp->options & RES_DNSRCH) != 0))
855 {
856 int done = 0;
857
858 for (i = 0; i < nsearch; i++)
859 {
860 domain = search[i];
861 searched = 1;
862
863 if (domain[0] == '\0' || (domain[0] == '.' && domain[1] == '\0')) root_on_list++;
864
865 ret = res_nquerydomain_2(statp, name, domain, class, type, answer, anslen, from, fromlen);
866 if (ret > 0) return ret;
867
868 /*
869 * If no server present, give up.
870 * If name isn't found in this domain,
871 * keep trying higher domains in the search list
872 * (if that's enabled).
873 * On a NO_DATA error, keep trying, otherwise
874 * a wildcard entry of another type could keep us
875 * from finding this entry higher in the domain.
876 * If we get some other error (negative answer or
877 * server failure), then stop searching up,
878 * but try the input name below in case it's
879 * fully-qualified.
880 */
881 if (errno == ECONNREFUSED)
882 {
883 __h_errno_set(statp, TRY_AGAIN);
884 return -1;
885 }
886
887 switch (statp->res_h_errno)
888 {
889 case NO_DATA:
890 got_nodata++;
891 /* FALLTHROUGH */
892 case HOST_NOT_FOUND:
893 /* keep trying */
894 break;
895 case TRY_AGAIN:
896 if (hp->rcode == ns_r_refused)
897 {
898 /* try next search element, if any */
899 got_servfail++;
900 break;
901 }
902 /* FALLTHROUGH */
903 default:
904 /* anything else implies that we're done */
905 done++;
906 }
907
908 /*
909 * if we got here for some reason other than DNSRCH,
910 * we only wanted one iteration of the loop, so stop.
911 */
912 if ((statp->options & RES_DNSRCH) == 0) done++;
913 }
914 }
915
916 /*
917 * If the query has not already been tried as is then try it
918 * unless RES_NOTLDQUERY is set and there were no dots.
919 */
920 if ((dots || !searched || (statp->options & RES_NOTLDQUERY) == 0) && !(tried_as_is || root_on_list))
921 {
922 ret = res_nquerydomain_2(statp, name, NULL, class, type, answer, anslen, from, fromlen);
923 if (ret > 0) return ret;
924 }
925
926 /*
927 * we got here, we didn't satisfy the search.
928 * if we did an initial full query, return that query's H_ERRNO
929 * (note that we wouldn't be here if that query had succeeded).
930 * else if we ever got a nodata, send that back as the reason.
931 * else send back meaningless H_ERRNO, that being the one from
932 * the last DNSRCH we did.
933 */
934 if (saved_herrno != -1) __h_errno_set(statp, saved_herrno);
935 else if (got_nodata) __h_errno_set(statp, NO_DATA);
936 else if (got_servfail) __h_errno_set(statp, TRY_AGAIN);
937 return -1;
938 }
939
940 int
941 res_nsearch(res_state statp, const char *name, int class, int type, u_char *answer, int anslen)
942 {
943 struct sockaddr_storage f;
944 int l;
945
946 l = sizeof(struct sockaddr_storage);
947
948 return res_nsearch_2(statp, name, class, type, answer, anslen, (struct sockaddr *)&f, &l);
949 }
950
951 const char *
952 res_hostalias(const res_state statp, const char *name, char *dst, size_t siz)
953 {
954 char *file, *cp1, *cp2;
955 char buf[BUFSIZ];
956 FILE *fp;
957
958 if (statp->options & RES_NOALIASES) return (NULL);
959
960 file = getenv("HOSTALIASES");
961 if (file == NULL || (fp = fopen(file, "r")) == NULL) return (NULL);
962
963 setbuf(fp, NULL);
964 buf[sizeof(buf) - 1] = '\0';
965 while (fgets(buf, sizeof(buf), fp))
966 {
967 for (cp1 = buf; *cp1 && !isspace((unsigned char)*cp1); ++cp1) ;
968
969 if (!*cp1) break;
970 *cp1 = '\0';
971
972 if (ns_samename(buf, name) == 1)
973 {
974 while (isspace((unsigned char)*++cp1)) ;
975
976 if (!*cp1) break;
977
978 for (cp2 = cp1 + 1; *cp2 && !isspace((unsigned char)*cp2); ++cp2) ;
979
980 *cp2 = '\0';
981 strncpy(dst, cp1, siz - 1);
982 dst[siz - 1] = '\0';
983 fclose(fp);
984 return (dst);
985 }
986 }
987
988 fclose(fp);
989 return (NULL);
990 }