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