2006-07-06 Jim Hyslop <jhyslop@dreampossible.ca> (tiny change)
[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))
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     /* FIXME: Support server bind mode. */
157     return EAI_NONAME;
158
159   if (servname)
160     {
161       struct servent *se = NULL;
162       const char *proto =
163         (hints && hints->ai_socktype == SOCK_DGRAM) ? "udp" : "tcp";
164
165       if (!(hints->ai_flags & AI_NUMERICSERV))
166         /* FIXME: Use getservbyname_r if available. */
167         se = getservbyname (servname, proto);
168
169       if (!se)
170         {
171           char *c;
172           if (!(*servname >= '0' && *servname <= '9'))
173             return EAI_NONAME;
174           port = strtoul (servname, &c, 10);
175           if (*c || port > 0xffff)
176             return EAI_NONAME;
177           port = htons (port);
178         }
179       else
180         port = se->s_port;
181     }
182
183   /* FIXME: Use gethostbyname_r if available. */
184   he = gethostbyname (nodename);
185   if (!he || he->h_addr_list[0] == NULL)
186     return EAI_NONAME;
187
188   switch (he->h_addrtype)
189     {
190 #if HAVE_IPV6
191     case PF_INET6:
192       size = sizeof (struct v6_pair);
193       break;
194 #endif
195
196 #if HAVE_IPV4
197     case PF_INET:
198       size = sizeof (struct v4_pair);
199       break;
200 #endif
201
202     default:
203       return EAI_NODATA;
204     }
205
206   storage = calloc (1, size);
207   if (!storage)
208     return EAI_MEMORY;
209
210   switch (he->h_addrtype)
211     {
212 #if HAVE_IPV6
213     case PF_INET6:
214       {
215         struct v6_pair *p = storage;
216         struct sockaddr_in6 *sinp = &p->sockaddr_in6;
217         tmp = &p->addrinfo;
218
219         if (port)
220           sinp->sin6_port = port;
221
222         if (he->h_length != sizeof (sinp->sin6_addr))
223           {
224             free (storage);
225             return EAI_SYSTEM; /* FIXME: Better return code?  Set errno? */
226           }
227
228         memcpy (&sinp->sin6_addr, he->h_addr_list[0], sizeof sinp->sin6_addr);
229
230         tmp->ai_addr = (struct sockaddr *) sinp;
231         tmp->ai_addrlen = sizeof *sinp;
232       }
233       break;
234 #endif
235
236 #if HAVE_IPV4
237     case PF_INET:
238       {
239         struct v4_pair *p = storage;
240         struct sockaddr_in *sinp = &p->sockaddr_in;
241         tmp = &p->addrinfo;
242
243         if (port)
244           sinp->sin_port = port;
245
246         if (he->h_length != sizeof (sinp->sin_addr))
247           {
248             free (storage);
249             return EAI_SYSTEM; /* FIXME: Better return code?  Set errno? */
250           }
251
252         memcpy (&sinp->sin_addr, he->h_addr_list[0], sizeof sinp->sin_addr);
253
254         tmp->ai_addr = (struct sockaddr *) sinp;
255         tmp->ai_addrlen = sizeof *sinp;
256       }
257       break;
258 #endif
259
260     default:
261       free (storage);
262       return EAI_NODATA;
263     }
264
265   if (hints && hints->ai_flags & AI_CANONNAME)
266     {
267       const char *cn;
268       if (he->h_name)
269         cn = he->h_name;
270       else
271         cn = nodename;
272
273       tmp->ai_canonname = strdup (cn);
274       if (!tmp->ai_canonname)
275         {
276           free (storage);
277           return EAI_MEMORY;
278         }
279     }
280
281   tmp->ai_protocol = (hints) ? hints->ai_protocol : 0;
282   tmp->ai_socktype = (hints) ? hints->ai_socktype : 0;
283   tmp->ai_addr->sa_family = he->h_addrtype;
284   tmp->ai_family = he->h_addrtype;
285
286   /* FIXME: If more than one address, create linked list of addrinfo's. */
287
288   *res = tmp;
289
290   return 0;
291 }
292
293 /* Free `addrinfo' structure AI including associated storage.  */
294 void
295 freeaddrinfo (struct addrinfo *ai)
296 {
297 #ifdef WIN32_NATIVE
298   if (use_win32_p ())
299     {
300       freeaddrinfo_ptr (ai);
301       return;
302     }
303 #endif
304
305   while (ai)
306     {
307       struct addrinfo *cur;
308
309       cur = ai;
310       ai = ai->ai_next;
311
312       if (cur->ai_canonname) free (cur->ai_canonname);
313       free (cur);
314     }
315 }
316
317 int getnameinfo(const struct sockaddr *restrict sa, socklen_t salen,
318                 char *restrict node, socklen_t nodelen,
319                 char *restrict service, socklen_t servicelen,
320                 int flags)
321 {
322 #ifdef WIN32_NATIVE
323   if (use_win32_p ())
324     return getnameinfo_ptr (sa, salen, node, nodelen,
325                             service, servicelen, flags);
326 #endif
327
328   /* FIXME: Support other flags. */
329   if ((node && nodelen > 0 && !(flags & NI_NUMERICHOST)) ||
330       (service && servicelen > 0 && !(flags & NI_NUMERICHOST)) ||
331       (flags & ~(NI_NUMERICHOST|NI_NUMERICSERV)))
332     return EAI_BADFLAGS;
333
334   if (sa == NULL || salen < sizeof (sa->sa_family))
335     return EAI_FAMILY;
336
337   switch (sa->sa_family)
338     {
339 #if HAVE_IPV4
340     case AF_INET:
341       if (salen < sizeof (struct sockaddr_in))
342         return EAI_FAMILY;
343       break;
344 #endif
345 #if HAVE_IPV6
346     case AF_INET6:
347       if (salen < sizeof (struct sockaddr_in6))
348         return EAI_FAMILY;
349       break;
350 #endif
351     default:
352       return EAI_FAMILY;
353     }
354
355   if (node && nodelen > 0 && flags & NI_NUMERICHOST)
356     {
357       switch (sa->sa_family)
358         {
359 #if HAVE_IPV4
360         case AF_INET:
361           if (!inet_ntop (AF_INET,
362                           &(((const struct sockaddr_in *) sa)->sin_addr),
363                           node, nodelen))
364             return EAI_SYSTEM;
365           break;
366 #endif
367
368 #if HAVE_IPV6
369         case AF_INET6:
370           if (!inet_ntop (AF_INET6,
371                           &(((const struct sockaddr_in6 *) sa)->sin6_addr),
372                           node, nodelen))
373             return EAI_SYSTEM;
374           break;
375 #endif
376
377         default:
378           return EAI_FAMILY;
379         }
380     }
381
382   if (service && servicelen > 0 && flags & NI_NUMERICSERV)
383     switch (sa->sa_family)
384       {
385 #if HAVE_IPV4
386       case AF_INET:
387 #endif
388 #if HAVE_IPV6
389       case AF_INET6:
390 #endif
391         if (snprintf (service, servicelen, "%d",
392                       ntohs (((const struct sockaddr_in *) sa)->sin_port))
393             + 1 > servicelen)
394           return EAI_OVERFLOW;
395         break;
396       }
397
398   return 0;
399 }