]> git.saurik.com Git - apple/libresolv.git/blob - res_query.c
libresolv-65.200.2.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 extern int interrupt_pipe_enabled;
105 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;
526 int32_t b;
527 int min, soa_min;
528
529 eom = msg + len;
530
531 handle._msg = msg;
532 handle._eom = eom;
533
534 if (msg + NS_INT16SZ > eom) return -1;
535 NS_GET16(handle._id, msg);
536
537 if (msg + NS_INT16SZ > eom) return -1;
538 NS_GET16(handle._flags, msg);
539
540 for (i = 0; i < ns_s_max; i++)
541 {
542 if (msg + NS_INT16SZ > eom) return -1;
543 NS_GET16(handle._counts[i], msg);
544 }
545
546 if (handle._counts[ns_s_ns] == 0) return -1;
547
548 /* Skip forward to nameserver section */
549 for (i = 0; i < ns_s_ns; i++)
550 {
551 if (handle._counts[i] == 0) handle._sections[i] = NULL;
552 else
553 {
554 b = ns_skiprr(msg, eom, (ns_sect)i, handle._counts[i]);
555 if (b < 0) return -1;
556
557 handle._sections[i] = msg;
558 msg += b;
559 }
560 }
561
562 min = -1;
563 for (i = 0; i < handle._counts[ns_s_ns]; i++)
564 {
565 b = ns_skiprr(msg, eom, ns_s_ns, 1);
566 if (b < 0) return -1;
567
568 memcpy(&soa_min, msg + b - sizeof(int32_t), sizeof(int32_t));
569 soa_min = ntohl(soa_min);
570 if ((i == 0) || (soa_min < min)) min = soa_min;
571 msg += b;
572 }
573
574 return min;
575 }
576
577 /*
578 * Formulate a normal query, send, and await answer.
579 * Returned answer is placed in supplied buffer "answer".
580 * Perform preliminary check of answer, returning success only
581 * if no error is indicated and the answer count is nonzero.
582 * Return the size of the response on success, -1 on error.
583 * Error number is left in H_ERRNO.
584 *
585 * Caller must parse answer and determine whether it answers the question.
586 */
587 __attribute__((__visibility__("hidden"))) int
588 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)
589 {
590 u_char buf[MAXPACKET];
591 HEADER *hp = (HEADER *) answer;
592 int n;
593 u_int oflags;
594
595 if (min != NULL) *min = -1;
596
597 oflags = statp->_flags;
598
599 again:
600 __h_errno_set(statp, 0);
601 hp->rcode = ns_r_noerror; /* default */
602
603 #ifdef DEBUG
604 if (statp->options & RES_DEBUG) printf(";; res_query(%s, %d, %d)\n", name, class, type);
605 #endif
606
607 n = res_nmkquery(statp, ns_o_query, name, class, type, NULL, 0, NULL, buf, sizeof(buf));
608 #ifdef RES_USE_EDNS0
609 if (n > 0 && (statp->_flags & RES_F_EDNS0ERR) == 0 && (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0)
610 n = res_nopt(statp, n, buf, sizeof(buf), anslen);
611 #endif
612 if (n <= 0)
613 {
614 #ifdef DEBUG
615 if (statp->options & RES_DEBUG) printf(";; res_query: mkquery failed\n");
616 #endif
617 __h_errno_set(statp, NO_RECOVERY);
618 return (n);
619 }
620
621 n = res_nsend_2(statp, buf, n, answer, anslen, from, fromlen);
622 if (n < 0)
623 {
624 #ifdef RES_USE_EDNS0
625 /* if the query choked with EDNS0, retry without EDNS0 */
626 if ((statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0 && ((oflags ^ statp->_flags) & RES_F_EDNS0ERR) != 0)
627 {
628 statp->_flags |= RES_F_EDNS0ERR;
629 if (statp->options & RES_DEBUG) printf(";; res_nquery: retry without EDNS0\n");
630 goto again;
631 }
632 #endif
633 #ifdef DEBUG
634 if (statp->options & RES_DEBUG) printf(";; res_query: send error\n");
635 #endif
636 __h_errno_set(statp, TRY_AGAIN);
637 return (n);
638 }
639
640 if ((hp->rcode == ns_r_nxdomain) || ((hp->rcode == ns_r_noerror) && (ntohs(hp->ancount) == 0)))
641 {
642 if (min != NULL)
643 {
644 *min = res_soa_minimum(answer, anslen);
645 if (statp->options & RES_DEBUG) printf(";; res_nquery: SOA minimum TTL = %d\n", *min);
646 }
647 }
648
649 if (hp->rcode != ns_r_noerror || ntohs(hp->ancount) == 0)
650 {
651 #ifdef DEBUG
652 if (statp->options & RES_DEBUG) printf(";; rcode = %d, ancount=%d\n", hp->rcode, ntohs(hp->ancount));
653 #endif
654 switch (hp->rcode)
655 {
656 case ns_r_nxdomain:
657 __h_errno_set(statp, HOST_NOT_FOUND);
658 break;
659 case ns_r_servfail:
660 __h_errno_set(statp, TRY_AGAIN);
661 break;
662 case ns_r_noerror:
663 __h_errno_set(statp, NO_DATA);
664 break;
665 case ns_r_formerr:
666 case ns_r_notimpl:
667 case ns_r_refused:
668 default:
669 __h_errno_set(statp, NO_RECOVERY);
670 break;
671 }
672
673 return (-1);
674 }
675
676 return (n);
677 }
678
679 __attribute__((__visibility__("hidden")))
680 int
681 res_nquery_2(res_state statp, const char *name, int class, int type, u_char *answer, int anslen, struct sockaddr *from, int *fromlen)
682 {
683 int unused = 0;
684
685 return res_nquery_soa_min(statp, name, class, type, answer, anslen, from, fromlen, &unused);
686 }
687
688 int
689 res_nquery(res_state statp, const char *name, int class, int type, u_char *answer, int anslen)
690 {
691 struct sockaddr_storage f;
692 int l;
693
694 l = sizeof(struct sockaddr_storage);
695
696 return res_nquery_2(statp, name, class, type, answer, anslen, (struct sockaddr *)&f, &l);
697 }
698
699 /*
700 * Perform a call on res_query on the concatenation of name and domain,
701 * removing a trailing dot from name if domain is NULL.
702 */
703 __attribute__((__visibility__("hidden")))
704 int
705 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)
706 {
707 char nbuf[NS_MAXDNAME];
708 const char *longname = nbuf;
709 int n, d;
710
711 #ifdef DEBUG
712 if (statp->options & RES_DEBUG) printf(";; res_nquerydomain(%s, %s, %d, %d)\n", name, domain?domain:"<Nil>", class, type);
713 #endif
714 if (domain == NULL)
715 {
716 /*
717 * Check for trailing '.';
718 * copy without '.' if present.
719 */
720 n = strlen(name);
721 if (n >= NS_MAXDNAME)
722 {
723 __h_errno_set(statp, NO_RECOVERY);
724 return (-1);
725 }
726
727 n--;
728 if (n >= 0 && name[n] == '.')
729 {
730 strncpy(nbuf, name, n);
731 nbuf[n] = '\0';
732 }
733 else
734 {
735 longname = name;
736 }
737 }
738 else
739 {
740 n = strlen(name);
741 d = strlen(domain);
742 if (n + d + 1 >= NS_MAXDNAME)
743 {
744 __h_errno_set(statp, NO_RECOVERY);
745 return (-1);
746 }
747
748 sprintf(nbuf, "%s.%s", name, domain);
749 }
750
751 return (res_nquery_2(statp, longname, class, type, answer, anslen, from, fromlen));
752 }
753
754 int
755 res_nquerydomain(res_state statp, const char *name, const char *domain, int class, int type, u_char *answer, int anslen)
756 {
757 struct sockaddr_storage f;
758 int l;
759
760 l = sizeof(struct sockaddr_storage);
761
762 return res_nquerydomain_2(statp, name, domain, class, type, answer, anslen, (struct sockaddr *)&f, &l);
763 }
764
765 /*
766 * Formulate a normal query, send, and retrieve answer in supplied buffer.
767 * Return the size of the response on success, -1 on error.
768 * If enabled, implement search rules until answer or unrecoverable failure
769 * is detected. Error code, if any, is left in H_ERRNO.
770 */
771 __attribute__((__visibility__("hidden")))
772 int
773 res_nsearch_2(res_state statp, const char *name, int class, int type, u_char *answer, int anslen, struct sockaddr *from, int *fromlen)
774 {
775 const char *cp, * const *domain;
776 HEADER *hp = (HEADER *) answer;
777 char tmp[NS_MAXDNAME];
778 u_int dots;
779 int trailing_dot, ret, saved_herrno;
780 int got_nodata = 0, got_servfail = 0, root_on_list = 0;
781 int tried_as_is = 0;
782 int searched = 0;
783
784 errno = 0;
785 __h_errno_set(statp, HOST_NOT_FOUND); /* True if we never query. */
786
787 dots = 0;
788 for (cp = name; *cp != '\0'; cp++) dots += (*cp == '.');
789
790 trailing_dot = 0;
791 if (cp > name && *--cp == '.') trailing_dot++;
792
793 /* If there aren't any dots, it could be a user-level alias. */
794 if (!dots && (cp = res_hostalias(statp, name, tmp, sizeof tmp))!= NULL)
795 return (res_nquery(statp, cp, class, type, answer, anslen));
796
797 /*
798 * If there are enough dots in the name, let's just give it a
799 * try 'as is'. The threshold can be set with the "ndots" option.
800 * Also, query 'as is', if there is a trailing dot in the name.
801 */
802 saved_herrno = -1;
803 if (dots >= statp->ndots || trailing_dot)
804 {
805 ret = res_nquerydomain_2(statp, name, NULL, class, type, answer, anslen, from, fromlen);
806 if (ret > 0 || trailing_dot) return (ret);
807 saved_herrno = h_errno;
808 tried_as_is++;
809 }
810
811 /*
812 * We do at least one level of search if
813 * - there is no dot and RES_DEFNAME is set, or
814 * - there is at least one dot, there is no trailing dot,
815 * and RES_DNSRCH is set.
816 */
817 if ((!dots && (statp->options & RES_DEFNAMES) != 0) || (dots && !trailing_dot && (statp->options & RES_DNSRCH) != 0))
818 {
819 int done = 0;
820
821 for (domain = (const char * const *)statp->dnsrch; *domain && !done; domain++)
822 {
823 searched = 1;
824
825 if (domain[0][0] == '\0' || (domain[0][0] == '.' && domain[0][1] == '\0')) root_on_list++;
826
827 ret = res_nquerydomain_2(statp, name, *domain, class, type, answer, anslen, from, fromlen);
828 if (ret > 0) return (ret);
829
830 /*
831 * If no server present, give up.
832 * If name isn't found in this domain,
833 * keep trying higher domains in the search list
834 * (if that's enabled).
835 * On a NO_DATA error, keep trying, otherwise
836 * a wildcard entry of another type could keep us
837 * from finding this entry higher in the domain.
838 * If we get some other error (negative answer or
839 * server failure), then stop searching up,
840 * but try the input name below in case it's
841 * fully-qualified.
842 */
843 if (errno == ECONNREFUSED)
844 {
845 __h_errno_set(statp, TRY_AGAIN);
846 return (-1);
847 }
848
849 switch (statp->res_h_errno)
850 {
851 case NO_DATA:
852 got_nodata++;
853 /* FALLTHROUGH */
854 case HOST_NOT_FOUND:
855 /* keep trying */
856 break;
857 case TRY_AGAIN:
858 if (hp->rcode == ns_r_refused)
859 {
860 /* try next search element, if any */
861 got_servfail++;
862 break;
863 }
864 /* FALLTHROUGH */
865 default:
866 /* anything else implies that we're done */
867 done++;
868 }
869
870 /* if we got here for some reason other than DNSRCH,
871 * we only wanted one iteration of the loop, so stop.
872 */
873 if ((statp->options & RES_DNSRCH) == 0) done++;
874 }
875 }
876
877 /*
878 * If the query has not already been tried as is then try it
879 * unless RES_NOTLDQUERY is set and there were no dots.
880 */
881 if ((dots || !searched || (statp->options & RES_NOTLDQUERY) == 0) && !(tried_as_is || root_on_list))
882 {
883 ret = res_nquerydomain_2(statp, name, NULL, class, type, answer, anslen, from, fromlen);
884 if (ret > 0) return (ret);
885 }
886
887 /* if we got here, we didn't satisfy the search.
888 * if we did an initial full query, return that query's H_ERRNO
889 * (note that we wouldn't be here if that query had succeeded).
890 * else if we ever got a nodata, send that back as the reason.
891 * else send back meaningless H_ERRNO, that being the one from
892 * the last DNSRCH we did.
893 */
894 if (saved_herrno != -1)
895 __h_errno_set(statp, saved_herrno);
896 else if (got_nodata)
897 __h_errno_set(statp, NO_DATA);
898 else if (got_servfail)
899 __h_errno_set(statp, TRY_AGAIN);
900 return (-1);
901 }
902
903 __attribute__((__visibility__("hidden")))
904 int
905 __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)
906 {
907 const char *cp, *domain;
908 HEADER *hp = (HEADER *) answer;
909 char tmp[NS_MAXDNAME];
910 u_int dots;
911 int trailing_dot, ret, saved_herrno, i;
912 int got_nodata = 0, got_servfail = 0, root_on_list = 0;
913 int tried_as_is = 0;
914 int searched = 0;
915
916 errno = 0;
917 __h_errno_set(statp, HOST_NOT_FOUND); /* True if we never query. */
918
919 dots = 0;
920 for (cp = name; *cp != '\0'; cp++) dots += (*cp == '.');
921
922 trailing_dot = 0;
923 if (cp > name && *--cp == '.') trailing_dot++;
924
925 /* If there aren't any dots, it could be a user-level alias. */
926 if (!dots && (cp = res_hostalias(statp, name, tmp, sizeof tmp)) != NULL)
927 return (res_nquery(statp, cp, class, type, answer, anslen));
928
929 /*
930 * If there are enough dots in the name, let's just give it a
931 * try 'as is'. The threshold can be set with the "ndots" option.
932 * Also, query 'as is', if there is a trailing dot in the name.
933 */
934 saved_herrno = -1;
935 if (dots >= statp->ndots || trailing_dot)
936 {
937 ret = res_nquerydomain(statp, name, NULL, class, type, answer, anslen);
938 if (ret > 0 || trailing_dot) return ret;
939 saved_herrno = h_errno;
940 tried_as_is++;
941 }
942
943 /*
944 * We do at least one level of search if
945 * - there is no dot and RES_DEFNAME is set, or
946 * - there is at least one dot, there is no trailing dot,
947 * and RES_DNSRCH is set.
948 */
949 if ((!dots && (statp->options & RES_DEFNAMES) != 0) || (dots && !trailing_dot && (statp->options & RES_DNSRCH) != 0))
950 {
951 int done = 0;
952
953 for (i = 0; i < nsearch; i++)
954 {
955 domain = search[i];
956 searched = 1;
957
958 if (domain[0] == '\0' || (domain[0] == '.' && domain[1] == '\0')) root_on_list++;
959
960 ret = res_nquerydomain_2(statp, name, domain, class, type, answer, anslen, from, fromlen);
961 if (ret > 0) return ret;
962
963 /*
964 * If no server present, give up.
965 * If name isn't found in this domain,
966 * keep trying higher domains in the search list
967 * (if that's enabled).
968 * On a NO_DATA error, keep trying, otherwise
969 * a wildcard entry of another type could keep us
970 * from finding this entry higher in the domain.
971 * If we get some other error (negative answer or
972 * server failure), then stop searching up,
973 * but try the input name below in case it's
974 * fully-qualified.
975 */
976 if (errno == ECONNREFUSED)
977 {
978 __h_errno_set(statp, TRY_AGAIN);
979 return -1;
980 }
981
982 switch (statp->res_h_errno)
983 {
984 case NO_DATA:
985 got_nodata++;
986 /* FALLTHROUGH */
987 case HOST_NOT_FOUND:
988 /* keep trying */
989 break;
990 case TRY_AGAIN:
991 if (hp->rcode == ns_r_refused)
992 {
993 /* try next search element, if any */
994 got_servfail++;
995 break;
996 }
997 /* FALLTHROUGH */
998 default:
999 /* anything else implies that we're done */
1000 done++;
1001 }
1002
1003 /*
1004 * if we got here for some reason other than DNSRCH,
1005 * we only wanted one iteration of the loop, so stop.
1006 */
1007 if ((statp->options & RES_DNSRCH) == 0) done++;
1008 }
1009 }
1010
1011 /*
1012 * If the query has not already been tried as is then try it
1013 * unless RES_NOTLDQUERY is set and there were no dots.
1014 */
1015 if ((dots || !searched || (statp->options & RES_NOTLDQUERY) == 0) && !(tried_as_is || root_on_list))
1016 {
1017 ret = res_nquerydomain_2(statp, name, NULL, class, type, answer, anslen, from, fromlen);
1018 if (ret > 0) return ret;
1019 }
1020
1021 /*
1022 * we got here, we didn't satisfy the search.
1023 * if we did an initial full query, return that query's H_ERRNO
1024 * (note that we wouldn't be here if that query had succeeded).
1025 * else if we ever got a nodata, send that back as the reason.
1026 * else send back meaningless H_ERRNO, that being the one from
1027 * the last DNSRCH we did.
1028 */
1029 if (saved_herrno != -1) __h_errno_set(statp, saved_herrno);
1030 else if (got_nodata) __h_errno_set(statp, NO_DATA);
1031 else if (got_servfail) __h_errno_set(statp, TRY_AGAIN);
1032 return -1;
1033 }
1034
1035 int
1036 res_nsearch(res_state statp, const char *name, int class, int type, u_char *answer, int anslen)
1037 {
1038 struct sockaddr_storage f;
1039 int l;
1040
1041 l = sizeof(struct sockaddr_storage);
1042
1043 return res_nsearch_2(statp, name, class, type, answer, anslen, (struct sockaddr *)&f, &l);
1044 }
1045
1046 const char *
1047 res_hostalias(const res_state statp, const char *name, char *dst, size_t siz)
1048 {
1049 char *file, *cp1, *cp2;
1050 char buf[BUFSIZ];
1051 FILE *fp;
1052
1053 if (statp->options & RES_NOALIASES) return (NULL);
1054
1055 file = getenv("HOSTALIASES");
1056 if (file == NULL || (fp = fopen(file, "r")) == NULL) return (NULL);
1057
1058 setbuf(fp, NULL);
1059 buf[sizeof(buf) - 1] = '\0';
1060 while (fgets(buf, sizeof(buf), fp))
1061 {
1062 for (cp1 = buf; *cp1 && !isspace((unsigned char)*cp1); ++cp1) ;
1063
1064 if (!*cp1) break;
1065 *cp1 = '\0';
1066
1067 if (ns_samename(buf, name) == 1)
1068 {
1069 while (isspace((unsigned char)*++cp1)) ;
1070
1071 if (!*cp1) break;
1072
1073 for (cp2 = cp1 + 1; *cp2 && !isspace((unsigned char)*cp2); ++cp2) ;
1074
1075 *cp2 = '\0';
1076 strncpy(dst, cp1, siz - 1);
1077 dst[siz - 1] = '\0';
1078 fclose(fp);
1079 return (dst);
1080 }
1081 }
1082
1083 fclose(fp);
1084 return (NULL);
1085 }