Imported Upstream version 2.5.2p1
[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 2, 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 #include <stdbool.h>
35
36 #include "gettext.h"
37 #define _(String) gettext (String)
38 #define N_(String) String
39
40 #include "inet_ntop.h"
41 #include "snprintf.h"
42
43 extern int h_errno;
44
45 /* BeOS has AF_INET, but not PF_INET.  */
46 #ifndef PF_INET
47 # define PF_INET AF_INET
48 #endif
49 /* BeOS also lacks PF_UNSPEC.  */
50 #ifndef PF_UNSPEC
51 # define PF_UNSPEC 0
52 #endif
53
54 #if defined _WIN32 || defined __WIN32__
55 # define WIN32_NATIVE
56 #endif
57
58 #ifdef WIN32_NATIVE
59 typedef int (WSAAPI *getaddrinfo_func) (const char*, const char*,
60                                         const struct addrinfo*,
61                                         struct addrinfo**);
62 typedef void (WSAAPI *freeaddrinfo_func) (struct addrinfo*);
63 typedef int (WSAAPI *getnameinfo_func) (const struct sockaddr*,
64                                         socklen_t, char*, DWORD,
65                                         char*, DWORD, int);
66
67 static getaddrinfo_func getaddrinfo_ptr = NULL;
68 static freeaddrinfo_func freeaddrinfo_ptr = NULL;
69 static getnameinfo_func getnameinfo_ptr = NULL;
70
71 static int
72 use_win32_p (void)
73 {
74   static int done = 0;
75   HMODULE h;
76
77   if (done)
78     return getaddrinfo_ptr ? 1 : 0;
79
80   done = 1;
81
82   h = GetModuleHandle ("ws2_32.dll");
83
84   if (h)
85     {
86       getaddrinfo_ptr = (getaddrinfo_func) GetProcAddress (h, "getaddrinfo");
87       freeaddrinfo_ptr = (freeaddrinfo_func) GetProcAddress (h, "freeaddrinfo");
88       getnameinfo_ptr = (getnameinfo_func) GetProcAddress (h, "getnameinfo");
89     }
90
91   /* If either is missing, something is odd. */
92   if (!getaddrinfo_ptr || !freeaddrinfo_ptr || !getnameinfo_ptr)
93     {
94       getaddrinfo_ptr = NULL;
95       freeaddrinfo_ptr = NULL;
96       getnameinfo_ptr = NULL;
97       return 0;
98     }
99
100   return 1;
101 }
102 #endif
103
104 static inline bool
105 validate_family (int family)
106 {
107   /* FIXME: Support more families. */
108 #if HAVE_IPV4
109      if (family == PF_INET)
110        return true;
111 #endif
112 #if HAVE_IPV6
113      if (family == PF_INET6)
114        return true;
115 #endif
116      if (family == PF_UNSPEC)
117        return true;
118      return false;
119 }
120
121 /* Translate name of a service location and/or a service name to set of
122    socket addresses. */
123 int
124 getaddrinfo (const char *restrict nodename,
125              const char *restrict servname,
126              const struct addrinfo *restrict hints,
127              struct addrinfo **restrict res)
128 {
129   struct addrinfo *tmp;
130   int port = 0;
131   struct hostent *he;
132   void *storage;
133   size_t size;
134 #if HAVE_IPV6
135   struct v6_pair {
136     struct addrinfo addrinfo;
137     struct sockaddr_in6 sockaddr_in6;
138   };
139 #endif
140 #if HAVE_IPV4
141   struct v4_pair {
142     struct addrinfo addrinfo;
143     struct sockaddr_in sockaddr_in;
144   };
145 #endif
146
147 #ifdef WIN32_NATIVE
148   if (use_win32_p ())
149     return getaddrinfo_ptr (nodename, servname, hints, res);
150 #endif
151
152   if (hints && (hints->ai_flags & ~(AI_CANONNAME | AI_PASSIVE)))
153     /* FIXME: Support more flags. */
154     return EAI_BADFLAGS;
155
156   if (hints && !validate_family (hints->ai_family))
157     return EAI_FAMILY;
158
159   if ((hints != NULL) &&
160       (hints->ai_socktype != 0) &&
161       (hints->ai_socktype != SOCK_STREAM) &&
162       (hints->ai_socktype != SOCK_DGRAM))
163     {
164       /* FIXME: Support other socktype. */
165       return EAI_SOCKTYPE; /* FIXME: Better return code? */
166     }
167
168   if (!nodename)
169     {
170       if (hints && !(hints->ai_flags & AI_PASSIVE))
171         return EAI_NONAME;
172
173 #ifdef HAVE_IPV6
174       nodename = (hints && (hints->ai_family == AF_INET6)) ? "::" : "0.0.0.0";
175 #else
176       nodename = "0.0.0.0";
177 #endif
178     }
179
180   if (servname)
181     {
182       struct servent *se = NULL;
183       const char *proto =
184         (hints && (hints->ai_socktype == SOCK_DGRAM)) ? "udp" : "tcp";
185
186       if ((hints == NULL) || !(hints->ai_flags & AI_NUMERICSERV))
187         {
188           /* FIXME: Use getservbyname_r if available. */
189           se = getservbyname (servname, proto);
190         }
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       if (cur->ai_canonname) free (cur->ai_canonname);
336       free (cur);
337     }
338 }
339
340 int getnameinfo(const struct sockaddr *restrict sa, socklen_t salen,
341                 char *restrict node, socklen_t nodelen,
342                 char *restrict 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 }