Fix up cross platform stuff
[cascardo/gnio.git] / gnio / gresolver.c
1 /* GNIO - GLib Network Layer of GIO
2  *
3  * Copyright (C) 2008 Christian Kellner, Samuel Cormier-Iijima
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library 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 GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General
16  * Public License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18  * Boston, MA 02111-1307, USA.
19  *
20  * Authors: Christian Kellner <gicmo@gnome.org>
21  *          Samuel Cormier-Iijima <sciyoshi@gmail.com>
22  */
23
24 #include <config.h>
25 #include <glib.h>
26 #include <gio/gio.h>
27
28 #include <string.h>
29 #ifndef G_OS_WIN32
30 # include <netinet/in.h>
31 # include <arpa/inet.h>
32 # include <netdb.h>
33 #else
34 # include <winsock2.h>
35 # include <winerror.h>
36 # include <ws2tcpip.h>
37 # undef HAVE_GETADDRINFO
38 # define HAVE_GETHOSTBYNAME_THREADSAFE 1
39 #endif
40 #include <errno.h>
41
42 #include "ginetaddress.h"
43 #include "ginet4address.h"
44 #include "ginet6address.h"
45 #include "gresolver.h"
46 #include "gnioerror.h"
47
48 G_DEFINE_TYPE (GResolver, g_resolver, G_TYPE_OBJECT);
49
50 #if defined(HAVE_GETHOSTBYNAME_R_GLIB_MUTEX) || defined(HAVE_GETADDRINFO_GLIB_MUTEX)
51 # ifndef G_THREADS_ENABLED
52 #  error Using GLib Mutex but thread are not enabled.
53 # endif
54 G_LOCK_DEFINE (dnslock);
55 #endif
56
57 #ifdef G_OS_WIN32
58 /* This is copied straight from giowin32.c, but its static there... */
59 /* Is there another way to get this functionality? */
60 static char *
61 winsock_error_message (int number)
62 {
63   static char unk[100];
64
65   switch (number) {
66   case WSAEINTR:
67     return "Interrupted function call";
68   case WSAEACCES:
69     return "Permission denied";
70   case WSAEFAULT:
71     return "Bad address";
72   case WSAEINVAL:
73     return "Invalid argument";
74   case WSAEMFILE:
75     return "Too many open sockets";
76   case WSAEWOULDBLOCK:
77     return "Resource temporarily unavailable";
78   case WSAEINPROGRESS:
79     return "Operation now in progress";
80   case WSAEALREADY:
81     return "Operation already in progress";
82   case WSAENOTSOCK:
83     return "Socket operation on nonsocket";
84   case WSAEDESTADDRREQ:
85     return "Destination address required";
86   case WSAEMSGSIZE:
87     return "Message too long";
88   case WSAEPROTOTYPE:
89     return "Protocol wrong type for socket";
90   case WSAENOPROTOOPT:
91     return "Bad protocol option";
92   case WSAEPROTONOSUPPORT:
93     return "Protocol not supported";
94   case WSAESOCKTNOSUPPORT:
95     return "Socket type not supported";
96   case WSAEOPNOTSUPP:
97     return "Operation not supported on transport endpoint";
98   case WSAEPFNOSUPPORT:
99     return "Protocol family not supported";
100   case WSAEAFNOSUPPORT:
101     return "Address family not supported by protocol family";
102   case WSAEADDRINUSE:
103     return "Address already in use";
104   case WSAEADDRNOTAVAIL:
105     return "Address not available";
106   case WSAENETDOWN:
107     return "Network interface is not configured";
108   case WSAENETUNREACH:
109     return "Network is unreachable";
110   case WSAENETRESET:
111     return "Network dropped connection on reset";
112   case WSAECONNABORTED:
113     return "Software caused connection abort";
114   case WSAECONNRESET:
115     return "Connection reset by peer";
116   case WSAENOBUFS:
117     return "No buffer space available";
118   case WSAEISCONN:
119     return "Socket is already connected";
120   case WSAENOTCONN:
121     return "Socket is not connected";
122   case WSAESHUTDOWN:
123     return "Can't send after socket shutdown";
124   case WSAETIMEDOUT:
125     return "Connection timed out";
126   case WSAECONNREFUSED:
127     return "Connection refused";
128   case WSAEHOSTDOWN:
129     return "Host is down";
130   case WSAEHOSTUNREACH:
131     return "Host is unreachable";
132   case WSAEPROCLIM:
133     return "Too many processes";
134   case WSASYSNOTREADY:
135     return "Network subsystem is unavailable";
136   case WSAVERNOTSUPPORTED:
137     return "Winsock.dll version out of range";
138   case WSANOTINITIALISED:
139     return "Successful WSAStartup not yet performed";
140   case WSAEDISCON:
141     return "Graceful shutdown in progress";
142   case WSATYPE_NOT_FOUND:
143     return "Class type not found";
144   case WSAHOST_NOT_FOUND:
145     return "Host not found";
146   case WSATRY_AGAIN:
147     return "Nonauthoritative host not found";
148   case WSANO_RECOVERY:
149     return "This is a nonrecoverable error";
150   case WSANO_DATA:
151     return "Valid name, no data record of requested type";
152   case WSA_INVALID_HANDLE:
153     return "Specified event object handle is invalid";
154   case WSA_INVALID_PARAMETER:
155     return "One or more parameters are invalid";
156   case WSA_IO_INCOMPLETE:
157     return "Overlapped I/O event object not in signaled state";
158   case WSA_NOT_ENOUGH_MEMORY:
159     return "Insufficient memory available";
160   case WSA_OPERATION_ABORTED:
161     return "Overlapped operation aborted";
162   case WSAEINVALIDPROCTABLE:
163     return "Invalid procedure table from service provider";
164   case WSAEINVALIDPROVIDER:
165     return "Invalid service provider version number";
166   case WSAEPROVIDERFAILEDINIT:
167     return "Unable to initialize a service provider";
168   case WSASYSCALLFAILURE:
169     return "System call failure";
170   default:
171     sprintf (unk, "Unknown WinSock error %d", number);
172     return unk;
173   }
174 }
175 #endif
176
177 static void
178 g_set_error_from_last_error (GError **error)
179 {
180   int code;
181
182 #ifdef G_OS_WIN32
183   int err = WSAGetLastError ();
184 #else
185   int err = h_errno;
186 #endif
187
188   switch (err)
189     {
190       case HOST_NOT_FOUND:
191         code = G_IO_ERROR_RESOLVER_NOT_FOUND;
192         break;
193       case NO_DATA:
194         code = G_IO_ERROR_RESOLVER_NO_DATA;
195         break;
196       default:
197         g_warning ("unknown h_errno code encountered");
198     }
199
200 #ifdef G_OS_WIN32
201   g_set_error (error, G_IO_ERROR, code, winsock_error_message (err));
202 #else
203   g_set_error (error, G_IO_ERROR, code, hstrerror (err));
204 #endif
205 }
206
207 #if !defined(HAVE_GETADDRINFO)
208 static GList *
209 hostent2list (const struct hostent *he)
210 {
211   GList *list = NULL;
212   int i;
213
214   g_return_val_if_fail (he != NULL, NULL);
215
216   for (i = 0; he->h_addr_list[i]; i++)
217     {
218       GInetAddress *address = NULL;
219
220       if (he->h_addrtype == AF_INET)
221         address = G_INET_ADDRESS (g_inet4_address_from_bytes ((guint8 *) he->h_addr_list[i]));
222       else if (he->h_addrtype == AF_INET6)
223         address = G_INET_ADDRESS (g_inet6_address_from_bytes ((guint8 *) he->h_addr_list[i]));
224
225       list = g_list_prepend (list, address);
226     }
227
228   return list;
229 }
230 #endif
231
232 #if defined(HAVE_GETADDRINFO)
233 static void
234 g_io_error_from_addrinfo (GError** error, int err)
235 {
236   GIOErrorEnum code = G_IO_ERROR_FAILED;
237   const gchar *message = NULL;
238
239   if (error == NULL)
240     return;
241
242   switch (err)
243     {
244       case EAI_NONAME:
245         code = G_IO_ERROR_RESOLVER_NOT_FOUND;
246         break;
247       case EAI_NODATA:
248         code = G_IO_ERROR_RESOLVER_NO_DATA;
249         break;
250       default:
251         g_warning ("unknown getaddrinfo() error code encountered");
252     }
253
254   if (message == NULL)
255     {
256 #ifndef G_OS_WIN32
257       /* FIXME: is gai_strerror() thread-safe? */
258       message = gai_strerror (err);
259 #else
260       message = winsock_error_message (WSAGetLastError ());
261 #endif
262     }
263
264   *error = g_error_new_literal (G_IO_ERROR, code, message);
265 }
266 #endif
267
268 static GList *
269 g_resolver_get_host_by_name (GResolver *resolver, const gchar *hostname, GError **error)
270 {
271   GList *list = NULL;
272
273 #if defined(HAVE_GETADDRINFO)
274   {
275     struct addrinfo hints;
276     struct addrinfo *res = NULL, *i;
277     int rv;
278
279     memset (&hints, 0, sizeof (hints));
280     hints.ai_socktype = SOCK_STREAM;
281
282 #ifdef HAVE_GETADDRINFO_GLIB_MUTEX
283     G_LOCK (dnslock);
284 #endif
285
286     if ((rv = getaddrinfo (hostname, NULL, &hints, &res)))
287       g_io_error_from_addrinfo (error, rv);
288     else
289       for (i = res; i != NULL; i = i->ai_next)
290         {
291           if (i->ai_family == PF_INET)
292             list = g_list_prepend (list, g_inet4_address_from_bytes ((guint8 *) &(((struct sockaddr_in *) i->ai_addr)->sin_addr.s_addr)));
293           else if (i->ai_family == PF_INET6)
294             list = g_list_prepend (list, g_inet6_address_from_bytes ((guint8 *) &(((struct sockaddr_in *) i->ai_addr)->sin_addr.s_addr)));
295         }
296
297     if (res)
298       freeaddrinfo (res);
299
300 #ifdef HAVE_GETADDRINFO_GLIB_MUTEX
301     G_UNLOCK (dnslock);
302 #endif
303   }
304 #elif defined(HAVE_GETHOSTBYNAME_THREADSAFE)
305   {
306     struct hostent *he = gethostbyname (hostname);
307
308     if (!he)
309       g_set_error_from_last_error (error);
310     else
311       list = hostent2list (he);
312   }
313 #elif defined(HAVE_GETHOSTBYNAME_R_GLIBC)
314   {
315     struct hostent result, *he;
316     gsize len = 1024;
317     gchar *buf = g_new (gchar, len);
318     gint rv, herr;
319
320     while ((rv = gethostbyname_r (hostname, &result, buf, len, &he, &herr)) == ERANGE)
321       {
322         len *= 2;
323         buf = g_renew (gchar, buf, len);
324       }
325
326     if (!rv)
327       list = hostent2list (he);
328     else
329       g_set_error_from_last_error (error);
330
331     g_free (buf);
332   }
333 #elif defined(HAVE_GETHOSTBYNAME_R_SOLARIS)
334   {
335     struct hostent result, *he;
336     gsize len = 8192;
337     char *buf = NULL;
338
339     do
340       {
341         buf = g_renew (gchar, buf, len);
342         errno = 0;
343         he = gethostbyname_r (hostname, &result, buf, len, &h_errno);
344         len += 1024;
345       }
346     while (errno == ERANGE);
347
348     if (he)
349       list = hostent2list (&result);
350     else
351       g_set_error_from_last_error (error);
352
353     g_free (buf);
354   }
355 #elif defined(HAVE_GETHOSTBYNAME_R_HPUX)
356   {
357     struct hostent he;
358     struct hostent_data buf;
359     int rv;
360
361     rv = gethostbyname_r (hostname, &he, &buf);
362
363     if (!rv)
364       list = hostent2list (&he);
365     else
366       g_set_error_from_last_error (error);
367   }
368 #else
369   {
370     struct hostent *he;
371
372 #ifdef HAVE_GETHOSTBYNAME_R_GLIB_MUTEX
373     G_LOCK (dnslock);
374 #endif
375
376     he = gethostbyname (hostname);
377     if (he)
378       list = hostent2list (he);
379     else
380       g_set_error_from_last_error (error);
381
382 #ifdef HAVE_GETHOSTBYNAME_R_GLIB_MUTEX
383     G_UNLOCK (dnslock);
384 #endif
385   }
386 #endif
387
388   if (list)
389     list = g_list_reverse (list);
390
391   return list;
392 }
393
394 static void
395 g_resolver_class_init (GResolverClass *klass)
396 {
397   GObjectClass *gobject_class G_GNUC_UNUSED = G_OBJECT_CLASS (klass);
398 }
399
400 static void
401 g_resolver_init (GResolver *address)
402 {
403
404 }
405
406 typedef struct {
407   GList *list;
408   const gchar *host;
409 } ResolveListData;
410
411 static void
412 resolve_list_thread (GSimpleAsyncResult *res,
413                      GObject            *object,
414                      GCancellable       *cancellable)
415 {
416   ResolveListData *op;
417   GError *error = NULL;
418
419   op = g_simple_async_result_get_op_res_gpointer (res);
420
421   op->list = g_resolver_resolve_list (G_RESOLVER (object), op->host, cancellable, &error);
422
423   if (op->list == NULL)
424     {
425       g_simple_async_result_set_from_error (res, error);
426       g_error_free (error);
427     }
428 }
429
430 GInetAddress *
431 g_resolver_resolve (GResolver     *resolver,
432                     const char    *host,
433                     GCancellable  *cancellable,
434                     GError       **error)
435 {
436   GList *list;
437   GInetAddress *address;
438
439   list = g_resolver_get_host_by_name (resolver, host, error);
440
441   if (!list)
442     return NULL;
443
444   address = G_INET_ADDRESS (g_object_ref (g_list_first (list)->data));
445
446   g_list_foreach (list, (GFunc) g_object_unref, NULL);
447
448   g_list_free (list);
449
450   return address;
451 }
452
453 void
454 g_resolver_resolve_async (GResolver           *resolver,
455                           const char          *host,
456                           GCancellable        *cancellable,
457                           GAsyncReadyCallback  callback,
458                           gpointer             user_data)
459 {
460   GSimpleAsyncResult *res;
461   ResolveListData *op;
462
463   op = g_new (ResolveListData, 1);
464
465   res = g_simple_async_result_new (G_OBJECT (resolver), callback, user_data, g_resolver_resolve_list_async);
466
467   g_simple_async_result_set_op_res_gpointer (res, op, g_free);
468
469   op->host = host;
470
471   g_simple_async_result_run_in_thread (res, resolve_list_thread, G_PRIORITY_DEFAULT, cancellable);
472
473   g_object_unref (res);
474 }
475
476 GInetAddress *
477 g_resolver_resolve_finish (GResolver     *resolver,
478                            GAsyncResult  *result,
479                            GError       **error)
480 {
481   GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (result);
482   ResolveListData *op;
483   GInetAddress *address;
484
485   g_warn_if_fail (g_simple_async_result_get_source_tag (res) == g_resolver_resolve_list_async);
486
487   op = g_simple_async_result_get_op_res_gpointer (res);
488
489   g_simple_async_result_propagate_error (res, error);
490
491   if (op->list == NULL)
492     return NULL;
493
494   address = G_INET_ADDRESS (g_object_ref (g_list_first (op->list)->data));
495
496   g_list_foreach (op->list, (GFunc) g_object_unref, NULL);
497
498   g_list_free (op->list);
499
500   return address;
501 }
502
503 GList *
504 g_resolver_resolve_list (GResolver     *resolver,
505                          const char    *host,
506                          GCancellable  *cancellable,
507                          GError       **error)
508 {
509   return g_resolver_get_host_by_name (resolver, host, error);
510 }
511
512 void
513 g_resolver_resolve_list_async (GResolver           *resolver,
514                                const char          *host,
515                                GCancellable        *cancellable,
516                                GAsyncReadyCallback  callback,
517                                gpointer             user_data)
518 {
519   GSimpleAsyncResult *res;
520   ResolveListData *op;
521
522   op = g_new (ResolveListData, 1);
523
524   res = g_simple_async_result_new (G_OBJECT (resolver), callback, user_data, g_resolver_resolve_list_async);
525
526   g_simple_async_result_set_op_res_gpointer (res, op, g_free);
527
528   op->host = host;
529
530   g_simple_async_result_run_in_thread (res, resolve_list_thread, G_PRIORITY_DEFAULT, cancellable);
531
532   g_object_unref (res);
533 }
534
535 GList *
536 g_resolver_resolve_list_finish (GResolver     *resolver,
537                                 GAsyncResult  *result,
538                                 GError       **error)
539 {
540   GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (result);
541   ResolveListData *op;
542
543   g_warn_if_fail (g_simple_async_result_get_source_tag (res) == g_resolver_resolve_list_async);
544
545   op = g_simple_async_result_get_op_res_gpointer (res);
546
547   g_simple_async_result_propagate_error (res, error);
548
549   return op->list;
550 }
551