Move some uri functions to a better place
[platform/upstream/glib.git] / gio / gunixresolver.c
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2
3 /* GIO - GLib Input, Output and Streaming Library
4  *
5  * Copyright (C) 2008 Red Hat, Inc.
6  *
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.
11  *
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.
16  *
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.
21  */
22
23 #include "config.h"
24 #include <glib.h>
25 #include "glibintl.h"
26
27 #include <stdio.h>
28 #include <string.h>
29
30 #include "gunixresolver.h"
31 #include "gnetworkingprivate.h"
32
33 #include "gcancellable.h"
34 #include "gsimpleasyncresult.h"
35 #include "gsocketaddress.h"
36
37 #include "gioalias.h"
38
39 G_DEFINE_TYPE (GUnixResolver, g_unix_resolver, G_TYPE_THREADED_RESOLVER)
40
41 static gboolean g_unix_resolver_watch (GIOChannel   *iochannel,
42                                        GIOCondition  condition,
43                                        gpointer      user_data);
44
45 static void
46 g_unix_resolver_init (GUnixResolver *gur)
47 {
48   gint fd;
49   GIOChannel *io;
50
51   /* FIXME: how many workers? */
52   gur->asyncns = _g_asyncns_new (2);
53
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);
59 }
60
61 static void
62 g_unix_resolver_finalize (GObject *object)
63 {
64   GUnixResolver *gur = G_UNIX_RESOLVER (object);
65
66   if (gur->watch)
67     g_source_remove (gur->watch);
68   _g_asyncns_free (gur->asyncns);
69
70   G_OBJECT_CLASS (g_unix_resolver_parent_class)->finalize (object);
71 }
72
73 /* The various request possibilities:
74  *
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
78  *    thread.
79  *
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:
83  *
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.
92  *
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.
102  *
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.
108  */
109
110 typedef struct _GUnixResolverRequest GUnixResolverRequest;
111 typedef void (*GUnixResolverFreeFunc) (GUnixResolverRequest *);
112
113 struct _GUnixResolverRequest {
114   GUnixResolver *gur;
115
116   _g_asyncns_query_t *qy;
117   union {
118     gchar *hostname;
119     GInetAddress *address;
120     gchar *service;
121   } u;
122   GUnixResolverFreeFunc free_func;
123
124   GCancellable *cancellable;
125   GSimpleAsyncResult *async_result;
126
127 };
128
129 static void g_unix_resolver_request_free (GUnixResolverRequest *req);
130 static void request_cancelled (GCancellable *cancellable,
131                                gpointer      user_data);
132
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)
139 {
140   GUnixResolverRequest *req;
141
142   req = g_slice_new0 (GUnixResolverRequest);
143   req->gur = g_object_ref (gur);
144   req->qy = qy;
145   req->free_func = free_func;
146
147   if (cancellable)
148     {
149       req->cancellable = g_object_ref (cancellable);
150       g_signal_connect (cancellable, "cancelled",
151                         G_CALLBACK (request_cancelled), req);
152     }
153
154   req->async_result = g_object_ref (async_result);
155
156   g_simple_async_result_set_op_res_gpointer (req->async_result, req, (GDestroyNotify)g_unix_resolver_request_free);
157
158   return req;
159 }
160
161 static void
162 g_unix_resolver_request_free (GUnixResolverRequest *req)
163 {
164   /* If the user didn't call _finish the qy will still be around. */
165   if (req->qy)
166     _g_asyncns_cancel (req->gur->asyncns, req->qy);
167
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.
170    */
171
172   g_slice_free (GUnixResolverRequest, req);
173 }
174
175 static void
176 g_unix_resolver_request_complete (GUnixResolverRequest *req,
177                                   gboolean              need_idle)
178 {
179   if (req->cancellable)
180     {
181       g_signal_handlers_disconnect_by_func (req->cancellable, request_cancelled, req);
182       g_object_unref (req->cancellable);
183       req->cancellable = NULL;
184     }
185
186   if (need_idle)
187     g_simple_async_result_complete_in_idle (req->async_result);
188   else
189     g_simple_async_result_complete (req->async_result);
190
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.
195    */
196   g_object_unref (req->async_result);
197 }
198
199 static void
200 request_cancelled (GCancellable *cancellable,
201                    gpointer      user_data)
202 {
203   GUnixResolverRequest *req = user_data;
204   GError *error = NULL;
205
206   _g_asyncns_cancel (req->gur->asyncns, req->qy);
207   req->qy = NULL;
208
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);
212
213   g_unix_resolver_request_complete (req, TRUE);
214 }
215
216 static gboolean
217 g_unix_resolver_watch (GIOChannel   *iochannel,
218                        GIOCondition  condition,
219                        gpointer      user_data)
220 {
221   GUnixResolver *gur = user_data;
222   _g_asyncns_query_t *qy;
223   GUnixResolverRequest *req;
224
225   if (condition & (G_IO_HUP | G_IO_ERR))
226     {
227       /* Shouldn't happen. Should we create a new asyncns? FIXME */
228       g_warning ("asyncns died");
229       gur->watch = 0;
230       return FALSE;
231     }
232
233   while (_g_asyncns_wait (gur->asyncns, FALSE) == 0 &&
234          (qy = _g_asyncns_getnext (gur->asyncns)) != NULL)
235     {
236       req = _g_asyncns_getuserdata (gur->asyncns, qy);
237       g_unix_resolver_request_complete (req, FALSE);
238     }
239
240   return TRUE;
241 }
242
243 static GUnixResolverRequest *
244 resolve_async (GUnixResolver         *gur,
245                _g_asyncns_query_t    *qy,
246                GUnixResolverFreeFunc  free_func,
247                GCancellable          *cancellable,
248                GAsyncReadyCallback    callback,
249                gpointer               user_data,
250                gpointer               tag)
251 {
252   GSimpleAsyncResult *result;
253   GUnixResolverRequest *req;
254
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);
259
260   return req;
261 }
262
263 static void
264 lookup_by_name_free (GUnixResolverRequest *req)
265 {
266   g_free (req->u.hostname);
267 }
268
269 static void
270 lookup_by_name_async (GResolver           *resolver,
271                       const gchar         *hostname,
272                       GCancellable        *cancellable,
273                       GAsyncReadyCallback  callback,
274                       gpointer             user_data)
275 {
276   GUnixResolver *gur = G_UNIX_RESOLVER (resolver);
277   GUnixResolverRequest *req;
278   _g_asyncns_query_t *qy;
279
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);
285 }
286
287 static GList *
288 lookup_by_name_finish (GResolver     *resolver,
289                        GAsyncResult  *result,
290                        GError       **error)
291 {
292   GSimpleAsyncResult *simple;
293   GUnixResolverRequest *req;
294   struct addrinfo *res;
295   gint retval;
296   GList *addresses;
297
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);
300
301   req = g_simple_async_result_get_op_res_gpointer (simple);
302   retval = _g_asyncns_getaddrinfo_done (req->gur->asyncns, req->qy, &res);
303   req->qy = NULL;
304   addresses = _g_resolver_addresses_from_addrinfo (req->u.hostname, res, retval, error);
305   if (res)
306     freeaddrinfo (res);
307
308   return addresses;
309 }
310
311
312 static void
313 lookup_by_address_free (GUnixResolverRequest *req)
314 {
315   g_object_unref (req->u.address);
316 }
317
318 static void
319 lookup_by_address_async (GResolver           *resolver,
320                          GInetAddress        *address,
321                          GCancellable        *cancellable,
322                          GAsyncReadyCallback  callback,
323                          gpointer             user_data)
324 {
325   GUnixResolver *gur = G_UNIX_RESOLVER (resolver);
326   GUnixResolverRequest *req;
327   _g_asyncns_query_t *qy;
328   struct sockaddr_storage sockaddr;
329   gsize sockaddr_size;
330
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);
338 }
339
340 static gchar *
341 lookup_by_address_finish (GResolver     *resolver,
342                           GAsyncResult  *result,
343                           GError       **error)
344 {
345   GSimpleAsyncResult *simple;
346   GUnixResolverRequest *req;
347   gchar host[NI_MAXHOST], *name;
348   gint retval;
349
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);
352
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);
356   req->qy = NULL;
357   name = _g_resolver_name_from_nameinfo (req->u.address, host, retval, error);
358
359   return name;
360 }
361
362
363 static void
364 lookup_service_free (GUnixResolverRequest *req)
365 {
366   g_free (req->u.service);
367 }
368
369 static void
370 lookup_service_async (GResolver           *resolver,
371                       const char          *rrname,
372                       GCancellable        *cancellable,
373                       GAsyncReadyCallback  callback,
374                       gpointer             user_data)
375 {
376   GUnixResolver *gur = G_UNIX_RESOLVER (resolver);
377   GUnixResolverRequest *req;
378   _g_asyncns_query_t *qy;
379
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);
384 }
385
386 static GList *
387 lookup_service_finish (GResolver     *resolver,
388                        GAsyncResult  *result,
389                        GError       **error)
390 {
391   GSimpleAsyncResult *simple;
392   GUnixResolverRequest *req;
393   guchar *answer;
394   gint len, herr;
395   GList *targets;
396
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);
399
400   req = g_simple_async_result_get_op_res_gpointer (simple);
401   len = _g_asyncns_res_done (req->gur->asyncns, req->qy, &answer);
402   req->qy = NULL;
403   if (len < 0)
404     herr = h_errno;
405   else
406     herr = 0;
407
408   targets = _g_resolver_targets_from_res_query (req->u.service, answer, len, herr, error);
409   _g_asyncns_freeanswer (answer);
410
411   return targets;
412 }
413
414
415 static void
416 g_unix_resolver_class_init (GUnixResolverClass *unix_class)
417 {
418   GResolverClass *resolver_class = G_RESOLVER_CLASS (unix_class);
419   GObjectClass *object_class = G_OBJECT_CLASS (unix_class);
420
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;
427
428   object_class->finalize = g_unix_resolver_finalize;
429 }
430
431 #define __G_UNIX_RESOLVER_C__
432 #include "gioaliasdef.c"