OSDN Git Service

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