af46ae9f32c92f7c60b0edc14d592e43ffc69d63
[cascardo/gnio.git] / gnio / gresolver.c
1 /* GNIO - GLib Network Layer of GIO
2  * 
3  * Copyright (C) 2008 Christian Kellner 
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  * Author: Christian Kellner <gicmo@gnome.org>
21  */
22
23 #include <config.h>
24 #include <glib.h>
25 #include <gio/gio.h>
26
27 #include <string.h>
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
30 #include <netdb.h>
31 #include <errno.h>
32
33 #include "ginetaddress.h"
34 #include "ginet4address.h"
35 #include "ginet6address.h"
36 #include "gresolver.h"
37 #include "gnioerror.h"
38
39 G_DEFINE_TYPE (GResolver, g_resolver, G_TYPE_OBJECT);
40
41 #if defined(HAVE_GETHOSTBYNAME_R_GLIB_MUTEX) || defined(HAVE_GETADDRINFO_GLIB_MUTEX)
42 # ifndef G_THREADS_ENABLED
43 #  error Using GLib Mutex but thread are not enabled.
44 # endif
45 G_LOCK_DEFINE (dnslock);
46 #endif
47
48 #if !defined(HAVE_GETADDRINFO)
49 static GList *
50 hostent2list (const struct hostent *he)
51 {
52   GList *list = NULL;
53   int i;
54
55   g_return_val_if_fail (he != NULL, NULL);
56
57   for (i = 0; he->h_addr_list[i]; i++)
58     {
59       GInetAddress *address = NULL;
60
61       if (he->h_addrtype == AF_INET)
62         address = G_INET_ADDRESS (g_inet4_address_from_bytes ((guint8 *) he->h_addr_list[i]));
63       else if (he->h_addrtype == AF_INET6)
64         address = G_INET_ADDRESS (g_inet6_address_from_bytes ((guint8 *) he->h_addr_list[i]));
65
66       list = g_list_prepend (list, address);
67     }
68
69   return list;
70 }
71 #endif
72
73 #if defined(HAVE_GETADDRINFO)
74 static void
75 g_io_error_from_addrinfo (GError** error, int err)
76 {
77   GIOErrorEnum code = G_IO_ERROR_FAILED;
78   const gchar *message = NULL;
79
80   if (error == NULL)
81     return;
82
83   switch (err)
84     {
85       case EAI_NONAME:
86         code = G_IO_ERROR_RESOLVER_NOT_FOUND;
87         break;
88       case EAI_NODATA:
89         code = G_IO_ERROR_RESOLVER_NO_DATA;
90         break;
91       default:
92         g_warning ("unknown getaddrinfo() error code encountered");
93     }
94
95   if (message == NULL)
96     {
97       /* FIXME: is gai_strerror() thread-safe? */
98       message = gai_strerror (err);
99     }
100
101   *error = g_error_new_literal (G_IO_ERROR, code, message);
102 }
103 #endif
104
105 static GList *
106 g_resolver_get_host_by_name (GResolver *resolver, const gchar *hostname, GError **error)
107 {
108   GList *list = NULL;
109
110 #if defined(HAVE_GETADDRINFO)
111   {
112     struct addrinfo hints;
113     struct addrinfo *res = NULL, *i;
114     int rv;
115
116     memset (&hints, 0, sizeof (hints));
117     hints.ai_socktype = SOCK_STREAM;
118
119 #ifdef HAVE_GETADDRINFO_GLIB_MUTEX
120     G_LOCK (dnslock);
121 #endif
122
123     if ((rv = getaddrinfo (hostname, NULL, &hints, &res)))
124       g_io_error_from_addrinfo (error, rv);
125     else
126       for (i = res; i != NULL; i = i->ai_next)
127         {
128           if (i->ai_family == PF_INET)
129             list = g_list_prepend (list, g_inet4_address_from_bytes ((guint8 *) &(((struct sockaddr_in *) i->ai_addr)->sin_addr.s_addr)));
130           else if (i->ai_family == PF_INET6)
131             list = g_list_prepend (list, g_inet6_address_from_bytes ((guint8 *) &(((struct sockaddr_in *) i->ai_addr)->sin_addr.s_addr)));
132         }
133
134     if (res)
135       freeaddrinfo (res);
136
137 #ifdef HAVE_GETADDRINFO_GLIB_MUTEX
138     G_UNLOCK (dnslock);
139 #endif
140   }
141 #elif defined(HAVE_GETHOSTBYNAME_THREADSAFE)
142   {
143     struct hostent *he = gethostbyname (hostname);
144
145     if (!he)
146       g_io_error_from_errno (error);
147     else
148       list = hostent2list (he);
149   }
150 #elif defined(HAVE_GETHOSTBYNAME_R_GLIBC)
151   {
152     struct hostent result, *he;
153     gsize len = 1024;
154     gchar *buf = g_new (gchar, len);
155     gint rv, herr;
156
157     while ((rv = gethostbyname_r (hostname, &result, buf, len, &he, &herr)) == ERANGE)
158       {
159         len *= 2;
160         buf = g_renew (gchar, buf, len);
161       }
162
163     if (!rv)
164       list = hostent2list (he);
165
166     g_free (buf);
167   }
168 #elif defined(HAVE_GETHOSTBYNAME_R_SOLARIS)
169   {
170     struct hostent result, *he;
171     gsize len = 8192;
172     char *buf = NULL;
173
174     do
175       {
176         buf = g_renew (gchar, buf, len);
177         errno = 0;
178         he = gethostbyname_r (hostname, &result, buf, len, &h_errno);
179         len += 1024;
180       }
181     while (errno == ERANGE);
182
183     if (he)
184       list = hostent2list (&result);
185
186     g_free (buf);
187   }
188 #elif defined(HAVE_GETHOSTBYNAME_R_HPUX)
189   {
190     struct hostent he;
191     struct hostent_data buf;
192     int rv;
193
194     rv = gethostbyname_r (hostname, &he, &buf);
195
196     if (!rv)
197       list = hostent2list (&he);
198   }
199 #else
200   {
201     struct hostent *he;
202
203 #ifdef HAVE_GETHOSTBYNAME_R_GLIB_MUTEX
204     G_LOCK (dnslock);
205 #endif
206
207     he = gethostbyname (hostname);
208     list = hostent2list (he);
209
210 #ifdef HAVE_GETHOSTBYNAME_R_GLIB_MUTEX
211     G_UNLOCK (dnslock);
212 #endif
213   }
214 #endif
215
216   if (list)
217     list = g_list_reverse (list);
218
219   return list;
220 }
221
222 static void
223 g_resolver_class_init (GResolverClass *klass)
224 {
225   GObjectClass *gobject_class G_GNUC_UNUSED = G_OBJECT_CLASS (klass);
226 }
227
228 static void
229 g_resolver_init (GResolver *address)
230 {
231
232 }
233
234 typedef struct {
235   GList *list;
236   const gchar *host;
237 } ResolveListData;
238
239 static void
240 resolve_list_thread (GSimpleAsyncResult *res,
241                      GObject            *object,
242                      GCancellable       *cancellable)
243 {
244   ResolveListData *op;
245   GError *error = NULL;
246
247   op = g_simple_async_result_get_op_res_gpointer (res);
248
249   op->list = g_resolver_resolve_list (G_RESOLVER (object), op->host, cancellable, &error);
250
251   if (op->list == NULL)
252     {
253       g_simple_async_result_set_from_error (res, error);
254       g_error_free (error);
255     }
256 }
257
258 GInetAddress *
259 g_resolver_resolve (GResolver     *resolver,
260                     const char    *host,
261                     GCancellable  *cancellable,
262                     GError       **error)
263 {
264   GList *list;
265   GInetAddress *address;
266
267   list = g_resolver_get_host_by_name (resolver, host, error);
268
269   if (!list)
270     return NULL;
271
272   address = G_INET_ADDRESS (g_object_ref (g_list_first (list)->data));
273
274   g_list_foreach (list, (GFunc) g_object_unref, NULL);
275
276   g_list_free (list);
277
278   return address;
279 }
280
281 void
282 g_resolver_resolve_async (GResolver           *resolver,
283                           const char          *host,
284                           GCancellable        *cancellable,
285                           GAsyncReadyCallback  callback,
286                           gpointer             user_data)
287 {
288   GSimpleAsyncResult *res;
289   ResolveListData *op;
290
291   op = g_new (ResolveListData, 1);
292
293   res = g_simple_async_result_new (G_OBJECT (resolver), callback, user_data, g_resolver_resolve_list_async);
294
295   g_simple_async_result_set_op_res_gpointer (res, op, g_free);
296
297   op->host = host;
298
299   g_simple_async_result_run_in_thread (res, resolve_list_thread, G_PRIORITY_DEFAULT, cancellable);
300
301   g_object_unref (res);
302 }
303
304 GInetAddress *
305 g_resolver_resolve_finish (GResolver     *resolver,
306                            GAsyncResult  *result,
307                            GError       **error)
308 {
309   GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (result);
310   ResolveListData *op;
311   GInetAddress *address;
312
313   g_warn_if_fail (g_simple_async_result_get_source_tag (res) == g_resolver_resolve_list_async);
314
315   op = g_simple_async_result_get_op_res_gpointer (res);
316
317   g_simple_async_result_propagate_error (res, error);
318
319   if (op->list == NULL)
320     return NULL;
321
322   address = G_INET_ADDRESS (g_object_ref (g_list_first (op->list)->data));
323
324   g_list_foreach (op->list, (GFunc) g_object_unref, NULL);
325
326   g_list_free (op->list);
327
328   return address;
329 }
330
331 GList *
332 g_resolver_resolve_list (GResolver     *resolver,
333                          const char    *host,
334                          GCancellable  *cancellable,
335                          GError       **error)
336 {
337   return g_resolver_get_host_by_name (resolver, host, error);
338 }
339
340 void
341 g_resolver_resolve_list_async (GResolver           *resolver,
342                                const char          *host,
343                                GCancellable        *cancellable,
344                                GAsyncReadyCallback  callback,
345                                gpointer             user_data)
346 {
347   GSimpleAsyncResult *res;
348   ResolveListData *op;
349
350   op = g_new (ResolveListData, 1);
351
352   res = g_simple_async_result_new (G_OBJECT (resolver), callback, user_data, g_resolver_resolve_list_async);
353
354   g_simple_async_result_set_op_res_gpointer (res, op, g_free);
355
356   op->host = host;
357
358   g_simple_async_result_run_in_thread (res, resolve_list_thread, G_PRIORITY_DEFAULT, cancellable);
359
360   g_object_unref (res);
361 }
362
363 GList *
364 g_resolver_resolve_list_finish (GResolver     *resolver,
365                                 GAsyncResult  *result,
366                                 GError       **error)
367 {
368   GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (result);
369   ResolveListData *op;
370
371   g_warn_if_fail (g_simple_async_result_get_source_tag (res) == g_resolver_resolve_list_async);
372
373   op = g_simple_async_result_get_op_res_gpointer (res);
374
375   g_simple_async_result_propagate_error (res, error);
376
377   return op->list;
378 }
379