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