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 "gwin32resolver.h"
31 #include "gnetworkingprivate.h"
33 #include "gcancellable.h"
34 #include "gsimpleasyncresult.h"
35 #include "gsocketaddress.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).
47 G_DEFINE_TYPE (GWin32Resolver, g_win32_resolver, G_TYPE_THREADED_RESOLVER)
50 g_win32_resolver_init (GWin32Resolver *gwr)
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
59 * The various request possibilities:
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
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
76 * a. The resolution completes: the threadpool function calls
77 * SetEvent() on the event handle and then returns.
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.
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
94 typedef struct GWin32ResolverRequest GWin32ResolverRequest;
95 typedef void (*GWin32ResolverRequestFreeFunc) (GWin32ResolverRequest *);
97 struct GWin32ResolverRequest {
98 GWin32ResolverRequestFreeFunc free_func;
100 GCancellable *cancellable;
104 GSimpleAsyncResult *async_result;
106 GSource *cancelled_idle;
112 struct addrinfo *res;
117 struct sockaddr_storage addr;
132 static GSource *g_win32_handle_source_add (HANDLE handle,
133 GSourceFunc callback,
136 static gboolean request_completed (gpointer user_data);
137 static void request_cancelled (GCancellable *cancellable,
140 GWin32ResolverRequest *
141 g_win32_resolver_request_new (GResolver *resolver,
142 GWin32ResolverRequestFreeFunc free_func,
143 GCancellable *cancellable,
144 GAsyncReadyCallback callback,
148 GWin32ResolverRequest *req;
150 req = g_slice_new0 (GWin32ResolverRequest);
151 req->free_func = free_func;
153 req->async_result = g_simple_async_result_new (G_OBJECT (resolver), callback,
155 g_simple_async_result_set_op_res_gpointer (req->async_result, req, NULL);
157 req->event = CreateEvent (NULL, FALSE, FALSE, NULL);
158 g_win32_handle_source_add (req->event, request_completed, req);
162 req->cancellable = g_object_ref (cancellable);
163 g_signal_connect (cancellable, "cancelled",
164 G_CALLBACK (request_cancelled), req);
171 request_completed (gpointer user_data)
173 GWin32ResolverRequest *req = user_data;
175 /* Clean up cancellation-related stuff first */
176 if (req->cancelled_idle)
178 g_source_destroy (req->cancelled_idle);
179 g_source_unref (req->cancelled_idle);
180 req->cancelled_idle = NULL;
182 if (req->cancellable)
184 g_signal_handlers_disconnect_by_func (req->cancellable, request_cancelled, req);
185 g_object_unref (req->cancellable);
188 /* Now complete the result (assuming it wasn't already completed) */
189 if (req->async_result)
191 g_simple_async_result_complete (req->async_result);
192 g_object_unref (req->async_result);
196 CloseHandle (req->event);
197 req->free_func (req);
198 g_slice_free (GWin32ResolverRequest, req);
204 request_cancelled_idle (gpointer user_data)
206 GWin32ResolverRequest *req = user_data;
207 GError *error = NULL;
209 g_source_unref (req->cancelled_idle);
210 req->cancelled_idle = NULL;
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);
216 g_object_unref (req->async_result);
217 req->async_result = NULL;
219 /* request_completed will eventually be called to free req */
225 request_cancelled (GCancellable *cancellable,
228 GWin32ResolverRequest *req = user_data;
230 if (req->cancellable)
232 g_signal_handlers_disconnect_by_func (req->cancellable, request_cancelled, req);
233 g_object_unref (req->cancellable);
234 req->cancellable = NULL;
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
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 ());
249 lookup_by_name_in_thread (LPVOID data)
251 GWin32ResolverRequest *req = data;
253 req->u.name.retval = getaddrinfo (req->u.name.name, NULL,
254 &_g_resolver_addrinfo_hints,
256 SetEvent (req->event);
261 free_lookup_by_name (GWin32ResolverRequest *req)
263 g_free (req->u.name.name);
265 freeaddrinfo (req->u.name.res);
269 lookup_by_name_async (GResolver *resolver,
270 const gchar *hostname,
271 GCancellable *cancellable,
272 GAsyncReadyCallback callback,
275 GWin32ResolverRequest *req;
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);
282 QueueUserWorkItem (lookup_by_name_in_thread, req, 0);
286 lookup_by_name_finish (GResolver *resolver,
287 GAsyncResult *result,
290 GSimpleAsyncResult *simple;
291 GWin32ResolverRequest *req;
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);
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);
303 lookup_by_addresses_in_thread (LPVOID data)
305 GWin32ResolverRequest *req = data;
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);
316 free_lookup_by_address (GWin32ResolverRequest *req)
318 g_object_unref (req->u.address.iaddr);
319 g_free (req->u.address.namebuf);
323 lookup_by_address_async (GResolver *resolver,
324 GInetAddress *address,
325 GCancellable *cancellable,
326 GAsyncReadyCallback callback,
329 GWin32ResolverRequest *req;
331 req = g_win32_resolver_request_new (resolver, free_lookup_by_address,
332 cancellable, callback, user_data,
333 lookup_by_address_async);
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);
340 QueueUserWorkItem (lookup_by_addresses_in_thread, req, 0);
344 lookup_by_address_finish (GResolver *resolver,
345 GAsyncResult *result,
348 GSimpleAsyncResult *simple;
349 GWin32ResolverRequest *req;
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);
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);
362 lookup_service_in_thread (LPVOID data)
364 GWin32ResolverRequest *req = data;
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);
374 free_lookup_service (GWin32ResolverRequest *req)
376 g_free (req->u.service.rrname);
377 if (req->u.service.results)
378 DnsRecordListFree (req->u.service.results, DnsFreeRecordList);
382 lookup_service_async (GResolver *resolver,
384 GCancellable *cancellable,
385 GAsyncReadyCallback callback,
388 GWin32ResolverRequest *req;
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);
395 QueueUserWorkItem (lookup_service_in_thread, req, 0);
399 lookup_service_finish (GResolver *resolver,
400 GAsyncResult *result,
403 GSimpleAsyncResult *simple;
404 GWin32ResolverRequest *req;
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);
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);
417 g_win32_resolver_class_init (GWin32ResolverClass *win32_class)
419 GResolverClass *resolver_class = G_RESOLVER_CLASS (win32_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;
430 /* Windows HANDLE GSource */
435 } GWin32HandleSource;
438 g_win32_handle_source_prepare (GSource *source,
446 g_win32_handle_source_check (GSource *source)
448 GWin32HandleSource *hsource = (GWin32HandleSource *)source;
450 return hsource->pollfd.revents;
454 g_win32_handle_source_dispatch (GSource *source,
455 GSourceFunc callback,
458 return (*callback) (user_data);
462 g_win32_handle_source_finalize (GSource *source)
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
475 g_win32_handle_source_add (HANDLE handle,
476 GSourceFunc callback,
479 GWin32HandleSource *hsource;
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);
489 g_source_set_callback (source, callback, user_data, NULL);
490 g_source_attach (source, g_main_context_get_thread_default ());
494 #define __G_WIN32_RESOLVER_C__
495 #include "gioaliasdef.c"