OSDN Git Service

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