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 * 4. Neither the name of the University nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30/*
31 * Portions Copyright (c) 1993 by Digital Equipment Corporation.
32 *
33 * Permission to use, copy, modify, and distribute this software for any
34 * purpose with or without fee is hereby granted, provided that the above
35 * copyright notice and this permission notice appear in all copies, and that
36 * the name of Digital Equipment Corporation not be used in advertising or
37 * publicity pertaining to distribution of the document or software without
38 * specific, written prior permission.
39 *
40 * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
41 * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
42 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
43 * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
44 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
45 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
46 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
47 * SOFTWARE.
48 */
49
50/*
51 * Portions Copyright (c) 1996-1999 by Internet Software Consortium.
52 *
53 * Permission to use, copy, modify, and distribute this software for any
54 * purpose with or without fee is hereby granted, provided that the above
55 * copyright notice and this permission notice appear in all copies.
56 *
57 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
58 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
59 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
60 * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
61 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
62 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
63 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
64 * SOFTWARE.
65 */
66
67#if defined(LIBC_SCCS) && !defined(lint)
68static const char sccsid[] = "@(#)res_query.c 8.1 (Berkeley) 6/4/93";
69static const char rcsid[] = "$BINDId: res_query.c,v 8.20 2000/02/29 05:39:12 vixie Exp $";
70#endif /* LIBC_SCCS and not lint */
71
72#include <assert.h>
73#include <sys/types.h>
74#include <sys/param.h>
75#include <netinet/in.h>
76#include <arpa/inet.h>
77#include <arpa/nameser.h>
78#include <ctype.h>
79#include <errno.h>
80#include <netdb.h>
81#include <resolv.h>
82#include <stdio.h>
83#include <stdlib.h>
84#include <string.h>
85
86/* Options. Leave them on. */
87/* #undef DEBUG */
88
89#if PACKETSZ > 65536
90#define MAXPACKET PACKETSZ
91#else
92#define MAXPACKET 65536
93#endif
94
95#define QUERYSIZE (HFIXEDSZ + QFIXEDSZ + MAXCDNAME + 1)
96
97static int
98__libc_res_nquerydomain(res_state statp, const char *name, const char *domain,
99 int class, int type, u_char *answer, int anslen,
100 u_char **answerp, u_char **answerp2, int *nanswerp2,
101 int *resplen2, int *answerp2_malloced);
102
103/*
104 * Formulate a normal query, send, and await answer.
105 * Returned answer is placed in supplied buffer "answer".
106 * Perform preliminary check of answer, returning success only
107 * if no error is indicated and the answer count is nonzero.
108 * Return the size of the response on success, -1 on error.
109 * Error number is left in H_ERRNO.
110 *
111 * Caller must parse answer and determine whether it answers the question.
112 */
113int
114__libc_res_nquery(res_state statp,
115 const char *name, /* domain name */
116 int class, int type, /* class and type of query */
117 u_char *answer, /* buffer to put answer */
118 int anslen, /* size of answer buffer */
119 u_char **answerp, /* if buffer needs to be enlarged */
120 u_char **answerp2,
121 int *nanswerp2,
122 int *resplen2,
123 int *answerp2_malloced)
124{
125 HEADER *hp = (HEADER *) answer;
126 HEADER *hp2;
127 int n, use_malloc = 0;
128 u_int oflags = statp->_flags;
129
130 size_t bufsize = (type == T_UNSPEC ? 2 : 1) * QUERYSIZE;
131 u_char *buf = alloca (bufsize);
132 u_char *query1 = buf;
133 int nquery1 = -1;
134 u_char *query2 = NULL;
135 int nquery2 = 0;
136
137 again:
138 hp->rcode = NOERROR; /* default */
139
140#ifdef DEBUG
141 if (statp->options & RES_DEBUG)
142 printf(";; res_query(%s, %d, %d)\n", name, class, type);
143#endif
144
145 if (type == T_UNSPEC)
146 {
147 n = res_nmkquery(statp, QUERY, name, class, T_A, NULL, 0, NULL,
148 query1, bufsize);
149 if (n > 0)
150 {
151 if ((oflags & RES_F_EDNS0ERR) == 0
152 && (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0)
153 {
154 n = __res_nopt(statp, n, query1, bufsize, anslen / 2);
155 if (n < 0)
156 goto unspec_nomem;
157 }
158
159 nquery1 = n;
160 /* Align the buffer. */
161 int npad = ((nquery1 + __alignof__ (HEADER) - 1)
162 & ~(__alignof__ (HEADER) - 1)) - nquery1;
163 if (n > bufsize - npad)
164 {
165 n = -1;
166 goto unspec_nomem;
167 }
168 int nused = n + npad;
169 query2 = buf + nused;
170 n = res_nmkquery(statp, QUERY, name, class, T_AAAA, NULL, 0,
171 NULL, query2, bufsize - nused);
172 if (n > 0
173 && (oflags & RES_F_EDNS0ERR) == 0
174 && (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0)
175 n = __res_nopt(statp, n, query2, bufsize - nused - n,
176 anslen / 2);
177 nquery2 = n;
178 }
179
180 unspec_nomem:;
181 }
182 else
183 {
184 n = res_nmkquery(statp, QUERY, name, class, type, NULL, 0, NULL,
185 query1, bufsize);
186
187 if (n > 0
188 && (oflags & RES_F_EDNS0ERR) == 0
189 && (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0)
190 n = __res_nopt(statp, n, query1, bufsize, anslen);
191
192 nquery1 = n;
193 }
194
195 if (__builtin_expect (n <= 0, 0) && !use_malloc) {
196 /* Retry just in case res_nmkquery failed because of too
197 short buffer. Shouldn't happen. */
198 bufsize = (type == T_UNSPEC ? 2 : 1) * MAXPACKET;
199 buf = malloc (bufsize);
200 if (buf != NULL) {
201 query1 = buf;
202 use_malloc = 1;
203 goto again;
204 }
205 }
206 if (__glibc_unlikely (n <= 0)) {
207 /* If the query choked with EDNS0, retry without EDNS0. */
208 if ((statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0
209 && ((oflags ^ statp->_flags) & RES_F_EDNS0ERR) != 0) {
210 statp->_flags |= RES_F_EDNS0ERR;
211#ifdef DEBUG
212 if (statp->options & RES_DEBUG)
213 printf(";; res_nquery: retry without EDNS0\n");
214#endif
215 goto again;
216 }
217#ifdef DEBUG
218 if (statp->options & RES_DEBUG)
219 printf(";; res_query: mkquery failed\n");
220#endif
221 RES_SET_H_ERRNO(statp, NO_RECOVERY);
222 if (use_malloc)
223 free (buf);
224 return (n);
225 }
226 assert (answerp == NULL || (void *) *answerp == (void *) answer);
227 n = __libc_res_nsend(statp, query1, nquery1, query2, nquery2, answer,
228 anslen, answerp, answerp2, nanswerp2, resplen2,
229 answerp2_malloced);
230 if (use_malloc)
231 free (buf);
232 if (n < 0) {
233#ifdef DEBUG
234 if (statp->options & RES_DEBUG)
235 printf(";; res_query: send error\n");
236#endif
237 RES_SET_H_ERRNO(statp, TRY_AGAIN);
238 return (n);
239 }
240
241 if (answerp != NULL)
242 /* __libc_res_nsend might have reallocated the buffer. */
243 hp = (HEADER *) *answerp;
244
245 /* We simplify the following tests by assigning HP to HP2 or
246 vice versa. It is easy to verify that this is the same as
247 ignoring all tests of HP or HP2. */
248 if (answerp2 == NULL || *resplen2 < (int) sizeof (HEADER))
249 {
250 hp2 = hp;
251 }
252 else
253 {
254 hp2 = (HEADER *) *answerp2;
255 if (n < (int) sizeof (HEADER))
256 {
257 hp = hp2;
258 }
259 }
260
261 /* Make sure both hp and hp2 are defined */
262 assert((hp != NULL) && (hp2 != NULL));
263
264 if ((hp->rcode != NOERROR || ntohs(hp->ancount) == 0)
265 && (hp2->rcode != NOERROR || ntohs(hp2->ancount) == 0)) {
266#ifdef DEBUG
267 if (statp->options & RES_DEBUG) {
268 printf(";; rcode = %d, ancount=%d\n", hp->rcode,
269 ntohs(hp->ancount));
270 if (hp != hp2)
271 printf(";; rcode2 = %d, ancount2=%d\n", hp2->rcode,
272 ntohs(hp2->ancount));
273 }
274#endif
275 switch (hp->rcode == NOERROR ? hp2->rcode : hp->rcode) {
276 case NXDOMAIN:
277 if ((hp->rcode == NOERROR && ntohs (hp->ancount) != 0)
278 || (hp2->rcode == NOERROR
279 && ntohs (hp2->ancount) != 0))
280 goto success;
281 RES_SET_H_ERRNO(statp, HOST_NOT_FOUND);
282 break;
283 case SERVFAIL:
284 RES_SET_H_ERRNO(statp, TRY_AGAIN);
285 break;
286 case NOERROR:
287 if (ntohs (hp->ancount) != 0
288 || ntohs (hp2->ancount) != 0)
289 goto success;
290 RES_SET_H_ERRNO(statp, NO_DATA);
291 break;
292 case FORMERR:
293 case NOTIMP:
294 /* Servers must not reply to AAAA queries with
295 NOTIMP etc but some of them do. */
296 if ((hp->rcode == NOERROR && ntohs (hp->ancount) != 0)
297 || (hp2->rcode == NOERROR
298 && ntohs (hp2->ancount) != 0))
299 goto success;
300 /* FALLTHROUGH */
301 case REFUSED:
302 default:
303 RES_SET_H_ERRNO(statp, NO_RECOVERY);
304 break;
305 }
306 return (-1);
307 }
308 success:
309 return (n);
310}
311libresolv_hidden_def (__libc_res_nquery)
312
313int
314res_nquery(res_state statp,
315 const char *name, /* domain name */
316 int class, int type, /* class and type of query */
317 u_char *answer, /* buffer to put answer */
318 int anslen) /* size of answer buffer */
319{
320 return __libc_res_nquery(statp, name, class, type, answer, anslen,
321 NULL, NULL, NULL, NULL, NULL);
322}
323libresolv_hidden_def (res_nquery)
324
325/*
326 * Formulate a normal query, send, and retrieve answer in supplied buffer.
327 * Return the size of the response on success, -1 on error.
328 * If enabled, implement search rules until answer or unrecoverable failure
329 * is detected. Error code, if any, is left in H_ERRNO.
330 */
331int
332__libc_res_nsearch(res_state statp,
333 const char *name, /* domain name */
334 int class, int type, /* class and type of query */
335 u_char *answer, /* buffer to put answer */
336 int anslen, /* size of answer */
337 u_char **answerp,
338 u_char **answerp2,
339 int *nanswerp2,
340 int *resplen2,
341 int *answerp2_malloced)
342{
343 const char *cp, * const *domain;
344 HEADER *hp = (HEADER *) answer;
345 char tmp[NS_MAXDNAME];
346 u_int dots;
347 int trailing_dot, ret, saved_herrno;
348 int got_nodata = 0, got_servfail = 0, root_on_list = 0;
349 int tried_as_is = 0;
350 int searched = 0;
351
352 __set_errno (0);
353 RES_SET_H_ERRNO(statp, HOST_NOT_FOUND); /* True if we never query. */
354
355 dots = 0;
356 for (cp = name; *cp != '\0'; cp++)
357 dots += (*cp == '.');
358 trailing_dot = 0;
359 if (cp > name && *--cp == '.')
360 trailing_dot++;
361
362 /* If there aren't any dots, it could be a user-level alias. */
363 if (!dots && (cp = res_hostalias(statp, name, tmp, sizeof tmp))!= NULL)
364 return (__libc_res_nquery(statp, cp, class, type, answer,
365 anslen, answerp, answerp2,
366 nanswerp2, resplen2, answerp2_malloced));
367
368#ifdef DEBUG
369 if (statp->options & RES_DEBUG)
370 printf("dots=%d, statp->ndots=%d, trailing_dot=%d, name=%s\n",
371 (int)dots,(int)statp->ndots,(int)trailing_dot,name);
372#endif
373
374 /*
375 * If there are enough dots in the name, let's just give it a
376 * try 'as is'. The threshold can be set with the "ndots" option.
377 * Also, query 'as is', if there is a trailing dot in the name.
378 */
379 saved_herrno = -1;
380 if (dots >= statp->ndots || trailing_dot) {
381 ret = __libc_res_nquerydomain(statp, name, NULL, class, type,
382 answer, anslen, answerp,
383 answerp2, nanswerp2, resplen2,
384 answerp2_malloced);
385 if (ret > 0 || trailing_dot
386 /* If the second response is valid then we use that. */
387 || (ret == 0 && resplen2 != NULL && *resplen2 > 0))
388 return (ret);
389 saved_herrno = h_errno;
390 tried_as_is++;
391 if (answerp && *answerp != answer) {
392 answer = *answerp;
393 anslen = MAXPACKET;
394 }
395 if (answerp2 && *answerp2_malloced)
396 {
397 free (*answerp2);
398 *answerp2 = NULL;
399 *nanswerp2 = 0;
400 *answerp2_malloced = 0;
401 }
402 }
403
404 /*
405 * We do at least one level of search if
406 * - there is no dot and RES_DEFNAME is set, or
407 * - there is at least one dot, there is no trailing dot,
408 * and RES_DNSRCH is set.
409 */
410 if ((!dots && (statp->options & RES_DEFNAMES) != 0) ||
411 (dots && !trailing_dot && (statp->options & RES_DNSRCH) != 0)) {
412 int done = 0;
413
414 for (domain = (const char * const *)statp->dnsrch;
415 *domain && !done;
416 domain++) {
417 const char *dname = domain[0];
418 searched = 1;
419
420 /* __libc_res_nquerydoman concatenates name
421 with dname with a "." in between. If we
422 pass it in dname the "." we got from the
423 configured default search path, we'll end
424 up with "name..", which won't resolve.
425 OTOH, passing it "" will result in "name.",
426 which has the intended effect for both
427 possible representations of the root
428 domain. */
429 if (dname[0] == '.')
430 dname++;
431 if (dname[0] == '\0')
432 root_on_list++;
433
434 ret = __libc_res_nquerydomain(statp, name, dname,
435 class, type,
436 answer, anslen, answerp,
437 answerp2, nanswerp2,
438 resplen2, answerp2_malloced);
439 if (ret > 0 || (ret == 0 && resplen2 != NULL
440 && *resplen2 > 0))
441 return (ret);
442
443 if (answerp && *answerp != answer) {
444 answer = *answerp;
445 anslen = MAXPACKET;
446 }
447 if (answerp2 && *answerp2_malloced)
448 {
449 free (*answerp2);
450 *answerp2 = NULL;
451 *nanswerp2 = 0;
452 *answerp2_malloced = 0;
453 }
454
455 /*
456 * If no server present, give up.
457 * If name isn't found in this domain,
458 * keep trying higher domains in the search list
459 * (if that's enabled).
460 * On a NO_DATA error, keep trying, otherwise
461 * a wildcard entry of another type could keep us
462 * from finding this entry higher in the domain.
463 * If we get some other error (negative answer or
464 * server failure), then stop searching up,
465 * but try the input name below in case it's
466 * fully-qualified.
467 */
468 if (errno == ECONNREFUSED) {
469 RES_SET_H_ERRNO(statp, TRY_AGAIN);
470 return (-1);
471 }
472
473 switch (statp->res_h_errno) {
474 case NO_DATA:
475 got_nodata++;
476 /* FALLTHROUGH */
477 case HOST_NOT_FOUND:
478 /* keep trying */
479 break;
480 case TRY_AGAIN:
481 if (hp->rcode == SERVFAIL) {
482 /* try next search element, if any */
483 got_servfail++;
484 break;
485 }
486 /* FALLTHROUGH */
487 default:
488 /* anything else implies that we're done */
489 done++;
490 }
491
492 /* if we got here for some reason other than DNSRCH,
493 * we only wanted one iteration of the loop, so stop.
494 */
495 if ((statp->options & RES_DNSRCH) == 0)
496 done++;
497 }
498 }
499
500 /*
501 * If the query has not already been tried as is then try it
502 * unless RES_NOTLDQUERY is set and there were no dots.
503 */
504 if ((dots || !searched || (statp->options & RES_NOTLDQUERY) == 0)
505 && !(tried_as_is || root_on_list)) {
506 ret = __libc_res_nquerydomain(statp, name, NULL, class, type,
507 answer, anslen, answerp,
508 answerp2, nanswerp2, resplen2,
509 answerp2_malloced);
510 if (ret > 0 || (ret == 0 && resplen2 != NULL
511 && *resplen2 > 0))
512 return (ret);
513 }
514
515 /* if we got here, we didn't satisfy the search.
516 * if we did an initial full query, return that query's H_ERRNO
517 * (note that we wouldn't be here if that query had succeeded).
518 * else if we ever got a nodata, send that back as the reason.
519 * else send back meaningless H_ERRNO, that being the one from
520 * the last DNSRCH we did.
521 */
522 if (answerp2 && *answerp2_malloced)
523 {
524 free (*answerp2);
525 *answerp2 = NULL;
526 *nanswerp2 = 0;
527 *answerp2_malloced = 0;
528 }
529 if (saved_herrno != -1)
530 RES_SET_H_ERRNO(statp, saved_herrno);
531 else if (got_nodata)
532 RES_SET_H_ERRNO(statp, NO_DATA);
533 else if (got_servfail)
534 RES_SET_H_ERRNO(statp, TRY_AGAIN);
535 return (-1);
536}
537libresolv_hidden_def (__libc_res_nsearch)
538
539int
540res_nsearch(res_state statp,
541 const char *name, /* domain name */
542 int class, int type, /* class and type of query */
543 u_char *answer, /* buffer to put answer */
544 int anslen) /* size of answer */
545{
546 return __libc_res_nsearch(statp, name, class, type, answer,
547 anslen, NULL, NULL, NULL, NULL, NULL);
548}
549libresolv_hidden_def (res_nsearch)
550
551/*
552 * Perform a call on res_query on the concatenation of name and domain.
553 */
554static int
555__libc_res_nquerydomain(res_state statp,
556 const char *name,
557 const char *domain,
558 int class, int type, /* class and type of query */
559 u_char *answer, /* buffer to put answer */
560 int anslen, /* size of answer */
561 u_char **answerp,
562 u_char **answerp2,
563 int *nanswerp2,
564 int *resplen2,
565 int *answerp2_malloced)
566{
567 char nbuf[MAXDNAME];
568 const char *longname = nbuf;
569 size_t n, d;
570
571#ifdef DEBUG
572 if (statp->options & RES_DEBUG)
573 printf(";; res_nquerydomain(%s, %s, %d, %d)\n",
574 name, domain?domain:"<Nil>", class, type);
575#endif
576 if (domain == NULL) {
577 n = strlen(name);
578
579 /* Decrement N prior to checking it against MAXDNAME
580 so that we detect a wrap to SIZE_MAX and return
581 a reasonable error. */
582 n--;
583 if (n >= MAXDNAME - 1) {
584 RES_SET_H_ERRNO(statp, NO_RECOVERY);
585 return (-1);
586 }
587 longname = name;
588 } else {
589 n = strlen(name);
590 d = strlen(domain);
591 if (n + d + 1 >= MAXDNAME) {
592 RES_SET_H_ERRNO(statp, NO_RECOVERY);
593 return (-1);
594 }
595 sprintf(nbuf, "%s.%s", name, domain);
596 }
597 return (__libc_res_nquery(statp, longname, class, type, answer,
598 anslen, answerp, answerp2, nanswerp2,
599 resplen2, answerp2_malloced));
600}
601
602int
603res_nquerydomain(res_state statp,
604 const char *name,
605 const char *domain,
606 int class, int type, /* class and type of query */
607 u_char *answer, /* buffer to put answer */
608 int anslen) /* size of answer */
609{
610 return __libc_res_nquerydomain(statp, name, domain, class, type,
611 answer, anslen, NULL, NULL, NULL, NULL,
612 NULL);
613}
614libresolv_hidden_def (res_nquerydomain)
615
616const char *
617res_hostalias(const res_state statp, const char *name, char *dst, size_t siz) {
618 char *file, *cp1, *cp2;
619 char buf[BUFSIZ];
620 FILE *fp;
621
622 if (statp->options & RES_NOALIASES)
623 return (NULL);
624 file = getenv("HOSTALIASES");
625 if (file == NULL || (fp = fopen(file, "rce")) == NULL)
626 return (NULL);
627 setbuf(fp, NULL);
628 buf[sizeof(buf) - 1] = '\0';
629 while (fgets(buf, sizeof(buf), fp)) {
630 for (cp1 = buf; *cp1 && !isspace(*cp1); ++cp1)
631 ;
632 if (!*cp1)
633 break;
634 *cp1 = '\0';
635 if (ns_samename(buf, name) == 1) {
636 while (isspace(*++cp1))
637 ;
638 if (!*cp1)
639 break;
640 for (cp2 = cp1 + 1; *cp2 && !isspace(*cp2); ++cp2)
641 ;
642 *cp2 = '\0';
643 strncpy(dst, cp1, siz - 1);
644 dst[siz - 1] = '\0';
645 fclose(fp);
646 return (dst);
647 }
648 }
649 fclose(fp);
650 return (NULL);
651}
652libresolv_hidden_def (res_hostalias)
653