OSDN Git Service

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