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