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 /* getaddrinfo() v1.13 */
46
47 #include <sys/types.h>
48 #include <stdlib.h>
49 #include <unistd.h>
50 #include <sys/socket.h>
51 #include <stdio.h>
52 #include <string.h>
53 #include <sys/utsname.h>
54 #include <sys/un.h>
55 #include <netinet/in.h>
56 #include <netdb.h>
57 #include <errno.h>
58 #include <arpa/inet.h>
59
60 #define GAIH_OKIFUNSPEC 0x0100
61 #define GAIH_EAI        ~(GAIH_OKIFUNSPEC)
62
63 #ifndef UNIX_PATH_MAX
64 #define UNIX_PATH_MAX  108
65 #endif
66
67 struct gaih_service
68   {
69     const char *name;
70     int num;
71   };
72
73 struct gaih_servtuple
74   {
75     struct gaih_servtuple *next;
76     int socktype;
77     int protocol;
78     int port;
79   };
80
81 static struct gaih_servtuple nullserv = { NULL, 0, 0, 0 };
82
83 struct gaih_addrtuple
84   {
85     struct gaih_addrtuple *next;
86     int family;
87     char addr[16];
88   };
89
90 struct gaih_typeproto
91   {
92     int socktype;
93     int protocol;
94     char *name;
95   };
96
97 static struct gaih_typeproto gaih_inet_typeproto[] =
98 {
99   { 0, 0, NULL },
100   { SOCK_STREAM, IPPROTO_TCP, (char *) "tcp" },
101   { SOCK_DGRAM, IPPROTO_UDP, (char *) "udp" },
102   { 0, 0, NULL }
103 };
104
105 struct gaih
106   {
107     int family;
108     int (*gaih)(const char *name, const struct gaih_service *service,
109                 const struct addrinfo *req, struct addrinfo **pai);
110   };
111
112 static struct addrinfo default_hints =
113         { 0, PF_UNSPEC, 0, 0, 0, NULL, NULL, NULL };
114
115
116 static int
117 gaih_local (const char *name, const struct gaih_service *service,
118             const struct addrinfo *req, struct addrinfo **pai)
119 {
120   struct utsname utsname;
121
122   if ((name != NULL) || (req->ai_flags & AI_CANONNAME))
123     if (uname (&utsname))
124       return -EAI_SYSTEM;
125
126   if (name != NULL)
127     {
128       if (strcmp(name, "localhost") &&
129           strcmp(name, "local") &&
130           strcmp(name, "unix") &&
131           strcmp(name, utsname.nodename))
132         return GAIH_OKIFUNSPEC | -EAI_NONAME;
133     }
134
135   if (req->ai_protocol || req->ai_socktype)
136     {
137       struct gaih_typeproto *tp = gaih_inet_typeproto;
138
139       for (tp++; tp->name &&
140              ((req->ai_socktype != tp->socktype) || !req->ai_socktype) &&
141              ((req->ai_protocol != tp->protocol) || !req->ai_protocol); tp++);
142       if (tp->name == NULL)
143         {
144           if (req->ai_socktype)
145             return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE);
146           else
147             return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
148         }
149     }
150
151   *pai = malloc (sizeof (struct addrinfo) + sizeof (struct sockaddr_un)
152                  + ((req->ai_flags & AI_CANONNAME)
153                     ? (strlen(utsname.nodename) + 1): 0));
154   if (*pai == NULL)
155     return -EAI_MEMORY;
156
157   (*pai)->ai_next = NULL;
158   (*pai)->ai_flags = req->ai_flags;
159   (*pai)->ai_family = AF_LOCAL;
160   (*pai)->ai_socktype = req->ai_socktype ? req->ai_socktype : SOCK_STREAM;
161   (*pai)->ai_protocol = req->ai_protocol;
162   (*pai)->ai_addrlen = sizeof (struct sockaddr_un);
163   (*pai)->ai_addr = (void *) (*pai) + sizeof (struct addrinfo);
164
165 #if SALEN
166   ((struct sockaddr_un *) (*pai)->ai_addr)->sun_len =
167     sizeof (struct sockaddr_un);
168 #endif /* SALEN */
169
170   ((struct sockaddr_un *)(*pai)->ai_addr)->sun_family = AF_LOCAL;
171   memset(((struct sockaddr_un *)(*pai)->ai_addr)->sun_path, 0, UNIX_PATH_MAX);
172
173   if (service)
174     {
175       struct sockaddr_un *sunp = (struct sockaddr_un *) (*pai)->ai_addr;
176
177       if (strchr (service->name, '/') != NULL)
178         {
179           if (strlen (service->name) >= sizeof (sunp->sun_path))
180             return GAIH_OKIFUNSPEC | -EAI_SERVICE;
181
182           strcpy (sunp->sun_path, service->name);
183         }
184       else
185         {
186           if (strlen (P_tmpdir "/") + 1 + strlen (service->name) >=
187               sizeof (sunp->sun_path))
188             return GAIH_OKIFUNSPEC | -EAI_SERVICE;
189
190           __stpcpy (__stpcpy (sunp->sun_path, P_tmpdir "/"), service->name);
191         }
192     }
193   else
194     {
195       if (tmpnam (((struct sockaddr_un *) (*pai)->ai_addr)->sun_path) == NULL)
196         return -EAI_SYSTEM;
197     }
198
199   if (req->ai_flags & AI_CANONNAME)
200     (*pai)->ai_canonname = strcpy ((char *) *pai + sizeof (struct addrinfo)
201                                    + sizeof (struct sockaddr_un),
202                                    utsname.nodename);
203   else
204     (*pai)->ai_canonname = NULL;
205   return 0;
206 }
207
208 static int
209 gaih_inet_serv (const char *servicename, struct gaih_typeproto *tp,
210                struct gaih_servtuple *st)
211 {
212   struct servent *s;
213   size_t tmpbuflen = 1024;
214   struct servent ts;
215   char *tmpbuf;
216   int r;
217
218   do
219     {
220       tmpbuf = __alloca (tmpbuflen);
221
222       r = __getservbyname_r (servicename, tp->name, &ts, tmpbuf, tmpbuflen,
223                              &s);
224       if (r != 0 || s == NULL)
225         {
226           if (r == ERANGE)
227             tmpbuflen *= 2;
228           else
229             return GAIH_OKIFUNSPEC | -EAI_SERVICE;
230         }
231     }
232   while (r);
233
234   st->next = NULL;
235   st->socktype = tp->socktype;
236   st->protocol = tp->protocol;
237   st->port = s->s_port;
238
239   return 0;
240 }
241
242 #define gethosts(_family, _type)                                \
243  {                                                              \
244   int i, herrno;                                                \
245   size_t tmpbuflen;                                             \
246   struct hostent th;                                            \
247   char *tmpbuf;                                                 \
248   tmpbuflen = 512;                                              \
249   do {                                                          \
250     tmpbuflen *= 2;                                             \
251     tmpbuf = __alloca (tmpbuflen);                              \
252     rc = __gethostbyname2_r (name, _family, &th, tmpbuf,        \
253          tmpbuflen, &h, &herrno);                               \
254   } while (rc == ERANGE && herrno == NETDB_INTERNAL);           \
255   if (rc != 0 && herrno == NETDB_INTERNAL)                      \
256     {                                                           \
257       __set_h_errno (herrno);                                   \
258       return -EAI_SYSTEM;                                       \
259     }                                                           \
260   if (h != NULL)                                                \
261     {                                                           \
262       for (i = 0; h->h_addr_list[i]; i++)                       \
263         {                                                       \
264           if (*pat == NULL)                                     \
265             *pat = __alloca (sizeof(struct gaih_addrtuple));    \
266           (*pat)->next = NULL;                                  \
267           (*pat)->family = _family;                             \
268           memcpy ((*pat)->addr, h->h_addr_list[i],              \
269                  sizeof(_type));                                \
270           pat = &((*pat)->next);                                \
271         }                                                       \
272     }                                                           \
273   no_data = rc != 0 && herrno == NO_DATA;                       \
274  }
275
276 static int
277 gaih_inet (const char *name, const struct gaih_service *service,
278            const struct addrinfo *req, struct addrinfo **pai)
279 {
280   struct gaih_typeproto *tp = gaih_inet_typeproto;
281   struct gaih_servtuple *st = &nullserv;
282   struct gaih_addrtuple *at = NULL;
283   int rc;
284
285   if (req->ai_protocol || req->ai_socktype)
286     {
287       for (tp++; tp->name &&
288              ((req->ai_socktype != tp->socktype) || !req->ai_socktype) &&
289              ((req->ai_protocol != tp->protocol) || !req->ai_protocol); tp++);
290       if (tp->name == NULL)
291         {
292           if (req->ai_socktype)
293             return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE);
294           else
295             return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
296         }
297     }
298
299   if (service != NULL)
300     {
301       if (service->num < 0)
302         {
303           if (tp->name != NULL)
304             {
305               st = (struct gaih_servtuple *)
306                 __alloca (sizeof (struct gaih_servtuple));
307
308               if ((rc = gaih_inet_serv (service->name, tp, st)))
309                 return rc;
310             }
311           else
312             {
313               struct gaih_servtuple **pst = &st;
314               for (tp++; tp->name; tp++)
315                 {
316                   struct gaih_servtuple *newp = (struct gaih_servtuple *)
317                     __alloca (sizeof (struct gaih_servtuple));
318
319                   if ((rc = gaih_inet_serv (service->name, tp, newp)))
320                     {
321                       if (rc & GAIH_OKIFUNSPEC)
322                         continue;
323                       return rc;
324                     }
325
326                   *pst = newp;
327                   pst = &(newp->next);
328                 }
329               if (st == &nullserv)
330                 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
331             }
332         }
333       else
334         {
335           st = __alloca (sizeof (struct gaih_servtuple));
336           st->next = NULL;
337           st->socktype = tp->socktype;
338           st->protocol = tp->protocol;
339           st->port = htons (service->num);
340         }
341     }
342
343   if (name != NULL)
344     {
345       at = __alloca (sizeof (struct gaih_addrtuple));
346
347       at->family = AF_UNSPEC;
348       at->next = NULL;
349
350       if (inet_pton (AF_INET, name, at->addr) > 0)
351         {
352           if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
353             at->family = AF_INET;
354           else
355             return -EAI_ADDRFAMILY;
356         }
357
358       if (at->family == AF_UNSPEC && inet_pton (AF_INET6, name, at->addr) > 0)
359         {
360           if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
361             at->family = AF_INET6;
362           else
363             return -EAI_ADDRFAMILY;
364         }
365
366       if (at->family == AF_UNSPEC)
367         {
368           struct hostent *h;
369           struct gaih_addrtuple **pat = &at;
370           int no_data = 0;
371           int no_inet6_data;
372
373           if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
374             gethosts (AF_INET6, struct in6_addr);
375           no_inet6_data = no_data;
376
377           if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
378             gethosts (AF_INET, struct in_addr);
379
380           if (no_data != 0 && no_inet6_data != 0)
381             /* We made requests but they turned out no data.  The name
382                is known, though.  */
383             return (GAIH_OKIFUNSPEC | -EAI_NODATA);
384         }
385
386       if (at->family == AF_UNSPEC)
387         return (GAIH_OKIFUNSPEC | -EAI_NONAME);
388     }
389   else
390     {
391       struct gaih_addrtuple *atr;
392       atr = at = __alloca (sizeof (struct gaih_addrtuple));
393       memset (at, '\0', sizeof (struct gaih_addrtuple));
394
395       if (req->ai_family == 0)
396         {
397           at->next = __alloca (sizeof (struct gaih_addrtuple));
398           memset (at->next, '\0', sizeof (struct gaih_addrtuple));
399         }
400
401       if (req->ai_family == 0 || req->ai_family == AF_INET6)
402         {
403           at->family = AF_INET6;
404           if ((req->ai_flags & AI_PASSIVE) == 0)
405             memcpy (at->addr, &in6addr_loopback, sizeof (struct in6_addr));
406           atr = at->next;
407         }
408
409       if (req->ai_family == 0 || req->ai_family == AF_INET)
410         {
411           atr->family = AF_INET;
412           if ((req->ai_flags & AI_PASSIVE) == 0)
413             *(uint32_t *) atr->addr = htonl (INADDR_LOOPBACK);
414         }
415     }
416
417   if (pai == NULL)
418     return 0;
419
420   {
421     const char *c = NULL;
422     struct gaih_servtuple *st2;
423     struct gaih_addrtuple *at2 = at;
424     size_t socklen, namelen;
425
426     /*
427       buffer is the size of an unformatted IPv6 address in printable format.
428      */
429     char buffer[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"];
430
431     while (at2 != NULL)
432       {
433         if (req->ai_flags & AI_CANONNAME)
434           {
435             struct hostent *h = NULL;
436
437             int herrno;
438             struct hostent th;
439             size_t tmpbuflen = 512;
440             char *tmpbuf;
441
442             do
443               {
444                 tmpbuflen *= 2;
445                 tmpbuf = __alloca (tmpbuflen);
446
447                 if (tmpbuf == NULL)
448                   return -EAI_MEMORY;
449
450                 rc = __gethostbyaddr_r (at2->addr,
451                                         ((at2->family == AF_INET6)
452                                          ? sizeof(struct in6_addr)
453                                          : sizeof(struct in_addr)),
454                                         at2->family, &th, tmpbuf, tmpbuflen,
455                                         &h, &herrno);
456
457               }
458             while (rc == errno && herrno == NETDB_INTERNAL);
459
460             if (rc != 0 && herrno == NETDB_INTERNAL)
461               {
462                 __set_h_errno (herrno);
463                 return -EAI_SYSTEM;
464               }
465
466             if (h == NULL)
467               c = inet_ntop (at2->family, at2->addr, buffer, sizeof(buffer));
468             else
469               c = h->h_name;
470
471             if (c == NULL)
472               return GAIH_OKIFUNSPEC | -EAI_NONAME;
473
474             namelen = strlen (c) + 1;
475           }
476         else
477           namelen = 0;
478
479         if (at2->family == AF_INET6)
480           socklen = sizeof (struct sockaddr_in6);
481         else
482           socklen = sizeof (struct sockaddr_in);
483
484         for (st2 = st; st2 != NULL; st2 = st2->next)
485           {
486             *pai = malloc (sizeof (struct addrinfo) + socklen + namelen);
487             if (*pai == NULL)
488               return -EAI_MEMORY;
489
490             (*pai)->ai_flags = req->ai_flags;
491             (*pai)->ai_family = at2->family;
492             (*pai)->ai_socktype = st2->socktype;
493             (*pai)->ai_protocol = st2->protocol;
494             (*pai)->ai_addrlen = socklen;
495             (*pai)->ai_addr = (void *) (*pai) + sizeof(struct addrinfo);
496 #if SALEN
497             ((struct sockaddr_in *) (*pai)->ai_addr)->sin_len = i;
498 #endif /* SALEN */
499             ((struct sockaddr_in *) (*pai)->ai_addr)->sin_family = at2->family;
500             ((struct sockaddr_in *) (*pai)->ai_addr)->sin_port = st2->port;
501
502             if (at2->family == AF_INET6)
503               {
504                 struct sockaddr_in6 *sin6p =
505                   (struct sockaddr_in6 *) (*pai)->ai_addr;
506
507                 sin6p->sin6_flowinfo = 0;
508                 memcpy (&sin6p->sin6_addr,
509                         at2->addr, sizeof (struct in6_addr));
510               }
511             else
512               {
513                 struct sockaddr_in *sinp =
514                   (struct sockaddr_in *) (*pai)->ai_addr;
515                 memcpy (&sinp->sin_addr,
516                         at2->addr, sizeof (struct in_addr));
517                 memset (sinp->sin_zero, '\0', sizeof (sinp->sin_zero));
518               }
519
520             if (c)
521               {
522                 (*pai)->ai_canonname = ((void *) (*pai) +
523                                         sizeof (struct addrinfo) + socklen);
524                 strcpy ((*pai)->ai_canonname, c);
525               }
526             else
527               (*pai)->ai_canonname = NULL;
528
529             (*pai)->ai_next = NULL;
530             pai = &((*pai)->ai_next);
531           }
532
533         at2 = at2->next;
534       }
535   }
536   return 0;
537 }
538
539 static struct gaih gaih[] =
540   {
541     { PF_INET6, gaih_inet },
542     { PF_INET, gaih_inet },
543     { PF_LOCAL, gaih_local },
544     { PF_UNSPEC, NULL }
545   };
546
547 int
548 getaddrinfo (const char *name, const char *service,
549              const struct addrinfo *hints, struct addrinfo **pai)
550 {
551   int i = 0, j = 0, last_i = 0;
552   struct addrinfo *p = NULL, **end;
553   struct gaih *g = gaih, *pg = NULL;
554   struct gaih_service gaih_service, *pservice;
555
556   if (name != NULL && name[0] == '*' && name[1] == 0)
557     name = NULL;
558
559   if (service != NULL && service[0] == '*' && service[1] == 0)
560     service = NULL;
561
562   if (name == NULL && service == NULL)
563     return EAI_NONAME;
564
565   if (hints == NULL)
566     hints = &default_hints;
567
568   if (hints->ai_flags & ~(AI_PASSIVE|AI_CANONNAME|AI_NUMERICHOST))
569     return EAI_BADFLAGS;
570
571   if ((hints->ai_flags & AI_CANONNAME) && name == NULL)
572     return EAI_BADFLAGS;
573
574   if (service && service[0])
575     {
576       char *c;
577       gaih_service.name = service;
578       gaih_service.num = strtoul (gaih_service.name, &c, 10);
579       if (*c)
580         gaih_service.num = -1;
581       else
582         /* Can't specify a numerical socket unless a protocol family was
583            given. */
584         if (hints->ai_socktype == 0)
585           return EAI_SERVICE;
586       pservice = &gaih_service;
587     }
588   else
589     pservice = NULL;
590
591   if (pai)
592     end = &p;
593   else
594     end = NULL;
595
596   while (g->gaih)
597     {
598       if (hints->ai_family == g->family || hints->ai_family == AF_UNSPEC)
599         {
600           j++;
601           if (pg == NULL || pg->gaih != g->gaih)
602             {
603               pg = g;
604               i = g->gaih (name, pservice, hints, end);
605               if (i != 0)
606                 {
607                   /* EAI_NODATA is a more specific result as it says that
608                      we found a result but it is not usable.  */
609                   if (last_i != (GAIH_OKIFUNSPEC | -EAI_NODATA))
610                     last_i = i;
611
612                   if (hints->ai_family == AF_UNSPEC && (i & GAIH_OKIFUNSPEC))
613                     continue;
614
615                   if (p)
616                     freeaddrinfo (p);
617
618                   return -(i & GAIH_EAI);
619                 }
620               if (end)
621                 while(*end) end = &((*end)->ai_next);
622             }
623         }
624       ++g;
625     }
626
627   if (j == 0)
628     return EAI_FAMILY;
629
630   if (p)
631     {
632       *pai = p;
633       return 0;
634     }
635
636   if (pai == NULL && last_i == 0)
637     return 0;
638
639   if (p)
640     freeaddrinfo (p);
641
642   return last_i ? -(last_i & GAIH_EAI) : EAI_NONAME;
643 }
644
645 void
646 freeaddrinfo (struct addrinfo *ai)
647 {
648   struct addrinfo *p;
649
650   while (ai != NULL)
651     {
652       p = ai;
653       ai = ai->ai_next;
654       free (p);
655     }
656 }