Native win32 support for getaddrinfo. Add getnameinfo function.
[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 "strdup.h"
42
43 #ifdef _WIN32
44 typedef void WSAAPI (*freeaddrinfo_func) (struct addrinfo*);
45 typedef int WSAAPI (*getaddrinfo_func) (const char*, const char*,
46                                         const struct addrinfo*,
47                                         struct addrinfo**);
48 typedef int WSAAPI (*getnameinfo_func) (const struct sockaddr*,
49                                         socklen_t, char*, DWORD,
50                                         char*, DWORD, int);
51
52 static getaddrinfo_func getaddrinfo_ptr = NULL;
53 static freeaddrinfo_func freeaddrinfo_ptr = NULL;
54 static getnameinfo_func getnameinfo_ptr = NULL;
55
56 int use_win32_p (void)
57 {
58   static int done = 0;
59   HMODULE h;
60
61   if (done)
62     return getaddrinfo_ptr ? 1 : 0;
63
64   done = 1;
65
66   h = GetModuleHandle ("ws2_32.dll");
67
68   if (h)
69     {
70       getaddrinfo_ptr = GetProcAddress (h, "getaddrinfo");
71       freeaddrinfo_ptr = GetProcAddress (h, "freeaddrinfo");
72       getnameinfo_ptr = GetProcAddress (h, "getnameinfo");
73     }
74
75   /* If either is missing, something is odd. */
76   if (!getaddrinfo_ptr || !freeaddrinfo_ptr || !getnameinfo_ptr)
77     {
78       getaddrinfo_ptr = NULL;
79       freeaddrinfo_ptr = NULL;
80       getnameinfo_ptr = NULL;
81       return 0;
82     }
83
84   return 1;
85 }
86 #endif
87
88 static inline bool
89 validate_family (int family)
90 {
91   /* FIXME: Support more families. */
92 #if HAVE_IPV4
93      if (family == PF_INET)
94        return true;
95 #endif
96 #if HAVE_IPV6
97      if (family == PF_INET6)
98        return true;
99 #endif
100      if (family == PF_UNSPEC)
101        return true;
102      return false;
103 }
104
105 /* Translate name of a service location and/or a service name to set of
106    socket addresses. */
107 int
108 getaddrinfo (const char *restrict nodename,
109              const char *restrict servname,
110              const struct addrinfo *restrict hints,
111              struct addrinfo **restrict res)
112 {
113   struct addrinfo *tmp;
114   int port = 0;
115   struct hostent *he;
116   void *storage;
117   size_t size;
118 #if HAVE_IPV6
119   struct v6_pair {
120     struct addrinfo addrinfo;
121     struct sockaddr_in6 sockaddr_in6;
122   };
123 #endif
124 #if HAVE_IPV4
125   struct v4_pair {
126     struct addrinfo addrinfo;
127     struct sockaddr_in sockaddr_in;
128   };
129 #endif
130
131 #ifdef _WIN32
132   if (use_win32_p ())
133     return getaddrinfo_ptr (nodename, servname, hints, res);
134 #endif
135
136   if (hints && (hints->ai_flags & ~AI_CANONNAME))
137     /* FIXME: Support more flags. */
138     return EAI_BADFLAGS;
139
140   if (hints && !validate_family (hints->ai_family))
141     return EAI_FAMILY;
142
143   if (hints &&
144       hints->ai_socktype != SOCK_STREAM && hints->ai_socktype != SOCK_DGRAM)
145     /* FIXME: Support other socktype. */
146     return EAI_SOCKTYPE; /* FIXME: Better return code? */
147
148   if (!nodename)
149     /* FIXME: Support server bind mode. */
150     return EAI_NONAME;
151
152   if (servname)
153     {
154       struct servent *se = NULL;
155       const char *proto =
156         (hints && hints->ai_socktype == SOCK_DGRAM) ? "udp" : "tcp";
157
158       if (!(hints->ai_flags & AI_NUMERICSERV))
159         /* FIXME: Use getservbyname_r if available. */
160         se = getservbyname (servname, proto);
161
162       if (!se)
163         {
164           char *c;
165           port = strtoul (servname, &c, 10);
166           if (*c)
167             return EAI_NONAME;
168           port = htons (port);
169         }
170       else
171         port = se->s_port;
172     }
173
174   /* FIXME: Use gethostbyname_r if available. */
175   he = gethostbyname (nodename);
176   if (!he || he->h_addr_list[0] == NULL)
177     return EAI_NONAME;
178
179   switch (he->h_addrtype)
180     {
181 #if HAVE_IPV6
182     case PF_INET6:
183       size = sizeof (struct v6_pair);
184       break;
185 #endif
186
187 #if HAVE_IPV4
188     case PF_INET:
189       size = sizeof (struct v4_pair);
190       break;
191 #endif
192
193     default:
194       return EAI_NODATA;
195     }
196
197   storage = calloc (1, size);
198   if (!storage)
199     return EAI_MEMORY;
200
201   switch (he->h_addrtype)
202     {
203 #if HAVE_IPV6
204     case PF_INET6:
205       {
206         struct v6_pair *p = storage;
207         struct sockaddr_in6 *sinp = &p->sockaddr_in6;
208         tmp = &p->addrinfo;
209
210         if (port)
211           sinp->sin6_port = port;
212
213         if (he->h_length != sizeof (sinp->sin6_addr))
214           {
215             free (storage);
216             return EAI_SYSTEM; /* FIXME: Better return code?  Set errno? */
217           }
218
219         memcpy (&sinp->sin6_addr, he->h_addr_list[0], sizeof sinp->sin6_addr);
220
221         tmp->ai_addr = (struct sockaddr *) sinp;
222         tmp->ai_addrlen = sizeof *sinp;
223       }
224       break;
225 #endif
226
227 #if HAVE_IPV4
228     case PF_INET:
229       {
230         struct v4_pair *p = storage;
231         struct sockaddr_in *sinp = &p->sockaddr_in;
232         tmp = &p->addrinfo;
233
234         if (port)
235           sinp->sin_port = port;
236
237         if (he->h_length != sizeof (sinp->sin_addr))
238           {
239             free (storage);
240             return EAI_SYSTEM; /* FIXME: Better return code?  Set errno? */
241           }
242
243         memcpy (&sinp->sin_addr, he->h_addr_list[0], sizeof sinp->sin_addr);
244
245         tmp->ai_addr = (struct sockaddr *) sinp;
246         tmp->ai_addrlen = sizeof *sinp;
247       }
248       break;
249 #endif
250
251     default:
252       free (storage);
253       return EAI_NODATA;
254     }
255
256   if (hints && hints->ai_flags & AI_CANONNAME)
257     {
258       const char *cn;
259       if (he->h_name)
260         cn = he->h_name;
261       else
262         cn = nodename;
263
264       tmp->ai_canonname = strdup (cn);
265       if (!tmp->ai_canonname)
266         {
267           free (storage);
268           return EAI_MEMORY;
269         }
270     }
271
272   tmp->ai_protocol = (hints) ? hints->ai_protocol : 0;
273   tmp->ai_socktype = (hints) ? hints->ai_socktype : 0;
274   tmp->ai_addr->sa_family = he->h_addrtype;
275   tmp->ai_family = he->h_addrtype;
276
277   /* FIXME: If more than one address, create linked list of addrinfo's. */
278
279   *res = tmp;
280
281   return 0;
282 }
283
284 /* Free `addrinfo' structure AI including associated storage.  */
285 void
286 freeaddrinfo (struct addrinfo *ai)
287 {
288 #ifdef _WIN32
289   if (use_win32_p ())
290     return freeaddrinfo_ptr (ai);
291 #endif
292
293   while (ai)
294     {
295       struct addrinfo *cur;
296
297       cur = ai;
298       ai = ai->ai_next;
299
300       if (cur->ai_canonname) free (cur->ai_canonname);
301       free (cur);
302     }
303 }
304
305 int getnameinfo(const struct sockaddr *restrict sa, socklen_t salen,
306                 char *restrict node, socklen_t nodelen,
307                 char *restrict service, socklen_t servicelen,
308                 int flags)
309 {
310 #if _WIN32
311   if (use_win32_p ())
312     return getnameinfo_ptr (sa, salen, node, nodelen,
313                             service, servicelen, flags);
314 #endif
315
316   /* FIXME: Support other flags. */
317   if ((node && nodelen > 0 && !(flags & NI_NUMERICHOST)) ||
318       (service && servicelen > 0 && !(flags & NI_NUMERICHOST)) ||
319       (flags & ~(NI_NUMERICHOST|NI_NUMERICSERV)))
320     return EAI_BADFLAGS;
321
322   if (sa == NULL || salen < sizeof (sa->sa_family))
323     return EAI_FAMILY;
324
325   switch (sa->sa_family)
326     {
327 #if HAVE_IPV4
328     case AF_INET:
329       if (salen < sizeof (struct sockaddr_in))
330         return EAI_FAMILY;
331       break;
332 #endif
333 #if HAVE_IPV6
334     case AF_INET6:
335       if (salen < sizeof (struct sockaddr_in6))
336         return EAI_FAMILY;
337       break;
338 #endif
339     default:
340       return EAI_FAMILY;
341     }
342
343   if (node && nodelen > 0 && flags & NI_NUMERICHOST)
344     {
345       switch (sa->sa_family)
346         {
347 #if HAVE_IPV4
348         case AF_INET:
349           if (!inet_ntop (AF_INET,
350                           (const void *)
351                           &(((const struct sockaddr_in *) sa)->sin_addr),
352                           node, nodelen))
353             return EAI_SYSTEM;
354           break;
355 #endif
356
357 #if HAVE_IPV6
358         case AF_INET6:
359           if (!inet_ntop (AF_INET6,
360                           (const void *)
361                           &(((const struct sockaddr_in6 *) sa)->sin6_addr),
362                           node, nodelen))
363             return EAI_SYSTEM;
364           break;
365 #endif
366
367         default:
368           return EAI_FAMILY;
369         }
370     }
371
372   if (service && servicelen > 0 && flags & NI_NUMERICSERV)
373     switch (sa->sa_family)
374       {
375 #if HAVE_IPV4
376       case AF_INET:
377 #endif
378 #if HAVE_IPV6
379       case AF_INET6:
380 #endif
381         if (snprintf (service, servicelen, "%d",
382                       ntohs (((const struct sockaddr_in *) sa)->sin_port))
383             + 1 > servicelen)
384           return EAI_OVERFLOW;
385         break;
386       }
387
388   return 0;
389 }