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