OSDN Git Service

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