OSDN Git Service

- adds several config-options to allow for turning off certain features
[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 /* Experimentally off - libc_hidden_proto(memcpy) */
71 /* Experimentally off - libc_hidden_proto(memset) */
72 /* libc_hidden_proto(strcmp) */
73 /* libc_hidden_proto(stpcpy) */
74 /* Experimentally off - libc_hidden_proto(strchr) */
75 /* Experimentally off - libc_hidden_proto(strcpy) */
76 /* Experimentally off - 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 defined __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 defined __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 defined __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 defined __UCLIBC_HAS_IPV6__
684             if (at2->family == AF_INET6 || v4mapped)
685             {
686                 family = AF_INET6;
687                 socklen = sizeof (struct sockaddr_in6);
688             }
689 #endif
690 #if defined __UCLIBC_HAS_IPV4__ && defined __UCLIBC_HAS_IPV6__
691             else
692 #endif
693 #if defined __UCLIBC_HAS_IPV4__
694             {
695                 family = AF_INET;
696                 socklen = sizeof (struct sockaddr_in);
697             }
698 #endif
699             for (st2 = st; st2 != NULL; st2 = st2->next)
700             {
701                 *pai = malloc (sizeof (struct addrinfo) + socklen + namelen);
702                 if (*pai == NULL)
703                     return -EAI_MEMORY;
704
705                 (*pai)->ai_flags = req->ai_flags;
706                 (*pai)->ai_family = family;
707                 (*pai)->ai_socktype = st2->socktype;
708                 (*pai)->ai_protocol = st2->protocol;
709                 (*pai)->ai_addrlen = socklen;
710                 (*pai)->ai_addr = (void *) (*pai) + sizeof(struct addrinfo);
711 #if SALEN
712                 (*pai)->ai_addr->sa_len = socklen;
713 #endif /* SALEN */
714                 (*pai)->ai_addr->sa_family = family;
715
716 #if defined __UCLIBC_HAS_IPV6__
717                 if (family == AF_INET6)
718                 {
719                     struct sockaddr_in6 *sin6p =
720                         (struct sockaddr_in6 *) (*pai)->ai_addr;
721
722                     sin6p->sin6_flowinfo = 0;
723                     if (at2->family == AF_INET6)
724                     {
725                         memcpy (&sin6p->sin6_addr,
726                                 at2->addr, sizeof (struct in6_addr));
727                     }
728                     else
729                     {
730                         sin6p->sin6_addr.s6_addr32[0] = 0;
731                         sin6p->sin6_addr.s6_addr32[1] = 0;
732                         sin6p->sin6_addr.s6_addr32[2] = htonl(0x0000ffff);
733                         memcpy(&sin6p->sin6_addr.s6_addr32[3], 
734                                at2->addr, sizeof (sin6p->sin6_addr.s6_addr32[3]));
735                     }
736                     sin6p->sin6_port = st2->port;
737                     sin6p->sin6_scope_id = at2->scopeid;
738                 }
739 #endif
740 #if defined __UCLIBC_HAS_IPV4__ && defined __UCLIBC_HAS_IPV6__
741                 else
742 #endif
743 #if defined __UCLIBC_HAS_IPV4__
744                 {
745                     struct sockaddr_in *sinp =
746                         (struct sockaddr_in *) (*pai)->ai_addr;
747
748                     memcpy (&sinp->sin_addr,
749                             at2->addr, sizeof (struct in_addr));
750                     sinp->sin_port = st2->port;
751                     memset (sinp->sin_zero, '\0', sizeof (sinp->sin_zero));
752                 }
753 #endif
754                 if (c)
755                 {
756                     (*pai)->ai_canonname = ((void *) (*pai) +
757                                             sizeof (struct addrinfo) + socklen);
758                     strcpy ((*pai)->ai_canonname, c);
759                 }
760                 else
761                     (*pai)->ai_canonname = NULL;
762
763                 (*pai)->ai_next = NULL;
764                 pai = &((*pai)->ai_next);
765             }
766
767             at2 = at2->next;
768         }
769     }
770     return 0;
771 }
772
773 static struct gaih gaih[] =
774 {
775 #if defined __UCLIBC_HAS_IPV6__
776     { PF_INET6, gaih_inet },
777 #endif
778     { PF_INET, gaih_inet },
779 #if 0
780     { PF_LOCAL, gaih_local },
781 #endif
782     { PF_UNSPEC, NULL }
783 };
784
785 libc_hidden_proto(freeaddrinfo)
786 void
787 freeaddrinfo (struct addrinfo *ai)
788 {
789     struct addrinfo *p;
790
791     while (ai != NULL)
792     {
793         p = ai;
794         ai = ai->ai_next;
795         free (p);
796     }
797 }
798 libc_hidden_def(freeaddrinfo)
799
800 libc_hidden_proto(getaddrinfo)
801 int
802 getaddrinfo (const char *name, const char *service,
803              const struct addrinfo *hints, struct addrinfo **pai)
804 {
805     int i = 0, j = 0, last_i = 0;
806     struct addrinfo *p = NULL, **end;
807     struct gaih *g = gaih, *pg = NULL;
808     struct gaih_service gaih_service, *pservice;
809
810     if (name != NULL && name[0] == '*' && name[1] == 0)
811         name = NULL;
812
813     if (service != NULL && service[0] == '*' && service[1] == 0)
814         service = NULL;
815
816     if (name == NULL && service == NULL)
817         return EAI_NONAME;
818
819     if (hints == NULL)
820         hints = &default_hints;
821
822     if (hints->ai_flags & ~(AI_PASSIVE|AI_CANONNAME|AI_NUMERICHOST|
823                             AI_ADDRCONFIG|AI_V4MAPPED|AI_NUMERICSERV|AI_ALL))
824         return EAI_BADFLAGS;
825
826     if ((hints->ai_flags & AI_CANONNAME) && name == NULL)
827         return EAI_BADFLAGS;
828
829     if (service && service[0])
830     {
831         char *c;
832         gaih_service.name = service;
833         gaih_service.num = strtoul (gaih_service.name, &c, 10);
834         if (*c != '\0') {
835                 if (hints->ai_flags & AI_NUMERICSERV)
836                         return EAI_NONAME;
837
838             gaih_service.num = -1;
839         }
840         else
841             /*
842              * Can't specify a numerical socket unless a protocol
843              * family was given.
844              */
845             if (hints->ai_socktype == 0 && hints->ai_protocol == 0)
846                 return EAI_SERVICE;
847         pservice = &gaih_service;
848     }
849     else
850         pservice = NULL;
851
852     if (pai)
853         end = &p;
854     else
855         end = NULL;
856
857     while (g->gaih)
858     {
859         if (hints->ai_family == g->family || hints->ai_family == AF_UNSPEC)
860         {
861             if ((hints->ai_flags & AI_ADDRCONFIG) && !addrconfig(g->family))
862                 continue;
863             j++;
864             if (pg == NULL || pg->gaih != g->gaih)
865             {
866                 pg = g;
867                 i = g->gaih (name, pservice, hints, end);
868                 if (i != 0)
869                 {
870                     last_i = i;
871
872                     if (hints->ai_family == AF_UNSPEC && (i & GAIH_OKIFUNSPEC))
873                         continue;
874
875                     if (p)
876                         freeaddrinfo (p);
877
878                     return -(i & GAIH_EAI);
879                 }
880                 if (end)
881                     while(*end) end = &((*end)->ai_next);
882             }
883         }
884         ++g;
885     }
886
887     if (j == 0)
888         return EAI_FAMILY;
889
890     if (p)
891     {
892         *pai = p;
893         return 0;
894     }
895
896     if (pai == NULL && last_i == 0)
897         return 0;
898
899     if (p)
900         freeaddrinfo (p);
901
902     return last_i ? -(last_i & GAIH_EAI) : EAI_NONAME;
903 }
904 libc_hidden_def(getaddrinfo)