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