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