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