OSDN Git Service

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