OSDN Git Service

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