OSDN Git Service

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