OSDN Git Service

mkostemp: fix implementation
[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 #include <assert.h>
55 #include <errno.h>
56 #include <netdb.h>
57 #ifdef __UCLIBC_HAS_TLS__
58 #include <tls.h>
59 #endif
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 0 /* 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) != 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;
397         struct gaih_servtuple *st;
398         struct gaih_addrtuple *at;
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 = 0;
403         if (req->ai_flags & AI_ADDRCONFIG) {
404                 /* "seen" is only used when AI_ADDRCONFIG is specified.
405                    Avoid unnecessary call to __check_pf() otherwise
406                    since it can be costly especially when RSBAC-Net is enabled.  */
407                 seen = __check_pf();
408         }
409
410         memset(&nullserv, 0, sizeof(nullserv));
411
412         tp = gaih_inet_typeproto;
413         if (req->ai_protocol || req->ai_socktype) {
414                 ++tp;
415                 while (tp->name[0]) {
416                         if ((req->ai_socktype == 0 || req->ai_socktype == tp->socktype)
417                          && (req->ai_protocol == 0 || req->ai_protocol == tp->protocol || (tp->protoflag & GAI_PROTO_PROTOANY))
418                         ) {
419                                 goto found;
420                         }
421                         ++tp;
422                 }
423                 if (req->ai_socktype)
424                         return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE);
425                 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
426  found: ;
427         }
428
429         st = &nullserv;
430         if (service != NULL) {
431                 if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
432                         return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
433
434                 if (service->num < 0) {
435                         if (tp->name[0]) {
436                                 st = alloca(sizeof(struct gaih_servtuple));
437                                 rc = gaih_inet_serv(service->name, tp, req, st);
438                                 if (rc)
439                                         return rc;
440                         } else {
441                                 struct gaih_servtuple **pst = &st;
442                                 for (tp++; tp->name[0]; tp++) {
443                                         struct gaih_servtuple *newp;
444
445                                         if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
446                                                 continue;
447
448                                         if (req->ai_socktype != 0 && req->ai_socktype != tp->socktype)
449                                                 continue;
450                                         if (req->ai_protocol != 0
451                                          && !(tp->protoflag & GAI_PROTO_PROTOANY)
452                                          && req->ai_protocol != tp->protocol)
453                                                 continue;
454
455                                         newp = alloca(sizeof(struct gaih_servtuple));
456                                         rc = gaih_inet_serv(service->name, tp, req, newp);
457                                         if (rc) {
458                                                 if (rc & GAIH_OKIFUNSPEC)
459                                                         continue;
460                                                 return rc;
461                                         }
462
463                                         *pst = newp;
464                                         pst = &(newp->next);
465                                 }
466                                 if (st == &nullserv)
467                                         return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
468                         }
469                 } else {
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 = htons(service->num);
476                 }
477         } else if (req->ai_socktype || req->ai_protocol) {
478                 st = alloca(sizeof(struct gaih_servtuple));
479                 st->next = NULL;
480                 st->socktype = tp->socktype;
481                 st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
482                                 ? req->ai_protocol : tp->protocol);
483                 st->port = 0;
484         } else {
485                 /*
486                  * Neither socket type nor protocol is set.  Return all socket types
487                  * we know about.
488                  */
489                 struct gaih_servtuple **lastp = &st;
490                 for (++tp; tp->name[0]; ++tp) {
491                         struct gaih_servtuple *newp;
492
493                         newp = alloca(sizeof(struct gaih_servtuple));
494                         newp->next = NULL;
495                         newp->socktype = tp->socktype;
496                         newp->protocol = tp->protocol;
497                         newp->port = 0;
498
499                         *lastp = newp;
500                         lastp = &newp->next;
501                 }
502         }
503
504         at = NULL;
505         if (name != NULL) {
506                 at = alloca(sizeof(struct gaih_addrtuple));
507                 at->family = AF_UNSPEC;
508                 at->scopeid = 0;
509                 at->next = NULL;
510
511                 if (inet_pton(AF_INET, name, at->addr) > 0) {
512                         if (req->ai_family != AF_UNSPEC && req->ai_family != AF_INET && !v4mapped)
513                                 return -EAI_FAMILY;
514                         at->family = AF_INET;
515                 }
516
517 #if defined __UCLIBC_HAS_IPV6__
518                 if (at->family == AF_UNSPEC) {
519                         char *namebuf = strdupa(name);
520                         char *scope_delim;
521
522                         scope_delim = strchr(namebuf, SCOPE_DELIMITER);
523                         if (scope_delim != NULL)
524                                 *scope_delim = '\0';
525
526                         if (inet_pton(AF_INET6, namebuf, at->addr) > 0) {
527                                 if (req->ai_family != AF_UNSPEC && req->ai_family != AF_INET6)
528                                         return -EAI_FAMILY;
529                                 at->family = AF_INET6;
530                                 if (scope_delim != NULL) {
531                                         int try_numericscope = 0;
532                                         uint32_t *a32 = (uint32_t*)at->addr;
533                                         if (IN6_IS_ADDR_LINKLOCAL(a32) || IN6_IS_ADDR_MC_LINKLOCAL(at->addr)) {
534                                                 at->scopeid = if_nametoindex(scope_delim + 1);
535                                                 if (at->scopeid == 0)
536                                                         try_numericscope = 1;
537                                         } else
538                                                 try_numericscope = 1;
539
540                                         if (try_numericscope != 0) {
541                                                 char *end;
542                                                 assert(sizeof(uint32_t) <= sizeof(unsigned long));
543                                                 at->scopeid = (uint32_t)strtoul(scope_delim + 1, &end, 10);
544                                                 if (*end != '\0')
545                                                         return (GAIH_OKIFUNSPEC | -EAI_NONAME);
546                                         }
547                                 }
548                         }
549                 }
550 #endif
551
552                 if (at->family == AF_UNSPEC && !(req->ai_flags & AI_NUMERICHOST)) {
553                         struct hostent *h;
554                         struct gaih_addrtuple **pat = &at;
555                         int no_data = 0;
556                         int no_inet6_data;
557
558                         /*
559                          * If we are looking for both IPv4 and IPv6 address we don't want
560                          * the lookup functions to automatically promote IPv4 addresses to
561                          * IPv6 addresses.
562                          */
563 #if defined __UCLIBC_HAS_IPV6__
564                         if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
565                                 if (!(req->ai_flags & AI_ADDRCONFIG) || (seen & SEEN_IPV6))
566                                         gethosts(AF_INET6, struct in6_addr);
567 #endif
568                         no_inet6_data = no_data;
569
570                         if (req->ai_family == AF_INET
571                          || (!v4mapped && req->ai_family == AF_UNSPEC)
572                          || (v4mapped && (no_inet6_data != 0 || (req->ai_flags & AI_ALL)))
573                         ) {
574                                 if (!(req->ai_flags & AI_ADDRCONFIG) || (seen & SEEN_IPV4))
575                                         gethosts(AF_INET, struct in_addr);
576                         }
577
578                         if (no_data != 0 && no_inet6_data != 0) {
579                                 /* If both requests timed out report this. */
580                                 if (no_data == EAI_AGAIN && no_inet6_data == EAI_AGAIN)
581                                         return -EAI_AGAIN;
582                                 /*
583                                  * We made requests but they turned out no data.
584                                  * The name is known, though.
585                                  */
586                                 return (GAIH_OKIFUNSPEC | -EAI_AGAIN);
587                         }
588                 }
589
590                 if (at->family == AF_UNSPEC)
591                         return (GAIH_OKIFUNSPEC | -EAI_NONAME);
592         } else {
593                 struct gaih_addrtuple *atr;
594
595                 atr = at = alloca(sizeof(struct gaih_addrtuple));
596                 memset(at, '\0', sizeof(struct gaih_addrtuple));
597                 if (req->ai_family == 0) {
598                         at->next = alloca(sizeof(struct gaih_addrtuple));
599                         memset(at->next, '\0', sizeof(struct gaih_addrtuple));
600                 }
601 #if defined __UCLIBC_HAS_IPV6__
602                 if (req->ai_family == 0 || req->ai_family == AF_INET6) {
603                         at->family = AF_INET6;
604                         if ((req->ai_flags & AI_PASSIVE) == 0)
605                                 memcpy(at->addr, &in6addr_loopback, sizeof(struct in6_addr));
606                         atr = at->next;
607                 }
608 #endif
609                 if (req->ai_family == 0 || req->ai_family == AF_INET) {
610                         atr->family = AF_INET;
611                         if ((req->ai_flags & AI_PASSIVE) == 0) {
612                                 uint32_t *a = (uint32_t*)atr->addr;
613                                 *a = htonl(INADDR_LOOPBACK);
614                         }
615                 }
616         }
617
618         if (pai == NULL)
619                 return 0;
620
621         {
622                 const char *c = NULL;
623                 struct gaih_servtuple *st2;
624                 struct gaih_addrtuple *at2 = at;
625                 size_t socklen, namelen;
626                 sa_family_t family;
627
628                 /*
629                  * buffer is the size of an unformatted IPv6 address in
630                  * printable format.
631                  */
632                 char buffer[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
633
634                 while (at2 != NULL) {
635                         c = inet_ntop(at2->family, at2->addr, buffer, sizeof(buffer));
636                         if (c) {
637                                 namelen = strlen(c) + 1;
638                         } else if (req->ai_flags & AI_CANONNAME) {
639                                 struct hostent *h = NULL;
640                                 int herrno;
641                                 struct hostent th;
642                                 size_t tmpbuflen = 512;
643                                 char *tmpbuf;
644
645                                 /* Hint says numeric, but address is not */
646                                 if (req->ai_flags & AI_NUMERICHOST)
647                                         return -EAI_NONAME;
648
649                                 do {
650                                         tmpbuflen *= 2;
651                                         tmpbuf = alloca(tmpbuflen);
652                                         rc = gethostbyaddr_r(at2->addr,
653 #ifdef __UCLIBC_HAS_IPV6__
654                                                 ((at2->family == AF_INET6)
655                                                  ? sizeof(struct in6_addr)
656                                                  : sizeof(struct in_addr)),
657 #else
658                                                 sizeof(struct in_addr),
659 #endif
660                                                 at2->family,
661                                                 &th, tmpbuf, tmpbuflen,
662                                                 &h, &herrno);
663                                 } while (rc == ERANGE && herrno == NETDB_INTERNAL);
664
665                                 if (rc != 0 && herrno == NETDB_INTERNAL) {
666                                         __set_h_errno(herrno);
667                                         return -EAI_SYSTEM;
668                                 }
669
670                                 if (h != NULL)
671                                         c = h->h_name;
672
673                                 if (c == NULL)
674                                         return (GAIH_OKIFUNSPEC | -EAI_NONAME);
675
676                                 namelen = strlen(c) + 1;
677                         } else
678                                 namelen = 0;
679
680 #if defined __UCLIBC_HAS_IPV6__
681                         if (at2->family == AF_INET6 || v4mapped) {
682                                 family = AF_INET6;
683                                 socklen = sizeof(struct sockaddr_in6);
684                         }
685 #endif
686 #if defined __UCLIBC_HAS_IPV4__ && defined __UCLIBC_HAS_IPV6__
687                         else
688 #endif
689 #if defined __UCLIBC_HAS_IPV4__
690                         {
691                                 family = AF_INET;
692                                 socklen = sizeof(struct sockaddr_in);
693                         }
694 #endif
695                         for (st2 = st; st2 != NULL; st2 = st2->next) {
696                                 if (req->ai_flags & AI_ADDRCONFIG) {
697                                         if (family == AF_INET && !(seen & SEEN_IPV4))
698                                                 break;
699 #if defined __UCLIBC_HAS_IPV6__
700                                         else if (family == AF_INET6 && !(seen & SEEN_IPV6))
701                                                 break;
702 #endif
703                                 }
704                                 *pai = malloc(sizeof(struct addrinfo) + socklen + namelen);
705                                 if (*pai == NULL)
706                                         return -EAI_MEMORY;
707
708                                 (*pai)->ai_flags = req->ai_flags;
709                                 (*pai)->ai_family = family;
710                                 (*pai)->ai_socktype = st2->socktype;
711                                 (*pai)->ai_protocol = st2->protocol;
712                                 (*pai)->ai_addrlen = socklen;
713                                 (*pai)->ai_addr = (void *) (*pai) + sizeof(struct addrinfo);
714 #if 0 /* SALEN */
715                                 (*pai)->ai_addr->sa_len = socklen;
716 #endif
717                                 (*pai)->ai_addr->sa_family = family;
718
719 #if defined __UCLIBC_HAS_IPV6__
720                                 if (family == AF_INET6) {
721                                         struct sockaddr_in6 *sin6p = (struct sockaddr_in6 *) (*pai)->ai_addr;
722
723                                         sin6p->sin6_flowinfo = 0;
724                                         if (at2->family == AF_INET6) {
725                                                 memcpy(&sin6p->sin6_addr,
726                                                         at2->addr, sizeof(struct in6_addr));
727                                         } else {
728                                                 sin6p->sin6_addr.s6_addr32[0] = 0;
729                                                 sin6p->sin6_addr.s6_addr32[1] = 0;
730                                                 sin6p->sin6_addr.s6_addr32[2] = htonl(0x0000ffff);
731                                                 memcpy(&sin6p->sin6_addr.s6_addr32[3],
732                                                         at2->addr, sizeof(sin6p->sin6_addr.s6_addr32[3]));
733                                         }
734                                         sin6p->sin6_port = st2->port;
735                                         sin6p->sin6_scope_id = at2->scopeid;
736                                 }
737 #endif
738 #if defined __UCLIBC_HAS_IPV4__ && defined __UCLIBC_HAS_IPV6__
739                                 else
740 #endif
741 #if defined __UCLIBC_HAS_IPV4__
742                                 {
743                                         struct sockaddr_in *sinp = (struct sockaddr_in *) (*pai)->ai_addr;
744
745                                         memcpy(&sinp->sin_addr, at2->addr, sizeof(struct in_addr));
746                                         sinp->sin_port = st2->port;
747                                         memset(sinp->sin_zero, '\0', sizeof(sinp->sin_zero));
748                                 }
749 #endif
750                                 if (c) {
751                                         (*pai)->ai_canonname = ((void *) (*pai) +
752                                                         sizeof(struct addrinfo) + socklen);
753                                         strcpy((*pai)->ai_canonname, c);
754                                 } else {
755                                         (*pai)->ai_canonname = NULL;
756                                 }
757                                 (*pai)->ai_next = NULL;
758                                 pai = &((*pai)->ai_next);
759                         }
760
761                         at2 = at2->next;
762                 }
763         }
764         return 0;
765 }
766
767 static const struct gaih gaih[] = {
768 #if defined __UCLIBC_HAS_IPV6__
769         { PF_INET6, gaih_inet },
770 #endif
771         { PF_INET, gaih_inet },
772 #if 0
773         { PF_LOCAL, gaih_local },
774 #endif
775         { PF_UNSPEC, NULL }
776 };
777
778 void
779 freeaddrinfo(struct addrinfo *ai)
780 {
781         struct addrinfo *p;
782
783         while (ai != NULL) {
784                 p = ai;
785                 ai = ai->ai_next;
786                 free(p);
787         }
788 }
789 libc_hidden_def(freeaddrinfo)
790
791 int
792 getaddrinfo(const char *name, const char *service,
793              const struct addrinfo *hints, struct addrinfo **pai)
794 {
795         int i, j, last_i;
796         struct addrinfo *p, **end;
797         const struct gaih *g, *pg;
798         struct gaih_service gaih_service, *pservice;
799         struct addrinfo default_hints;
800
801         if (name != NULL && name[0] == '*' && name[1] == 0)
802                 name = NULL;
803
804         if (service != NULL && service[0] == '*' && service[1] == 0)
805                 service = NULL;
806
807         if (name == NULL && service == NULL)
808                 return EAI_NONAME;
809
810         if (hints == NULL) {
811                 memset(&default_hints, 0, sizeof(default_hints));
812                 if (AF_UNSPEC != 0)
813                         default_hints.ai_family = AF_UNSPEC;
814                 hints = &default_hints;
815         }
816
817         if (hints->ai_flags & ~(AI_PASSIVE|AI_CANONNAME|AI_NUMERICHOST|
818                         AI_ADDRCONFIG|AI_V4MAPPED|AI_NUMERICSERV|AI_ALL))
819                 return EAI_BADFLAGS;
820
821         if ((hints->ai_flags & AI_CANONNAME) && name == NULL)
822                 return EAI_BADFLAGS;
823
824         if (service && service[0]) {
825                 char *c;
826                 gaih_service.name = service;
827                 gaih_service.num = strtoul(gaih_service.name, &c, 10);
828                 if (*c != '\0') {
829                         if (hints->ai_flags & AI_NUMERICSERV)
830                                 return EAI_NONAME;
831                         gaih_service.num = -1;
832                 }
833                 pservice = &gaih_service;
834         } else
835                 pservice = NULL;
836
837         g = gaih;
838         pg = NULL;
839         p = NULL;
840         end = NULL;
841         if (pai)
842                 end = &p;
843         i = 0;
844         last_i = 0;
845         j = 0;
846         while (g->gaih) {
847                 if (hints->ai_family == g->family || hints->ai_family == AF_UNSPEC) {
848                         if ((hints->ai_flags & AI_ADDRCONFIG) && !addrconfig(g->family)) {
849                                 ++g;
850                                 continue;
851                         }
852                         j++;
853                         if (pg == NULL || pg->gaih != g->gaih) {
854                                 pg = g;
855                                 i = g->gaih(name, pservice, hints, end);
856                                 if (i != 0) {
857                                         last_i = i;
858                                         if (hints->ai_family == AF_UNSPEC && (i & GAIH_OKIFUNSPEC))
859                                                 continue;
860                                         /*if (p) - freeaddrinfo works ok on NULL too */
861                                                 freeaddrinfo(p);
862                                         return -(i & GAIH_EAI);
863                                 }
864                                 if (end)
865                                         while (*end)
866                                                 end = &((*end)->ai_next);
867                         }
868                 }
869                 ++g;
870         }
871
872         if (j == 0)
873                 return EAI_FAMILY;
874
875         if (p) {
876                 *pai = p;
877                 return 0;
878         }
879
880         if (pai == NULL && last_i == 0)
881                 return 0;
882
883         /* if (p) - never happens, see above */
884         /*      freeaddrinfo(p); */
885
886         return last_i ? -(last_i & GAIH_EAI) : EAI_NONAME;
887 }
888 libc_hidden_def(getaddrinfo)