OSDN Git Service

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