OSDN Git Service

Added support for the AI_ADDRCONFIG flag in the hints->ai_flags parameter to getaddri...
[uclinux-h8/uClibc.git] / libc / inet / getaddrinfo.c
1 /*
2  * Copyright 1996 by Craig Metz 
3  * Copyright (C) 2000-2006 Erik Andersen <andersen@uclibc.org>
4  *
5  * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
6  */
7
8 /* $USAGI: getaddrinfo.c,v 1.16 2001/10/04 09:52:03 sekiya Exp $ */
9
10 /* The Inner Net License, Version 2.00
11
12   The author(s) grant permission for redistribution and use in source and
13 binary forms, with or without modification, of the software and documentation
14 provided that the following conditions are met:
15
16 0. If you receive a version of the software that is specifically labelled
17    as not being for redistribution (check the version message and/or README),
18    you are not permitted to redistribute that version of the software in any
19    way or form.
20 1. All terms of the all other applicable copyrights and licenses must be
21    followed.
22 2. Redistributions of source code must retain the authors' copyright
23    notice(s), this list of conditions, and the following disclaimer.
24 3. Redistributions in binary form must reproduce the authors' copyright
25    notice(s), this list of conditions, and the following disclaimer in the
26    documentation and/or other materials provided with the distribution.
27 4. All advertising materials mentioning features or use of this software
28    must display the following acknowledgement with the name(s) of the
29    authors as specified in the copyright notice(s) substituted where
30    indicated:
31
32         This product includes software developed by <name(s)>, The Inner
33         Net, and other contributors.
34
35 5. Neither the name(s) of the author(s) nor the names of its contributors
36    may be used to endorse or promote products derived from this software
37    without specific prior written permission.
38
39 THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS ``AS IS'' AND ANY
40 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
41 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
42 DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY
43 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
44 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
45 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
46 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
47 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
48 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
49
50   If these license terms cause you a real problem, contact the author.  */
51
52 #define __FORCE_GLIBC
53 #include <features.h>
54 #include <assert.h>
55 #include <errno.h>
56 #include <netdb.h>
57 #include <resolv.h>
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <unistd.h>
62 #include <arpa/inet.h>
63 #include <sys/socket.h>
64 #include <netinet/in.h>
65 #include <sys/types.h>
66 #include <sys/un.h>
67 #include <sys/utsname.h>
68 #include <net/if.h>
69 #include <ifaddrs.h>
70
71 /* Experimentally off - libc_hidden_proto(memcpy) */
72 /* Experimentally off - libc_hidden_proto(memset) */
73 /* libc_hidden_proto(strcmp) */
74 /* libc_hidden_proto(stpcpy) */
75 /* Experimentally off - libc_hidden_proto(strchr) */
76 /* Experimentally off - libc_hidden_proto(strcpy) */
77 /* Experimentally off - libc_hidden_proto(strlen) */
78 libc_hidden_proto(socket)
79 libc_hidden_proto(close)
80 libc_hidden_proto(getservbyname_r)
81 libc_hidden_proto(gethostbyname_r)
82 libc_hidden_proto(gethostbyname2_r)
83 libc_hidden_proto(gethostbyaddr_r)
84 libc_hidden_proto(inet_pton)
85 libc_hidden_proto(inet_ntop)
86 libc_hidden_proto(strtoul)
87 libc_hidden_proto(if_nametoindex)
88 libc_hidden_proto(__h_errno_location)
89 /* libc_hidden_proto(uname) */
90 #ifdef __UCLIBC_HAS_IPV6__
91 libc_hidden_proto(in6addr_loopback)
92 #endif
93
94 #define GAIH_OKIFUNSPEC 0x0100
95 #define GAIH_EAI        ~(GAIH_OKIFUNSPEC)
96
97 #ifndef UNIX_PATH_MAX
98 #define UNIX_PATH_MAX  108
99 #endif
100
101 struct gaih_service
102 {
103     const char *name;
104     int num;
105 };
106
107 struct gaih_servtuple
108 {
109     struct gaih_servtuple *next;
110     int socktype;
111     int protocol;
112     int port;
113 };
114
115 static const struct gaih_servtuple nullserv;
116
117 struct gaih_addrtuple
118 {
119     struct gaih_addrtuple *next;
120     int family;
121     char addr[16];
122     uint32_t scopeid;
123 };
124
125 struct gaih_typeproto
126 {
127     int socktype;
128     int protocol;
129     char name[4];
130     int protoflag;
131 };
132
133 /* Values for `protoflag'.  */
134 #define GAI_PROTO_NOSERVICE     1
135 #define GAI_PROTO_PROTOANY      2
136
137 static const struct gaih_typeproto gaih_inet_typeproto[] =
138 {
139     { 0, 0, "", 0 },
140     { SOCK_STREAM, IPPROTO_TCP, "tcp", 0 },
141     { SOCK_DGRAM, IPPROTO_UDP, "udp", 0 },
142     { SOCK_RAW, 0, "raw", GAI_PROTO_PROTOANY|GAI_PROTO_NOSERVICE },
143     { 0, 0, "", 0 }
144 };
145
146 struct gaih
147 {
148     int family;
149     int (*gaih)(const char *name, const struct gaih_service *service,
150                 const struct addrinfo *req, struct addrinfo **pai);
151 };
152
153 #if PF_UNSPEC == 0
154 static const struct addrinfo default_hints;
155 #else
156 static const struct addrinfo default_hints =
157 { 0, PF_UNSPEC, 0, 0, 0, NULL, NULL, NULL };
158 #endif
159
160 static int addrconfig (sa_family_t af)
161 {
162     int s;
163     int ret;
164     int saved_errno = errno;
165     bool seen_ipv4;
166     bool seen_ipv6;
167
168     __check_pf(&seen_ipv4, &seen_ipv6);
169     if (af == AF_INET)
170         ret = (int)seen_ipv4;
171     else if (af == AF_INET6)
172         ret = (int)seen_ipv6;
173     else
174     {
175         s = socket(af, SOCK_DGRAM, 0);
176         if (s < 0)
177             ret = (errno == EMFILE) ? 1 : 0;
178         else
179         {
180             close(s);
181             ret = 1;
182         }
183     }
184     __set_errno (saved_errno);
185     return ret;
186 }
187
188 #if 0
189 /* Using Unix sockets this way is a security risk.  */
190 static int
191 gaih_local (const char *name, const struct gaih_service *service,
192             const struct addrinfo *req, struct addrinfo **pai)
193 {
194     struct utsname utsname;
195
196     if ((name != NULL) && (req->ai_flags & AI_NUMERICHOST))
197         return GAIH_OKIFUNSPEC | -EAI_NONAME;
198
199     if ((name != NULL) || (req->ai_flags & AI_CANONNAME))
200         if (uname (&utsname) < 0)
201             return -EAI_SYSTEM;
202
203     if (name != NULL)
204     {
205         if (strcmp(name, "localhost") &&
206             strcmp(name, "local") &&
207             strcmp(name, "unix") &&
208             strcmp(name, utsname.nodename))
209             return GAIH_OKIFUNSPEC | -EAI_NONAME;
210     }
211
212     if (req->ai_protocol || req->ai_socktype)
213     {
214         const struct gaih_typeproto *tp = gaih_inet_typeproto + 1;
215
216         while (tp->name[0]
217                && ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0
218                    || (req->ai_socktype != 0 && req->ai_socktype != tp->socktype)
219                    || (req->ai_protocol != 0
220                        && !(tp->protoflag & GAI_PROTO_PROTOANY)
221                        && req->ai_protocol != tp->protocol)))
222             ++tp;
223
224         if (! tp->name[0])
225         {
226             if (req->ai_socktype)
227                 return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE);
228             else
229                 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
230         }
231     }
232
233     *pai = malloc (sizeof (struct addrinfo) + sizeof (struct sockaddr_un)
234                    + ((req->ai_flags & AI_CANONNAME)
235                       ? (strlen(utsname.nodename) + 1): 0));
236     if (*pai == NULL)
237         return -EAI_MEMORY;
238
239     (*pai)->ai_next = NULL;
240     (*pai)->ai_flags = req->ai_flags;
241     (*pai)->ai_family = AF_LOCAL;
242     (*pai)->ai_socktype = req->ai_socktype ? req->ai_socktype : SOCK_STREAM;
243     (*pai)->ai_protocol = req->ai_protocol;
244     (*pai)->ai_addrlen = sizeof (struct sockaddr_un);
245     (*pai)->ai_addr = (void *) (*pai) + sizeof (struct addrinfo);
246
247 #if SALEN
248     ((struct sockaddr_un *) (*pai)->ai_addr)->sun_len =
249         sizeof (struct sockaddr_un);
250 #endif /* SALEN */
251
252     ((struct sockaddr_un *)(*pai)->ai_addr)->sun_family = AF_LOCAL;
253     memset(((struct sockaddr_un *)(*pai)->ai_addr)->sun_path, 0, UNIX_PATH_MAX);
254
255     if (service)
256     {
257         struct sockaddr_un *sunp = (struct sockaddr_un *) (*pai)->ai_addr;
258
259         if (strchr (service->name, '/') != NULL)
260         {
261             if (strlen (service->name) >= sizeof (sunp->sun_path))
262                 return GAIH_OKIFUNSPEC | -EAI_SERVICE;
263
264             strcpy (sunp->sun_path, service->name);
265         }
266         else
267         {
268             if (strlen (P_tmpdir "/") + 1 + strlen (service->name) >=
269                 sizeof (sunp->sun_path))
270                 return GAIH_OKIFUNSPEC | -EAI_SERVICE;
271
272             stpcpy (stpcpy (sunp->sun_path, P_tmpdir "/"), service->name);
273         }
274     }
275     else
276     {
277         /* This is a dangerous use of the interface since there is a time
278            window between the test for the file and the actual creation
279            (done by the caller) in which a file with the same name could
280            be created.  */
281         char *buf = ((struct sockaddr_un *) (*pai)->ai_addr)->sun_path;
282
283         if (__builtin_expect (__path_search (buf, L_tmpnam, NULL, NULL, 0),
284                               0) != 0
285             || __builtin_expect (__gen_tempname (buf, __GT_NOCREATE), 0) != 0)
286             return -EAI_SYSTEM;
287     }
288
289     if (req->ai_flags & AI_CANONNAME)
290         (*pai)->ai_canonname = strcpy ((char *) *pai + sizeof (struct addrinfo)
291                                        + sizeof (struct sockaddr_un),
292                                        utsname.nodename);
293     else
294         (*pai)->ai_canonname = NULL;
295     return 0;
296 }
297 #endif  /* 0 */
298
299 static int
300 gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
301                 const struct addrinfo *req, struct gaih_servtuple *st)
302 {
303     struct servent *s;
304     size_t tmpbuflen = 1024;
305     struct servent ts;
306     char *tmpbuf;
307     int r;
308
309     do
310     {
311         tmpbuf = alloca (tmpbuflen);
312
313         r = getservbyname_r (servicename, tp->name, &ts, tmpbuf, tmpbuflen,
314                              &s);
315         if (r != 0 || s == NULL)
316         {
317             if (r == ERANGE)
318                 tmpbuflen *= 2;
319             else
320                 return GAIH_OKIFUNSPEC | -EAI_SERVICE;
321         }
322     }
323     while (r);
324
325     st->next = NULL;
326     st->socktype = tp->socktype;
327     st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
328                     ? req->ai_protocol : tp->protocol);
329     st->port = s->s_port;
330
331     return 0;
332 }
333
334 #define gethosts(_family, _type)                                        \
335 {                                                                       \
336     int i, herrno;                                                      \
337     size_t tmpbuflen;                                                   \
338     struct hostent th;                                                  \
339     char *tmpbuf;                                                       \
340     tmpbuflen = 512;                                                    \
341     no_data = 0;                                                        \
342     do {                                                                \
343         tmpbuflen *= 2;                                                 \
344         tmpbuf = alloca (tmpbuflen);                                    \
345         rc = gethostbyname2_r (name, _family, &th, tmpbuf,              \
346                                tmpbuflen, &h, &herrno);                 \
347     } while (rc == ERANGE && herrno == NETDB_INTERNAL);                 \
348     if (rc != 0)                                                        \
349     {                                                                   \
350         if (herrno == NETDB_INTERNAL)                                   \
351         {                                                               \
352             __set_h_errno (herrno);                                     \
353                 return -EAI_SYSTEM;                                     \
354         }                                                               \
355         if (herrno == TRY_AGAIN)                                        \
356             no_data = EAI_AGAIN;                                        \
357         else                                                            \
358             no_data = herrno == NO_DATA;                                \
359     }                                                                   \
360     else if (h != NULL)                                                 \
361     {                                                                   \
362         for (i = 0; h->h_addr_list[i]; i++)                             \
363         {                                                               \
364             if (*pat == NULL) {                                         \
365                 *pat = alloca (sizeof(struct gaih_addrtuple));          \
366                     (*pat)->scopeid = 0;                                \
367             }                                                           \
368             (*pat)->next = NULL;                                        \
369                 (*pat)->family = _family;                               \
370                 memcpy ((*pat)->addr, h->h_addr_list[i],                \
371                         sizeof(_type));                                 \
372                 pat = &((*pat)->next);                                  \
373         }                                                               \
374     }                                                                   \
375 }
376
377 static int
378 gaih_inet (const char *name, const struct gaih_service *service,
379            const struct addrinfo *req, struct addrinfo **pai)
380 {
381     const struct gaih_typeproto *tp = gaih_inet_typeproto;
382     struct gaih_servtuple *st = (struct gaih_servtuple *) &nullserv;
383     struct gaih_addrtuple *at = NULL;
384     int rc;
385     int v4mapped = (req->ai_family == PF_UNSPEC || req->ai_family == PF_INET6) &&
386         (req->ai_flags & AI_V4MAPPED);
387     bool seen_ipv4;
388     bool seen_ipv6;
389     __check_pf(&seen_ipv4, &seen_ipv6);
390
391     if (req->ai_protocol || req->ai_socktype)
392     {
393         ++tp;
394
395         while (tp->name[0]
396                && ((req->ai_socktype != 0 && req->ai_socktype != tp->socktype)
397                    || (req->ai_protocol != 0
398                        && !(tp->protoflag & GAI_PROTO_PROTOANY)
399                        && req->ai_protocol != tp->protocol)))
400             ++tp;
401
402         if (! tp->name[0])
403         {
404             if (req->ai_socktype)
405                 return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE);
406             else
407                 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
408         }
409     }
410
411     if (service != NULL)
412     {
413         if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
414             return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
415
416         if (service->num < 0)
417         {
418             if (tp->name[0])
419             {
420                 st = (struct gaih_servtuple *)
421                     alloca (sizeof (struct gaih_servtuple));
422
423                 if ((rc = gaih_inet_serv (service->name, tp, req, st)))
424                     return rc;
425             }
426             else
427             {
428                 struct gaih_servtuple **pst = &st;
429                 for (tp++; tp->name[0]; tp++)
430                 {
431                     struct gaih_servtuple *newp;
432
433                     if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
434                         continue;
435
436                     if (req->ai_socktype != 0
437                         && req->ai_socktype != tp->socktype)
438                         continue;
439                     if (req->ai_protocol != 0
440                         && !(tp->protoflag & GAI_PROTO_PROTOANY)
441                         && req->ai_protocol != tp->protocol)
442                         continue;
443
444                     newp = (struct gaih_servtuple *)
445                         alloca (sizeof (struct gaih_servtuple));
446
447                     if ((rc = gaih_inet_serv (service->name, tp, req, newp)))
448                     {
449                         if (rc & GAIH_OKIFUNSPEC)
450                             continue;
451                         return rc;
452                     }
453
454                     *pst = newp;
455                     pst = &(newp->next);
456                 }
457                 if (st == (struct gaih_servtuple *) &nullserv)
458                     return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
459             }
460         }
461         else
462         {
463             st = alloca (sizeof (struct gaih_servtuple));
464             st->next = NULL;
465             st->socktype = tp->socktype;
466             st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
467                             ? req->ai_protocol : tp->protocol);
468             st->port = htons (service->num);
469         }
470     }
471     else if (req->ai_socktype || req->ai_protocol)
472     {
473         st = alloca (sizeof (struct gaih_servtuple));
474         st->next = NULL;
475         st->socktype = tp->socktype;
476         st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
477                         ? req->ai_protocol : tp->protocol);
478         st->port = 0;
479     }
480     else
481     {
482         /* 
483          * Neither socket type nor protocol is set.  Return all socket types
484          * we know about.
485          */
486         struct gaih_servtuple **lastp = &st;
487         for (++tp; tp->name[0]; ++tp)
488         {
489             struct gaih_servtuple *newp;
490
491             newp = alloca (sizeof (struct gaih_servtuple));
492             newp->next = NULL;
493             newp->socktype = tp->socktype;
494             newp->protocol = tp->protocol;
495             newp->port = 0;
496
497             *lastp = newp;
498             lastp = &newp->next;
499         }
500     }
501
502     if (name != NULL)
503     {
504         at = alloca (sizeof (struct gaih_addrtuple));
505
506         at->family = AF_UNSPEC;
507         at->scopeid = 0;
508         at->next = NULL;
509
510         if (inet_pton (AF_INET, name, at->addr) > 0)
511         {
512             if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET || v4mapped)
513                 at->family = AF_INET;
514             else
515                 return -EAI_FAMILY;
516         }
517
518 #if defined __UCLIBC_HAS_IPV6__
519         if (at->family == AF_UNSPEC)
520         {
521             char *namebuf = strdupa (name);
522             char *scope_delim;
523
524             scope_delim = strchr (namebuf, SCOPE_DELIMITER);
525             if (scope_delim != NULL)
526                 *scope_delim = '\0';
527
528             if (inet_pton (AF_INET6, namebuf, at->addr) > 0)
529             {
530                 if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
531                     at->family = AF_INET6;
532                 else
533                     return -EAI_FAMILY;
534
535                 if (scope_delim != NULL)
536                 {
537                     int try_numericscope = 0;
538                     if (IN6_IS_ADDR_LINKLOCAL (at->addr)
539                         || IN6_IS_ADDR_MC_LINKLOCAL (at->addr))
540                     {
541                         at->scopeid = if_nametoindex (scope_delim + 1);
542                         if (at->scopeid == 0)
543                             try_numericscope = 1;
544                     }
545                     else
546                         try_numericscope = 1;
547
548                     if (try_numericscope != 0)
549                     {
550                         char *end;
551                         assert (sizeof (uint32_t) <= sizeof (unsigned long));
552                         at->scopeid = (uint32_t) strtoul (scope_delim + 1, &end,
553                                                           10);
554                         if (*end != '\0')
555                             return GAIH_OKIFUNSPEC | -EAI_NONAME;
556                     }
557                 }
558             }
559         }
560 #endif
561
562         if (at->family == AF_UNSPEC && (req->ai_flags & AI_NUMERICHOST) == 0)
563         {
564             struct hostent *h;
565             struct gaih_addrtuple **pat = &at;
566             int no_data = 0;
567             int no_inet6_data;
568
569             /*
570              * If we are looking for both IPv4 and IPv6 address we don't want
571              * the lookup functions to automatically promote IPv4 addresses to
572              * IPv6 addresses.
573              */
574
575 #if defined __UCLIBC_HAS_IPV6__
576             if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
577                 if (!(req->ai_flags & AI_ADDRCONFIG) || seen_ipv6)
578                     gethosts (AF_INET6, struct in6_addr);
579 #endif
580             no_inet6_data = no_data;
581
582             if (req->ai_family == AF_INET ||
583                 (!v4mapped && req->ai_family == AF_UNSPEC) ||
584                 (v4mapped && (no_inet6_data != 0 || (req->ai_flags & AI_ALL))))
585                 if (!(req->ai_flags & AI_ADDRCONFIG) || seen_ipv4)
586                     gethosts (AF_INET, struct in_addr);
587
588             if (no_data != 0 && no_inet6_data != 0)
589             {
590                 /* If both requests timed out report this. */
591                 if (no_data == EAI_AGAIN && no_inet6_data == EAI_AGAIN)
592                     return -EAI_AGAIN;
593
594                 /*
595                  * We made requests but they turned out no data.
596                  * The name is known, though.
597                  */
598                 return (GAIH_OKIFUNSPEC | -EAI_AGAIN);
599             }
600         }
601
602         if (at->family == AF_UNSPEC)
603             return (GAIH_OKIFUNSPEC | -EAI_NONAME);
604     }
605     else
606     {
607         struct gaih_addrtuple *atr;
608         atr = at = alloca (sizeof (struct gaih_addrtuple));
609         memset (at, '\0', sizeof (struct gaih_addrtuple));
610
611         if (req->ai_family == 0)
612         {
613             at->next = alloca (sizeof (struct gaih_addrtuple));
614             memset (at->next, '\0', sizeof (struct gaih_addrtuple));
615         }
616
617 #if defined __UCLIBC_HAS_IPV6__
618         if (req->ai_family == 0 || req->ai_family == AF_INET6)
619         {
620             at->family = AF_INET6;
621             if ((req->ai_flags & AI_PASSIVE) == 0)
622                 memcpy (at->addr, &in6addr_loopback, sizeof (struct in6_addr));
623             atr = at->next;
624         }
625 #endif
626
627         if (req->ai_family == 0 || req->ai_family == AF_INET)
628         {
629             atr->family = AF_INET;
630             if ((req->ai_flags & AI_PASSIVE) == 0)
631                 *(uint32_t *) atr->addr = htonl (INADDR_LOOPBACK);
632         }
633     }
634
635     if (pai == NULL)
636         return 0;
637
638     {
639         const char *c = NULL;
640         struct gaih_servtuple *st2;
641         struct gaih_addrtuple *at2 = at;
642         size_t socklen, namelen;
643         sa_family_t family;
644
645         /*
646          * buffer is the size of an unformatted IPv6 address in
647          * printable format.
648          */
649         char buffer[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"];
650
651         while (at2 != NULL)
652         {
653             if (req->ai_flags & AI_CANONNAME)
654             {
655                 struct hostent *h = NULL;
656
657                 int herrno;
658                 struct hostent th;
659                 size_t tmpbuflen = 512;
660                 char *tmpbuf;
661
662                 do
663                 {
664                     tmpbuflen *= 2;
665                     tmpbuf = alloca (tmpbuflen);
666
667                     if (tmpbuf == NULL)
668                         return -EAI_MEMORY;
669
670                     rc = gethostbyaddr_r (at2->addr,
671                                           ((at2->family == AF_INET6)
672                                            ? sizeof(struct in6_addr)
673                                            : sizeof(struct in_addr)),
674                                           at2->family, &th, tmpbuf, tmpbuflen,
675                                           &h, &herrno);
676
677                 }
678                 while (rc == errno && herrno == NETDB_INTERNAL);
679
680                 if (rc != 0 && herrno == NETDB_INTERNAL)
681                 {
682                     __set_h_errno (herrno);
683                     return -EAI_SYSTEM;
684                 }
685
686                 if (h == NULL)
687                     c = inet_ntop (at2->family, at2->addr, buffer, sizeof(buffer));
688                 else
689                     c = h->h_name;
690
691                 if (c == NULL)
692                     return GAIH_OKIFUNSPEC | -EAI_NONAME;
693
694                 namelen = strlen (c) + 1;
695             }
696             else
697                 namelen = 0;
698
699 #if defined __UCLIBC_HAS_IPV6__
700             if (at2->family == AF_INET6 || v4mapped)
701             {
702                 family = AF_INET6;
703                 socklen = sizeof (struct sockaddr_in6);
704             }
705 #endif
706 #if defined __UCLIBC_HAS_IPV4__ && defined __UCLIBC_HAS_IPV6__
707             else
708 #endif
709 #if defined __UCLIBC_HAS_IPV4__
710             {
711                 family = AF_INET;
712                 socklen = sizeof (struct sockaddr_in);
713             }
714 #endif
715             for (st2 = st; st2 != NULL; st2 = st2->next)
716             {
717                 if (req->ai_flags & AI_ADDRCONFIG) {
718                     if (family == AF_INET && !seen_ipv4)
719                         break;
720 #if defined __UCLIBC_HAS_IPV6__
721                     else if (family == AF_INET6 && !seen_ipv6)
722                         break;
723 #endif
724                 }
725                 *pai = malloc (sizeof (struct addrinfo) + socklen + namelen);
726                 if (*pai == NULL)
727                     return -EAI_MEMORY;
728
729                 (*pai)->ai_flags = req->ai_flags;
730                 (*pai)->ai_family = family;
731                 (*pai)->ai_socktype = st2->socktype;
732                 (*pai)->ai_protocol = st2->protocol;
733                 (*pai)->ai_addrlen = socklen;
734                 (*pai)->ai_addr = (void *) (*pai) + sizeof(struct addrinfo);
735 #if SALEN
736                 (*pai)->ai_addr->sa_len = socklen;
737 #endif /* SALEN */
738                 (*pai)->ai_addr->sa_family = family;
739
740 #if defined __UCLIBC_HAS_IPV6__
741                 if (family == AF_INET6)
742                 {
743                     struct sockaddr_in6 *sin6p =
744                         (struct sockaddr_in6 *) (*pai)->ai_addr;
745
746                     sin6p->sin6_flowinfo = 0;
747                     if (at2->family == AF_INET6)
748                     {
749                         memcpy (&sin6p->sin6_addr,
750                                 at2->addr, sizeof (struct in6_addr));
751                     }
752                     else
753                     {
754                         sin6p->sin6_addr.s6_addr32[0] = 0;
755                         sin6p->sin6_addr.s6_addr32[1] = 0;
756                         sin6p->sin6_addr.s6_addr32[2] = htonl(0x0000ffff);
757                         memcpy(&sin6p->sin6_addr.s6_addr32[3], 
758                                at2->addr, sizeof (sin6p->sin6_addr.s6_addr32[3]));
759                     }
760                     sin6p->sin6_port = st2->port;
761                     sin6p->sin6_scope_id = at2->scopeid;
762                 }
763 #endif
764 #if defined __UCLIBC_HAS_IPV4__ && defined __UCLIBC_HAS_IPV6__
765                 else
766 #endif
767 #if defined __UCLIBC_HAS_IPV4__
768                 {
769                     struct sockaddr_in *sinp =
770                         (struct sockaddr_in *) (*pai)->ai_addr;
771
772                     memcpy (&sinp->sin_addr,
773                             at2->addr, sizeof (struct in_addr));
774                     sinp->sin_port = st2->port;
775                     memset (sinp->sin_zero, '\0', sizeof (sinp->sin_zero));
776                 }
777 #endif
778                 if (c)
779                 {
780                     (*pai)->ai_canonname = ((void *) (*pai) +
781                                             sizeof (struct addrinfo) + socklen);
782                     strcpy ((*pai)->ai_canonname, c);
783                 }
784                 else
785                     (*pai)->ai_canonname = NULL;
786
787                 (*pai)->ai_next = NULL;
788                 pai = &((*pai)->ai_next);
789             }
790
791             at2 = at2->next;
792         }
793     }
794     return 0;
795 }
796
797 static struct gaih gaih[] =
798 {
799 #if defined __UCLIBC_HAS_IPV6__
800     { PF_INET6, gaih_inet },
801 #endif
802     { PF_INET, gaih_inet },
803 #if 0
804     { PF_LOCAL, gaih_local },
805 #endif
806     { PF_UNSPEC, NULL }
807 };
808
809 libc_hidden_proto(freeaddrinfo)
810 void
811 freeaddrinfo (struct addrinfo *ai)
812 {
813     struct addrinfo *p;
814
815     while (ai != NULL)
816     {
817         p = ai;
818         ai = ai->ai_next;
819         free (p);
820     }
821 }
822 libc_hidden_def(freeaddrinfo)
823
824 libc_hidden_proto(getaddrinfo)
825 int
826 getaddrinfo (const char *name, const char *service,
827              const struct addrinfo *hints, struct addrinfo **pai)
828 {
829     int i = 0, j = 0, last_i = 0;
830     struct addrinfo *p = NULL, **end;
831     struct gaih *g = gaih, *pg = NULL;
832     struct gaih_service gaih_service, *pservice;
833
834     if (name != NULL && name[0] == '*' && name[1] == 0)
835         name = NULL;
836
837     if (service != NULL && service[0] == '*' && service[1] == 0)
838         service = NULL;
839
840     if (name == NULL && service == NULL)
841         return EAI_NONAME;
842
843     if (hints == NULL)
844         hints = &default_hints;
845
846     if (hints->ai_flags & ~(AI_PASSIVE|AI_CANONNAME|AI_NUMERICHOST|
847                             AI_ADDRCONFIG|AI_V4MAPPED|AI_NUMERICSERV|AI_ALL))
848         return EAI_BADFLAGS;
849
850     if ((hints->ai_flags & AI_CANONNAME) && name == NULL)
851         return EAI_BADFLAGS;
852
853     if (service && service[0])
854     {
855         char *c;
856         gaih_service.name = service;
857         gaih_service.num = strtoul (gaih_service.name, &c, 10);
858         if (*c != '\0') {
859                 if (hints->ai_flags & AI_NUMERICSERV)
860                         return EAI_NONAME;
861
862             gaih_service.num = -1;
863         }
864         else
865             /*
866              * Can't specify a numerical socket unless a protocol
867              * family was given.
868              */
869             if (hints->ai_socktype == 0 && hints->ai_protocol == 0)
870                 return EAI_SERVICE;
871         pservice = &gaih_service;
872     }
873     else
874         pservice = NULL;
875
876     if (pai)
877         end = &p;
878     else
879         end = NULL;
880
881     while (g->gaih)
882     {
883         if (hints->ai_family == g->family || hints->ai_family == AF_UNSPEC)
884         {
885             if ((hints->ai_flags & AI_ADDRCONFIG) && !addrconfig(g->family))
886             {
887                 ++g;
888                 continue;
889             }
890             j++;
891             if (pg == NULL || pg->gaih != g->gaih)
892             {
893                 pg = g;
894                 i = g->gaih (name, pservice, hints, end);
895                 if (i != 0)
896                 {
897                     last_i = i;
898
899                     if (hints->ai_family == AF_UNSPEC && (i & GAIH_OKIFUNSPEC))
900                         continue;
901
902                     if (p)
903                         freeaddrinfo (p);
904
905                     return -(i & GAIH_EAI);
906                 }
907                 if (end)
908                     while(*end) end = &((*end)->ai_next);
909             }
910         }
911         ++g;
912     }
913
914     if (j == 0)
915         return EAI_FAMILY;
916
917     if (p)
918     {
919         *pai = p;
920         return 0;
921     }
922
923     if (pai == NULL && last_i == 0)
924         return 0;
925
926     if (p)
927         freeaddrinfo (p);
928
929     return last_i ? -(last_i & GAIH_EAI) : EAI_NONAME;
930 }
931 libc_hidden_def(getaddrinfo)