libresolv-41.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 #include <notify.h>
98 #include <pthread.h>
99 #ifndef __APPLE__
100 #include "port_after.h"
101 #endif
102
103 /* interrupt mechanism is implemented in res_send.c */
104 __private_extern__ int interrupt_pipe_enabled;
105 __private_extern__ pthread_key_t interrupt_pipe_key;
106
107 /* Options. Leave them on. */
108 #define DEBUG
109
110 #if PACKETSZ > 1024
111 #define MAXPACKET PACKETSZ
112 #else
113 #define MAXPACKET 1024
114 #endif
115
116 #define BILLION 1000000000
117
118 /* 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" */
119 #define IPv6_REVERSE_LEN 72
120
121 /* index of the trailing char that must be "8", "9", "A", "a", "b", or "B" */
122 #define IPv6_REVERSE_LINK_LOCAL_TRAILING_CHAR 58
123
124 /* index of low-order nibble of embedded scope id */
125 #define IPv6_REVERSE_LINK_LOCAL_SCOPE_ID_LOW 48
126
127 const static uint8_t hexval[128] =
128 {
129 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0 - 15 */
130 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16 - 31 */
131 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 32 - 47 */
132 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, /* 48 - 63 */
133 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 64 - 79 */
134 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 95 */
135 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 96 - 111 */
136 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 112 - 127 */
137 };
138
139 struct res_query_context
140 {
141 u_char *answer;
142 size_t anslen;
143 size_t ansmaxlen;
144 uint16_t lastanstype;
145 uint32_t ifnum;
146 uint32_t res_flags;
147 DNSServiceFlags flags;
148 DNSServiceErrorType error;
149 };
150
151 static void
152 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)
153 {
154 struct res_query_context *context;
155 int n;
156 size_t buflen;
157 u_char *dnlist[2], *cp;
158 HEADER *ans;
159 struct in6_addr a6;
160
161 context = (struct res_query_context *)ctx;
162
163 context->flags = flags;
164 context->error = errorCode;
165
166 if (errorCode != kDNSServiceErr_NoError)
167 {
168 if (context->res_flags & RES_DEBUG) printf(";; res_query_mDNSResponder callback [%s %hu %hu]: error %u\n", fullname, rrtype, rrclass, errorCode);
169 return;
170 }
171
172 buflen = context->ansmaxlen - context->anslen;
173 if (buflen < NS_HFIXEDSZ)
174 {
175 if (context->res_flags & RES_DEBUG) printf(";; res_query_mDNSResponder callback [%s %hu %hu]: malformed reply\n", fullname, rrtype, rrclass);
176 return;
177 }
178
179 dnlist[0] = context->answer + NS_HFIXEDSZ;
180 dnlist[1] = NULL;
181
182 cp = context->answer + context->anslen;
183
184 n = dn_comp((char *)fullname, cp, buflen, dnlist, &dnlist[1]);
185 if (n < 0)
186 {
187 if (context->res_flags & RES_DEBUG) printf(";; res_query_mDNSResponder callback [%s %hu %hu]: name mismatch\n", fullname, rrtype, rrclass);
188 return;
189 }
190
191 /*
192 * Check that there is enough space in the buffer for the resource name (n),
193 * the resource record data (rdlen) and the resource record header (10).
194 */
195 if (buflen < n + rdlen + 10)
196 {
197 if (context->res_flags & RES_DEBUG) printf(";; res_query_mDNSResponder callback [%s %hu %hu]: insufficient buffer space for reply\n", fullname, rrtype, rrclass);
198 return;
199 }
200
201 if (context->res_flags & RES_DEBUG) printf(";; res_query_mDNSResponder callback for %s %hu %hu\n", fullname, rrtype, rrclass);
202
203 cp += n;
204 buflen -= n;
205
206 putshort(rrtype, cp);
207 cp += sizeof(uint16_t);
208
209 putshort(rrclass, cp);
210 cp += sizeof(uint16_t);
211
212 putlong(ttl, cp);
213 cp += sizeof(uint32_t);
214
215 putshort(rdlen, cp);
216 cp += sizeof(uint16_t);
217
218 memcpy(cp, rdata, rdlen);
219 cp += rdlen;
220
221 ans = (HEADER *)context->answer;
222 ans->ancount = htons(ntohs(ans->ancount) + 1);
223
224 context->anslen = (size_t)(cp - context->answer);
225
226 context->lastanstype = rrtype;
227
228 /*
229 * Save the interface number for the first AAAA record for link-local addresses.
230 * It's used by getaddrinfo to set the scope id.
231 */
232 if ((context->ifnum == 0) && (rrtype == ns_t_aaaa))
233 {
234 memset(&a6, 0, sizeof(struct in6_addr));
235 memcpy(&a6, rdata, sizeof(struct in6_addr));
236 if (IN6_IS_ADDR_LINKLOCAL(&a6)) context->ifnum = ifIndex;
237 }
238 }
239
240 static void
241 h_errno_for_dnssd_err(DNSServiceErrorType dnssd_err, int *h_errno_err)
242 {
243 switch (dnssd_err)
244 {
245 case kDNSServiceErr_NoError:
246 *h_errno_err = NETDB_SUCCESS;
247 break;
248 case kDNSServiceErr_Unknown:
249 *h_errno_err = NO_RECOVERY;
250 break;
251 case kDNSServiceErr_NoSuchRecord:
252 *h_errno_err = NO_DATA;
253 break;
254 case kDNSServiceErr_NoSuchName:
255 *h_errno_err = HOST_NOT_FOUND;
256 break;
257 case kDNSServiceErr_NoMemory:
258 default:
259 *h_errno_err = NETDB_INTERNAL;
260 break;
261 }
262 }
263
264 static int
265 _is_rev_link_local(const char *name)
266 {
267 int len, i;
268
269 if (name == NULL) return 0;
270
271 len = strlen(name);
272 if (len == 0) return 0;
273
274 /* check for trailing '.' */
275 if (name[len - 1] == '.') len--;
276
277 if (len != IPv6_REVERSE_LEN) return 0;
278
279 i = IPv6_REVERSE_LINK_LOCAL_TRAILING_CHAR;
280 if ((name[i] != '8') && (name[i] != '9') && (name[i] != 'A') && (name[i] != 'a') && (name[i] != 'B') && (name[i] != 'b')) return 0;
281
282 i = IPv6_REVERSE_LINK_LOCAL_TRAILING_CHAR + 1;
283 if (strncasecmp(name + i, ".e.f.ip6.arpa", 13)) return 0;
284
285 for (i = 0; i < IPv6_REVERSE_LINK_LOCAL_TRAILING_CHAR; i += 2)
286 {
287 if (name[i] < '0') return 0;
288 if ((name[i] > '9') && (name[i] < 'A')) return 0;
289 if ((name[i] > 'F') && (name[i] < 'a')) return 0;
290 if (name[i] > 'f') return 0;
291 if (name[i + 1] != '.') return 0;
292 }
293
294 return 1;
295 }
296
297 int
298 res_query_mDNSResponder(res_state statp, const char *name, int class, int type, u_char *answer, int anslen, struct sockaddr *from, uint32_t *fromlen)
299 {
300 DNSServiceRef sdRef;
301 DNSServiceErrorType result;
302 struct res_query_context context;
303 int i, kq, n, wait, cancelled, notify_token, status;
304 struct kevent mevent, ievent, event;
305 struct timeval ctv;
306 struct timespec now, finish, timeout;
307 HEADER *ans;
308 uint32_t iface;
309 uint16_t nibble;
310 char *qname, *notify_name;
311 int *interrupt_pipe;
312 uint64_t exit_requested;
313
314 interrupt_pipe = NULL;
315 result = 0;
316 kq = -1;
317 ans = (HEADER *)answer;
318 cancelled = 0;
319 ans->rcode = 0;
320
321 memset(&context, 0, sizeof(struct res_query_context));
322
323 /* Build a dummy DNS header with question for the answer */
324 context.res_flags = statp->options;
325 context.answer = answer;
326 context.ansmaxlen = anslen;
327 context.anslen = res_nmkquery(statp, ns_o_query, name, class, type, NULL, 0, NULL, answer, anslen);
328 if (context.anslen <= 0) return 0;
329
330 /* Mark DNS packet as a response */
331 ans->qr = 1;
332 ans->qr = htons(ans->qr);
333
334 /* Pull out Scope ID in link-local reverse queries */
335 qname = (char *)name;
336 iface = 0;
337 if (_is_rev_link_local(name))
338 {
339 /* _is_rev_link_local rejects chars > 127 so it's safe to index into hexval */
340 i = IPv6_REVERSE_LINK_LOCAL_SCOPE_ID_LOW;
341 nibble = hexval[(uint32_t)name[i]];
342 iface = nibble;
343
344 i += 2;
345 nibble = hexval[(uint32_t)name[i]];
346 iface += (nibble << 4);
347
348 i += 2;
349 nibble = hexval[(uint32_t)name[i]];
350 iface += (nibble << 8);
351
352 i += 2;
353 nibble = hexval[(uint32_t)name[i]];
354 iface += (nibble << 12);
355
356 if (iface != 0)
357 {
358 qname = strdup(name);
359 if (qname == NULL)
360 {
361 h_errno = NO_RECOVERY;
362 errno = ENOMEM;
363 return -1;
364 }
365
366 i = IPv6_REVERSE_LINK_LOCAL_SCOPE_ID_LOW;
367 qname[i] = '0';
368 qname[i + 2] = '0';
369 qname[i + 4] = '0';
370 qname[i + 6] = '0';
371 }
372 }
373
374 if (statp->options & RES_DEBUG) printf(";; res_query_mDNSResponder\n");
375
376 result = DNSServiceQueryRecord(&sdRef, kDNSServiceFlagsReturnIntermediates, iface, qname, type, class, res_query_callback, &context);
377 if (iface != 0) free(qname);
378
379 if (result != 0) return 0;
380
381 /* Use a kqueue to wait for a response from mDNSResponder */
382 kq = kqueue();
383
384 /* determine the maximum time to wait for a result */
385 gettimeofday(&ctv, NULL);
386
387 /* N.B. statp->retrans is actually the total timeount in seconds */
388 timeout.tv_sec = statp->retrans;
389 timeout.tv_nsec = 0;
390
391 finish.tv_sec = ctv.tv_sec + statp->retrans;
392 finish.tv_nsec = ctv.tv_usec * 1000;
393
394 /* add mdns reply FD to kqueue */
395 EV_SET(&mevent, DNSServiceRefSockFD(sdRef), EVFILT_READ, EV_ADD, 0, 0, 0);
396 n = kevent(kq, &mevent, 1, NULL, 0, NULL);
397 if (n != 0) return 0;
398
399 /* add interrupt pipe to kqueue if interrupt is enabled */
400 if (interrupt_pipe_enabled != 0)
401 {
402 interrupt_pipe = pthread_getspecific(interrupt_pipe_key);
403 if (interrupt_pipe != NULL)
404 {
405 if (interrupt_pipe[0] >= 0)
406 {
407 EV_SET(&ievent, interrupt_pipe[0], EVFILT_READ, EV_ADD, 0, 0, (void *)name);
408 /* allow this to fail silently (should never happen, but it would only break interrupts */
409 n = kevent(kq, &ievent, 1, NULL, 0, NULL);
410 }
411 }
412 }
413
414 /*
415 * Get notification token
416 * we use a self-notification token to allow a caller
417 * to signal the thread doing this DNS query to quit.
418 */
419 notify_name = NULL;
420 notify_token = -1;
421
422 asprintf(&notify_name, "self.thread.%lu", (unsigned long)pthread_self());
423 if (notify_name != NULL)
424 {
425 status = notify_register_plain(notify_name, &notify_token);
426 free(notify_name);
427 }
428
429 wait = 1;
430 while (wait == 1)
431 {
432 memset(&event, 0, sizeof(struct kevent));
433 n = kevent(kq, NULL, 0, &event, 1, &timeout);
434
435 if (notify_token != -1)
436 {
437 exit_requested = 0;
438 status = notify_get_state(notify_token, &exit_requested);
439 if (exit_requested == ThreadStateExitRequested)
440 {
441 /* interrupted */
442 if (statp->options & RES_DEBUG) printf(";; cancelled\n");
443 cancelled = 1;
444 break;
445 }
446 }
447
448 if (n < 0)
449 {
450 if (errno == EINTR) goto keep_waiting;
451 h_errno = NO_RECOVERY;
452 wait = 0;
453 }
454 else if ((n == 0) && (ans->ancount == 0))
455 {
456 h_errno = TRY_AGAIN;
457 wait = 0;
458 }
459 else if (event.udata == (void *)name)
460 {
461 /* interrupted */
462 if (statp->options & RES_DEBUG) printf(";; cancelled\n");
463 cancelled = 1;
464 break;
465 }
466 else
467 {
468 result = DNSServiceProcessResult(sdRef);
469 if ((result != 0) || (context.error != 0))
470 {
471 if (result == 0) result = context.error;
472 h_errno_for_dnssd_err(result, &h_errno);
473 wait = 0;
474 }
475
476 if ((ans->ancount > 0) && ((context.flags & kDNSServiceFlagsMoreComing) == 0) && ((context.lastanstype != ns_t_cname) || (type == ns_t_cname))) wait = 0;
477 }
478
479 keep_waiting:
480
481 if (wait == 1)
482 {
483 /* calculate remaining timeout */
484 gettimeofday(&ctv, NULL);
485
486 now.tv_sec = ctv.tv_sec;
487 now.tv_nsec = ctv.tv_usec * 1000;
488
489 timeout.tv_sec = finish.tv_sec - now.tv_sec;
490 if (finish.tv_nsec >= now.tv_nsec)
491 {
492 timeout.tv_nsec = finish.tv_nsec - now.tv_nsec;
493 }
494 else
495 {
496 timeout.tv_nsec = BILLION - now.tv_nsec + finish.tv_nsec;
497 timeout.tv_sec--;
498 }
499 }
500 }
501
502 if (notify_token != -1) notify_cancel(notify_token);
503 DNSServiceRefDeallocate(sdRef);
504 close(kq);
505
506 if ((ans->ancount == 0) || (cancelled == 1)) context.anslen = -1;
507
508 if ((from != NULL) && (fromlen != NULL) && (context.ifnum != 0))
509 {
510 ((struct sockaddr_in6 *)from)->sin6_len = sizeof(struct sockaddr_in6);
511 ((struct sockaddr_in6 *)from)->sin6_family = AF_INET6;
512 ((struct sockaddr_in6 *)from)->sin6_addr.__u6_addr.__u6_addr8[15] = 1;
513 ((struct sockaddr_in6 *)from)->sin6_scope_id = context.ifnum;
514 *fromlen = sizeof(struct sockaddr_in6);
515 }
516
517 return context.anslen;
518 }
519
520 static int
521 res_soa_minimum(const u_char *msg, int len)
522 {
523 ns_msg handle;
524 const u_char *eom;
525 uint32_t i, b;
526 int min, soa_min;
527
528 eom = msg + len;
529
530 handle._msg = msg;
531 handle._eom = eom;
532
533 if (msg + NS_INT16SZ > eom) return -1;
534 NS_GET16(handle._id, msg);
535
536 if (msg + NS_INT16SZ > eom) return -1;
537 NS_GET16(handle._flags, msg);
538
539 for (i = 0; i < ns_s_max; i++)
540 {
541 if (msg + NS_INT16SZ > eom) return -1;
542 NS_GET16(handle._counts[i], msg);
543 }
544
545 if (handle._counts[ns_s_ns] == 0) return -1;
546
547 /* Skip forward to nameserver section */
548 for (i = 0; i < ns_s_ns; i++)
549 {
550 if (handle._counts[i] == 0) handle._sections[i] = NULL;
551 else
552 {
553 b = ns_skiprr(msg, eom, (ns_sect)i, handle._counts[i]);
554 if (b < 0) return -1;
555
556 handle._sections[i] = msg;
557 msg += b;
558 }
559 }
560
561 min = -1;
562 for (i = 0; i < handle._counts[ns_s_ns]; i++)
563 {
564 b = ns_skiprr(msg, eom, ns_s_ns, 1);
565 if (b < 0) return -1;
566
567 memcpy(&soa_min, msg + b - sizeof(int32_t), sizeof(int32_t));
568 soa_min = ntohl(soa_min);
569 if ((i == 0) || (soa_min < min)) min = soa_min;
570 msg += b;
571 }
572
573 return min;
574 }
575
576 /*
577 * Formulate a normal query, send, and await answer.
578 * Returned answer is placed in supplied buffer "answer".
579 * Perform preliminary check of answer, returning success only
580 * if no error is indicated and the answer count is nonzero.
581 * Return the size of the response on success, -1 on error.
582 * Error number is left in H_ERRNO.
583 *
584 * Caller must parse answer and determine whether it answers the question.
585 */
586 __private_extern__ int
587 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)
588 {
589 u_char buf[MAXPACKET];
590 HEADER *hp = (HEADER *) answer;
591 int n;
592 u_int oflags;
593
594 if (min != NULL) *min = -1;
595
596 oflags = statp->_flags;
597
598 again:
599 __h_errno_set(statp, 0);
600 hp->rcode = ns_r_noerror; /* default */
601
602 #ifdef DEBUG
603 if (statp->options & RES_DEBUG) printf(";; res_query(%s, %d, %d)\n", name, class, type);
604 #endif
605
606 n = res_nmkquery(statp, ns_o_query, name, class, type, NULL, 0, NULL, buf, sizeof(buf));
607 #ifdef RES_USE_EDNS0
608 if (n > 0 && (statp->_flags & RES_F_EDNS0ERR) == 0 && (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0)
609 n = res_nopt(statp, n, buf, sizeof(buf), anslen);
610 #endif
611 if (n <= 0)
612 {
613 #ifdef DEBUG
614 if (statp->options & RES_DEBUG) printf(";; res_query: mkquery failed\n");
615 #endif
616 __h_errno_set(statp, NO_RECOVERY);
617 return (n);
618 }
619
620 n = res_nsend_2(statp, buf, n, answer, anslen, from, fromlen);
621 if (n < 0)
622 {
623 #ifdef RES_USE_EDNS0
624 /* if the query choked with EDNS0, retry without EDNS0 */
625 if ((statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0 && ((oflags ^ statp->_flags) & RES_F_EDNS0ERR) != 0)
626 {
627 statp->_flags |= RES_F_EDNS0ERR;
628 if (statp->options & RES_DEBUG) printf(";; res_nquery: retry without EDNS0\n");
629 goto again;
630 }
631 #endif
632 #ifdef DEBUG
633 if (statp->options & RES_DEBUG) printf(";; res_query: send error\n");
634 #endif
635 __h_errno_set(statp, TRY_AGAIN);
636 return (n);
637 }
638
639 if ((hp->rcode == ns_r_nxdomain) || ((hp->rcode == ns_r_noerror) && (ntohs(hp->ancount) == 0)))
640 {
641 if (min != NULL)
642 {
643 *min = res_soa_minimum(answer, anslen);
644 if (statp->options & RES_DEBUG) printf(";; res_nquery: SOA minimum TTL = %d\n", *min);
645 }
646 }
647
648 if (hp->rcode != ns_r_noerror || ntohs(hp->ancount) == 0)
649 {
650 #ifdef DEBUG
651 if (statp->options & RES_DEBUG) printf(";; rcode = %d, ancount=%d\n", hp->rcode, ntohs(hp->ancount));
652 #endif
653 switch (hp->rcode)
654 {
655 case ns_r_nxdomain:
656 __h_errno_set(statp, HOST_NOT_FOUND);
657 break;
658 case ns_r_servfail:
659 __h_errno_set(statp, TRY_AGAIN);
660 break;
661 case ns_r_noerror:
662 __h_errno_set(statp, NO_DATA);
663 break;
664 case ns_r_formerr:
665 case ns_r_notimpl:
666 case ns_r_refused:
667 default:
668 __h_errno_set(statp, NO_RECOVERY);
669 break;
670 }
671
672 return (-1);
673 }
674
675 return (n);
676 }
677
678 int
679 res_nquery_2(res_state statp, const char *name, int class, int type, u_char *answer, int anslen, struct sockaddr *from, int *fromlen)
680 {
681 int unused = 0;
682
683 return res_nquery_soa_min(statp, name, class, type, answer, anslen, from, fromlen, &unused);
684 }
685
686 int
687 res_nquery(res_state statp, const char *name, int class, int type, u_char *answer, int anslen)
688 {
689 struct sockaddr_storage f;
690 int l;
691
692 l = sizeof(struct sockaddr_storage);
693
694 return res_nquery_2(statp, name, class, type, answer, anslen, (struct sockaddr *)&f, &l);
695 }
696
697 /*
698 * Perform a call on res_query on the concatenation of name and domain,
699 * removing a trailing dot from name if domain is NULL.
700 */
701 int
702 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)
703 {
704 char nbuf[NS_MAXDNAME];
705 const char *longname = nbuf;
706 int n, d;
707
708 #ifdef DEBUG
709 if (statp->options & RES_DEBUG) printf(";; res_nquerydomain(%s, %s, %d, %d)\n", name, domain?domain:"<Nil>", class, type);
710 #endif
711 if (domain == NULL)
712 {
713 /*
714 * Check for trailing '.';
715 * copy without '.' if present.
716 */
717 n = strlen(name);
718 if (n >= NS_MAXDNAME)
719 {
720 __h_errno_set(statp, NO_RECOVERY);
721 return (-1);
722 }
723
724 n--;
725 if (n >= 0 && name[n] == '.')
726 {
727 strncpy(nbuf, name, n);
728 nbuf[n] = '\0';
729 }
730 else
731 {
732 longname = name;
733 }
734 }
735 else
736 {
737 n = strlen(name);
738 d = strlen(domain);
739 if (n + d + 1 >= NS_MAXDNAME)
740 {
741 __h_errno_set(statp, NO_RECOVERY);
742 return (-1);
743 }
744
745 sprintf(nbuf, "%s.%s", name, domain);
746 }
747
748 return (res_nquery_2(statp, longname, class, type, answer, anslen, from, fromlen));
749 }
750
751 int
752 res_nquerydomain(res_state statp, const char *name, const char *domain, int class, int type, u_char *answer, int anslen)
753 {
754 struct sockaddr_storage f;
755 int l;
756
757 l = sizeof(struct sockaddr_storage);
758
759 return res_nquerydomain_2(statp, name, domain, class, type, answer, anslen, (struct sockaddr *)&f, &l);
760 }
761
762 /*
763 * Formulate a normal query, send, and retrieve answer in supplied buffer.
764 * Return the size of the response on success, -1 on error.
765 * If enabled, implement search rules until answer or unrecoverable failure
766 * is detected. Error code, if any, is left in H_ERRNO.
767 */
768 int
769 res_nsearch_2(res_state statp, const char *name, int class, int type, u_char *answer, int anslen, struct sockaddr *from, int *fromlen)
770 {
771 const char *cp, * const *domain;
772 HEADER *hp = (HEADER *) answer;
773 char tmp[NS_MAXDNAME];
774 u_int dots;
775 int trailing_dot, ret, saved_herrno;
776 int got_nodata = 0, got_servfail = 0, root_on_list = 0;
777 int tried_as_is = 0;
778 int searched = 0;
779
780 errno = 0;
781 __h_errno_set(statp, HOST_NOT_FOUND); /* True if we never query. */
782
783 dots = 0;
784 for (cp = name; *cp != '\0'; cp++) dots += (*cp == '.');
785
786 trailing_dot = 0;
787 if (cp > name && *--cp == '.') trailing_dot++;
788
789 /* If there aren't any dots, it could be a user-level alias. */
790 if (!dots && (cp = res_hostalias(statp, name, tmp, sizeof tmp))!= NULL)
791 return (res_nquery(statp, cp, class, type, answer, anslen));
792
793 /*
794 * If there are enough dots in the name, let's just give it a
795 * try 'as is'. The threshold can be set with the "ndots" option.
796 * Also, query 'as is', if there is a trailing dot in the name.
797 */
798 saved_herrno = -1;
799 if (dots >= statp->ndots || trailing_dot)
800 {
801 ret = res_nquerydomain_2(statp, name, NULL, class, type, answer, anslen, from, fromlen);
802 if (ret > 0 || trailing_dot) return (ret);
803 saved_herrno = h_errno;
804 tried_as_is++;
805 }
806
807 /*
808 * We do at least one level of search if
809 * - there is no dot and RES_DEFNAME is set, or
810 * - there is at least one dot, there is no trailing dot,
811 * and RES_DNSRCH is set.
812 */
813 if ((!dots && (statp->options & RES_DEFNAMES) != 0) || (dots && !trailing_dot && (statp->options & RES_DNSRCH) != 0))
814 {
815 int done = 0;
816
817 for (domain = (const char * const *)statp->dnsrch; *domain && !done; domain++)
818 {
819 searched = 1;
820
821 if (domain[0][0] == '\0' || (domain[0][0] == '.' && domain[0][1] == '\0')) root_on_list++;
822
823 ret = res_nquerydomain_2(statp, name, *domain, class, type, answer, anslen, from, fromlen);
824 if (ret > 0) return (ret);
825
826 /*
827 * If no server present, give up.
828 * If name isn't found in this domain,
829 * keep trying higher domains in the search list
830 * (if that's enabled).
831 * On a NO_DATA error, keep trying, otherwise
832 * a wildcard entry of another type could keep us
833 * from finding this entry higher in the domain.
834 * If we get some other error (negative answer or
835 * server failure), then stop searching up,
836 * but try the input name below in case it's
837 * fully-qualified.
838 */
839 if (errno == ECONNREFUSED)
840 {
841 __h_errno_set(statp, TRY_AGAIN);
842 return (-1);
843 }
844
845 switch (statp->res_h_errno)
846 {
847 case NO_DATA:
848 got_nodata++;
849 /* FALLTHROUGH */
850 case HOST_NOT_FOUND:
851 /* keep trying */
852 break;
853 case TRY_AGAIN:
854 if (hp->rcode == ns_r_refused)
855 {
856 /* try next search element, if any */
857 got_servfail++;
858 break;
859 }
860 /* FALLTHROUGH */
861 default:
862 /* anything else implies that we're done */
863 done++;
864 }
865
866 /* if we got here for some reason other than DNSRCH,
867 * we only wanted one iteration of the loop, so stop.
868 */
869 if ((statp->options & RES_DNSRCH) == 0) done++;
870 }
871 }
872
873 /*
874 * If the query has not already been tried as is then try it
875 * unless RES_NOTLDQUERY is set and there were no dots.
876 */
877 if ((dots || !searched || (statp->options & RES_NOTLDQUERY) == 0) && !(tried_as_is || root_on_list))
878 {
879 ret = res_nquerydomain_2(statp, name, NULL, class, type, answer, anslen, from, fromlen);
880 if (ret > 0) return (ret);
881 }
882
883 /* if we got here, we didn't satisfy the search.
884 * if we did an initial full query, return that query's H_ERRNO
885 * (note that we wouldn't be here if that query had succeeded).
886 * else if we ever got a nodata, send that back as the reason.
887 * else send back meaningless H_ERRNO, that being the one from
888 * the last DNSRCH we did.
889 */
890 if (saved_herrno != -1)
891 __h_errno_set(statp, saved_herrno);
892 else if (got_nodata)
893 __h_errno_set(statp, NO_DATA);
894 else if (got_servfail)
895 __h_errno_set(statp, TRY_AGAIN);
896 return (-1);
897 }
898
899 int
900 __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)
901 {
902 const char *cp, *domain;
903 HEADER *hp = (HEADER *) answer;
904 char tmp[NS_MAXDNAME];
905 u_int dots;
906 int trailing_dot, ret, saved_herrno, i;
907 int got_nodata = 0, got_servfail = 0, root_on_list = 0;
908 int tried_as_is = 0;
909 int searched = 0;
910
911 errno = 0;
912 __h_errno_set(statp, HOST_NOT_FOUND); /* True if we never query. */
913
914 dots = 0;
915 for (cp = name; *cp != '\0'; cp++) dots += (*cp == '.');
916
917 trailing_dot = 0;
918 if (cp > name && *--cp == '.') trailing_dot++;
919
920 /* If there aren't any dots, it could be a user-level alias. */
921 if (!dots && (cp = res_hostalias(statp, name, tmp, sizeof tmp)) != NULL)
922 return (res_nquery(statp, cp, class, type, answer, anslen));
923
924 /*
925 * If there are enough dots in the name, let's just give it a
926 * try 'as is'. The threshold can be set with the "ndots" option.
927 * Also, query 'as is', if there is a trailing dot in the name.
928 */
929 saved_herrno = -1;
930 if (dots >= statp->ndots || trailing_dot)
931 {
932 ret = res_nquerydomain(statp, name, NULL, class, type, answer, anslen);
933 if (ret > 0 || trailing_dot) return ret;
934 saved_herrno = h_errno;
935 tried_as_is++;
936 }
937
938 /*
939 * We do at least one level of search if
940 * - there is no dot and RES_DEFNAME is set, or
941 * - there is at least one dot, there is no trailing dot,
942 * and RES_DNSRCH is set.
943 */
944 if ((!dots && (statp->options & RES_DEFNAMES) != 0) || (dots && !trailing_dot && (statp->options & RES_DNSRCH) != 0))
945 {
946 int done = 0;
947
948 for (i = 0; i < nsearch; i++)
949 {
950 domain = search[i];
951 searched = 1;
952
953 if (domain[0] == '\0' || (domain[0] == '.' && domain[1] == '\0')) root_on_list++;
954
955 ret = res_nquerydomain_2(statp, name, domain, class, type, answer, anslen, from, fromlen);
956 if (ret > 0) return ret;
957
958 /*
959 * If no server present, give up.
960 * If name isn't found in this domain,
961 * keep trying higher domains in the search list
962 * (if that's enabled).
963 * On a NO_DATA error, keep trying, otherwise
964 * a wildcard entry of another type could keep us
965 * from finding this entry higher in the domain.
966 * If we get some other error (negative answer or
967 * server failure), then stop searching up,
968 * but try the input name below in case it's
969 * fully-qualified.
970 */
971 if (errno == ECONNREFUSED)
972 {
973 __h_errno_set(statp, TRY_AGAIN);
974 return -1;
975 }
976
977 switch (statp->res_h_errno)
978 {
979 case NO_DATA:
980 got_nodata++;
981 /* FALLTHROUGH */
982 case HOST_NOT_FOUND:
983 /* keep trying */
984 break;
985 case TRY_AGAIN:
986 if (hp->rcode == ns_r_refused)
987 {
988 /* try next search element, if any */
989 got_servfail++;
990 break;
991 }
992 /* FALLTHROUGH */
993 default:
994 /* anything else implies that we're done */
995 done++;
996 }
997
998 /*
999 * if we got here for some reason other than DNSRCH,
1000 * we only wanted one iteration of the loop, so stop.
1001 */
1002 if ((statp->options & RES_DNSRCH) == 0) done++;
1003 }
1004 }
1005
1006 /*
1007 * If the query has not already been tried as is then try it
1008 * unless RES_NOTLDQUERY is set and there were no dots.
1009 */
1010 if ((dots || !searched || (statp->options & RES_NOTLDQUERY) == 0) && !(tried_as_is || root_on_list))
1011 {
1012 ret = res_nquerydomain_2(statp, name, NULL, class, type, answer, anslen, from, fromlen);
1013 if (ret > 0) return ret;
1014 }
1015
1016 /*
1017 * we got here, we didn't satisfy the search.
1018 * if we did an initial full query, return that query's H_ERRNO
1019 * (note that we wouldn't be here if that query had succeeded).
1020 * else if we ever got a nodata, send that back as the reason.
1021 * else send back meaningless H_ERRNO, that being the one from
1022 * the last DNSRCH we did.
1023 */
1024 if (saved_herrno != -1) __h_errno_set(statp, saved_herrno);
1025 else if (got_nodata) __h_errno_set(statp, NO_DATA);
1026 else if (got_servfail) __h_errno_set(statp, TRY_AGAIN);
1027 return -1;
1028 }
1029
1030 int
1031 res_nsearch(res_state statp, const char *name, int class, int type, u_char *answer, int anslen)
1032 {
1033 struct sockaddr_storage f;
1034 int l;
1035
1036 l = sizeof(struct sockaddr_storage);
1037
1038 return res_nsearch_2(statp, name, class, type, answer, anslen, (struct sockaddr *)&f, &l);
1039 }
1040
1041 const char *
1042 res_hostalias(const res_state statp, const char *name, char *dst, size_t siz)
1043 {
1044 char *file, *cp1, *cp2;
1045 char buf[BUFSIZ];
1046 FILE *fp;
1047
1048 if (statp->options & RES_NOALIASES) return (NULL);
1049
1050 file = getenv("HOSTALIASES");
1051 if (file == NULL || (fp = fopen(file, "r")) == NULL) return (NULL);
1052
1053 setbuf(fp, NULL);
1054 buf[sizeof(buf) - 1] = '\0';
1055 while (fgets(buf, sizeof(buf), fp))
1056 {
1057 for (cp1 = buf; *cp1 && !isspace((unsigned char)*cp1); ++cp1) ;
1058
1059 if (!*cp1) break;
1060 *cp1 = '\0';
1061
1062 if (ns_samename(buf, name) == 1)
1063 {
1064 while (isspace((unsigned char)*++cp1)) ;
1065
1066 if (!*cp1) break;
1067
1068 for (cp2 = cp1 + 1; *cp2 && !isspace((unsigned char)*cp2); ++cp2) ;
1069
1070 *cp2 = '\0';
1071 strncpy(dst, cp1, siz - 1);
1072 dst[siz - 1] = '\0';
1073 fclose(fp);
1074 return (dst);
1075 }
1076 }
1077
1078 fclose(fp);
1079 return (NULL);
1080 }