c5ff2e7d7f9d137590a991569ed6ecfde87763eb
[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 #if !defined(HAVE_GETADDRINFO)
178 static GList *
179 hostent2list (const struct hostent *he)
180 {
181   GList *list = NULL;
182   int i;
183
184   g_return_val_if_fail (he != NULL, NULL);
185
186   for (i = 0; he->h_addr_list[i]; i++)
187     {
188       GInetAddress *address = NULL;
189
190       if (he->h_addrtype == AF_INET)
191         address = G_INET_ADDRESS (g_inet4_address_from_bytes ((guint8 *) he->h_addr_list[i]));
192       else if (he->h_addrtype == AF_INET6)
193         address = G_INET_ADDRESS (g_inet6_address_from_bytes ((guint8 *) he->h_addr_list[i]));
194
195       list = g_list_prepend (list, address);
196     }
197
198   return list;
199 }
200 #endif
201
202 #if defined(HAVE_GETADDRINFO)
203 static void
204 g_io_error_from_addrinfo (GError** error, int err)
205 {
206   GIOErrorEnum code = G_IO_ERROR_FAILED;
207   const gchar *message = NULL;
208
209   if (error == NULL)
210     return;
211
212   switch (err)
213     {
214       case EAI_NONAME:
215         code = G_IO_ERROR_RESOLVER_NOT_FOUND;
216         break;
217       case EAI_NODATA:
218         code = G_IO_ERROR_RESOLVER_NO_DATA;
219         break;
220       default:
221         g_warning ("unknown getaddrinfo() error code encountered");
222     }
223
224   if (message == NULL)
225     {
226 #ifndef G_OS_WIN32
227       /* FIXME: is gai_strerror() thread-safe? */
228       message = gai_strerror (err);
229 #else
230       message = winsock_error_message (WSAGetLastError ());
231 #endif
232     }
233
234   *error = g_error_new_literal (G_IO_ERROR, code, message);
235 }
236 #endif
237
238 static GList *
239 g_resolver_get_host_by_name (GResolver *resolver, const gchar *hostname, GError **error)
240 {
241   GList *list = NULL;
242
243 #if defined(HAVE_GETADDRINFO)
244   {
245     struct addrinfo hints;
246     struct addrinfo *res = NULL, *i;
247     int rv;
248
249     memset (&hints, 0, sizeof (hints));
250     hints.ai_socktype = SOCK_STREAM;
251
252 #ifdef HAVE_GETADDRINFO_GLIB_MUTEX
253     G_LOCK (dnslock);
254 #endif
255
256     if ((rv = getaddrinfo (hostname, NULL, &hints, &res)))
257       g_io_error_from_addrinfo (error, rv);
258     else
259       for (i = res; i != NULL; i = i->ai_next)
260         {
261           if (i->ai_family == PF_INET)
262             list = g_list_prepend (list, g_inet4_address_from_bytes ((guint8 *) &(((struct sockaddr_in *) i->ai_addr)->sin_addr.s_addr)));
263           else if (i->ai_family == PF_INET6)
264             list = g_list_prepend (list, g_inet6_address_from_bytes ((guint8 *) &(((struct sockaddr_in *) i->ai_addr)->sin_addr.s_addr)));
265         }
266
267     if (res)
268       freeaddrinfo (res);
269
270 #ifdef HAVE_GETADDRINFO_GLIB_MUTEX
271     G_UNLOCK (dnslock);
272 #endif
273   }
274 #elif defined(HAVE_GETHOSTBYNAME_THREADSAFE)
275   {
276     struct hostent *he = gethostbyname (hostname);
277
278     if (!he)
279       {
280         if (error)
281           *error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_FAILED, winsock_error_message (WSAGetLastError ()));
282       }
283     else
284       list = hostent2list (he);
285   }
286 #elif defined(HAVE_GETHOSTBYNAME_R_GLIBC)
287   {
288     struct hostent result, *he;
289     gsize len = 1024;
290     gchar *buf = g_new (gchar, len);
291     gint rv, herr;
292
293     while ((rv = gethostbyname_r (hostname, &result, buf, len, &he, &herr)) == ERANGE)
294       {
295         len *= 2;
296         buf = g_renew (gchar, buf, len);
297       }
298
299     if (!rv)
300       list = hostent2list (he);
301
302     g_free (buf);
303   }
304 #elif defined(HAVE_GETHOSTBYNAME_R_SOLARIS)
305   {
306     struct hostent result, *he;
307     gsize len = 8192;
308     char *buf = NULL;
309
310     do
311       {
312         buf = g_renew (gchar, buf, len);
313         errno = 0;
314         he = gethostbyname_r (hostname, &result, buf, len, &h_errno);
315         len += 1024;
316       }
317     while (errno == ERANGE);
318
319     if (he)
320       list = hostent2list (&result);
321
322     g_free (buf);
323   }
324 #elif defined(HAVE_GETHOSTBYNAME_R_HPUX)
325   {
326     struct hostent he;
327     struct hostent_data buf;
328     int rv;
329
330     rv = gethostbyname_r (hostname, &he, &buf);
331
332     if (!rv)
333       list = hostent2list (&he);
334   }
335 #else
336   {
337     struct hostent *he;
338
339 #ifdef HAVE_GETHOSTBYNAME_R_GLIB_MUTEX
340     G_LOCK (dnslock);
341 #endif
342
343     he = gethostbyname (hostname);
344     list = hostent2list (he);
345
346 #ifdef HAVE_GETHOSTBYNAME_R_GLIB_MUTEX
347     G_UNLOCK (dnslock);
348 #endif
349   }
350 #endif
351
352   if (list)
353     list = g_list_reverse (list);
354
355   if (!list && error)
356     *error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_FAILED, winsock_error_message (WSAGetLastError ()));
357
358   return list;
359 }
360
361 static void
362 g_resolver_class_init (GResolverClass *klass)
363 {
364   GObjectClass *gobject_class G_GNUC_UNUSED = G_OBJECT_CLASS (klass);
365 }
366
367 static void
368 g_resolver_init (GResolver *address)
369 {
370
371 }
372
373 typedef struct {
374   GList *list;
375   const gchar *host;
376 } ResolveListData;
377
378 static void
379 resolve_list_thread (GSimpleAsyncResult *res,
380                      GObject            *object,
381                      GCancellable       *cancellable)
382 {
383   ResolveListData *op;
384   GError *error = NULL;
385
386   op = g_simple_async_result_get_op_res_gpointer (res);
387
388   op->list = g_resolver_resolve_list (G_RESOLVER (object), op->host, cancellable, &error);
389
390   if (op->list == NULL)
391     {
392       g_simple_async_result_set_from_error (res, error);
393       g_error_free (error);
394     }
395 }
396
397 GInetAddress *
398 g_resolver_resolve (GResolver     *resolver,
399                     const char    *host,
400                     GCancellable  *cancellable,
401                     GError       **error)
402 {
403   GList *list;
404   GInetAddress *address;
405
406   list = g_resolver_get_host_by_name (resolver, host, error);
407
408   if (!list)
409     return NULL;
410
411   address = G_INET_ADDRESS (g_object_ref (g_list_first (list)->data));
412
413   g_list_foreach (list, (GFunc) g_object_unref, NULL);
414
415   g_list_free (list);
416
417   return address;
418 }
419
420 void
421 g_resolver_resolve_async (GResolver           *resolver,
422                           const char          *host,
423                           GCancellable        *cancellable,
424                           GAsyncReadyCallback  callback,
425                           gpointer             user_data)
426 {
427   GSimpleAsyncResult *res;
428   ResolveListData *op;
429
430   op = g_new (ResolveListData, 1);
431
432   res = g_simple_async_result_new (G_OBJECT (resolver), callback, user_data, g_resolver_resolve_list_async);
433
434   g_simple_async_result_set_op_res_gpointer (res, op, g_free);
435
436   op->host = host;
437
438   g_simple_async_result_run_in_thread (res, resolve_list_thread, G_PRIORITY_DEFAULT, cancellable);
439
440   g_object_unref (res);
441 }
442
443 GInetAddress *
444 g_resolver_resolve_finish (GResolver     *resolver,
445                            GAsyncResult  *result,
446                            GError       **error)
447 {
448   GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (result);
449   ResolveListData *op;
450   GInetAddress *address;
451
452   g_warn_if_fail (g_simple_async_result_get_source_tag (res) == g_resolver_resolve_list_async);
453
454   op = g_simple_async_result_get_op_res_gpointer (res);
455
456   g_simple_async_result_propagate_error (res, error);
457
458   if (op->list == NULL)
459     return NULL;
460
461   address = G_INET_ADDRESS (g_object_ref (g_list_first (op->list)->data));
462
463   g_list_foreach (op->list, (GFunc) g_object_unref, NULL);
464
465   g_list_free (op->list);
466
467   return address;
468 }
469
470 GList *
471 g_resolver_resolve_list (GResolver     *resolver,
472                          const char    *host,
473                          GCancellable  *cancellable,
474                          GError       **error)
475 {
476   return g_resolver_get_host_by_name (resolver, host, error);
477 }
478
479 void
480 g_resolver_resolve_list_async (GResolver           *resolver,
481                                const char          *host,
482                                GCancellable        *cancellable,
483                                GAsyncReadyCallback  callback,
484                                gpointer             user_data)
485 {
486   GSimpleAsyncResult *res;
487   ResolveListData *op;
488
489   op = g_new (ResolveListData, 1);
490
491   res = g_simple_async_result_new (G_OBJECT (resolver), callback, user_data, g_resolver_resolve_list_async);
492
493   g_simple_async_result_set_op_res_gpointer (res, op, g_free);
494
495   op->host = host;
496
497   g_simple_async_result_run_in_thread (res, resolve_list_thread, G_PRIORITY_DEFAULT, cancellable);
498
499   g_object_unref (res);
500 }
501
502 GList *
503 g_resolver_resolve_list_finish (GResolver     *resolver,
504                                 GAsyncResult  *result,
505                                 GError       **error)
506 {
507   GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (result);
508   ResolveListData *op;
509
510   g_warn_if_fail (g_simple_async_result_get_source_tag (res) == g_resolver_resolve_list_async);
511
512   op = g_simple_async_result_get_op_res_gpointer (res);
513
514   g_simple_async_result_propagate_error (res, error);
515
516   return op->list;
517 }
518