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) and then
88 * immediately completes the async_result (since
89 * g_unix_resolver_watch() is already run from main-loop
90 * time.) After completing the async_result, it unrefs it,
91 * causing the req to be freed as well.
93 * b. The resolution is cancelled: request_cancelled() calls
94 * _g_asyncns_cancel() to cancel the resolution. Then it calls
95 * g_unix_resolver_request_complete(), which detaches the
96 * signal handler, and queues async_result to complete in an
97 * idle handler. It then unrefs the async_result to ensure
98 * that after its callback runs, it will be destroyed, in turn
99 * causing the req to be freed. Because the asyncns resolution
100 * was cancelled, g_unix_resolver_watch() will never be
101 * triggered for this req.
103 * Since there's only a single thread, it's not possible for the
104 * request to both complete and be cancelled "at the same time",
105 * and each of the two possibilities takes steps to block the other
106 * from being able to happen later, so it's always safe to free req
107 * after the async_result completes.
110 typedef struct _GUnixResolverRequest GUnixResolverRequest;
111 typedef void (*GUnixResolverFreeFunc) (GUnixResolverRequest *);
113 struct _GUnixResolverRequest {
116 _g_asyncns_query_t *qy;
119 GInetAddress *address;
122 GUnixResolverFreeFunc free_func;
124 GCancellable *cancellable;
125 GSimpleAsyncResult *async_result;
129 static void g_unix_resolver_request_free (GUnixResolverRequest *req);
130 static void request_cancelled (GCancellable *cancellable,
133 static GUnixResolverRequest *
134 g_unix_resolver_request_new (GUnixResolver *gur,
135 _g_asyncns_query_t *qy,
136 GUnixResolverFreeFunc free_func,
137 GCancellable *cancellable,
138 GSimpleAsyncResult *async_result)
140 GUnixResolverRequest *req;
142 req = g_slice_new0 (GUnixResolverRequest);
143 req->gur = g_object_ref (gur);
145 req->free_func = free_func;
149 req->cancellable = g_object_ref (cancellable);
150 g_signal_connect (cancellable, "cancelled",
151 G_CALLBACK (request_cancelled), req);
154 req->async_result = g_object_ref (async_result);
156 g_simple_async_result_set_op_res_gpointer (req->async_result, req, (GDestroyNotify)g_unix_resolver_request_free);
162 g_unix_resolver_request_free (GUnixResolverRequest *req)
164 /* If the user didn't call _finish the qy will still be around. */
166 _g_asyncns_cancel (req->gur->asyncns, req->qy);
168 /* We don't have to free req->cancellable and req->async_result,
169 * since they must already have been freed if we're here.
172 g_slice_free (GUnixResolverRequest, req);
176 g_unix_resolver_request_complete (GUnixResolverRequest *req,
179 if (req->cancellable)
181 g_signal_handlers_disconnect_by_func (req->cancellable, request_cancelled, req);
182 g_object_unref (req->cancellable);
183 req->cancellable = NULL;
187 g_simple_async_result_complete_in_idle (req->async_result);
189 g_simple_async_result_complete (req->async_result);
191 /* If we completed_in_idle, that will have taken an extra ref on
192 * req->async_result; if not, then we're already done. Either way we
193 * need to unref the async_result to make sure it eventually is
194 * destroyed, causing req to be freed.
196 g_object_unref (req->async_result);
200 request_cancelled (GCancellable *cancellable,
203 GUnixResolverRequest *req = user_data;
204 GError *error = NULL;
206 _g_asyncns_cancel (req->gur->asyncns, req->qy);
209 g_cancellable_set_error_if_cancelled (cancellable, &error);
210 g_simple_async_result_set_from_error (req->async_result, error);
211 g_error_free (error);
213 g_unix_resolver_request_complete (req, TRUE);
217 g_unix_resolver_watch (GIOChannel *iochannel,
218 GIOCondition condition,
221 GUnixResolver *gur = user_data;
222 _g_asyncns_query_t *qy;
223 GUnixResolverRequest *req;
225 if (condition & (G_IO_HUP | G_IO_ERR))
227 /* Shouldn't happen. Should we create a new asyncns? FIXME */
228 g_warning ("asyncns died");
233 while (_g_asyncns_wait (gur->asyncns, FALSE) == 0 &&
234 (qy = _g_asyncns_getnext (gur->asyncns)) != NULL)
236 req = _g_asyncns_getuserdata (gur->asyncns, qy);
237 g_unix_resolver_request_complete (req, FALSE);
243 static GUnixResolverRequest *
244 resolve_async (GUnixResolver *gur,
245 _g_asyncns_query_t *qy,
246 GUnixResolverFreeFunc free_func,
247 GCancellable *cancellable,
248 GAsyncReadyCallback callback,
252 GSimpleAsyncResult *result;
253 GUnixResolverRequest *req;
255 result = g_simple_async_result_new (G_OBJECT (gur), callback, user_data, tag);
256 req = g_unix_resolver_request_new (gur, qy, free_func, cancellable, result);
257 g_object_unref (result);
258 _g_asyncns_setuserdata (gur->asyncns, qy, req);
264 lookup_by_name_free (GUnixResolverRequest *req)
266 g_free (req->u.hostname);
270 lookup_by_name_async (GResolver *resolver,
271 const gchar *hostname,
272 GCancellable *cancellable,
273 GAsyncReadyCallback callback,
276 GUnixResolver *gur = G_UNIX_RESOLVER (resolver);
277 GUnixResolverRequest *req;
278 _g_asyncns_query_t *qy;
280 qy = _g_asyncns_getaddrinfo (gur->asyncns, hostname, NULL,
281 &_g_resolver_addrinfo_hints);
282 req = resolve_async (gur, qy, lookup_by_name_free, cancellable,
283 callback, user_data, lookup_by_name_async);
284 req->u.hostname = g_strdup (hostname);
288 lookup_by_name_finish (GResolver *resolver,
289 GAsyncResult *result,
292 GSimpleAsyncResult *simple;
293 GUnixResolverRequest *req;
294 struct addrinfo *res;
298 g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (resolver), lookup_by_name_async), FALSE);
299 simple = G_SIMPLE_ASYNC_RESULT (result);
301 req = g_simple_async_result_get_op_res_gpointer (simple);
302 retval = _g_asyncns_getaddrinfo_done (req->gur->asyncns, req->qy, &res);
304 addresses = _g_resolver_addresses_from_addrinfo (req->u.hostname, res, retval, error);
313 lookup_by_address_free (GUnixResolverRequest *req)
315 g_object_unref (req->u.address);
319 lookup_by_address_async (GResolver *resolver,
320 GInetAddress *address,
321 GCancellable *cancellable,
322 GAsyncReadyCallback callback,
325 GUnixResolver *gur = G_UNIX_RESOLVER (resolver);
326 GUnixResolverRequest *req;
327 _g_asyncns_query_t *qy;
328 struct sockaddr_storage sockaddr;
331 _g_resolver_address_to_sockaddr (address, &sockaddr, &sockaddr_size);
332 qy = _g_asyncns_getnameinfo (gur->asyncns,
333 (struct sockaddr *)&sockaddr, sockaddr_size,
334 NI_NAMEREQD, TRUE, FALSE);
335 req = resolve_async (gur, qy, lookup_by_address_free, cancellable,
336 callback, user_data, lookup_by_address_async);
337 req->u.address = g_object_ref (address);
341 lookup_by_address_finish (GResolver *resolver,
342 GAsyncResult *result,
345 GSimpleAsyncResult *simple;
346 GUnixResolverRequest *req;
347 gchar host[NI_MAXHOST], *name;
350 g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (resolver), lookup_by_address_async), FALSE);
351 simple = G_SIMPLE_ASYNC_RESULT (result);
353 req = g_simple_async_result_get_op_res_gpointer (simple);
354 retval = _g_asyncns_getnameinfo_done (req->gur->asyncns, req->qy,
355 host, sizeof (host), NULL, 0);
357 name = _g_resolver_name_from_nameinfo (req->u.address, host, retval, error);
364 lookup_service_free (GUnixResolverRequest *req)
366 g_free (req->u.service);
370 lookup_service_async (GResolver *resolver,
372 GCancellable *cancellable,
373 GAsyncReadyCallback callback,
376 GUnixResolver *gur = G_UNIX_RESOLVER (resolver);
377 GUnixResolverRequest *req;
378 _g_asyncns_query_t *qy;
380 qy = _g_asyncns_res_query (gur->asyncns, rrname, C_IN, T_SRV);
381 req = resolve_async (gur, qy, lookup_service_free, cancellable,
382 callback, user_data, lookup_service_async);
383 req->u.service = g_strdup (rrname);
387 lookup_service_finish (GResolver *resolver,
388 GAsyncResult *result,
391 GSimpleAsyncResult *simple;
392 GUnixResolverRequest *req;
397 g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (resolver), lookup_service_async), FALSE);
398 simple = G_SIMPLE_ASYNC_RESULT (result);
400 req = g_simple_async_result_get_op_res_gpointer (simple);
401 len = _g_asyncns_res_done (req->gur->asyncns, req->qy, &answer);
408 targets = _g_resolver_targets_from_res_query (req->u.service, answer, len, herr, error);
409 _g_asyncns_freeanswer (answer);
416 g_unix_resolver_class_init (GUnixResolverClass *unix_class)
418 GResolverClass *resolver_class = G_RESOLVER_CLASS (unix_class);
419 GObjectClass *object_class = G_OBJECT_CLASS (unix_class);
421 resolver_class->lookup_by_name_async = lookup_by_name_async;
422 resolver_class->lookup_by_name_finish = lookup_by_name_finish;
423 resolver_class->lookup_by_address_async = lookup_by_address_async;
424 resolver_class->lookup_by_address_finish = lookup_by_address_finish;
425 resolver_class->lookup_service_async = lookup_service_async;
426 resolver_class->lookup_service_finish = lookup_service_finish;
428 object_class->finalize = g_unix_resolver_finalize;
431 #define __G_UNIX_RESOLVER_C__
432 #include "gioaliasdef.c"