1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
3 /* GIO - GLib Input, Output and Streaming Library
5 * Copyright (C) 2008 Red Hat, Inc.
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General
18 * Public License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
20 * Boston, MA 02111-1307, USA.
30 #include "gunixresolver.h"
31 #include "gnetworkingprivate.h"
33 #include "gcancellable.h"
34 #include "gsimpleasyncresult.h"
35 #include "gsocketaddress.h"
39 G_DEFINE_TYPE (GUnixResolver, g_unix_resolver, G_TYPE_THREADED_RESOLVER)
41 static gboolean g_unix_resolver_watch (GIOChannel *iochannel,
42 GIOCondition condition,
46 g_unix_resolver_init (GUnixResolver *gur)
51 /* FIXME: how many workers? */
52 gur->asyncns = _g_asyncns_new (2);
54 fd = _g_asyncns_fd (gur->asyncns);
55 io = g_io_channel_unix_new (fd);
56 gur->watch = g_io_add_watch (io, G_IO_IN | G_IO_HUP | G_IO_ERR,
57 g_unix_resolver_watch, gur);
58 g_io_channel_unref (io);
62 g_unix_resolver_finalize (GObject *object)
64 GUnixResolver *gur = G_UNIX_RESOLVER (object);
67 g_source_remove (gur->watch);
68 _g_asyncns_free (gur->asyncns);
70 G_OBJECT_CLASS (g_unix_resolver_parent_class)->finalize (object);
73 /* The various request possibilities:
75 * 1. Synchronous: handed off to the base class (GThreadedResolver);
76 * since it's never possible to cancel a synchronous request in a
77 * single-threaded program, the request is done in the calling
80 * 2. Asynchronous: An appropriate _g_asyncns_query_t is created, and
81 * then a GUnixResolverRequest is created with that query and a
82 * GSimpleAsyncResult. Two sub-possibilities:
84 * a. The resolution completes: g_unix_resolver_watch() sees that
85 * the request has completed, and calls
86 * g_unix_resolver_request_complete(), which detaches the
87 * "cancelled" signal handler (if it was present), queues the
88 * async_result to be completed, and then unrefs it.
90 * b. The resolution is cancelled: request_cancelled() calls
91 * _g_asyncns_cancel() to cancel the resolution. Then it calls
92 * g_unix_resolver_request_complete(), which detaches the
93 * signal handler, and queues async_result to complete in an
94 * idle handler. Because the asyncns resolution was cancelled,
95 * g_unix_resolver_watch() will never be triggered for this
98 * Since there's only a single thread, it's not possible for the
99 * request to both complete and be cancelled "at the same time",
100 * and each of the two possibilities takes steps to block the other
101 * from being able to happen later, so it's always safe to free req
102 * after the async_result completes.
105 typedef struct _GUnixResolverRequest GUnixResolverRequest;
106 typedef void (*GUnixResolverFunc) (GUnixResolverRequest *);
108 struct _GUnixResolverRequest {
111 _g_asyncns_query_t *qy;
119 GInetAddress *address;
128 GUnixResolverFunc process_func, free_func;
130 GCancellable *cancellable;
131 GSimpleAsyncResult *async_result;
135 static void g_unix_resolver_request_free (GUnixResolverRequest *req);
136 static void request_cancelled (GCancellable *cancellable,
139 static GUnixResolverRequest *
140 g_unix_resolver_request_new (GUnixResolver *gur,
141 _g_asyncns_query_t *qy,
142 GUnixResolverFunc process_func,
143 GUnixResolverFunc free_func,
144 GCancellable *cancellable,
145 GSimpleAsyncResult *async_result)
147 GUnixResolverRequest *req;
149 req = g_slice_new0 (GUnixResolverRequest);
150 req->gur = g_object_ref (gur);
152 req->process_func = process_func;
153 req->free_func = free_func;
157 req->cancellable = g_object_ref (cancellable);
158 g_signal_connect (cancellable, "cancelled",
159 G_CALLBACK (request_cancelled), req);
162 req->async_result = g_object_ref (async_result);
164 g_simple_async_result_set_op_res_gpointer (req->async_result, req, (GDestroyNotify)g_unix_resolver_request_free);
170 g_unix_resolver_request_free (GUnixResolverRequest *req)
172 req->free_func (req);
173 g_object_unref (req->gur);
175 /* We don't have to free req->cancellable and req->async_result,
176 * since they must already have been freed if we're here.
179 g_slice_free (GUnixResolverRequest, req);
183 g_unix_resolver_request_complete (GUnixResolverRequest *req)
185 if (req->cancellable)
187 g_signal_handlers_disconnect_by_func (req->cancellable, request_cancelled, req);
188 g_object_unref (req->cancellable);
189 req->cancellable = NULL;
192 /* We always complete_in_idle, even if we were called from
193 * g_unix_resolver_watch(), since we might have been started under a
194 * non-default g_main_context_get_thread_default().
196 g_simple_async_result_complete_in_idle (req->async_result);
197 g_object_unref (req->async_result);
201 request_cancelled (GCancellable *cancellable,
204 GUnixResolverRequest *req = user_data;
205 GError *error = NULL;
207 _g_asyncns_cancel (req->gur->asyncns, req->qy);
210 g_cancellable_set_error_if_cancelled (cancellable, &error);
211 g_simple_async_result_set_from_error (req->async_result, error);
212 g_error_free (error);
214 g_unix_resolver_request_complete (req);
218 g_unix_resolver_watch (GIOChannel *iochannel,
219 GIOCondition condition,
222 GUnixResolver *gur = user_data;
223 _g_asyncns_query_t *qy;
224 GUnixResolverRequest *req;
226 if (condition & (G_IO_HUP | G_IO_ERR))
228 /* Shouldn't happen. Should we create a new asyncns? FIXME */
229 g_warning ("asyncns died");
234 while (_g_asyncns_wait (gur->asyncns, FALSE) == 0 &&
235 (qy = _g_asyncns_getnext (gur->asyncns)) != NULL)
237 req = _g_asyncns_getuserdata (gur->asyncns, qy);
238 req->process_func (req);
239 g_unix_resolver_request_complete (req);
245 static GUnixResolverRequest *
246 resolve_async (GUnixResolver *gur,
247 _g_asyncns_query_t *qy,
248 GUnixResolverFunc process_func,
249 GUnixResolverFunc free_func,
250 GCancellable *cancellable,
251 GAsyncReadyCallback callback,
255 GSimpleAsyncResult *result;
256 GUnixResolverRequest *req;
258 result = g_simple_async_result_new (G_OBJECT (gur), callback, user_data, tag);
259 req = g_unix_resolver_request_new (gur, qy, process_func, free_func,
260 cancellable, result);
261 g_object_unref (result);
262 _g_asyncns_setuserdata (gur->asyncns, qy, req);
268 lookup_by_name_process (GUnixResolverRequest *req)
270 struct addrinfo *res;
272 GError *error = NULL;
274 retval = _g_asyncns_getaddrinfo_done (req->gur->asyncns, req->qy, &res);
275 req->u.name.addresses =
276 _g_resolver_addresses_from_addrinfo (req->u.name.hostname,
277 res, retval, &error);
283 g_simple_async_result_set_from_error (req->async_result, error);
284 g_error_free (error);
289 lookup_by_name_free (GUnixResolverRequest *req)
291 g_free (req->u.name.hostname);
292 if (req->u.name.addresses)
293 g_resolver_free_addresses (req->u.name.addresses);
297 lookup_by_name_async (GResolver *resolver,
298 const gchar *hostname,
299 GCancellable *cancellable,
300 GAsyncReadyCallback callback,
303 GUnixResolver *gur = G_UNIX_RESOLVER (resolver);
304 GUnixResolverRequest *req;
305 _g_asyncns_query_t *qy;
307 qy = _g_asyncns_getaddrinfo (gur->asyncns, hostname, NULL,
308 &_g_resolver_addrinfo_hints);
309 req = resolve_async (gur, qy, lookup_by_name_process, lookup_by_name_free,
310 cancellable, callback, user_data, lookup_by_name_async);
311 req->u.name.hostname = g_strdup (hostname);
315 lookup_by_name_finish (GResolver *resolver,
316 GAsyncResult *result,
319 GSimpleAsyncResult *simple;
320 GUnixResolverRequest *req;
323 g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (resolver), lookup_by_name_async), FALSE);
324 simple = G_SIMPLE_ASYNC_RESULT (result);
326 if (g_simple_async_result_propagate_error (simple, error))
329 req = g_simple_async_result_get_op_res_gpointer (simple);
330 addresses = req->u.name.addresses;
331 req->u.name.addresses = NULL;
338 lookup_by_address_process (GUnixResolverRequest *req)
340 gchar host[NI_MAXHOST];
342 GError *error = NULL;
344 retval = _g_asyncns_getnameinfo_done (req->gur->asyncns, req->qy,
345 host, sizeof (host), NULL, 0);
346 req->u.address.hostname =
347 _g_resolver_name_from_nameinfo (req->u.address.address,
348 host, retval, &error);
352 g_simple_async_result_set_from_error (req->async_result, error);
353 g_error_free (error);
358 lookup_by_address_free (GUnixResolverRequest *req)
360 g_object_unref (req->u.address.address);
361 if (req->u.address.hostname)
362 g_free (req->u.address.hostname);
366 lookup_by_address_async (GResolver *resolver,
367 GInetAddress *address,
368 GCancellable *cancellable,
369 GAsyncReadyCallback callback,
372 GUnixResolver *gur = G_UNIX_RESOLVER (resolver);
373 GUnixResolverRequest *req;
374 _g_asyncns_query_t *qy;
375 struct sockaddr_storage sockaddr;
378 _g_resolver_address_to_sockaddr (address, &sockaddr, &sockaddr_size);
379 qy = _g_asyncns_getnameinfo (gur->asyncns,
380 (struct sockaddr *)&sockaddr, sockaddr_size,
381 NI_NAMEREQD, TRUE, FALSE);
382 req = resolve_async (gur, qy, lookup_by_address_process,
383 lookup_by_address_free, cancellable,
384 callback, user_data, lookup_by_address_async);
385 req->u.address.address = g_object_ref (address);
389 lookup_by_address_finish (GResolver *resolver,
390 GAsyncResult *result,
393 GSimpleAsyncResult *simple;
394 GUnixResolverRequest *req;
397 g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (resolver), lookup_by_address_async), FALSE);
398 simple = G_SIMPLE_ASYNC_RESULT (result);
400 if (g_simple_async_result_propagate_error (simple, error))
403 req = g_simple_async_result_get_op_res_gpointer (simple);
404 name = req->u.address.hostname;
405 req->u.address.hostname = NULL;
412 lookup_service_process (GUnixResolverRequest *req)
416 GError *error = NULL;
418 len = _g_asyncns_res_done (req->gur->asyncns, req->qy, &answer);
424 req->u.service.targets =
425 _g_resolver_targets_from_res_query (req->u.service.service,
426 answer, len, herr, &error);
427 _g_asyncns_freeanswer (answer);
431 g_simple_async_result_set_from_error (req->async_result, error);
432 g_error_free (error);
437 lookup_service_free (GUnixResolverRequest *req)
439 g_free (req->u.service.service);
440 if (req->u.service.targets)
441 g_resolver_free_targets (req->u.service.targets);
445 lookup_service_async (GResolver *resolver,
447 GCancellable *cancellable,
448 GAsyncReadyCallback callback,
451 GUnixResolver *gur = G_UNIX_RESOLVER (resolver);
452 GUnixResolverRequest *req;
453 _g_asyncns_query_t *qy;
455 qy = _g_asyncns_res_query (gur->asyncns, rrname, C_IN, T_SRV);
456 req = resolve_async (gur, qy, lookup_service_process, lookup_service_free,
457 cancellable, callback, user_data, lookup_service_async);
458 req->u.service.service = g_strdup (rrname);
462 lookup_service_finish (GResolver *resolver,
463 GAsyncResult *result,
466 GSimpleAsyncResult *simple;
467 GUnixResolverRequest *req;
470 g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (resolver), lookup_service_async), FALSE);
471 simple = G_SIMPLE_ASYNC_RESULT (result);
473 if (g_simple_async_result_propagate_error (simple, error))
476 req = g_simple_async_result_get_op_res_gpointer (simple);
477 targets = req->u.service.targets;
478 req->u.service.targets = NULL;
485 g_unix_resolver_class_init (GUnixResolverClass *unix_class)
487 GResolverClass *resolver_class = G_RESOLVER_CLASS (unix_class);
488 GObjectClass *object_class = G_OBJECT_CLASS (unix_class);
490 resolver_class->lookup_by_name_async = lookup_by_name_async;
491 resolver_class->lookup_by_name_finish = lookup_by_name_finish;
492 resolver_class->lookup_by_address_async = lookup_by_address_async;
493 resolver_class->lookup_by_address_finish = lookup_by_address_finish;
494 resolver_class->lookup_service_async = lookup_service_async;
495 resolver_class->lookup_service_finish = lookup_service_finish;
497 object_class->finalize = g_unix_resolver_finalize;
500 #define __G_UNIX_RESOLVER_C__
501 #include "gioaliasdef.c"