OSDN Git Service

some global data relocs gone
[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 _GNU_SOURCE
53 #define __FORCE_GLIBC
54 #include <features.h>
55 #include <assert.h>
56 #include <errno.h>
57 #include <netdb.h>
58 #include <resolv.h>
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <string.h>
62 #include <unistd.h>
63 #include <arpa/inet.h>
64 #include <sys/socket.h>
65 #include <netinet/in.h>
66 #include <sys/types.h>
67 #include <sys/un.h>
68 #include <sys/utsname.h>
69 #include <net/if.h>
70
71 libc_hidden_proto(memcpy)
72 libc_hidden_proto(memset)
73 /* libc_hidden_proto(strcmp) */
74 /* libc_hidden_proto(stpcpy) */
75 libc_hidden_proto(strchr)
76 libc_hidden_proto(strcpy)
77 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 /* The following declarations and definitions have been removed from
95  *    the public header since we don't want people to use them.  */
96 #define AI_V4MAPPED     0x0008  /* IPv4-mapped addresses are acceptable.  */
97 #define AI_ALL          0x0010  /* Return both IPv4 and IPv6 addresses.  */
98 #define AI_ADDRCONFIG   0x0020  /* Use configuration of this host to choose 
99                                     returned address type.  */
100 #define AI_DEFAULT    (AI_V4MAPPED | AI_ADDRCONFIG)
101
102
103 #define GAIH_OKIFUNSPEC 0x0100
104 #define GAIH_EAI        ~(GAIH_OKIFUNSPEC)
105
106 #ifndef UNIX_PATH_MAX
107 #define UNIX_PATH_MAX  108
108 #endif
109
110 struct gaih_service
111 {
112     const char *name;
113     int num;
114 };
115
116 struct gaih_servtuple
117 {
118     struct gaih_servtuple *next;
119     int socktype;
120     int protocol;
121     int port;
122 };
123
124 static const struct gaih_servtuple nullserv;
125
126 struct gaih_addrtuple
127 {
128     struct gaih_addrtuple *next;
129     int family;
130     char addr[16];
131     uint32_t scopeid;
132 };
133
134 struct gaih_typeproto
135 {
136     int socktype;
137     int protocol;
138     char name[4];
139     int protoflag;
140 };
141
142 /* Values for `protoflag'.  */
143 #define GAI_PROTO_NOSERVICE     1
144 #define GAI_PROTO_PROTOANY      2
145
146 static const struct gaih_typeproto gaih_inet_typeproto[] =
147 {
148     { 0, 0, "", 0 },
149     { SOCK_STREAM, IPPROTO_TCP, "tcp", 0 },
150     { SOCK_DGRAM, IPPROTO_UDP, "udp", 0 },
151     { SOCK_RAW, 0, "raw", GAI_PROTO_PROTOANY|GAI_PROTO_NOSERVICE },
152     { 0, 0, "", 0 }
153 };
154
155 struct gaih
156 {
157     int family;
158     int (*gaih)(const char *name, const struct gaih_service *service,
159                 const struct addrinfo *req, struct addrinfo **pai);
160 };
161
162 #if PF_UNSPEC == 0
163 static const struct addrinfo default_hints;
164 #else
165 static const struct addrinfo default_hints =
166 { 0, PF_UNSPEC, 0, 0, 0, NULL, NULL, NULL };
167 #endif
168
169
170 static int addrconfig (sa_family_t af)
171 {
172     int s;
173     int ret;
174     int saved_errno = errno;
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     __set_errno (saved_errno);
184     return ret;
185 }
186
187 #if 0
188 /* Using Unix sockets this way is a security risk.  */
189 static int
190 gaih_local (const char *name, const struct gaih_service *service,
191             const struct addrinfo *req, struct addrinfo **pai)
192 {
193     struct utsname utsname;
194
195     if ((name != NULL) && (req->ai_flags & AI_NUMERICHOST))
196         return GAIH_OKIFUNSPEC | -EAI_NONAME;
197
198     if ((name != NULL) || (req->ai_flags & AI_CANONNAME))
199         if (uname (&utsname) < 0)
200             return -EAI_SYSTEM;
201
202     if (name != NULL)
203     {
204         if (strcmp(name, "localhost") &&
205             strcmp(name, "local") &&
206             strcmp(name, "unix") &&
207             strcmp(name, utsname.nodename))
208             return GAIH_OKIFUNSPEC | -EAI_NONAME;
209     }
210
211     if (req->ai_protocol || req->ai_socktype)
212     {
213         const struct gaih_typeproto *tp = gaih_inet_typeproto + 1;
214
215         while (tp->name[0]
216                && ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0
217                    || (req->ai_socktype != 0 && req->ai_socktype != tp->socktype)
218                    || (req->ai_protocol != 0
219                        && !(tp->protoflag & GAI_PROTO_PROTOANY)
220                        && req->ai_protocol != tp->protocol)))
221             ++tp;
222
223         if (! tp->name[0])
224         {
225             if (req->ai_socktype)
226                 return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE);
227             else
228                 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
229         }
230     }
231
232     *pai = malloc (sizeof (struct addrinfo) + sizeof (struct sockaddr_un)
233                    + ((req->ai_flags & AI_CANONNAME)
234                       ? (strlen(utsname.nodename) + 1): 0));
235     if (*pai == NULL)
236         return -EAI_MEMORY;
237
238     (*pai)->ai_next = NULL;
239     (*pai)->ai_flags = req->ai_flags;
240     (*pai)->ai_family = AF_LOCAL;
241     (*pai)->ai_socktype = req->ai_socktype ? req->ai_socktype : SOCK_STREAM;
242     (*pai)->ai_protocol = req->ai_protocol;
243     (*pai)->ai_addrlen = sizeof (struct sockaddr_un);
244     (*pai)->ai_addr = (void *) (*pai) + sizeof (struct addrinfo);
245
246 #if SALEN
247     ((struct sockaddr_un *) (*pai)->ai_addr)->sun_len =
248         sizeof (struct sockaddr_un);
249 #endif /* SALEN */
250
251     ((struct sockaddr_un *)(*pai)->ai_addr)->sun_family = AF_LOCAL;
252     memset(((struct sockaddr_un *)(*pai)->ai_addr)->sun_path, 0, UNIX_PATH_MAX);
253
254     if (service)
255     {
256         struct sockaddr_un *sunp = (struct sockaddr_un *) (*pai)->ai_addr;
257
258         if (strchr (service->name, '/') != NULL)
259         {
260             if (strlen (service->name) >= sizeof (sunp->sun_path))
261                 return GAIH_OKIFUNSPEC | -EAI_SERVICE;
262
263             strcpy (sunp->sun_path, service->name);
264         }
265         else
266         {
267             if (strlen (P_tmpdir "/") + 1 + strlen (service->name) >=
268                 sizeof (sunp->sun_path))
269                 return GAIH_OKIFUNSPEC | -EAI_SERVICE;
270
271             stpcpy (stpcpy (sunp->sun_path, P_tmpdir "/"), service->name);
272         }
273     }
274     else
275     {
276         /* This is a dangerous use of the interface since there is a time
277            window between the test for the file and the actual creation
278            (done by the caller) in which a file with the same name could
279            be created.  */
280         char *buf = ((struct sockaddr_un *) (*pai)->ai_addr)->sun_path;
281
282         if (__builtin_expect (__path_search (buf, L_tmpnam, NULL, NULL, 0),
283                               0) != 0
284             || __builtin_expect (__gen_tempname (buf, __GT_NOCREATE), 0) != 0)
285             return -EAI_SYSTEM;
286     }
287
288     if (req->ai_flags & AI_CANONNAME)
289         (*pai)->ai_canonname = strcpy ((char *) *pai + sizeof (struct addrinfo)
290                                        + sizeof (struct sockaddr_un),
291                                        utsname.nodename);
292     else
293         (*pai)->ai_canonname = NULL;
294     return 0;
295 }
296 #endif  /* 0 */
297
298 static int
299 gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
300                 const struct addrinfo *req, struct gaih_servtuple *st)
301 {
302     struct servent *s;
303     size_t tmpbuflen = 1024;
304     struct servent ts;
305     char *tmpbuf;
306     int r;
307
308     do
309     {
310         tmpbuf = alloca (tmpbuflen);
311
312         r = getservbyname_r (servicename, tp->name, &ts, tmpbuf, tmpbuflen,
313                              &s);
314         if (r != 0 || s == NULL)
315         {
316             if (r == ERANGE)
317                 tmpbuflen *= 2;
318             else
319                 return GAIH_OKIFUNSPEC | -EAI_SERVICE;
320         }
321     }
322     while (r);
323
324     st->next = NULL;
325     st->socktype = tp->socktype;
326     st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
327                     ? req->ai_protocol : tp->protocol);
328     st->port = s->s_port;
329
330     return 0;
331 }
332
333 #define gethosts(_family, _type)                                        \
334 {                                                                       \
335     int i, herrno;                                                      \
336     size_t tmpbuflen;                                                   \
337     struct hostent th;                                                  \
338     char *tmpbuf;                                                       \
339     tmpbuflen = 512;                                                    \
340     no_data = 0;                                                        \
341     do {                                                                \
342         tmpbuflen *= 2;                                                 \
343         tmpbuf = alloca (tmpbuflen);                                    \
344         rc = gethostbyname2_r (name, _family, &th, tmpbuf,              \
345                                tmpbuflen, &h, &herrno);                 \
346     } while (rc == ERANGE && herrno == NETDB_INTERNAL);                 \
347     if (rc != 0)                                                        \
348     {                                                                   \
349         if (herrno == NETDB_INTERNAL)                                   \
350         {                                                               \
351             __set_h_errno (herrno);                                     \
352                 return -EAI_SYSTEM;                                     \
353         }                                                               \
354         if (herrno == TRY_AGAIN)                                        \
355             no_data = EAI_AGAIN;                                        \
356         else                                                            \
357             no_data = herrno == NO_DATA;                                \
358     }                                                                   \
359     else if (h != NULL)                                                 \
360     {                                                                   \
361         for (i = 0; h->h_addr_list[i]; i++)                             \
362         {                                                               \
363             if (*pat == NULL) {                                         \
364                 *pat = alloca (sizeof(struct gaih_addrtuple));          \
365                     (*pat)->scopeid = 0;                                \
366             }                                                           \
367             (*pat)->next = NULL;                                        \
368                 (*pat)->family = _family;                               \
369                 memcpy ((*pat)->addr, h->h_addr_list[i],                \
370                         sizeof(_type));                                 \
371                 pat = &((*pat)->next);                                  \
372         }                                                               \
373     }                                                                   \
374 }
375
376 static int
377 gaih_inet (const char *name, const struct gaih_service *service,
378            const struct addrinfo *req, struct addrinfo **pai)
379 {
380     const struct gaih_typeproto *tp = gaih_inet_typeproto;
381     struct gaih_servtuple *st = (struct gaih_servtuple *) &nullserv;
382     struct gaih_addrtuple *at = NULL;
383     int rc;
384     int v4mapped = (req->ai_family == PF_UNSPEC || req->ai_family == PF_INET6) &&
385         (req->ai_flags & AI_V4MAPPED);
386
387     if (req->ai_protocol || req->ai_socktype)
388     {
389         ++tp;
390
391         while (tp->name[0]
392                && ((req->ai_socktype != 0 && req->ai_socktype != tp->socktype)
393                    || (req->ai_protocol != 0
394                        && !(tp->protoflag & GAI_PROTO_PROTOANY)
395                        && req->ai_protocol != tp->protocol)))
396             ++tp;
397
398         if (! tp->name[0])
399         {
400             if (req->ai_socktype)
401                 return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE);
402             else
403                 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
404         }
405     }
406
407     if (service != NULL)
408     {
409         if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
410             return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
411
412         if (service->num < 0)
413         {
414             if (tp->name[0])
415             {
416                 st = (struct gaih_servtuple *)
417                     alloca (sizeof (struct gaih_servtuple));
418
419                 if ((rc = gaih_inet_serv (service->name, tp, req, st)))
420                     return rc;
421             }
422             else
423             {
424                 struct gaih_servtuple **pst = &st;
425                 for (tp++; tp->name[0]; tp++)
426                 {
427                     struct gaih_servtuple *newp;
428
429                     if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
430                         continue;
431
432                     if (req->ai_socktype != 0
433                         && req->ai_socktype != tp->socktype)
434                         continue;
435                     if (req->ai_protocol != 0
436                         && !(tp->protoflag & GAI_PROTO_PROTOANY)
437                         && req->ai_protocol != tp->protocol)
438                         continue;
439
440                     newp = (struct gaih_servtuple *)
441                         alloca (sizeof (struct gaih_servtuple));
442
443                     if ((rc = gaih_inet_serv (service->name, tp, req, newp)))
444                     {
445                         if (rc & GAIH_OKIFUNSPEC)
446                             continue;
447                         return rc;
448                     }
449
450                     *pst = newp;
451                     pst = &(newp->next);
452                 }
453                 if (st == (struct gaih_servtuple *) &nullserv)
454                     return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
455             }
456         }
457         else
458         {
459             st = alloca (sizeof (struct gaih_servtuple));
460             st->next = NULL;
461             st->socktype = tp->socktype;
462             st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
463                             ? req->ai_protocol : tp->protocol);
464             st->port = htons (service->num);
465         }
466     }
467     else if (req->ai_socktype || req->ai_protocol)
468     {
469         st = alloca (sizeof (struct gaih_servtuple));
470         st->next = NULL;
471         st->socktype = tp->socktype;
472         st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
473                         ? req->ai_protocol : tp->protocol);
474         st->port = 0;
475     }
476     else
477     {
478         /* 
479          * Neither socket type nor protocol is set.  Return all socket types
480          * we know about.
481          */
482         struct gaih_servtuple **lastp = &st;
483         for (++tp; tp->name[0]; ++tp)
484         {
485             struct gaih_servtuple *newp;
486
487             newp = alloca (sizeof (struct gaih_servtuple));
488             newp->next = NULL;
489             newp->socktype = tp->socktype;
490             newp->protocol = tp->protocol;
491             newp->port = 0;
492
493             *lastp = newp;
494             lastp = &newp->next;
495         }
496     }
497
498     if (name != NULL)
499     {
500         at = alloca (sizeof (struct gaih_addrtuple));
501
502         at->family = AF_UNSPEC;
503         at->scopeid = 0;
504         at->next = NULL;
505
506         if (inet_pton (AF_INET, name, at->addr) > 0)
507         {
508             if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET || v4mapped)
509                 at->family = AF_INET;
510             else
511                 return -EAI_FAMILY;
512         }
513
514 #if __UCLIBC_HAS_IPV6__
515         if (at->family == AF_UNSPEC)
516         {
517             char *namebuf = strdupa (name);
518             char *scope_delim;
519
520             scope_delim = strchr (namebuf, SCOPE_DELIMITER);
521             if (scope_delim != NULL)
522                 *scope_delim = '\0';
523
524             if (inet_pton (AF_INET6, namebuf, at->addr) > 0)
525             {
526                 if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
527                     at->family = AF_INET6;
528                 else
529                     return -EAI_FAMILY;
530
531                 if (scope_delim != NULL)
532                 {
533                     int try_numericscope = 0;
534                     if (IN6_IS_ADDR_LINKLOCAL (at->addr)
535                         || IN6_IS_ADDR_MC_LINKLOCAL (at->addr))
536                     {
537                         at->scopeid = if_nametoindex (scope_delim + 1);
538                         if (at->scopeid == 0)
539                             try_numericscope = 1;
540                     }
541                     else
542                         try_numericscope = 1;
543
544                     if (try_numericscope != 0)
545                     {
546                         char *end;
547                         assert (sizeof (uint32_t) <= sizeof (unsigned long));
548                         at->scopeid = (uint32_t) strtoul (scope_delim + 1, &end,
549                                                           10);
550                         if (*end != '\0')
551                             return GAIH_OKIFUNSPEC | -EAI_NONAME;
552                     }
553                 }
554             }
555         }
556 #endif
557
558         if (at->family == AF_UNSPEC && (req->ai_flags & AI_NUMERICHOST) == 0)
559         {
560             struct hostent *h;
561             struct gaih_addrtuple **pat = &at;
562             int no_data = 0;
563             int no_inet6_data;
564
565             /*
566              * If we are looking for both IPv4 and IPv6 address we don't want
567              * the lookup functions to automatically promote IPv4 addresses to
568              * IPv6 addresses.
569              */
570
571 #if __UCLIBC_HAS_IPV6__
572             if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
573                 gethosts (AF_INET6, struct in6_addr);
574 #endif
575             no_inet6_data = no_data;
576
577             if (req->ai_family == AF_INET ||
578                 (!v4mapped && req->ai_family == AF_UNSPEC) ||
579                 (v4mapped && (no_inet6_data != 0 || (req->ai_flags & AI_ALL))))
580                 gethosts (AF_INET, struct in_addr);
581
582             if (no_data != 0 && no_inet6_data != 0)
583             {
584                 /* If both requests timed out report this. */
585                 if (no_data == EAI_AGAIN && no_inet6_data == EAI_AGAIN)
586                     return -EAI_AGAIN;
587
588                 /*
589                  * We made requests but they turned out no data.
590                  * The name is known, though.
591                  */
592                 return (GAIH_OKIFUNSPEC | -EAI_AGAIN);
593             }
594         }
595
596         if (at->family == AF_UNSPEC)
597             return (GAIH_OKIFUNSPEC | -EAI_NONAME);
598     }
599     else
600     {
601         struct gaih_addrtuple *atr;
602         atr = at = alloca (sizeof (struct gaih_addrtuple));
603         memset (at, '\0', sizeof (struct gaih_addrtuple));
604
605         if (req->ai_family == 0)
606         {
607             at->next = alloca (sizeof (struct gaih_addrtuple));
608             memset (at->next, '\0', sizeof (struct gaih_addrtuple));
609         }
610
611 #if __UCLIBC_HAS_IPV6__
612         if (req->ai_family == 0 || req->ai_family == AF_INET6)
613         {
614             at->family = AF_INET6;
615             if ((req->ai_flags & AI_PASSIVE) == 0)
616                 memcpy (at->addr, &in6addr_loopback, sizeof (struct in6_addr));
617             atr = at->next;
618         }
619 #endif
620
621         if (req->ai_family == 0 || req->ai_family == AF_INET)
622         {
623             atr->family = AF_INET;
624             if ((req->ai_flags & AI_PASSIVE) == 0)
625                 *(uint32_t *) atr->addr = htonl (INADDR_LOOPBACK);
626         }
627     }
628
629     if (pai == NULL)
630         return 0;
631
632     {
633         const char *c = NULL;
634         struct gaih_servtuple *st2;
635         struct gaih_addrtuple *at2 = at;
636         size_t socklen, namelen;
637         sa_family_t family;
638
639         /*
640          * buffer is the size of an unformatted IPv6 address in
641          * printable format.
642          */
643         char buffer[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"];
644
645         while (at2 != NULL)
646         {
647             if (req->ai_flags & AI_CANONNAME)
648             {
649                 struct hostent *h = NULL;
650
651                 int herrno;
652                 struct hostent th;
653                 size_t tmpbuflen = 512;
654                 char *tmpbuf;
655
656                 do
657                 {
658                     tmpbuflen *= 2;
659                     tmpbuf = alloca (tmpbuflen);
660
661                     if (tmpbuf == NULL)
662                         return -EAI_MEMORY;
663
664                     rc = gethostbyaddr_r (at2->addr,
665                                           ((at2->family == AF_INET6)
666                                            ? sizeof(struct in6_addr)
667                                            : sizeof(struct in_addr)),
668                                           at2->family, &th, tmpbuf, tmpbuflen,
669                                           &h, &herrno);
670
671                 }
672                 while (rc == errno && herrno == NETDB_INTERNAL);
673
674                 if (rc != 0 && herrno == NETDB_INTERNAL)
675                 {
676                     __set_h_errno (herrno);
677                     return -EAI_SYSTEM;
678                 }
679
680                 if (h == NULL)
681                     c = inet_ntop (at2->family, at2->addr, buffer, sizeof(buffer));
682                 else
683                     c = h->h_name;
684
685                 if (c == NULL)
686                     return GAIH_OKIFUNSPEC | -EAI_NONAME;
687
688                 namelen = strlen (c) + 1;
689             }
690             else
691                 namelen = 0;
692
693 #if __UCLIBC_HAS_IPV6__
694             if (at2->family == AF_INET6 || v4mapped)
695             {
696                 family = AF_INET6;
697                 socklen = sizeof (struct sockaddr_in6);
698             }
699             else
700 #endif
701             {
702                 family = AF_INET;
703                 socklen = sizeof (struct sockaddr_in);
704             }
705
706             for (st2 = st; st2 != NULL; st2 = st2->next)
707             {
708                 *pai = malloc (sizeof (struct addrinfo) + socklen + namelen);
709                 if (*pai == NULL)
710                     return -EAI_MEMORY;
711
712                 (*pai)->ai_flags = req->ai_flags;
713                 (*pai)->ai_family = family;
714                 (*pai)->ai_socktype = st2->socktype;
715                 (*pai)->ai_protocol = st2->protocol;
716                 (*pai)->ai_addrlen = socklen;
717                 (*pai)->ai_addr = (void *) (*pai) + sizeof(struct addrinfo);
718 #if SALEN
719                 (*pai)->ai_addr->sa_len = socklen;
720 #endif /* SALEN */
721                 (*pai)->ai_addr->sa_family = family;
722
723 #if __UCLIBC_HAS_IPV6__
724                 if (family == AF_INET6)
725                 {
726                     struct sockaddr_in6 *sin6p =
727                         (struct sockaddr_in6 *) (*pai)->ai_addr;
728
729                     sin6p->sin6_flowinfo = 0;
730                     if (at2->family == AF_INET6)
731                     {
732                         memcpy (&sin6p->sin6_addr,
733                                 at2->addr, sizeof (struct in6_addr));
734                     }
735                     else
736                     {
737                         sin6p->sin6_addr.s6_addr32[0] = 0;
738                         sin6p->sin6_addr.s6_addr32[1] = 0;
739                         sin6p->sin6_addr.s6_addr32[2] = htonl(0x0000ffff);
740                         memcpy(&sin6p->sin6_addr.s6_addr32[3], 
741                                at2->addr, sizeof (sin6p->sin6_addr.s6_addr32[3]));
742                     }
743                     sin6p->sin6_port = st2->port;
744                     sin6p->sin6_scope_id = at2->scopeid;
745                 }
746                 else
747 #endif
748                 {
749                     struct sockaddr_in *sinp =
750                         (struct sockaddr_in *) (*pai)->ai_addr;
751
752                     memcpy (&sinp->sin_addr,
753                             at2->addr, sizeof (struct in_addr));
754                     sinp->sin_port = st2->port;
755                     memset (sinp->sin_zero, '\0', sizeof (sinp->sin_zero));
756                 }
757
758                 if (c)
759                 {
760                     (*pai)->ai_canonname = ((void *) (*pai) +
761                                             sizeof (struct addrinfo) + socklen);
762                     strcpy ((*pai)->ai_canonname, c);
763                 }
764                 else
765                     (*pai)->ai_canonname = NULL;
766
767                 (*pai)->ai_next = NULL;
768                 pai = &((*pai)->ai_next);
769             }
770
771             at2 = at2->next;
772         }
773     }
774     return 0;
775 }
776
777 static struct gaih gaih[] =
778 {
779 #if __UCLIBC_HAS_IPV6__
780     { PF_INET6, gaih_inet },
781 #endif
782     { PF_INET, gaih_inet },
783 #if 0
784     { PF_LOCAL, gaih_local },
785 #endif
786     { PF_UNSPEC, NULL }
787 };
788
789 libc_hidden_proto(freeaddrinfo)
790 void
791 freeaddrinfo (struct addrinfo *ai)
792 {
793     struct addrinfo *p;
794
795     while (ai != NULL)
796     {
797         p = ai;
798         ai = ai->ai_next;
799         free (p);
800     }
801 }
802 libc_hidden_def(freeaddrinfo)
803
804 libc_hidden_proto(getaddrinfo)
805 int
806 getaddrinfo (const char *name, const char *service,
807              const struct addrinfo *hints, struct addrinfo **pai)
808 {
809     int i = 0, j = 0, last_i = 0;
810     struct addrinfo *p = NULL, **end;
811     struct gaih *g = gaih, *pg = NULL;
812     struct gaih_service gaih_service, *pservice;
813
814     if (name != NULL && name[0] == '*' && name[1] == 0)
815         name = NULL;
816
817     if (service != NULL && service[0] == '*' && service[1] == 0)
818         service = NULL;
819
820     if (name == NULL && service == NULL)
821         return EAI_NONAME;
822
823     if (hints == NULL)
824         hints = &default_hints;
825
826     if (hints->ai_flags & ~(AI_PASSIVE|AI_CANONNAME|AI_NUMERICHOST|
827                             AI_ADDRCONFIG|AI_V4MAPPED|AI_ALL))
828         return EAI_BADFLAGS;
829
830     if ((hints->ai_flags & AI_CANONNAME) && name == NULL)
831         return EAI_BADFLAGS;
832
833     if (service && service[0])
834     {
835         char *c;
836         gaih_service.name = service;
837         gaih_service.num = strtoul (gaih_service.name, &c, 10);
838         if (*c)
839             gaih_service.num = -1;
840         else
841             /*
842              * Can't specify a numerical socket unless a protocol
843              * family was given.
844              */
845             if (hints->ai_socktype == 0 && hints->ai_protocol == 0)
846                 return EAI_SERVICE;
847         pservice = &gaih_service;
848     }
849     else
850         pservice = NULL;
851
852     if (pai)
853         end = &p;
854     else
855         end = NULL;
856
857     while (g->gaih)
858     {
859         if (hints->ai_family == g->family || hints->ai_family == AF_UNSPEC)
860         {
861             if ((hints->ai_flags & AI_ADDRCONFIG) && !addrconfig(g->family))
862                 continue;
863             j++;
864             if (pg == NULL || pg->gaih != g->gaih)
865             {
866                 pg = g;
867                 i = g->gaih (name, pservice, hints, end);
868                 if (i != 0)
869                 {
870                     last_i = i;
871
872                     if (hints->ai_family == AF_UNSPEC && (i & GAIH_OKIFUNSPEC))
873                         continue;
874
875                     if (p)
876                         freeaddrinfo (p);
877
878                     return -(i & GAIH_EAI);
879                 }
880                 if (end)
881                     while(*end) end = &((*end)->ai_next);
882             }
883         }
884         ++g;
885     }
886
887     if (j == 0)
888         return EAI_FAMILY;
889
890     if (p)
891     {
892         *pai = p;
893         return 0;
894     }
895
896     if (pai == NULL && last_i == 0)
897         return 0;
898
899     if (p)
900         freeaddrinfo (p);
901
902     return last_i ? -(last_i & GAIH_EAI) : EAI_NONAME;
903 }
904 libc_hidden_def(getaddrinfo)