OSDN Git Service

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