OSDN Git Service

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