OSDN Git Service

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