]>
Commit | Line | Data |
---|---|---|
8a97ab44 A |
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> | |
9571391b A |
97 | #include <notify.h> |
98 | #include <pthread.h> | |
8a97ab44 A |
99 | #ifndef __APPLE__ |
100 | #include "port_after.h" | |
101 | #endif | |
102 | ||
9571391b A |
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 | ||
8a97ab44 A |
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; | |
9571391b | 144 | uint16_t lastanstype; |
8a97ab44 | 145 | uint32_t ifnum; |
9571391b | 146 | uint32_t res_flags; |
8a97ab44 A |
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 | ||
9571391b A |
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 | } | |
8a97ab44 A |
171 | |
172 | buflen = context->ansmaxlen - context->anslen; | |
9571391b A |
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 | } | |
8a97ab44 A |
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]); | |
9571391b A |
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 | } | |
8a97ab44 A |
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 | */ | |
9571391b A |
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); | |
8a97ab44 A |
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 | ||
9571391b A |
226 | context->lastanstype = rrtype; |
227 | ||
8a97ab44 A |
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 | ||
9571391b | 297 | int |
8a97ab44 A |
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; | |
9571391b A |
303 | int i, kq, n, wait, cancelled, notify_token, status; |
304 | struct kevent mevent, ievent, event; | |
8a97ab44 A |
305 | struct timeval ctv; |
306 | struct timespec now, finish, timeout; | |
307 | HEADER *ans; | |
308 | uint32_t iface; | |
309 | uint16_t nibble; | |
9571391b A |
310 | char *qname, *notify_name; |
311 | int *interrupt_pipe; | |
312 | uint64_t exit_requested; | |
313 | ||
314 | interrupt_pipe = NULL; | |
8a97ab44 A |
315 | result = 0; |
316 | kq = -1; | |
317 | ans = (HEADER *)answer; | |
9571391b | 318 | cancelled = 0; |
8a97ab44 A |
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 */ | |
9571391b | 324 | context.res_flags = statp->options; |
8a97ab44 A |
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 | ||
9571391b A |
374 | if (statp->options & RES_DEBUG) printf(";; res_query_mDNSResponder\n"); |
375 | ||
8a97ab44 A |
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 | ||
9571391b A |
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(¬ify_name, "self.thread.%lu", (unsigned long)pthread_self()); | |
423 | if (notify_name != NULL) | |
424 | { | |
425 | status = notify_register_plain(notify_name, ¬ify_token); | |
426 | free(notify_name); | |
427 | } | |
8a97ab44 A |
428 | |
429 | wait = 1; | |
430 | while (wait == 1) | |
431 | { | |
9571391b A |
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 | } | |
8a97ab44 A |
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 | } | |
9571391b A |
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 | } | |
8a97ab44 A |
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 | ||
9571391b | 476 | if ((ans->ancount > 0) && ((context.flags & kDNSServiceFlagsMoreComing) == 0) && ((context.lastanstype != ns_t_cname) || (type == ns_t_cname))) wait = 0; |
8a97ab44 A |
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 | ||
9571391b | 502 | if (notify_token != -1) notify_cancel(notify_token); |
8a97ab44 A |
503 | DNSServiceRefDeallocate(sdRef); |
504 | close(kq); | |
505 | ||
9571391b | 506 | if ((ans->ancount == 0) || (cancelled == 1)) context.anslen = -1; |
8a97ab44 A |
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; | |
70a8bbea A |
525 | uint32_t i; |
526 | int32_t b; | |
8a97ab44 A |
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 | __private_extern__ 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 | ||
e01cf2fc | 640 | if ((hp->rcode == ns_r_nxdomain) || ((hp->rcode == ns_r_noerror) && (ntohs(hp->ancount) == 0))) |
8a97ab44 A |
641 | { |
642 | if (min != NULL) | |
643 | { | |
644 | *min = res_soa_minimum(answer, anslen); | |
e01cf2fc | 645 | if (statp->options & RES_DEBUG) printf(";; res_nquery: SOA minimum TTL = %d\n", *min); |
8a97ab44 | 646 | } |
e01cf2fc | 647 | } |
8a97ab44 | 648 | |
e01cf2fc A |
649 | if (hp->rcode != ns_r_noerror || ntohs(hp->ancount) == 0) |
650 | { | |
8a97ab44 A |
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 | int | |
680 | res_nquery_2(res_state statp, const char *name, int class, int type, u_char *answer, int anslen, struct sockaddr *from, int *fromlen) | |
681 | { | |
682 | int unused = 0; | |
683 | ||
684 | return res_nquery_soa_min(statp, name, class, type, answer, anslen, from, fromlen, &unused); | |
685 | } | |
686 | ||
687 | int | |
688 | res_nquery(res_state statp, const char *name, int class, int type, u_char *answer, int anslen) | |
689 | { | |
690 | struct sockaddr_storage f; | |
691 | int l; | |
692 | ||
693 | l = sizeof(struct sockaddr_storage); | |
694 | ||
695 | return res_nquery_2(statp, name, class, type, answer, anslen, (struct sockaddr *)&f, &l); | |
696 | } | |
697 | ||
698 | /* | |
699 | * Perform a call on res_query on the concatenation of name and domain, | |
700 | * removing a trailing dot from name if domain is NULL. | |
701 | */ | |
702 | int | |
703 | 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) | |
704 | { | |
705 | char nbuf[NS_MAXDNAME]; | |
706 | const char *longname = nbuf; | |
707 | int n, d; | |
708 | ||
709 | #ifdef DEBUG | |
710 | if (statp->options & RES_DEBUG) printf(";; res_nquerydomain(%s, %s, %d, %d)\n", name, domain?domain:"<Nil>", class, type); | |
711 | #endif | |
712 | if (domain == NULL) | |
713 | { | |
714 | /* | |
715 | * Check for trailing '.'; | |
716 | * copy without '.' if present. | |
717 | */ | |
718 | n = strlen(name); | |
719 | if (n >= NS_MAXDNAME) | |
720 | { | |
721 | __h_errno_set(statp, NO_RECOVERY); | |
722 | return (-1); | |
723 | } | |
724 | ||
725 | n--; | |
726 | if (n >= 0 && name[n] == '.') | |
727 | { | |
728 | strncpy(nbuf, name, n); | |
729 | nbuf[n] = '\0'; | |
730 | } | |
731 | else | |
732 | { | |
733 | longname = name; | |
734 | } | |
735 | } | |
736 | else | |
737 | { | |
738 | n = strlen(name); | |
739 | d = strlen(domain); | |
740 | if (n + d + 1 >= NS_MAXDNAME) | |
741 | { | |
742 | __h_errno_set(statp, NO_RECOVERY); | |
743 | return (-1); | |
744 | } | |
745 | ||
746 | sprintf(nbuf, "%s.%s", name, domain); | |
747 | } | |
748 | ||
749 | return (res_nquery_2(statp, longname, class, type, answer, anslen, from, fromlen)); | |
750 | } | |
751 | ||
752 | int | |
753 | res_nquerydomain(res_state statp, const char *name, const char *domain, int class, int type, u_char *answer, int anslen) | |
754 | { | |
755 | struct sockaddr_storage f; | |
756 | int l; | |
757 | ||
758 | l = sizeof(struct sockaddr_storage); | |
759 | ||
760 | return res_nquerydomain_2(statp, name, domain, class, type, answer, anslen, (struct sockaddr *)&f, &l); | |
761 | } | |
762 | ||
763 | /* | |
764 | * Formulate a normal query, send, and retrieve answer in supplied buffer. | |
765 | * Return the size of the response on success, -1 on error. | |
766 | * If enabled, implement search rules until answer or unrecoverable failure | |
767 | * is detected. Error code, if any, is left in H_ERRNO. | |
768 | */ | |
769 | int | |
770 | res_nsearch_2(res_state statp, const char *name, int class, int type, u_char *answer, int anslen, struct sockaddr *from, int *fromlen) | |
771 | { | |
772 | const char *cp, * const *domain; | |
773 | HEADER *hp = (HEADER *) answer; | |
774 | char tmp[NS_MAXDNAME]; | |
775 | u_int dots; | |
776 | int trailing_dot, ret, saved_herrno; | |
777 | int got_nodata = 0, got_servfail = 0, root_on_list = 0; | |
778 | int tried_as_is = 0; | |
779 | int searched = 0; | |
780 | ||
781 | errno = 0; | |
782 | __h_errno_set(statp, HOST_NOT_FOUND); /* True if we never query. */ | |
783 | ||
784 | dots = 0; | |
785 | for (cp = name; *cp != '\0'; cp++) dots += (*cp == '.'); | |
786 | ||
787 | trailing_dot = 0; | |
788 | if (cp > name && *--cp == '.') trailing_dot++; | |
789 | ||
790 | /* If there aren't any dots, it could be a user-level alias. */ | |
791 | if (!dots && (cp = res_hostalias(statp, name, tmp, sizeof tmp))!= NULL) | |
792 | return (res_nquery(statp, cp, class, type, answer, anslen)); | |
793 | ||
794 | /* | |
795 | * If there are enough dots in the name, let's just give it a | |
796 | * try 'as is'. The threshold can be set with the "ndots" option. | |
797 | * Also, query 'as is', if there is a trailing dot in the name. | |
798 | */ | |
799 | saved_herrno = -1; | |
800 | if (dots >= statp->ndots || trailing_dot) | |
801 | { | |
802 | ret = res_nquerydomain_2(statp, name, NULL, class, type, answer, anslen, from, fromlen); | |
803 | if (ret > 0 || trailing_dot) return (ret); | |
804 | saved_herrno = h_errno; | |
805 | tried_as_is++; | |
806 | } | |
807 | ||
808 | /* | |
809 | * We do at least one level of search if | |
810 | * - there is no dot and RES_DEFNAME is set, or | |
811 | * - there is at least one dot, there is no trailing dot, | |
812 | * and RES_DNSRCH is set. | |
813 | */ | |
814 | if ((!dots && (statp->options & RES_DEFNAMES) != 0) || (dots && !trailing_dot && (statp->options & RES_DNSRCH) != 0)) | |
815 | { | |
816 | int done = 0; | |
817 | ||
818 | for (domain = (const char * const *)statp->dnsrch; *domain && !done; domain++) | |
819 | { | |
820 | searched = 1; | |
821 | ||
822 | if (domain[0][0] == '\0' || (domain[0][0] == '.' && domain[0][1] == '\0')) root_on_list++; | |
823 | ||
824 | ret = res_nquerydomain_2(statp, name, *domain, class, type, answer, anslen, from, fromlen); | |
825 | if (ret > 0) return (ret); | |
826 | ||
827 | /* | |
828 | * If no server present, give up. | |
829 | * If name isn't found in this domain, | |
830 | * keep trying higher domains in the search list | |
831 | * (if that's enabled). | |
832 | * On a NO_DATA error, keep trying, otherwise | |
833 | * a wildcard entry of another type could keep us | |
834 | * from finding this entry higher in the domain. | |
835 | * If we get some other error (negative answer or | |
836 | * server failure), then stop searching up, | |
837 | * but try the input name below in case it's | |
838 | * fully-qualified. | |
839 | */ | |
840 | if (errno == ECONNREFUSED) | |
841 | { | |
842 | __h_errno_set(statp, TRY_AGAIN); | |
843 | return (-1); | |
844 | } | |
845 | ||
846 | switch (statp->res_h_errno) | |
847 | { | |
848 | case NO_DATA: | |
849 | got_nodata++; | |
850 | /* FALLTHROUGH */ | |
851 | case HOST_NOT_FOUND: | |
852 | /* keep trying */ | |
853 | break; | |
854 | case TRY_AGAIN: | |
855 | if (hp->rcode == ns_r_refused) | |
856 | { | |
857 | /* try next search element, if any */ | |
858 | got_servfail++; | |
859 | break; | |
860 | } | |
861 | /* FALLTHROUGH */ | |
862 | default: | |
863 | /* anything else implies that we're done */ | |
864 | done++; | |
865 | } | |
866 | ||
867 | /* if we got here for some reason other than DNSRCH, | |
868 | * we only wanted one iteration of the loop, so stop. | |
869 | */ | |
870 | if ((statp->options & RES_DNSRCH) == 0) done++; | |
871 | } | |
872 | } | |
873 | ||
874 | /* | |
875 | * If the query has not already been tried as is then try it | |
876 | * unless RES_NOTLDQUERY is set and there were no dots. | |
877 | */ | |
878 | if ((dots || !searched || (statp->options & RES_NOTLDQUERY) == 0) && !(tried_as_is || root_on_list)) | |
879 | { | |
880 | ret = res_nquerydomain_2(statp, name, NULL, class, type, answer, anslen, from, fromlen); | |
881 | if (ret > 0) return (ret); | |
882 | } | |
883 | ||
884 | /* if we got here, we didn't satisfy the search. | |
885 | * if we did an initial full query, return that query's H_ERRNO | |
886 | * (note that we wouldn't be here if that query had succeeded). | |
887 | * else if we ever got a nodata, send that back as the reason. | |
888 | * else send back meaningless H_ERRNO, that being the one from | |
889 | * the last DNSRCH we did. | |
890 | */ | |
891 | if (saved_herrno != -1) | |
892 | __h_errno_set(statp, saved_herrno); | |
893 | else if (got_nodata) | |
894 | __h_errno_set(statp, NO_DATA); | |
895 | else if (got_servfail) | |
896 | __h_errno_set(statp, TRY_AGAIN); | |
897 | return (-1); | |
898 | } | |
899 | ||
900 | int | |
901 | __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) | |
902 | { | |
903 | const char *cp, *domain; | |
904 | HEADER *hp = (HEADER *) answer; | |
905 | char tmp[NS_MAXDNAME]; | |
906 | u_int dots; | |
907 | int trailing_dot, ret, saved_herrno, i; | |
908 | int got_nodata = 0, got_servfail = 0, root_on_list = 0; | |
909 | int tried_as_is = 0; | |
910 | int searched = 0; | |
911 | ||
912 | errno = 0; | |
913 | __h_errno_set(statp, HOST_NOT_FOUND); /* True if we never query. */ | |
914 | ||
915 | dots = 0; | |
916 | for (cp = name; *cp != '\0'; cp++) dots += (*cp == '.'); | |
917 | ||
918 | trailing_dot = 0; | |
919 | if (cp > name && *--cp == '.') trailing_dot++; | |
920 | ||
921 | /* If there aren't any dots, it could be a user-level alias. */ | |
922 | if (!dots && (cp = res_hostalias(statp, name, tmp, sizeof tmp)) != NULL) | |
923 | return (res_nquery(statp, cp, class, type, answer, anslen)); | |
924 | ||
925 | /* | |
926 | * If there are enough dots in the name, let's just give it a | |
927 | * try 'as is'. The threshold can be set with the "ndots" option. | |
928 | * Also, query 'as is', if there is a trailing dot in the name. | |
929 | */ | |
930 | saved_herrno = -1; | |
931 | if (dots >= statp->ndots || trailing_dot) | |
932 | { | |
933 | ret = res_nquerydomain(statp, name, NULL, class, type, answer, anslen); | |
934 | if (ret > 0 || trailing_dot) return ret; | |
935 | saved_herrno = h_errno; | |
936 | tried_as_is++; | |
937 | } | |
938 | ||
939 | /* | |
940 | * We do at least one level of search if | |
941 | * - there is no dot and RES_DEFNAME is set, or | |
942 | * - there is at least one dot, there is no trailing dot, | |
943 | * and RES_DNSRCH is set. | |
944 | */ | |
945 | if ((!dots && (statp->options & RES_DEFNAMES) != 0) || (dots && !trailing_dot && (statp->options & RES_DNSRCH) != 0)) | |
946 | { | |
947 | int done = 0; | |
948 | ||
949 | for (i = 0; i < nsearch; i++) | |
950 | { | |
951 | domain = search[i]; | |
952 | searched = 1; | |
953 | ||
954 | if (domain[0] == '\0' || (domain[0] == '.' && domain[1] == '\0')) root_on_list++; | |
955 | ||
956 | ret = res_nquerydomain_2(statp, name, domain, class, type, answer, anslen, from, fromlen); | |
957 | if (ret > 0) return ret; | |
958 | ||
959 | /* | |
960 | * If no server present, give up. | |
961 | * If name isn't found in this domain, | |
962 | * keep trying higher domains in the search list | |
963 | * (if that's enabled). | |
964 | * On a NO_DATA error, keep trying, otherwise | |
965 | * a wildcard entry of another type could keep us | |
966 | * from finding this entry higher in the domain. | |
967 | * If we get some other error (negative answer or | |
968 | * server failure), then stop searching up, | |
969 | * but try the input name below in case it's | |
970 | * fully-qualified. | |
971 | */ | |
972 | if (errno == ECONNREFUSED) | |
973 | { | |
974 | __h_errno_set(statp, TRY_AGAIN); | |
975 | return -1; | |
976 | } | |
977 | ||
978 | switch (statp->res_h_errno) | |
979 | { | |
980 | case NO_DATA: | |
981 | got_nodata++; | |
982 | /* FALLTHROUGH */ | |
983 | case HOST_NOT_FOUND: | |
984 | /* keep trying */ | |
985 | break; | |
986 | case TRY_AGAIN: | |
987 | if (hp->rcode == ns_r_refused) | |
988 | { | |
989 | /* try next search element, if any */ | |
990 | got_servfail++; | |
991 | break; | |
992 | } | |
993 | /* FALLTHROUGH */ | |
994 | default: | |
995 | /* anything else implies that we're done */ | |
996 | done++; | |
997 | } | |
998 | ||
999 | /* | |
1000 | * if we got here for some reason other than DNSRCH, | |
1001 | * we only wanted one iteration of the loop, so stop. | |
1002 | */ | |
1003 | if ((statp->options & RES_DNSRCH) == 0) done++; | |
1004 | } | |
1005 | } | |
1006 | ||
1007 | /* | |
1008 | * If the query has not already been tried as is then try it | |
1009 | * unless RES_NOTLDQUERY is set and there were no dots. | |
1010 | */ | |
1011 | if ((dots || !searched || (statp->options & RES_NOTLDQUERY) == 0) && !(tried_as_is || root_on_list)) | |
1012 | { | |
1013 | ret = res_nquerydomain_2(statp, name, NULL, class, type, answer, anslen, from, fromlen); | |
1014 | if (ret > 0) return ret; | |
1015 | } | |
1016 | ||
1017 | /* | |
1018 | * we got here, we didn't satisfy the search. | |
1019 | * if we did an initial full query, return that query's H_ERRNO | |
1020 | * (note that we wouldn't be here if that query had succeeded). | |
1021 | * else if we ever got a nodata, send that back as the reason. | |
1022 | * else send back meaningless H_ERRNO, that being the one from | |
1023 | * the last DNSRCH we did. | |
1024 | */ | |
1025 | if (saved_herrno != -1) __h_errno_set(statp, saved_herrno); | |
1026 | else if (got_nodata) __h_errno_set(statp, NO_DATA); | |
1027 | else if (got_servfail) __h_errno_set(statp, TRY_AGAIN); | |
1028 | return -1; | |
1029 | } | |
1030 | ||
1031 | int | |
1032 | res_nsearch(res_state statp, const char *name, int class, int type, u_char *answer, int anslen) | |
1033 | { | |
1034 | struct sockaddr_storage f; | |
1035 | int l; | |
1036 | ||
1037 | l = sizeof(struct sockaddr_storage); | |
1038 | ||
1039 | return res_nsearch_2(statp, name, class, type, answer, anslen, (struct sockaddr *)&f, &l); | |
1040 | } | |
1041 | ||
1042 | const char * | |
1043 | res_hostalias(const res_state statp, const char *name, char *dst, size_t siz) | |
1044 | { | |
1045 | char *file, *cp1, *cp2; | |
1046 | char buf[BUFSIZ]; | |
1047 | FILE *fp; | |
1048 | ||
1049 | if (statp->options & RES_NOALIASES) return (NULL); | |
1050 | ||
1051 | file = getenv("HOSTALIASES"); | |
1052 | if (file == NULL || (fp = fopen(file, "r")) == NULL) return (NULL); | |
1053 | ||
1054 | setbuf(fp, NULL); | |
1055 | buf[sizeof(buf) - 1] = '\0'; | |
1056 | while (fgets(buf, sizeof(buf), fp)) | |
1057 | { | |
1058 | for (cp1 = buf; *cp1 && !isspace((unsigned char)*cp1); ++cp1) ; | |
1059 | ||
1060 | if (!*cp1) break; | |
1061 | *cp1 = '\0'; | |
1062 | ||
1063 | if (ns_samename(buf, name) == 1) | |
1064 | { | |
1065 | while (isspace((unsigned char)*++cp1)) ; | |
1066 | ||
1067 | if (!*cp1) break; | |
1068 | ||
1069 | for (cp2 = cp1 + 1; *cp2 && !isspace((unsigned char)*cp2); ++cp2) ; | |
1070 | ||
1071 | *cp2 = '\0'; | |
1072 | strncpy(dst, cp1, siz - 1); | |
1073 | dst[siz - 1] = '\0'; | |
1074 | fclose(fp); | |
1075 | return (dst); | |
1076 | } | |
1077 | } | |
1078 | ||
1079 | fclose(fp); | |
1080 | return (NULL); | |
1081 | } |