b14f8096bd85463e5a34188af16fc31a9da0cd98
[debian/amanda] / gnulib / getaddrinfo.c
1 /* Get address information (partial implementation).
2    Copyright (C) 1997, 2001, 2002, 2004, 2005, 2006, 2007 Free Software
3    Foundation, Inc.
4    Contributed by Simon Josefsson <simon@josefsson.org>.
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3, or (at your option)
9    any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software Foundation,
18    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
19
20 #include <config.h>
21
22 #include "getaddrinfo.h"
23
24 #if HAVE_NETINET_IN_H
25 # include <netinet/in.h>
26 #endif
27
28 /* Get calloc. */
29 #include <stdlib.h>
30
31 /* Get memcpy, strdup. */
32 #include <string.h>
33
34 /* Get snprintf. */
35 #include <stdio.h>
36
37 #include <stdbool.h>
38
39 #include "gettext.h"
40 #define _(String) gettext (String)
41 #define N_(String) String
42
43 #include "inet_ntop.h"
44
45 extern int h_errno;
46
47 /* BeOS has AF_INET, but not PF_INET.  */
48 #ifndef PF_INET
49 # define PF_INET AF_INET
50 #endif
51 /* BeOS also lacks PF_UNSPEC.  */
52 #ifndef PF_UNSPEC
53 # define PF_UNSPEC 0
54 #endif
55
56 #if defined _WIN32 || defined __WIN32__
57 # define WIN32_NATIVE
58 #endif
59
60 #ifdef WIN32_NATIVE
61 typedef int (WSAAPI *getaddrinfo_func) (const char*, const char*,
62                                         const struct addrinfo*,
63                                         struct addrinfo**);
64 typedef void (WSAAPI *freeaddrinfo_func) (struct addrinfo*);
65 typedef int (WSAAPI *getnameinfo_func) (const struct sockaddr*,
66                                         socklen_t, char*, DWORD,
67                                         char*, DWORD, int);
68
69 static getaddrinfo_func getaddrinfo_ptr = NULL;
70 static freeaddrinfo_func freeaddrinfo_ptr = NULL;
71 static getnameinfo_func getnameinfo_ptr = NULL;
72
73 static int
74 use_win32_p (void)
75 {
76   static int done = 0;
77   HMODULE h;
78
79   if (done)
80     return getaddrinfo_ptr ? 1 : 0;
81
82   done = 1;
83
84   h = GetModuleHandle ("ws2_32.dll");
85
86   if (h)
87     {
88       getaddrinfo_ptr = (getaddrinfo_func) GetProcAddress (h, "getaddrinfo");
89       freeaddrinfo_ptr = (freeaddrinfo_func) GetProcAddress (h, "freeaddrinfo");
90       getnameinfo_ptr = (getnameinfo_func) GetProcAddress (h, "getnameinfo");
91     }
92
93   /* If either is missing, something is odd. */
94   if (!getaddrinfo_ptr || !freeaddrinfo_ptr || !getnameinfo_ptr)
95     {
96       getaddrinfo_ptr = NULL;
97       freeaddrinfo_ptr = NULL;
98       getnameinfo_ptr = NULL;
99       return 0;
100     }
101
102   return 1;
103 }
104 #endif
105
106 static inline bool
107 validate_family (int family)
108 {
109   /* FIXME: Support more families. */
110 #if HAVE_IPV4
111      if (family == PF_INET)
112        return true;
113 #endif
114 #if HAVE_IPV6
115      if (family == PF_INET6)
116        return true;
117 #endif
118      if (family == PF_UNSPEC)
119        return true;
120      return false;
121 }
122
123 /* Translate name of a service location and/or a service name to set of
124    socket addresses. */
125 int
126 getaddrinfo (const char *nodename,
127              const char *servname,
128              const struct addrinfo *hints,
129              struct addrinfo **res)
130 {
131   struct addrinfo *tmp;
132   int port = 0;
133   struct hostent *he;
134   void *storage;
135   size_t size;
136 #if HAVE_IPV6
137   struct v6_pair {
138     struct addrinfo addrinfo;
139     struct sockaddr_in6 sockaddr_in6;
140   };
141 #endif
142 #if HAVE_IPV4
143   struct v4_pair {
144     struct addrinfo addrinfo;
145     struct sockaddr_in sockaddr_in;
146   };
147 #endif
148
149 #ifdef WIN32_NATIVE
150   if (use_win32_p ())
151     return getaddrinfo_ptr (nodename, servname, hints, res);
152 #endif
153
154   if (hints && (hints->ai_flags & ~(AI_CANONNAME | AI_PASSIVE)))
155     /* FIXME: Support more flags. */
156     return EAI_BADFLAGS;
157
158   if (hints && !validate_family (hints->ai_family))
159     return EAI_FAMILY;
160
161   if ((hints != NULL) &&
162       (hints->ai_socktype != 0) &&
163       (hints->ai_socktype != SOCK_STREAM) &&
164       (hints->ai_socktype != SOCK_DGRAM))
165     {
166       /* FIXME: Support other socktype. */
167       return EAI_SOCKTYPE; /* FIXME: Better return code? */
168     }
169
170   if (!nodename)
171     {
172       if (hints && !(hints->ai_flags & AI_PASSIVE))
173         return EAI_NONAME;
174
175 #ifdef HAVE_IPV6
176       nodename = (hints && (hints->ai_family == AF_INET6)) ? "::" : "0.0.0.0";
177 #else
178       nodename = "0.0.0.0";
179 #endif
180     }
181
182   if (servname)
183     {
184       struct servent *se = NULL;
185       const char *proto =
186         (hints && hints->ai_socktype == SOCK_DGRAM) ? "udp" : "tcp";
187
188       if (hints == NULL || !(hints->ai_flags & AI_NUMERICSERV))
189         /* FIXME: Use getservbyname_r if available. */
190         se = getservbyname (servname, proto);
191
192       if (!se)
193         {
194           char *c;
195           if (!(*servname >= '0' && *servname <= '9'))
196             return EAI_NONAME;
197           port = strtoul (servname, &c, 10);
198           if (*c || port > 0xffff)
199             return EAI_NONAME;
200           port = htons (port);
201         }
202       else
203         port = se->s_port;
204     }
205
206   /* FIXME: Use gethostbyname_r if available. */
207   he = gethostbyname(nodename);
208   if (!he || he->h_addr_list[0] == NULL)
209     return EAI_NONAME;
210
211   switch (he->h_addrtype)
212     {
213 #if HAVE_IPV6
214     case PF_INET6:
215       size = sizeof (struct v6_pair);
216       break;
217 #endif
218
219 #if HAVE_IPV4
220     case PF_INET:
221       size = sizeof (struct v4_pair);
222       break;
223 #endif
224
225     default:
226       return EAI_NODATA;
227     }
228
229   storage = calloc (1, size);
230   if (!storage)
231     return EAI_MEMORY;
232
233   switch (he->h_addrtype)
234     {
235 #if HAVE_IPV6
236     case PF_INET6:
237       {
238         struct v6_pair *p = storage;
239         struct sockaddr_in6 *sinp = &p->sockaddr_in6;
240         tmp = &p->addrinfo;
241
242         if (port)
243           sinp->sin6_port = port;
244
245         if (he->h_length != sizeof (sinp->sin6_addr))
246           {
247             free (storage);
248             return EAI_SYSTEM; /* FIXME: Better return code?  Set errno? */
249           }
250
251         memcpy (&sinp->sin6_addr, he->h_addr_list[0], sizeof sinp->sin6_addr);
252
253         tmp->ai_addr = (struct sockaddr *) sinp;
254         tmp->ai_addrlen = sizeof *sinp;
255       }
256       break;
257 #endif
258
259 #if HAVE_IPV4
260     case PF_INET:
261       {
262         struct v4_pair *p = storage;
263         struct sockaddr_in *sinp = &p->sockaddr_in;
264         tmp = &p->addrinfo;
265
266         if (port)
267           sinp->sin_port = port;
268
269         if (he->h_length != sizeof (sinp->sin_addr))
270           {
271             free (storage);
272             return EAI_SYSTEM; /* FIXME: Better return code?  Set errno? */
273           }
274
275         memcpy (&sinp->sin_addr, he->h_addr_list[0], sizeof sinp->sin_addr);
276
277         tmp->ai_addr = (struct sockaddr *) sinp;
278         tmp->ai_addrlen = sizeof *sinp;
279       }
280       break;
281 #endif
282
283     default:
284       free (storage);
285       return EAI_NODATA;
286     }
287
288   if (hints && (hints->ai_flags & AI_CANONNAME))
289     {
290       const char *cn;
291       if (he->h_name)
292         cn = he->h_name;
293       else
294         cn = nodename;
295
296       tmp->ai_canonname = strdup (cn);
297       if (!tmp->ai_canonname)
298         {
299           free (storage);
300           return EAI_MEMORY;
301         }
302     }
303
304   tmp->ai_protocol = (hints) ? hints->ai_protocol : 0;
305   tmp->ai_socktype = (hints) ? hints->ai_socktype : 0;
306   tmp->ai_addr->sa_family = he->h_addrtype;
307   tmp->ai_family = he->h_addrtype;
308
309   /* FIXME: If more than one address, create linked list of addrinfo's. */
310
311   *res = tmp;
312
313   return 0;
314 }
315
316 /* Free `addrinfo' structure AI including associated storage.  */
317 void
318 freeaddrinfo (struct addrinfo *ai)
319 {
320 #ifdef WIN32_NATIVE
321   if (use_win32_p ())
322     {
323       freeaddrinfo_ptr (ai);
324       return;
325     }
326 #endif
327
328   while (ai)
329     {
330       struct addrinfo *cur;
331
332       cur = ai;
333       ai = ai->ai_next;
334
335       free (cur->ai_canonname);
336       free (cur);
337     }
338 }
339
340 int getnameinfo(const struct sockaddr *sa, socklen_t salen,
341                 char *node, socklen_t nodelen,
342                 char *service, socklen_t servicelen,
343                 int flags)
344 {
345 #ifdef WIN32_NATIVE
346   if (use_win32_p ())
347     return getnameinfo_ptr (sa, salen, node, nodelen,
348                             service, servicelen, flags);
349 #endif
350
351   /* FIXME: Support other flags. */
352   if (flags & ~(NI_NUMERICHOST | NI_NUMERICSERV))
353     return EAI_BADFLAGS;
354
355   if (sa == NULL || salen < sizeof (sa->sa_family))
356     return EAI_FAMILY;
357
358   switch (sa->sa_family)
359     {
360 #if HAVE_IPV4
361     case AF_INET:
362       if (salen < sizeof (struct sockaddr_in))
363         return EAI_FAMILY;
364       break;
365 #endif
366 #if HAVE_IPV6
367     case AF_INET6:
368       if (salen < sizeof (struct sockaddr_in6))
369         return EAI_FAMILY;
370       break;
371 #endif
372     default:
373       return EAI_FAMILY;
374     }
375
376   if (node && (nodelen > 0))
377     {
378       char      addrbuf[256];
379
380       switch (sa->sa_family)
381         {
382 #if HAVE_IPV4
383         case AF_INET:
384           if (flags & NI_NUMERICHOST)
385             {
386               if (!inet_ntop (AF_INET,
387                           &(((const struct sockaddr_in *) sa)->sin_addr),
388                           addrbuf, sizeof(addrbuf)))
389               return EAI_SYSTEM;
390             }
391           else
392             {
393               struct hostent *host_ent = gethostbyaddr(
394                           (char *)&(((struct sockaddr_in *)sa)->sin_addr),
395                           sizeof(struct sockaddr_in),
396                           sa->sa_family);
397               if (host_ent != NULL)
398                 {
399                   if (nodelen <= snprintf(node, nodelen, "%s",
400                                         host_ent->h_name))
401                      return EAI_OVERFLOW;
402                 }
403               else
404                 {
405                   if (!inet_ntop (AF_INET,
406                           &(((const struct sockaddr_in *) sa)->sin_addr),
407                           addrbuf, sizeof(addrbuf)))
408                     { 
409                       return EAI_SYSTEM;
410                     }
411                     if (nodelen <= snprintf(node, nodelen, "%s", addrbuf))
412                       return EAI_OVERFLOW;
413                 }
414             }
415           break;
416 #endif
417
418 #if HAVE_IPV6
419         case AF_INET6:
420           if (flags & NI_NUMERICHOST)
421             {
422               if (!inet_ntop (AF_INET6,
423                           &(((const struct sockaddr_in6 *) sa)->sin6_addr),
424                           addrbuf, sizeof(addrbuf)))
425               return EAI_SYSTEM;
426             }
427           else
428             {
429               struct hostent *host_ent = gethostbyaddr(
430                           (char *)&(((struct sockaddr_in6 *)sa)->sin6_addr),
431                           sizeof(struct sockaddr_in6),
432                           sa->sa_family);
433               if (host_ent != NULL)
434                 {
435                   if (nodelen <= snprintf(node, nodelen, "%s",
436                                         host_ent->h_name))
437                      return EAI_OVERFLOW;
438                 }
439               else
440                 {
441                   if (!inet_ntop (AF_INET6,
442                           &(((const struct sockaddr_in6 *) sa)->sin6_addr),
443                           addrbuf, sizeof(addrbuf)))
444                     { 
445                       return EAI_SYSTEM;
446                     }
447                     if (nodelen <= snprintf(node, nodelen, "%s", addrbuf))
448                       return EAI_OVERFLOW;
449                 }
450             }
451           break;
452 #endif
453         default:
454           return EAI_FAMILY;
455         }
456     }
457
458   if (service && (servicelen > 0))
459     switch (sa->sa_family)
460       {
461 #if HAVE_IPV4
462       case AF_INET:
463 #endif
464 #if HAVE_IPV6
465       case AF_INET6:
466 #endif
467         {
468           unsigned short int port
469             = ntohs (((const struct sockaddr_in *) sa)->sin_port);
470           if (servicelen <= snprintf (service, servicelen, "%u", port))
471             return EAI_OVERFLOW;
472         }
473         break;
474       }
475
476   return 0;
477 }