Initial commit
[platform/upstream/glib2.0.git] / gio / gwin32resolver.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 "gwin32resolver.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 (GWin32Resolver, g_win32_resolver, G_TYPE_THREADED_RESOLVER)
40
41 static void
42 g_win32_resolver_init (GWin32Resolver *gwr)
43 {
44 }
45
46 /* This is simpler than GThreadedResolver since we don't have to worry
47  * about multiple application-level threads, but more complicated than
48  * GUnixResolver, since we do have to deal with multiple threads of
49  * our own.
50  *
51  * The various request possibilities:
52  *
53  * 1. Synchronous: handed off to the base class (GThreadedResolver);
54  *    since it's never possible to cancel a synchronous request in a
55  *    single-threaded program, the request is done in the calling
56  *    thread.
57  *
58  * 2. Asynchronous: A GWin32ResolverRequest is created with
59  *    appropriate query-specific information, a Windows event handle,
60  *    and a GSimpleAsyncResult. This is then handed to the
61  *    Windows-internal thread pool, which does the raw DNS query part
62  *    of the operation (being careful to not call any glib methods
63  *    that might fail when called from another thread when
64  *    g_thread_init() has not been called). The main thread sets up a
65  *    GSource to asynchronously poll the event handle. There are two
66  *    sub-possibilities:
67  *
68  *      a. The resolution completes: the threadpool function calls
69  *         SetEvent() on the event handle and then returns.
70  *
71  *      b. The resolution is cancelled: request_cancelled()
72  *         disconnects the "cancelled" signal handler, and queues an
73  *         idle handler to complete the async_result.
74  *
75  *    Since we can't free the request from the threadpool thread
76  *    (because of glib locking issues), we *always* have to have it
77  *    call SetEvent and trigger the callback to indicate that it is
78  *    done. But this means that it's possible for the request to be
79  *    cancelled (queuing an idle handler to return that result) and
80  *    then have the resolution thread complete before the idle handler
81  *    runs. So the event callback and the idle handler need to both
82  *    watch out for this, making sure we don't complete the same
83  *    result twice.
84  */
85
86 typedef struct GWin32ResolverRequest GWin32ResolverRequest;
87 typedef void (*GWin32ResolverRequestFreeFunc) (GWin32ResolverRequest *);
88
89 struct GWin32ResolverRequest {
90   GWin32ResolverRequestFreeFunc free_func;
91
92   GCancellable *cancellable;
93   GError *error;
94
95   HANDLE *event;
96   GSimpleAsyncResult *async_result;
97   gboolean complete;
98   GSource *cancelled_idle;
99
100   union {
101     struct {
102       gchar *name;
103       gint retval;
104       struct addrinfo *res;
105     } name;
106
107     struct {
108       GInetAddress *iaddr;
109       struct sockaddr_storage addr;
110       gsize addrlen;
111       gint retval;
112       gchar *namebuf;
113     } address;
114
115     struct {
116       gchar *rrname;
117       DNS_STATUS retval;
118       DNS_RECORD *results;
119     } service;
120   } u;
121
122 };
123
124 static GSource *g_win32_handle_source_add (HANDLE      handle,
125                                            GSourceFunc callback,
126                                            gpointer    user_data);
127
128 static gboolean request_completed (gpointer      user_data);
129 static void     request_cancelled (GCancellable *cancellable,
130                                    gpointer      user_data);
131
132 GWin32ResolverRequest *
133 g_win32_resolver_request_new (GResolver                     *resolver,
134                               GWin32ResolverRequestFreeFunc  free_func,
135                               GCancellable                  *cancellable,
136                               GAsyncReadyCallback            callback,
137                               gpointer                       user_data,
138                               gpointer                       tag)
139 {
140   GWin32ResolverRequest *req;
141
142   req = g_slice_new0 (GWin32ResolverRequest);
143   req->free_func = free_func;
144
145   req->async_result = g_simple_async_result_new (G_OBJECT (resolver), callback,
146                                                  user_data, tag);
147   g_simple_async_result_set_op_res_gpointer (req->async_result, req, NULL);
148
149   req->event = CreateEvent (NULL, FALSE, FALSE, NULL);
150   g_win32_handle_source_add (req->event, request_completed, req);  
151
152   if (cancellable)
153     {
154       req->cancellable = g_object_ref (cancellable);
155       g_signal_connect (cancellable, "cancelled",
156                         G_CALLBACK (request_cancelled), req);
157     }
158
159   return req;
160 }
161
162 static gboolean
163 request_completed (gpointer user_data)
164 {
165   GWin32ResolverRequest *req = user_data;
166
167   /* Clean up cancellation-related stuff first */
168   if (req->cancelled_idle)
169     {
170       g_source_destroy (req->cancelled_idle);
171       g_source_unref (req->cancelled_idle);
172       req->cancelled_idle = NULL;
173     }
174   if (req->cancellable)
175     {
176       g_signal_handlers_disconnect_by_func (req->cancellable, request_cancelled, req);
177       g_object_unref (req->cancellable);
178     }
179
180   /* Now complete the result (assuming it wasn't already completed) */
181   if (req->async_result)
182     {
183       g_simple_async_result_complete (req->async_result);
184       g_object_unref (req->async_result);
185     }
186
187   /* And free req */
188   CloseHandle (req->event);
189   req->free_func (req);
190   g_slice_free (GWin32ResolverRequest, req);
191
192   return FALSE;
193 }
194
195 static gboolean
196 request_cancelled_idle (gpointer user_data)
197 {
198   GWin32ResolverRequest *req = user_data;
199   GError *error = NULL;
200
201   g_source_unref (req->cancelled_idle);
202   req->cancelled_idle = NULL;
203
204   g_cancellable_set_error_if_cancelled (req->cancellable, &error);
205   g_simple_async_result_set_from_error (req->async_result, error);
206   g_simple_async_result_complete (req->async_result);
207
208   g_object_unref (req->async_result);
209   req->async_result = NULL;
210
211   /* request_completed will eventually be called to free req */
212
213   return FALSE;
214 }
215
216 static void
217 request_cancelled (GCancellable *cancellable,
218                    gpointer      user_data)
219 {
220   GWin32ResolverRequest *req = user_data;
221
222   if (req->cancellable)
223     {
224       g_signal_handlers_disconnect_by_func (req->cancellable, request_cancelled, req);
225       g_object_unref (req->cancellable);
226       req->cancellable = NULL;
227     }
228
229   /* We need to wait until main-loop-time to actually complete the
230    * result; we don't use _complete_in_idle() here because we need to
231    * keep track of the source so we can potentially cancel it before
232    * it runs.
233    */
234   req->cancelled_idle = g_idle_source_new ();
235   g_source_set_callback (req->cancelled_idle,
236                          (GSourceFunc)request_cancelled_idle, req, NULL);
237   g_source_attach (req->cancelled_idle, g_main_context_get_thread_default ());
238 }
239
240 static DWORD WINAPI
241 lookup_by_name_in_thread (LPVOID data)
242 {
243   GWin32ResolverRequest *req = data;
244
245   req->u.name.retval = getaddrinfo (req->u.name.name, NULL,
246                                     &_g_resolver_addrinfo_hints,
247                                     &req->u.name.res);
248   SetEvent (req->event);
249   return 0;
250 }
251
252 static void
253 free_lookup_by_name (GWin32ResolverRequest *req)
254 {
255   g_free (req->u.name.name);
256   if (req->u.name.res)
257     freeaddrinfo (req->u.name.res);
258 }
259
260 static void
261 lookup_by_name_async (GResolver           *resolver,
262                       const gchar         *hostname,
263                       GCancellable        *cancellable,
264                       GAsyncReadyCallback  callback,
265                       gpointer             user_data)
266 {
267   GWin32ResolverRequest *req;
268
269   req = g_win32_resolver_request_new (resolver, free_lookup_by_name,
270                                       cancellable, callback, user_data,
271                                       lookup_by_name_async);
272   req->u.name.name = g_strdup (hostname);
273
274   QueueUserWorkItem (lookup_by_name_in_thread, req, 0);
275 }
276
277 static GList *
278 lookup_by_name_finish (GResolver     *resolver,
279                        GAsyncResult  *result,
280                        GError       **error)
281 {
282   GSimpleAsyncResult *simple;
283   GWin32ResolverRequest *req;
284
285   g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (resolver), lookup_by_name_async), NULL);
286   simple = G_SIMPLE_ASYNC_RESULT (result);
287
288   req = g_simple_async_result_get_op_res_gpointer (simple);
289   return _g_resolver_addresses_from_addrinfo (req->u.name.name, req->u.name.res,
290                                               req->u.name.retval, error);
291 }
292
293
294 static DWORD WINAPI
295 lookup_by_addresses_in_thread (LPVOID data)
296 {
297   GWin32ResolverRequest *req = data;
298
299   req->u.address.retval =
300     getnameinfo ((struct sockaddr *)&req->u.address.addr,
301                  req->u.address.addrlen,
302                  req->u.address.namebuf, NI_MAXHOST, NULL, 0, NI_NAMEREQD);
303   SetEvent (req->event);
304   return 0;
305 }
306
307 static void
308 free_lookup_by_address (GWin32ResolverRequest *req)
309 {
310   g_object_unref (req->u.address.iaddr);
311   g_free (req->u.address.namebuf);
312 }
313
314 static void
315 lookup_by_address_async (GResolver           *resolver,
316                          GInetAddress        *address,
317                          GCancellable        *cancellable,
318                          GAsyncReadyCallback  callback,
319                          gpointer             user_data)
320 {
321   GWin32ResolverRequest *req;
322
323   req = g_win32_resolver_request_new (resolver, free_lookup_by_address,
324                                       cancellable, callback, user_data,
325                                       lookup_by_address_async);
326
327   req->u.address.iaddr = g_object_ref (address);
328   _g_resolver_address_to_sockaddr (address, &req->u.address.addr,
329                                    &req->u.address.addrlen);
330   req->u.address.namebuf = g_malloc (NI_MAXHOST);
331
332   QueueUserWorkItem (lookup_by_addresses_in_thread, req, 0);
333 }
334
335 static char *
336 lookup_by_address_finish (GResolver     *resolver,
337                           GAsyncResult  *result,
338                           GError       **error)
339 {
340   GSimpleAsyncResult *simple;
341   GWin32ResolverRequest *req;
342
343   g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (resolver), lookup_by_address_async), NULL);
344   simple = G_SIMPLE_ASYNC_RESULT (result);
345
346   req = g_simple_async_result_get_op_res_gpointer (simple);
347   return _g_resolver_name_from_nameinfo (req->u.address.iaddr,
348                                          req->u.address.namebuf,
349                                          req->u.address.retval, error);
350 }
351
352
353 static DWORD WINAPI
354 lookup_service_in_thread (LPVOID data)
355 {
356   GWin32ResolverRequest *req = data;
357
358   req->u.service.retval =
359     DnsQuery_A (req->u.service.rrname, DNS_TYPE_SRV, DNS_QUERY_STANDARD,
360                 NULL, &req->u.service.results, NULL);
361   SetEvent (req->event);
362   return 0;
363 }
364
365 static void
366 free_lookup_service (GWin32ResolverRequest *req)
367 {
368   g_free (req->u.service.rrname);
369   if (req->u.service.results)
370     DnsRecordListFree (req->u.service.results, DnsFreeRecordList);
371 }
372
373 static void
374 lookup_service_async (GResolver           *resolver,
375                       const char          *rrname,
376                       GCancellable        *cancellable,
377                       GAsyncReadyCallback  callback,
378                       gpointer             user_data)
379 {
380   GWin32ResolverRequest *req;
381
382   req = g_win32_resolver_request_new (resolver, free_lookup_service,
383                                       cancellable, callback, user_data,
384                                       lookup_service_async);
385   req->u.service.rrname = g_strdup (rrname);
386
387   QueueUserWorkItem (lookup_service_in_thread, req, 0);
388 }
389
390 static GList *
391 lookup_service_finish (GResolver     *resolver,
392                        GAsyncResult  *result,
393                        GError       **error)
394 {
395   GSimpleAsyncResult *simple;
396   GWin32ResolverRequest *req;
397
398   g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (resolver), lookup_service_async), NULL);
399   simple = G_SIMPLE_ASYNC_RESULT (result);
400
401   req = g_simple_async_result_get_op_res_gpointer (simple);
402   return _g_resolver_targets_from_DnsQuery (req->u.service.rrname,
403                                             req->u.service.retval,
404                                             req->u.service.results, error);
405 }
406
407
408 static void
409 g_win32_resolver_class_init (GWin32ResolverClass *win32_class)
410 {
411   GResolverClass *resolver_class = G_RESOLVER_CLASS (win32_class);
412
413   resolver_class->lookup_by_name_async     = lookup_by_name_async;
414   resolver_class->lookup_by_name_finish    = lookup_by_name_finish;
415   resolver_class->lookup_by_address_async  = lookup_by_address_async;
416   resolver_class->lookup_by_address_finish = lookup_by_address_finish;
417   resolver_class->lookup_service_async     = lookup_service_async;
418   resolver_class->lookup_service_finish    = lookup_service_finish;
419 }
420
421
422 /* Windows HANDLE GSource */
423
424 typedef struct {
425   GSource source;
426   GPollFD pollfd;
427 } GWin32HandleSource;
428
429 static gboolean
430 g_win32_handle_source_prepare (GSource *source,
431                                gint    *timeout)
432 {
433   *timeout = -1;
434   return FALSE;
435 }
436
437 static gboolean
438 g_win32_handle_source_check (GSource *source)
439 {
440   GWin32HandleSource *hsource = (GWin32HandleSource *)source;
441
442   return hsource->pollfd.revents;
443 }
444
445 static gboolean
446 g_win32_handle_source_dispatch (GSource     *source,
447                                 GSourceFunc  callback,
448                                 gpointer     user_data)
449 {
450   return (*callback) (user_data);
451 }
452
453 static void
454 g_win32_handle_source_finalize (GSource *source)
455 {
456   ;
457 }
458
459 GSourceFuncs g_win32_handle_source_funcs = {
460   g_win32_handle_source_prepare,
461   g_win32_handle_source_check,
462   g_win32_handle_source_dispatch,
463   g_win32_handle_source_finalize
464 };
465
466 static GSource *
467 g_win32_handle_source_add (HANDLE      handle,
468                            GSourceFunc callback,
469                            gpointer    user_data)
470 {
471   GWin32HandleSource *hsource;
472   GSource *source;
473
474   source = g_source_new (&g_win32_handle_source_funcs, sizeof (GWin32HandleSource));
475   hsource = (GWin32HandleSource *)source;
476   hsource->pollfd.fd = (gint)handle;
477   hsource->pollfd.events = G_IO_IN;
478   hsource->pollfd.revents = 0;
479   g_source_add_poll (source, &hsource->pollfd);
480
481   g_source_set_callback (source, callback, user_data, NULL);
482   g_source_attach (source, g_main_context_get_thread_default ());
483   return source;
484 }
485
486 #define __G_WIN32_RESOLVER_C__
487 #include "gioaliasdef.c"