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.
31 #include "gunixresolver.h"
32 #include "gnetworkingprivate.h"
34 #include "gcancellable.h"
35 #include "gsimpleasyncresult.h"
36 #include "gsocketaddress.h"
40 G_DEFINE_TYPE (GUnixResolver, g_unix_resolver, G_TYPE_THREADED_RESOLVER)
42 static gboolean g_unix_resolver_watch (GIOChannel *iochannel,
43 GIOCondition condition,
47 g_unix_resolver_init (GUnixResolver *gur)
52 /* FIXME: how many workers? */
53 gur->asyncns = _g_asyncns_new (2);
55 fd = _g_asyncns_fd (gur->asyncns);
56 io = g_io_channel_unix_new (fd);
57 gur->watch = g_io_add_watch (io, G_IO_IN | G_IO_HUP | G_IO_ERR,
58 g_unix_resolver_watch, gur);
59 g_io_channel_unref (io);
63 g_unix_resolver_finalize (GObject *object)
65 GUnixResolver *gur = G_UNIX_RESOLVER (object);
68 g_source_remove (gur->watch);
69 _g_asyncns_free (gur->asyncns);
71 G_OBJECT_CLASS (g_unix_resolver_parent_class)->finalize (object);
74 /* The various request possibilities:
76 * 1. Synchronous: handed off to the base class (GThreadedResolver);
77 * since it's never possible to cancel a synchronous request in a
78 * single-threaded program, the request is done in the calling
81 * 2. Asynchronous: An appropriate _g_asyncns_query_t is created, and
82 * then a GUnixResolverRequest is created with that query and a
83 * GSimpleAsyncResult. Two sub-possibilities:
85 * a. The resolution completes: g_unix_resolver_watch() sees that
86 * the request has completed, and calls
87 * g_unix_resolver_request_complete(), which detaches the
88 * "cancelled" signal handler (if it was present) and then
89 * immediately completes the async_result (since
90 * g_unix_resolver_watch() is already run from main-loop
91 * time.) After completing the async_result, it unrefs it,
92 * causing the req to be freed as well.
94 * b. The resolution is cancelled: request_cancelled() calls
95 * _g_asyncns_cancel() to cancel the resolution. Then it calls
96 * g_unix_resolver_request_complete(), which detaches the
97 * signal handler, and queues async_result to complete in an
98 * idle handler. It then unrefs the async_result to ensure
99 * that after its callback runs, it will be destroyed, in turn
100 * causing the req to be freed. Because the asyncns resolution
101 * was cancelled, g_unix_resolver_watch() will never be
102 * triggered for this req.
104 * Since there's only a single thread, it's not possible for the
105 * request to both complete and be cancelled "at the same time",
106 * and each of the two possibilities takes steps to block the other
107 * from being able to happen later, so it's always safe to free req
108 * after the async_result completes.
111 typedef struct _GUnixResolverRequest GUnixResolverRequest;
112 typedef void (*GUnixResolverFreeFunc) (GUnixResolverRequest *);
114 struct _GUnixResolverRequest {
117 _g_asyncns_query_t *qy;
120 GInetAddress *address;
123 GUnixResolverFreeFunc free_func;
125 GCancellable *cancellable;
126 GSimpleAsyncResult *async_result;
130 static void g_unix_resolver_request_free (GUnixResolverRequest *req);
131 static void request_cancelled (GCancellable *cancellable,
134 static GUnixResolverRequest *
135 g_unix_resolver_request_new (GUnixResolver *gur,
136 _g_asyncns_query_t *qy,
137 GUnixResolverFreeFunc free_func,
138 GCancellable *cancellable,
139 GSimpleAsyncResult *async_result)
141 GUnixResolverRequest *req;
143 req = g_slice_new0 (GUnixResolverRequest);
144 req->gur = g_object_ref (gur);
146 req->free_func = free_func;
150 req->cancellable = g_object_ref (cancellable);
151 g_signal_connect (cancellable, "cancelled",
152 G_CALLBACK (request_cancelled), req);
155 req->async_result = g_object_ref (async_result);
157 g_simple_async_result_set_op_res_gpointer (req->async_result, req, (GDestroyNotify)g_unix_resolver_request_free);
163 g_unix_resolver_request_free (GUnixResolverRequest *req)
165 /* If the user didn't call _finish the qy will still be around. */
167 _g_asyncns_cancel (req->gur->asyncns, req->qy);
169 /* We don't have to free req->cancellable and req->async_result,
170 * since they must already have been freed if we're here.
173 g_slice_free (GUnixResolverRequest, req);
177 g_unix_resolver_request_complete (GUnixResolverRequest *req,
180 if (req->cancellable)
182 g_signal_handlers_disconnect_by_func (req->cancellable, request_cancelled, req);
183 g_object_unref (req->cancellable);
184 req->cancellable = NULL;
188 g_simple_async_result_complete_in_idle (req->async_result);
190 g_simple_async_result_complete (req->async_result);
192 /* If we completed_in_idle, that will have taken an extra ref on
193 * req->async_result; if not, then we're already done. Either way we
194 * need to unref the async_result to make sure it eventually is
195 * destroyed, causing req to be freed.
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, TRUE);
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 g_unix_resolver_request_complete (req, FALSE);
244 static GUnixResolverRequest *
245 resolve_async (GUnixResolver *gur,
246 _g_asyncns_query_t *qy,
247 GUnixResolverFreeFunc free_func,
248 GCancellable *cancellable,
249 GAsyncReadyCallback callback,
253 GSimpleAsyncResult *result;
254 GUnixResolverRequest *req;
256 result = g_simple_async_result_new (G_OBJECT (gur), callback, user_data, tag);
257 req = g_unix_resolver_request_new (gur, qy, free_func, cancellable, result);
258 g_object_unref (result);
259 _g_asyncns_setuserdata (gur->asyncns, qy, req);
265 lookup_by_name_free (GUnixResolverRequest *req)
267 g_free (req->u.hostname);
271 lookup_by_name_async (GResolver *resolver,
272 const gchar *hostname,
273 GCancellable *cancellable,
274 GAsyncReadyCallback callback,
277 GUnixResolver *gur = G_UNIX_RESOLVER (resolver);
278 GUnixResolverRequest *req;
279 _g_asyncns_query_t *qy;
281 qy = _g_asyncns_getaddrinfo (gur->asyncns, hostname, NULL,
282 &_g_resolver_addrinfo_hints);
283 req = resolve_async (gur, qy, lookup_by_name_free, cancellable,
284 callback, user_data, lookup_by_name_async);
285 req->u.hostname = g_strdup (hostname);
289 lookup_by_name_finish (GResolver *resolver,
290 GAsyncResult *result,
293 GSimpleAsyncResult *simple;
294 GUnixResolverRequest *req;
295 struct addrinfo *res;
299 g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (resolver), lookup_by_name_async), FALSE);
300 simple = G_SIMPLE_ASYNC_RESULT (result);
302 req = g_simple_async_result_get_op_res_gpointer (simple);
303 retval = _g_asyncns_getaddrinfo_done (req->gur->asyncns, req->qy, &res);
305 addresses = _g_resolver_addresses_from_addrinfo (req->u.hostname, res, retval, error);
314 lookup_by_address_free (GUnixResolverRequest *req)
316 g_object_unref (req->u.address);
320 lookup_by_address_async (GResolver *resolver,
321 GInetAddress *address,
322 GCancellable *cancellable,
323 GAsyncReadyCallback callback,
326 GUnixResolver *gur = G_UNIX_RESOLVER (resolver);
327 GUnixResolverRequest *req;
328 _g_asyncns_query_t *qy;
329 struct sockaddr_storage sockaddr;
332 _g_resolver_address_to_sockaddr (address, &sockaddr, &sockaddr_size);
333 qy = _g_asyncns_getnameinfo (gur->asyncns,
334 (struct sockaddr *)&sockaddr, sockaddr_size,
335 NI_NAMEREQD, TRUE, FALSE);
336 req = resolve_async (gur, qy, lookup_by_address_free, cancellable,
337 callback, user_data, lookup_by_address_async);
338 req->u.address = g_object_ref (address);
342 lookup_by_address_finish (GResolver *resolver,
343 GAsyncResult *result,
346 GSimpleAsyncResult *simple;
347 GUnixResolverRequest *req;
348 gchar host[NI_MAXHOST], *name;
351 g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (resolver), lookup_by_address_async), FALSE);
352 simple = G_SIMPLE_ASYNC_RESULT (result);
354 req = g_simple_async_result_get_op_res_gpointer (simple);
355 retval = _g_asyncns_getnameinfo_done (req->gur->asyncns, req->qy,
356 host, sizeof (host), NULL, 0);
358 name = _g_resolver_name_from_nameinfo (req->u.address, host, retval, error);
365 lookup_service_free (GUnixResolverRequest *req)
367 g_free (req->u.service);
371 lookup_service_async (GResolver *resolver,
373 GCancellable *cancellable,
374 GAsyncReadyCallback callback,
377 GUnixResolver *gur = G_UNIX_RESOLVER (resolver);
378 GUnixResolverRequest *req;
379 _g_asyncns_query_t *qy;
381 qy = _g_asyncns_res_query (gur->asyncns, rrname, C_IN, T_SRV);
382 req = resolve_async (gur, qy, lookup_service_free, cancellable,
383 callback, user_data, lookup_service_async);
384 req->u.service = g_strdup (rrname);
388 lookup_service_finish (GResolver *resolver,
389 GAsyncResult *result,
392 GSimpleAsyncResult *simple;
393 GUnixResolverRequest *req;
398 g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (resolver), lookup_service_async), FALSE);
399 simple = G_SIMPLE_ASYNC_RESULT (result);
401 req = g_simple_async_result_get_op_res_gpointer (simple);
402 len = _g_asyncns_res_done (req->gur->asyncns, req->qy, &answer);
409 targets = _g_resolver_targets_from_res_query (req->u.service, answer, len, herr, error);
410 _g_asyncns_freeanswer (answer);
417 g_unix_resolver_class_init (GUnixResolverClass *unix_class)
419 GResolverClass *resolver_class = G_RESOLVER_CLASS (unix_class);
420 GObjectClass *object_class = G_OBJECT_CLASS (unix_class);
422 resolver_class->lookup_by_name_async = lookup_by_name_async;
423 resolver_class->lookup_by_name_finish = lookup_by_name_finish;
424 resolver_class->lookup_by_address_async = lookup_by_address_async;
425 resolver_class->lookup_by_address_finish = lookup_by_address_finish;
426 resolver_class->lookup_service_async = lookup_service_async;
427 resolver_class->lookup_service_finish = lookup_service_finish;
429 object_class->finalize = g_unix_resolver_finalize;
432 #define __G_UNIX_RESOLVER_C__
433 #include "gioaliasdef.c"