(gaih_local): Test protocol and socktype.
[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. 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
372           if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
373             gethosts (AF_INET6, struct in6_addr);
374
375           if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
376             gethosts (AF_INET, struct in_addr);
377
378           if (no_data != 0)
379             /* We made requests but they turned out no data.  The name
380                is known, though.  */
381             return (GAIH_OKIFUNSPEC | -EAI_NODATA);
382         }
383
384       if (at->family == AF_UNSPEC)
385         return (GAIH_OKIFUNSPEC | -EAI_NONAME);
386     }
387   else
388     {
389       struct gaih_addrtuple *atr;
390       atr = at = __alloca (sizeof (struct gaih_addrtuple));
391       memset (at, '\0', sizeof (struct gaih_addrtuple));
392
393       if (req->ai_family == 0)
394         {
395           at->next = __alloca (sizeof (struct gaih_addrtuple));
396           memset (at->next, '\0', sizeof (struct gaih_addrtuple));
397         }
398
399       if (req->ai_family == 0 || req->ai_family == AF_INET6)
400         {
401           at->family = AF_INET6;
402           if ((req->ai_flags & AI_PASSIVE) == 0)
403             memcpy (at->addr, &in6addr_loopback, sizeof (struct in6_addr));
404           atr = at->next;
405         }
406
407       if (req->ai_family == 0 || req->ai_family == AF_INET)
408         {
409           atr->family = AF_INET;
410           if ((req->ai_flags & AI_PASSIVE) == 0)
411             *(uint32_t *) atr->addr = htonl (INADDR_LOOPBACK);
412         }
413     }
414
415   if (pai == NULL)
416     return 0;
417
418   {
419     const char *c = NULL;
420     struct gaih_servtuple *st2;
421     struct gaih_addrtuple *at2 = at;
422     size_t socklen, namelen;
423
424     /*
425       buffer is the size of an unformatted IPv6 address in printable format.
426      */
427     char buffer[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"];
428
429     while (at2 != NULL)
430       {
431         if (req->ai_flags & AI_CANONNAME)
432           {
433             struct hostent *h = NULL;
434
435             int herrno;
436             struct hostent th;
437             size_t tmpbuflen = 512;
438             char *tmpbuf;
439
440             do
441               {
442                 tmpbuflen *= 2;
443                 tmpbuf = __alloca (tmpbuflen);
444
445                 if (tmpbuf == NULL)
446                   return -EAI_MEMORY;
447
448                 rc = __gethostbyaddr_r (at2->addr,
449                                         ((at2->family == AF_INET6)
450                                          ? sizeof(struct in6_addr)
451                                          : sizeof(struct in_addr)),
452                                         at2->family, &th, tmpbuf, tmpbuflen,
453                                         &h, &herrno);
454
455               }
456             while (rc == errno && herrno == NETDB_INTERNAL);
457
458             if (rc != 0 && herrno == NETDB_INTERNAL)
459               {
460                 __set_h_errno (herrno);
461                 return -EAI_SYSTEM;
462               }
463
464             if (h == NULL)
465               c = inet_ntop (at2->family, at2->addr, buffer, sizeof(buffer));
466             else
467               c = h->h_name;
468
469             if (c == NULL)
470               return GAIH_OKIFUNSPEC | -EAI_NONAME;
471
472             namelen = strlen (c) + 1;
473           }
474         else
475           namelen = 0;
476
477         if (at2->family == AF_INET6)
478           socklen = sizeof (struct sockaddr_in6);
479         else
480           socklen = sizeof (struct sockaddr_in);
481
482         for (st2 = st; st2 != NULL; st2 = st2->next)
483           {
484             *pai = malloc (sizeof (struct addrinfo) + socklen + namelen);
485             if (*pai == NULL)
486               return -EAI_MEMORY;
487
488             (*pai)->ai_flags = req->ai_flags;
489             (*pai)->ai_family = at2->family;
490             (*pai)->ai_socktype = st2->socktype;
491             (*pai)->ai_protocol = st2->protocol;
492             (*pai)->ai_addrlen = socklen;
493             (*pai)->ai_addr = (void *) (*pai) + sizeof(struct addrinfo);
494 #if SALEN
495             ((struct sockaddr_in *) (*pai)->ai_addr)->sin_len = i;
496 #endif /* SALEN */
497             ((struct sockaddr_in *) (*pai)->ai_addr)->sin_family = at2->family;
498             ((struct sockaddr_in *) (*pai)->ai_addr)->sin_port = st2->port;
499
500             if (at2->family == AF_INET6)
501               {
502                 struct sockaddr_in6 *sin6p =
503                   (struct sockaddr_in6 *) (*pai)->ai_addr;
504
505                 sin6p->sin6_flowinfo = 0;
506                 memcpy (&sin6p->sin6_addr,
507                         at2->addr, sizeof (struct in6_addr));
508               }
509             else
510               {
511                 struct sockaddr_in *sinp =
512                   (struct sockaddr_in *) (*pai)->ai_addr;
513                 memcpy (&sinp->sin_addr,
514                         at2->addr, sizeof (struct in_addr));
515                 memset (sinp->sin_zero, '\0', sizeof (sinp->sin_zero));
516               }
517
518             if (c)
519               {
520                 (*pai)->ai_canonname = ((void *) (*pai) +
521                                         sizeof (struct addrinfo) + socklen);
522                 strcpy ((*pai)->ai_canonname, c);
523               }
524             else
525               (*pai)->ai_canonname = NULL;
526
527             (*pai)->ai_next = NULL;
528             pai = &((*pai)->ai_next);
529           }
530
531         at2 = at2->next;
532       }
533   }
534   return 0;
535 }
536
537 static struct gaih gaih[] =
538   {
539     { PF_INET6, gaih_inet },
540     { PF_INET, gaih_inet },
541     { PF_LOCAL, gaih_local },
542     { PF_UNSPEC, NULL }
543   };
544
545 int
546 getaddrinfo (const char *name, const char *service,
547              const struct addrinfo *hints, struct addrinfo **pai)
548 {
549   int i = 0, j = 0, last_i = 0;
550   struct addrinfo *p = NULL, **end;
551   struct gaih *g = gaih, *pg = NULL;
552   struct gaih_service gaih_service, *pservice;
553
554   if (name != NULL && name[0] == '*' && name[1] == 0)
555     name = NULL;
556
557   if (service != NULL && service[0] == '*' && service[1] == 0)
558     service = NULL;
559
560   if (name == NULL && service == NULL)
561     return EAI_NONAME;
562
563   if (hints == NULL)
564     hints = &default_hints;
565
566   if (hints->ai_flags & ~(AI_PASSIVE|AI_CANONNAME|AI_NUMERICHOST))
567     return EAI_BADFLAGS;
568
569   if ((hints->ai_flags & AI_CANONNAME) && name == NULL)
570     return EAI_BADFLAGS;
571
572   if (service && service[0])
573     {
574       char *c;
575       gaih_service.name = service;
576       gaih_service.num = strtoul (gaih_service.name, &c, 10);
577       if (*c)
578         gaih_service.num = -1;
579       else
580         /* Can't specify a numerical socket unless a protocol family was
581            given. */
582         if (hints->ai_socktype == 0)
583           return EAI_SERVICE;
584       pservice = &gaih_service;
585     }
586   else
587     pservice = NULL;
588
589   if (pai)
590     end = &p;
591   else
592     end = NULL;
593
594   while (g->gaih)
595     {
596       if (hints->ai_family == g->family || hints->ai_family == AF_UNSPEC)
597         {
598           j++;
599           if (pg == NULL || pg->gaih != g->gaih)
600             {
601               pg = g;
602               i = g->gaih (name, pservice, hints, end);
603               if (i != 0)
604                 {
605                   /* EAI_NODATA is a more specific result as it says that
606                      we found a result but it is not usable.  */
607                   if (last_i != (GAIH_OKIFUNSPEC | -EAI_NODATA))
608                     last_i = i;
609
610                   if (hints->ai_family == AF_UNSPEC && (i & GAIH_OKIFUNSPEC))
611                     continue;
612
613                   if (p)
614                     freeaddrinfo (p);
615
616                   return -(i & GAIH_EAI);
617                 }
618               if (end)
619                 while(*end) end = &((*end)->ai_next);
620             }
621         }
622       ++g;
623     }
624
625   if (j == 0)
626     return EAI_FAMILY;
627
628   if (p)
629     {
630       *pai = p;
631       return 0;
632     }
633
634   if (pai == NULL && last_i == 0)
635     return 0;
636
637   if (p)
638     freeaddrinfo (p);
639
640   return last_i ? -(last_i & GAIH_EAI) : EAI_NONAME;
641 }
642
643 void
644 freeaddrinfo (struct addrinfo *ai)
645 {
646   struct addrinfo *p;
647
648   while (ai != NULL)
649     {
650       p = ai;
651       ai = ai->ai_next;
652       free (p);
653     }
654 }