OSDN Git Service

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