2dc5e90a32fa3fe9239e5e4de768a85021c80430
[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     while (at2 != NULL)
568       {
569         if (req->ai_flags & AI_CANONNAME)
570           {
571             struct hostent *h = NULL;
572
573             int herrno;
574             struct hostent th;
575             size_t tmpbuflen = 512;
576             char *tmpbuf;
577
578             do
579               {
580                 tmpbuflen *= 2;
581                 tmpbuf = __alloca (tmpbuflen);
582
583                 rc = __gethostbyaddr_r (at2->addr,
584                                         ((at2->family == AF_INET6)
585                                          ? sizeof(struct in6_addr)
586                                          : sizeof(struct in_addr)),
587                                         at2->family, &th, tmpbuf, tmpbuflen,
588                                         &h, &herrno);
589
590               }
591             while (rc == errno && herrno == NETDB_INTERNAL);
592
593             if (rc != 0 && herrno == NETDB_INTERNAL)
594               {
595                 __set_h_errno (herrno);
596                 return -EAI_SYSTEM;
597               }
598
599             if (h != NULL)
600               c = h->h_name;
601             else
602               {
603                 /* We have to try to get the canonical in some other
604                    way.  If we are looking for either AF_INET or
605                    AF_INET6 try the other line.  */
606                 if (req->ai_family == AF_UNSPEC)
607                   {
608                     struct addrinfo *p = NULL;
609                     struct addrinfo **end = &p;
610                     struct addrinfo localreq = *req;
611                     struct addrinfo *runp;
612
613                     localreq.ai_family = AF_INET + AF_INET6 - at2->family;
614                     (void) gaih_inet (name, service, &localreq, end);
615
616                     runp = p;
617                     while (runp != NULL)
618                       {
619                         if (p->ai_canonname != name)
620                           {
621                             c = strdupa (p->ai_canonname);
622                             break;
623                           }
624                         runp = runp->ai_next;
625                       }
626
627                     freeaddrinfo (p);
628                   }
629
630                 /* If this code is used the missing canonical name is
631                    substituted with the name passed in by the user.  */
632                 if (c == NULL)
633                   c = name;
634               }
635
636             if (c == NULL)
637               return GAIH_OKIFUNSPEC | -EAI_NONAME;
638
639             namelen = strlen (c) + 1;
640           }
641         else
642           namelen = 0;
643
644         if (at2->family == AF_INET6)
645           {
646             family = AF_INET6;
647             socklen = sizeof (struct sockaddr_in6);
648           }
649         else
650           {
651             family = AF_INET;
652             socklen = sizeof (struct sockaddr_in);
653           }
654
655         for (st2 = st; st2 != NULL; st2 = st2->next)
656           {
657             *pai = malloc (sizeof (struct addrinfo) + socklen + namelen);
658             if (*pai == NULL)
659               return -EAI_MEMORY;
660
661             (*pai)->ai_flags = req->ai_flags;
662             (*pai)->ai_family = family;
663             (*pai)->ai_socktype = st2->socktype;
664             (*pai)->ai_protocol = st2->protocol;
665             (*pai)->ai_addrlen = socklen;
666             (*pai)->ai_addr = (void *) (*pai) + sizeof(struct addrinfo);
667 #if SALEN
668             (*pai)->ai_addr->sa_len = socklen;
669 #endif /* SALEN */
670             (*pai)->ai_addr->sa_family = family;
671
672             if (family == AF_INET6)
673               {
674                 struct sockaddr_in6 *sin6p =
675                   (struct sockaddr_in6 *) (*pai)->ai_addr;
676
677                 sin6p->sin6_flowinfo = 0;
678                 memcpy (&sin6p->sin6_addr,
679                         at2->addr, sizeof (struct in6_addr));
680                 sin6p->sin6_port = st2->port;
681                 sin6p->sin6_scope_id = at2->scopeid;
682               }
683             else
684               {
685                 struct sockaddr_in *sinp =
686                   (struct sockaddr_in *) (*pai)->ai_addr;
687                 memcpy (&sinp->sin_addr,
688                         at2->addr, sizeof (struct in_addr));
689                 sinp->sin_port = st2->port;
690                 memset (sinp->sin_zero, '\0', sizeof (sinp->sin_zero));
691               }
692
693             if (c)
694               {
695                 (*pai)->ai_canonname = ((void *) (*pai) +
696                                         sizeof (struct addrinfo) + socklen);
697                 strcpy ((*pai)->ai_canonname, c);
698               }
699             else
700               (*pai)->ai_canonname = NULL;
701
702             (*pai)->ai_next = NULL;
703             pai = &((*pai)->ai_next);
704           }
705
706         at2 = at2->next;
707       }
708   }
709   return 0;
710 }
711
712 static struct gaih gaih[] =
713   {
714     { PF_INET6, gaih_inet },
715     { PF_INET, gaih_inet },
716 #if 0
717     { PF_LOCAL, gaih_local },
718 #endif
719     { PF_UNSPEC, NULL }
720   };
721
722 int
723 getaddrinfo (const char *name, const char *service,
724              const struct addrinfo *hints, struct addrinfo **pai)
725 {
726   int i = 0, j = 0, last_i = 0;
727   struct addrinfo *p = NULL, **end;
728   struct gaih *g = gaih, *pg = NULL;
729   struct gaih_service gaih_service, *pservice;
730
731   if (name != NULL && name[0] == '*' && name[1] == 0)
732     name = NULL;
733
734   if (service != NULL && service[0] == '*' && service[1] == 0)
735     service = NULL;
736
737   if (name == NULL && service == NULL)
738     return EAI_NONAME;
739
740   if (hints == NULL)
741     hints = &default_hints;
742
743   if (hints->ai_flags & ~(AI_PASSIVE|AI_CANONNAME|AI_NUMERICHOST))
744     return EAI_BADFLAGS;
745
746   if ((hints->ai_flags & AI_CANONNAME) && name == NULL)
747     return EAI_BADFLAGS;
748
749   if (service && service[0])
750     {
751       char *c;
752       gaih_service.name = service;
753       gaih_service.num = strtoul (gaih_service.name, &c, 10);
754       if (*c)
755         gaih_service.num = -1;
756       else
757         /* Can't specify a numerical socket unless a protocol family was
758            given. */
759         if (hints->ai_socktype == 0 && hints->ai_protocol == 0)
760           return EAI_SERVICE;
761       pservice = &gaih_service;
762     }
763   else
764     pservice = NULL;
765
766   if (pai)
767     end = &p;
768   else
769     end = NULL;
770
771   while (g->gaih)
772     {
773       if (hints->ai_family == g->family || hints->ai_family == AF_UNSPEC)
774         {
775           j++;
776           if (pg == NULL || pg->gaih != g->gaih)
777             {
778               pg = g;
779               i = g->gaih (name, pservice, hints, end);
780               if (i != 0)
781                 {
782                   /* EAI_NODATA is a more specific result as it says that
783                      we found a result but it is not usable.  */
784                   if (last_i != (GAIH_OKIFUNSPEC | -EAI_NODATA))
785                     last_i = i;
786
787                   if (hints->ai_family == AF_UNSPEC && (i & GAIH_OKIFUNSPEC))
788                     {
789                       ++g;
790                       continue;
791                     }
792
793                   freeaddrinfo (p);
794
795                   return -(i & GAIH_EAI);
796                 }
797               if (end)
798                 while(*end) end = &((*end)->ai_next);
799             }
800         }
801       ++g;
802     }
803
804   if (j == 0)
805     return EAI_FAMILY;
806
807   if (p)
808     {
809       *pai = p;
810       return 0;
811     }
812
813   if (pai == NULL && last_i == 0)
814     return 0;
815
816   freeaddrinfo (p);
817
818   return last_i ? -(last_i & GAIH_EAI) : EAI_NONAME;
819 }
820 libc_hidden_def (getaddrinfo)
821
822 void
823 freeaddrinfo (struct addrinfo *ai)
824 {
825   struct addrinfo *p;
826
827   while (ai != NULL)
828     {
829       p = ai;
830       ai = ai->ai_next;
831       free (p);
832     }
833 }
834 libc_hidden_def (freeaddrinfo)