Branch data Line data Source code
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 : : * Copyright (C) 2018 Igalia S.L.
7 : : *
8 : : * SPDX-License-Identifier: LGPL-2.1-or-later
9 : : *
10 : : * This library is free software; you can redistribute it and/or
11 : : * modify it under the terms of the GNU Lesser General Public
12 : : * License as published by the Free Software Foundation; either
13 : : * version 2.1 of the License, or (at your option) any later version.
14 : : *
15 : : * This library is distributed in the hope that it will be useful,
16 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 : : * Lesser General Public License for more details.
19 : : *
20 : : * You should have received a copy of the GNU Lesser General
21 : : * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
22 : : */
23 : :
24 : : #include "config.h"
25 : : #include <glib.h>
26 : : #include "glibintl.h"
27 : :
28 : : #include <stdio.h>
29 : : #include <string.h>
30 : :
31 : : #include "glib/glib-private.h"
32 : : #include "gthreadedresolver.h"
33 : : #include "gthreadedresolver-private.h"
34 : : #include "gnetworkingprivate.h"
35 : :
36 : : #include "gcancellable.h"
37 : : #include "ginetaddress.h"
38 : : #include "ginetsocketaddress.h"
39 : : #include "gnetworkmonitorbase.h"
40 : : #include "gtask.h"
41 : : #include "gsocketaddress.h"
42 : : #include "gsrvtarget.h"
43 : :
44 : : #if HAVE_GETIFADDRS
45 : : #include <ifaddrs.h>
46 : : #endif
47 : :
48 : : /*
49 : : * GThreadedResolver is a threaded wrapper around the system libc’s
50 : : * `getaddrinfo()`.
51 : : *
52 : : * It has to be threaded, as `getaddrinfo()` is synchronous. libc does provide
53 : : * `getaddrinfo_a()` as an asynchronous version of `getaddrinfo()`, but it does
54 : : * not integrate with a poll loop. It requires use of sigevent to notify of
55 : : * completion of an asynchronous operation. That either emits a signal, or calls
56 : : * a callback function in a newly spawned thread.
57 : : *
58 : : * A signal (`SIGEV_SIGNAL`) can’t be used for completion as (aside from being
59 : : * another expensive round trip into the kernel) GLib cannot pick a `SIG*`
60 : : * number which is guaranteed to not be in use elsewhere in the process. Various
61 : : * other things could be interfering with signal dispositions, such as gdb or
62 : : * other libraries in the process. Using a `signalfd()`
63 : : * [cannot improve this situation](https://ldpreload.com/blog/signalfd-is-useless).
64 : : *
65 : : * A callback function in a newly spawned thread (`SIGEV_THREAD`) could be used,
66 : : * but that is very expensive. Internally, glibc currently also just implements
67 : : * `getaddrinfo_a()`
68 : : * [using its own thread pool](https://github.com/bminor/glibc/blob/master/resolv/gai_misc.c),
69 : : * and then
70 : : * [spawns an additional thread for each completion callback](https://github.com/bminor/glibc/blob/master/resolv/gai_notify.c).
71 : : * That is very expensive.
72 : : *
73 : : * No other appropriate sigevent callback types
74 : : * [currently exist](https://sourceware.org/bugzilla/show_bug.cgi?id=30287), and
75 : : * [others agree that sigevent is not great](http://davmac.org/davpage/linux/async-io.html#posixaio).
76 : : *
77 : : * Hence, #GThreadedResolver calls the normal synchronous `getaddrinfo()` in its
78 : : * own thread pool. Previously, #GThreadedResolver used the thread pool which is
79 : : * internal to #GTask by calling g_task_run_in_thread(). That lead to exhaustion
80 : : * of the #GTask thread pool in some situations, though, as DNS lookups are
81 : : * quite frequent leaf operations in some use cases. Now, #GThreadedResolver
82 : : * uses its own private thread pool.
83 : : *
84 : : * This is similar to what
85 : : * [libasyncns](http://git.0pointer.net/libasyncns.git/tree/libasyncns/asyncns.h)
86 : : * and other multi-threaded users of `getaddrinfo()` do.
87 : : */
88 : :
89 : : struct _GThreadedResolver
90 : : {
91 : : GResolver parent_instance;
92 : :
93 : : GThreadPool *thread_pool; /* (owned) */
94 : :
95 : : GMutex interface_mutex;
96 : : GNetworkMonitor *network_monitor; /* (owned) */
97 : : gboolean monitor_supports_caching;
98 : : int network_is_loopback_only;
99 : : };
100 : :
101 : 24 : G_DEFINE_TYPE (GThreadedResolver, g_threaded_resolver, G_TYPE_RESOLVER)
102 : :
103 : : static void run_task_in_thread_pool_async (GThreadedResolver *self,
104 : : GTask *task);
105 : : static void run_task_in_thread_pool_sync (GThreadedResolver *self,
106 : : GTask *task);
107 : : static void threaded_resolver_worker_cb (gpointer task_data,
108 : : gpointer user_data);
109 : :
110 : : static void
111 : 5 : g_threaded_resolver_init (GThreadedResolver *self)
112 : : {
113 : 5 : self->thread_pool = g_thread_pool_new_full (threaded_resolver_worker_cb,
114 : : self,
115 : : (GDestroyNotify) g_object_unref,
116 : : 20,
117 : : FALSE,
118 : : NULL);
119 : :
120 : 5 : self->network_is_loopback_only = -1;
121 : :
122 : 5 : g_mutex_init (&self->interface_mutex);
123 : 5 : }
124 : :
125 : : static void
126 : 0 : g_threaded_resolver_finalize (GObject *object)
127 : : {
128 : 0 : GThreadedResolver *self = G_THREADED_RESOLVER (object);
129 : :
130 : 0 : g_thread_pool_free (self->thread_pool, TRUE, FALSE);
131 : 0 : self->thread_pool = NULL;
132 : :
133 : 0 : if (self->network_monitor)
134 : 0 : g_signal_handlers_disconnect_by_data (self->network_monitor, object);
135 : :
136 : 0 : g_clear_object (&self->network_monitor);
137 : 0 : g_mutex_clear (&self->interface_mutex);
138 : :
139 : 0 : G_OBJECT_CLASS (g_threaded_resolver_parent_class)->finalize (object);
140 : 0 : }
141 : :
142 : : static GResolverError
143 : 3 : g_resolver_error_from_addrinfo_error (gint err)
144 : : {
145 : 3 : switch (err)
146 : : {
147 : 3 : case EAI_FAIL:
148 : : #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
149 : : case EAI_NODATA:
150 : : #endif
151 : : case EAI_NONAME:
152 : 3 : return G_RESOLVER_ERROR_NOT_FOUND;
153 : :
154 : 0 : case EAI_AGAIN:
155 : 0 : return G_RESOLVER_ERROR_TEMPORARY_FAILURE;
156 : :
157 : 0 : default:
158 : 0 : return G_RESOLVER_ERROR_INTERNAL;
159 : : }
160 : : }
161 : :
162 : : typedef struct {
163 : : enum {
164 : : LOOKUP_BY_NAME,
165 : : LOOKUP_BY_ADDRESS,
166 : : LOOKUP_RECORDS,
167 : : } lookup_type;
168 : :
169 : : union {
170 : : struct {
171 : : char *hostname;
172 : : int address_family;
173 : : } lookup_by_name;
174 : : struct {
175 : : GInetAddress *address; /* (owned) */
176 : : } lookup_by_address;
177 : : struct {
178 : : char *rrname;
179 : : GResolverRecordType record_type;
180 : : } lookup_records;
181 : : };
182 : :
183 : : GCond cond; /* used for signalling completion of the task when running it sync */
184 : : GMutex lock;
185 : :
186 : : GSource *timeout_source; /* (nullable) (owned) */
187 : : GSource *cancellable_source; /* (nullable) (owned) */
188 : :
189 : : /* This enum indicates that a particular code path has claimed the
190 : : * task and is shortly about to call g_task_return_*() on it.
191 : : * This must be accessed with GThreadedResolver.lock held. */
192 : : enum
193 : : {
194 : : NOT_YET,
195 : : COMPLETED, /* libc lookup call has completed successfully or errored */
196 : : TIMED_OUT,
197 : : CANCELLED,
198 : : } will_return;
199 : :
200 : : /* Whether the thread pool thread executing this lookup has finished executing
201 : : * it and g_task_return_*() has been called on it already.
202 : : * This must be accessed with GThreadedResolver.lock held. */
203 : : gboolean has_returned;
204 : : } LookupData;
205 : :
206 : : static LookupData *
207 : 3 : lookup_data_new_by_name (const char *hostname,
208 : : int address_family)
209 : : {
210 : 3 : LookupData *data = g_new0 (LookupData, 1);
211 : 3 : data->lookup_type = LOOKUP_BY_NAME;
212 : 3 : g_cond_init (&data->cond);
213 : 3 : g_mutex_init (&data->lock);
214 : 3 : data->lookup_by_name.hostname = g_strdup (hostname);
215 : 3 : data->lookup_by_name.address_family = address_family;
216 : 3 : return g_steal_pointer (&data);
217 : : }
218 : :
219 : : static LookupData *
220 : 0 : lookup_data_new_by_address (GInetAddress *address)
221 : : {
222 : 0 : LookupData *data = g_new0 (LookupData, 1);
223 : 0 : data->lookup_type = LOOKUP_BY_ADDRESS;
224 : 0 : g_cond_init (&data->cond);
225 : 0 : g_mutex_init (&data->lock);
226 : 0 : data->lookup_by_address.address = g_object_ref (address);
227 : 0 : return g_steal_pointer (&data);
228 : : }
229 : :
230 : : static LookupData *
231 : 0 : lookup_data_new_records (const gchar *rrname,
232 : : GResolverRecordType record_type)
233 : : {
234 : 0 : LookupData *data = g_new0 (LookupData, 1);
235 : 0 : data->lookup_type = LOOKUP_RECORDS;
236 : 0 : g_cond_init (&data->cond);
237 : 0 : g_mutex_init (&data->lock);
238 : 0 : data->lookup_records.rrname = g_strdup (rrname);
239 : 0 : data->lookup_records.record_type = record_type;
240 : 0 : return g_steal_pointer (&data);
241 : : }
242 : :
243 : : static void
244 : 0 : lookup_data_free (LookupData *data)
245 : : {
246 : 0 : switch (data->lookup_type) {
247 : 0 : case LOOKUP_BY_NAME:
248 : 0 : g_free (data->lookup_by_name.hostname);
249 : 0 : break;
250 : 0 : case LOOKUP_BY_ADDRESS:
251 : 0 : g_clear_object (&data->lookup_by_address.address);
252 : 0 : break;
253 : 0 : case LOOKUP_RECORDS:
254 : 0 : g_free (data->lookup_records.rrname);
255 : 0 : break;
256 : 0 : default:
257 : : g_assert_not_reached ();
258 : : }
259 : :
260 : 0 : if (data->timeout_source != NULL)
261 : : {
262 : 0 : g_source_destroy (data->timeout_source);
263 : 0 : g_clear_pointer (&data->timeout_source, g_source_unref);
264 : : }
265 : :
266 : 0 : if (data->cancellable_source != NULL)
267 : : {
268 : 0 : g_source_destroy (data->cancellable_source);
269 : 0 : g_clear_pointer (&data->cancellable_source, g_source_unref);
270 : : }
271 : :
272 : 0 : g_mutex_clear (&data->lock);
273 : 0 : g_cond_clear (&data->cond);
274 : :
275 : 0 : g_free (data);
276 : 0 : }
277 : :
278 : : static gboolean
279 : 1 : check_only_has_loopback_interfaces (void)
280 : : {
281 : : #if HAVE_GETIFADDRS
282 : : struct ifaddrs *addrs;
283 : 1 : gboolean only_loopback = TRUE;
284 : :
285 : 1 : if (getifaddrs (&addrs) != 0)
286 : : {
287 : 0 : int saved_errno = errno;
288 : 0 : g_debug ("getifaddrs() failed: %s", g_strerror (saved_errno));
289 : 0 : return FALSE;
290 : : }
291 : :
292 : 4 : for (struct ifaddrs *addr = addrs; addr; addr = addr->ifa_next)
293 : : {
294 : 4 : struct sockaddr *sa = addr->ifa_addr;
295 : : size_t addrlen;
296 : : GSocketAddress *saddr;
297 : 4 : if (!sa)
298 : 0 : continue;
299 : :
300 : 4 : if (sa->sa_family == AF_INET)
301 : 2 : addrlen = sizeof (struct sockaddr_in);
302 : 2 : else if (sa->sa_family == AF_INET6)
303 : 0 : addrlen = sizeof (struct sockaddr_in6);
304 : : else
305 : 2 : continue;
306 : :
307 : 2 : saddr = g_socket_address_new_from_native (sa, addrlen);
308 : 2 : if (!saddr)
309 : 0 : continue;
310 : :
311 : 2 : if (!G_IS_INET_SOCKET_ADDRESS (saddr))
312 : : {
313 : 0 : g_object_unref (saddr);
314 : 0 : continue;
315 : : }
316 : :
317 : 2 : GInetAddress *inetaddr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (saddr));
318 : 2 : if (!g_inet_address_get_is_loopback (inetaddr))
319 : : {
320 : 1 : only_loopback = FALSE;
321 : 1 : g_object_unref (saddr);
322 : 1 : break;
323 : : }
324 : :
325 : 1 : g_object_unref (saddr);
326 : : }
327 : :
328 : 1 : freeifaddrs (addrs);
329 : 1 : return only_loopback;
330 : : #else /* FIXME: Check GetAdaptersAddresses() on win32. */
331 : : return FALSE;
332 : : #endif
333 : : }
334 : :
335 : : static void
336 : 0 : network_changed_cb (GNetworkMonitor *monitor,
337 : : gboolean network_available,
338 : : GThreadedResolver *resolver)
339 : : {
340 : 0 : g_mutex_lock (&resolver->interface_mutex);
341 : 0 : resolver->network_is_loopback_only = -1;
342 : 0 : g_mutex_unlock (&resolver->interface_mutex);
343 : 0 : }
344 : :
345 : : static gboolean
346 : 3 : only_has_loopback_interfaces_cached (GThreadedResolver *resolver)
347 : : {
348 : 3 : g_mutex_lock (&resolver->interface_mutex);
349 : :
350 : 3 : if (!resolver->network_monitor)
351 : : {
352 : 1 : resolver->network_monitor = g_object_ref (g_network_monitor_get_default ());
353 : 1 : resolver->monitor_supports_caching = G_TYPE_FROM_INSTANCE (resolver->network_monitor) != G_TYPE_NETWORK_MONITOR_BASE;
354 : 1 : g_signal_connect_object (resolver->network_monitor, "network-changed", G_CALLBACK (network_changed_cb), resolver, G_CONNECT_DEFAULT);
355 : : }
356 : :
357 : 3 : if (!resolver->monitor_supports_caching || resolver->network_is_loopback_only == -1)
358 : 1 : resolver->network_is_loopback_only = check_only_has_loopback_interfaces ();
359 : :
360 : 3 : g_mutex_unlock (&resolver->interface_mutex);
361 : :
362 : 3 : return resolver->network_is_loopback_only;
363 : : }
364 : :
365 : : static GList *
366 : 3 : do_lookup_by_name (GThreadedResolver *resolver,
367 : : const gchar *hostname,
368 : : int address_family,
369 : : GCancellable *cancellable,
370 : : GError **error)
371 : : {
372 : 3 : struct addrinfo *res = NULL;
373 : : GList *addresses;
374 : : gint retval;
375 : 3 : struct addrinfo addrinfo_hints = { 0 };
376 : :
377 : : /* In general we only want IPs for valid interfaces.
378 : : * However this will return nothing if you only have loopback interfaces.
379 : : * Instead in this case we will manually filter out invalid IPs. */
380 : 3 : gboolean only_loopback = only_has_loopback_interfaces_cached (resolver);
381 : :
382 : : #ifdef AI_ADDRCONFIG
383 : 3 : if (!only_loopback)
384 : 3 : addrinfo_hints.ai_flags = AI_ADDRCONFIG;
385 : : #endif
386 : : /* socktype and protocol don't actually matter, they just get copied into the
387 : : * returned addrinfo structures (and then we ignore them). But if
388 : : * we leave them unset, we'll get back duplicate answers.
389 : : */
390 : 3 : addrinfo_hints.ai_socktype = SOCK_STREAM;
391 : 3 : addrinfo_hints.ai_protocol = IPPROTO_TCP;
392 : :
393 : 3 : addrinfo_hints.ai_family = address_family;
394 : 3 : retval = getaddrinfo (hostname, NULL, &addrinfo_hints, &res);
395 : :
396 : 3 : if (retval == 0)
397 : : {
398 : 0 : addresses = NULL;
399 : 0 : for (struct addrinfo *ai = res; ai; ai = ai->ai_next)
400 : : {
401 : : GInetAddress *addr;
402 : 0 : GSocketAddress *sockaddr = g_socket_address_new_from_native (ai->ai_addr, ai->ai_addrlen);
403 : :
404 : 0 : if (!sockaddr)
405 : 0 : continue;
406 : 0 : if (!G_IS_INET_SOCKET_ADDRESS (sockaddr))
407 : : {
408 : 0 : g_clear_object (&sockaddr);
409 : 0 : continue;
410 : : }
411 : :
412 : 0 : addr = g_inet_socket_address_get_address ((GInetSocketAddress *) sockaddr);
413 : 0 : if (only_loopback && !g_inet_address_get_is_loopback (addr))
414 : : {
415 : 0 : g_object_unref (sockaddr);
416 : 0 : continue;
417 : : }
418 : :
419 : 0 : addresses = g_list_prepend (addresses, g_object_ref (addr));
420 : 0 : g_object_unref (sockaddr);
421 : : }
422 : :
423 : 0 : g_clear_pointer (&res, freeaddrinfo);
424 : :
425 : 0 : if (addresses != NULL)
426 : : {
427 : 0 : addresses = g_list_reverse (addresses);
428 : 0 : return g_steal_pointer (&addresses);
429 : : }
430 : : else
431 : : {
432 : : /* All addresses failed to be converted to GSocketAddresses. */
433 : 0 : g_set_error (error,
434 : : G_RESOLVER_ERROR,
435 : : G_RESOLVER_ERROR_NOT_FOUND,
436 : : _("Error resolving “%s”: %s"),
437 : : hostname,
438 : : _("No valid addresses were found"));
439 : 0 : return NULL;
440 : : }
441 : : }
442 : : else
443 : : {
444 : : #ifdef G_OS_WIN32
445 : : gchar *error_message = g_win32_error_message (WSAGetLastError ());
446 : : #else
447 : 3 : gchar *error_message = g_locale_to_utf8 (gai_strerror (retval), -1, NULL, NULL, NULL);
448 : 3 : if (error_message == NULL)
449 : 0 : error_message = g_strdup ("[Invalid UTF-8]");
450 : : #endif
451 : :
452 : 3 : g_clear_pointer (&res, freeaddrinfo);
453 : :
454 : 6 : g_set_error (error,
455 : : G_RESOLVER_ERROR,
456 : 3 : g_resolver_error_from_addrinfo_error (retval),
457 : : _("Error resolving “%s”: %s"),
458 : : hostname, error_message);
459 : 3 : g_free (error_message);
460 : :
461 : 3 : return NULL;
462 : : }
463 : : }
464 : :
465 : : static GList *
466 : 3 : lookup_by_name (GResolver *resolver,
467 : : const gchar *hostname,
468 : : GCancellable *cancellable,
469 : : GError **error)
470 : : {
471 : 3 : GThreadedResolver *self = G_THREADED_RESOLVER (resolver);
472 : : GTask *task;
473 : : GList *addresses;
474 : : LookupData *data;
475 : :
476 : 3 : data = lookup_data_new_by_name (hostname, AF_UNSPEC);
477 : 3 : task = g_task_new (resolver, cancellable, NULL, NULL);
478 : 3 : g_task_set_source_tag (task, lookup_by_name);
479 : 3 : g_task_set_name (task, "[gio] resolver lookup");
480 : 3 : g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) lookup_data_free);
481 : :
482 : 3 : run_task_in_thread_pool_sync (self, task);
483 : :
484 : 3 : addresses = g_task_propagate_pointer (task, error);
485 : 3 : g_object_unref (task);
486 : :
487 : 3 : return addresses;
488 : : }
489 : :
490 : : static int
491 : 0 : flags_to_family (GResolverNameLookupFlags flags)
492 : : {
493 : 0 : int address_family = AF_UNSPEC;
494 : :
495 : 0 : if (flags & G_RESOLVER_NAME_LOOKUP_FLAGS_IPV4_ONLY)
496 : 0 : address_family = AF_INET;
497 : :
498 : 0 : if (flags & G_RESOLVER_NAME_LOOKUP_FLAGS_IPV6_ONLY)
499 : : {
500 : 0 : address_family = AF_INET6;
501 : : /* You can only filter by one family at a time */
502 : 0 : g_return_val_if_fail (!(flags & G_RESOLVER_NAME_LOOKUP_FLAGS_IPV4_ONLY), address_family);
503 : : }
504 : :
505 : 0 : return address_family;
506 : : }
507 : :
508 : : static GList *
509 : 0 : lookup_by_name_with_flags (GResolver *resolver,
510 : : const gchar *hostname,
511 : : GResolverNameLookupFlags flags,
512 : : GCancellable *cancellable,
513 : : GError **error)
514 : : {
515 : 0 : GThreadedResolver *self = G_THREADED_RESOLVER (resolver);
516 : : GTask *task;
517 : : GList *addresses;
518 : : LookupData *data;
519 : :
520 : 0 : data = lookup_data_new_by_name (hostname, flags_to_family (flags));
521 : 0 : task = g_task_new (resolver, cancellable, NULL, NULL);
522 : 0 : g_task_set_source_tag (task, lookup_by_name_with_flags);
523 : 0 : g_task_set_name (task, "[gio] resolver lookup");
524 : 0 : g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) lookup_data_free);
525 : :
526 : 0 : run_task_in_thread_pool_sync (self, task);
527 : :
528 : 0 : addresses = g_task_propagate_pointer (task, error);
529 : 0 : g_object_unref (task);
530 : :
531 : 0 : return addresses;
532 : : }
533 : :
534 : : static void
535 : 0 : lookup_by_name_with_flags_async (GResolver *resolver,
536 : : const gchar *hostname,
537 : : GResolverNameLookupFlags flags,
538 : : GCancellable *cancellable,
539 : : GAsyncReadyCallback callback,
540 : : gpointer user_data)
541 : : {
542 : 0 : GThreadedResolver *self = G_THREADED_RESOLVER (resolver);
543 : : GTask *task;
544 : : LookupData *data;
545 : :
546 : 0 : data = lookup_data_new_by_name (hostname, flags_to_family (flags));
547 : 0 : task = g_task_new (resolver, cancellable, callback, user_data);
548 : :
549 : 0 : g_debug ("%s: starting new lookup for %s with GTask %p, LookupData %p",
550 : : G_STRFUNC, hostname, task, data);
551 : :
552 : 0 : g_task_set_source_tag (task, lookup_by_name_with_flags_async);
553 : 0 : g_task_set_name (task, "[gio] resolver lookup");
554 : 0 : g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) lookup_data_free);
555 : :
556 : 0 : run_task_in_thread_pool_async (self, task);
557 : :
558 : 0 : g_object_unref (task);
559 : 0 : }
560 : :
561 : : static void
562 : 0 : lookup_by_name_async (GResolver *resolver,
563 : : const gchar *hostname,
564 : : GCancellable *cancellable,
565 : : GAsyncReadyCallback callback,
566 : : gpointer user_data)
567 : : {
568 : 0 : lookup_by_name_with_flags_async (resolver,
569 : : hostname,
570 : : G_RESOLVER_NAME_LOOKUP_FLAGS_DEFAULT,
571 : : cancellable,
572 : : callback,
573 : : user_data);
574 : 0 : }
575 : :
576 : : static GList *
577 : 0 : lookup_by_name_finish (GResolver *resolver,
578 : : GAsyncResult *result,
579 : : GError **error)
580 : : {
581 : 0 : g_return_val_if_fail (g_task_is_valid (result, resolver), NULL);
582 : :
583 : 0 : return g_task_propagate_pointer (G_TASK (result), error);
584 : : }
585 : :
586 : : static GList *
587 : 0 : lookup_by_name_with_flags_finish (GResolver *resolver,
588 : : GAsyncResult *result,
589 : : GError **error)
590 : : {
591 : 0 : g_return_val_if_fail (g_task_is_valid (result, resolver), NULL);
592 : :
593 : 0 : return g_task_propagate_pointer (G_TASK (result), error);
594 : : }
595 : :
596 : : static gchar *
597 : 0 : do_lookup_by_address (GInetAddress *address,
598 : : GCancellable *cancellable,
599 : : GError **error)
600 : : {
601 : : struct sockaddr_storage sockaddr_address;
602 : : gsize sockaddr_address_size;
603 : : GSocketAddress *gsockaddr;
604 : : gchar name[NI_MAXHOST];
605 : : gint retval;
606 : :
607 : 0 : gsockaddr = g_inet_socket_address_new (address, 0);
608 : 0 : g_socket_address_to_native (gsockaddr, (struct sockaddr *)&sockaddr_address,
609 : : sizeof (sockaddr_address), NULL);
610 : 0 : sockaddr_address_size = g_socket_address_get_native_size (gsockaddr);
611 : 0 : g_object_unref (gsockaddr);
612 : :
613 : 0 : retval = getnameinfo ((struct sockaddr *) &sockaddr_address, sockaddr_address_size,
614 : : name, sizeof (name), NULL, 0, NI_NAMEREQD);
615 : 0 : if (retval == 0)
616 : 0 : return g_strdup (name);
617 : : else
618 : : {
619 : : gchar *phys;
620 : :
621 : : #ifdef G_OS_WIN32
622 : : gchar *error_message = g_win32_error_message (WSAGetLastError ());
623 : : #else
624 : 0 : gchar *error_message = g_locale_to_utf8 (gai_strerror (retval), -1, NULL, NULL, NULL);
625 : 0 : if (error_message == NULL)
626 : 0 : error_message = g_strdup ("[Invalid UTF-8]");
627 : : #endif
628 : :
629 : 0 : phys = g_inet_address_to_string (address);
630 : 0 : g_set_error (error,
631 : : G_RESOLVER_ERROR,
632 : 0 : g_resolver_error_from_addrinfo_error (retval),
633 : : _("Error reverse-resolving “%s”: %s"),
634 : : phys ? phys : "(unknown)",
635 : : error_message);
636 : 0 : g_free (phys);
637 : 0 : g_free (error_message);
638 : :
639 : 0 : return NULL;
640 : : }
641 : : }
642 : :
643 : : static gchar *
644 : 0 : lookup_by_address (GResolver *resolver,
645 : : GInetAddress *address,
646 : : GCancellable *cancellable,
647 : : GError **error)
648 : : {
649 : 0 : GThreadedResolver *self = G_THREADED_RESOLVER (resolver);
650 : 0 : LookupData *data = NULL;
651 : : GTask *task;
652 : : gchar *name;
653 : :
654 : 0 : data = lookup_data_new_by_address (address);
655 : 0 : task = g_task_new (resolver, cancellable, NULL, NULL);
656 : 0 : g_task_set_source_tag (task, lookup_by_address);
657 : 0 : g_task_set_name (task, "[gio] resolver lookup");
658 : 0 : g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) lookup_data_free);
659 : :
660 : 0 : run_task_in_thread_pool_sync (self, task);
661 : :
662 : 0 : name = g_task_propagate_pointer (task, error);
663 : 0 : g_object_unref (task);
664 : :
665 : 0 : return name;
666 : : }
667 : :
668 : : static void
669 : 0 : lookup_by_address_async (GResolver *resolver,
670 : : GInetAddress *address,
671 : : GCancellable *cancellable,
672 : : GAsyncReadyCallback callback,
673 : : gpointer user_data)
674 : : {
675 : 0 : GThreadedResolver *self = G_THREADED_RESOLVER (resolver);
676 : 0 : LookupData *data = NULL;
677 : : GTask *task;
678 : :
679 : 0 : data = lookup_data_new_by_address (address);
680 : 0 : task = g_task_new (resolver, cancellable, callback, user_data);
681 : 0 : g_task_set_source_tag (task, lookup_by_address_async);
682 : 0 : g_task_set_name (task, "[gio] resolver lookup");
683 : 0 : g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) lookup_data_free);
684 : :
685 : 0 : run_task_in_thread_pool_async (self, task);
686 : :
687 : 0 : g_object_unref (task);
688 : 0 : }
689 : :
690 : : static gchar *
691 : 0 : lookup_by_address_finish (GResolver *resolver,
692 : : GAsyncResult *result,
693 : : GError **error)
694 : : {
695 : 0 : g_return_val_if_fail (g_task_is_valid (result, resolver), NULL);
696 : :
697 : 0 : return g_task_propagate_pointer (G_TASK (result), error);
698 : : }
699 : :
700 : :
701 : : #if defined(G_OS_UNIX)
702 : :
703 : : #if defined __BIONIC__ && !defined BIND_4_COMPAT
704 : : /* Copy from bionic/libc/private/arpa_nameser_compat.h
705 : : * and bionic/libc/private/arpa_nameser.h */
706 : : typedef struct {
707 : : unsigned id :16; /* query identification number */
708 : : #if BYTE_ORDER == BIG_ENDIAN
709 : : /* fields in third byte */
710 : : unsigned qr: 1; /* response flag */
711 : : unsigned opcode: 4; /* purpose of message */
712 : : unsigned aa: 1; /* authoritative answer */
713 : : unsigned tc: 1; /* truncated message */
714 : : unsigned rd: 1; /* recursion desired */
715 : : /* fields in fourth byte */
716 : : unsigned ra: 1; /* recursion available */
717 : : unsigned unused :1; /* unused bits (MBZ as of 4.9.3a3) */
718 : : unsigned ad: 1; /* authentic data from named */
719 : : unsigned cd: 1; /* checking disabled by resolver */
720 : : unsigned rcode :4; /* response code */
721 : : #endif
722 : : #if BYTE_ORDER == LITTLE_ENDIAN || BYTE_ORDER == PDP_ENDIAN
723 : : /* fields in third byte */
724 : : unsigned rd :1; /* recursion desired */
725 : : unsigned tc :1; /* truncated message */
726 : : unsigned aa :1; /* authoritative answer */
727 : : unsigned opcode :4; /* purpose of message */
728 : : unsigned qr :1; /* response flag */
729 : : /* fields in fourth byte */
730 : : unsigned rcode :4; /* response code */
731 : : unsigned cd: 1; /* checking disabled by resolver */
732 : : unsigned ad: 1; /* authentic data from named */
733 : : unsigned unused :1; /* unused bits (MBZ as of 4.9.3a3) */
734 : : unsigned ra :1; /* recursion available */
735 : : #endif
736 : : /* remaining bytes */
737 : : unsigned qdcount :16; /* number of question entries */
738 : : unsigned ancount :16; /* number of answer entries */
739 : : unsigned nscount :16; /* number of authority entries */
740 : : unsigned arcount :16; /* number of resource entries */
741 : : } HEADER;
742 : :
743 : : #define NS_INT32SZ 4 /* #/bytes of data in a uint32_t */
744 : : #define NS_INT16SZ 2 /* #/bytes of data in a uint16_t */
745 : :
746 : : #define NS_GET16(s, cp) do { \
747 : : const u_char *t_cp = (const u_char *)(cp); \
748 : : (s) = ((uint16_t)t_cp[0] << 8) \
749 : : | ((uint16_t)t_cp[1]) \
750 : : ; \
751 : : (cp) += NS_INT16SZ; \
752 : : } while (/*CONSTCOND*/0)
753 : :
754 : : #define NS_GET32(l, cp) do { \
755 : : const u_char *t_cp = (const u_char *)(cp); \
756 : : (l) = ((uint32_t)t_cp[0] << 24) \
757 : : | ((uint32_t)t_cp[1] << 16) \
758 : : | ((uint32_t)t_cp[2] << 8) \
759 : : | ((uint32_t)t_cp[3]) \
760 : : ; \
761 : : (cp) += NS_INT32SZ; \
762 : : } while (/*CONSTCOND*/0)
763 : :
764 : : #define GETSHORT NS_GET16
765 : : #define GETLONG NS_GET32
766 : :
767 : : #define C_IN 1
768 : :
769 : : /* From bionic/libc/private/resolv_private.h */
770 : : int dn_expand(const u_char *, const u_char *, const u_char *, char *, int);
771 : : #define dn_skipname __dn_skipname
772 : : int dn_skipname(const u_char *, const u_char *);
773 : :
774 : : /* From bionic/libc/private/arpa_nameser_compat.h */
775 : : #define T_MX ns_t_mx
776 : : #define T_TXT ns_t_txt
777 : : #define T_SOA ns_t_soa
778 : : #define T_NS ns_t_ns
779 : :
780 : : /* From bionic/libc/private/arpa_nameser.h */
781 : : typedef enum __ns_type {
782 : : ns_t_invalid = 0, /* Cookie. */
783 : : ns_t_a = 1, /* Host address. */
784 : : ns_t_ns = 2, /* Authoritative server. */
785 : : ns_t_md = 3, /* Mail destination. */
786 : : ns_t_mf = 4, /* Mail forwarder. */
787 : : ns_t_cname = 5, /* Canonical name. */
788 : : ns_t_soa = 6, /* Start of authority zone. */
789 : : ns_t_mb = 7, /* Mailbox domain name. */
790 : : ns_t_mg = 8, /* Mail group member. */
791 : : ns_t_mr = 9, /* Mail rename name. */
792 : : ns_t_null = 10, /* Null resource record. */
793 : : ns_t_wks = 11, /* Well known service. */
794 : : ns_t_ptr = 12, /* Domain name pointer. */
795 : : ns_t_hinfo = 13, /* Host information. */
796 : : ns_t_minfo = 14, /* Mailbox information. */
797 : : ns_t_mx = 15, /* Mail routing information. */
798 : : ns_t_txt = 16, /* Text strings. */
799 : : ns_t_rp = 17, /* Responsible person. */
800 : : ns_t_afsdb = 18, /* AFS cell database. */
801 : : ns_t_x25 = 19, /* X_25 calling address. */
802 : : ns_t_isdn = 20, /* ISDN calling address. */
803 : : ns_t_rt = 21, /* Router. */
804 : : ns_t_nsap = 22, /* NSAP address. */
805 : : ns_t_nsap_ptr = 23, /* Reverse NSAP lookup (deprecated). */
806 : : ns_t_sig = 24, /* Security signature. */
807 : : ns_t_key = 25, /* Security key. */
808 : : ns_t_px = 26, /* X.400 mail mapping. */
809 : : ns_t_gpos = 27, /* Geographical position (withdrawn). */
810 : : ns_t_aaaa = 28, /* Ip6 Address. */
811 : : ns_t_loc = 29, /* Location Information. */
812 : : ns_t_nxt = 30, /* Next domain (security). */
813 : : ns_t_eid = 31, /* Endpoint identifier. */
814 : : ns_t_nimloc = 32, /* Nimrod Locator. */
815 : : ns_t_srv = 33, /* Server Selection. */
816 : : ns_t_atma = 34, /* ATM Address */
817 : : ns_t_naptr = 35, /* Naming Authority PoinTeR */
818 : : ns_t_kx = 36, /* Key Exchange */
819 : : ns_t_cert = 37, /* Certification record */
820 : : ns_t_a6 = 38, /* IPv6 address (deprecates AAAA) */
821 : : ns_t_dname = 39, /* Non-terminal DNAME (for IPv6) */
822 : : ns_t_sink = 40, /* Kitchen sink (experimental) */
823 : : ns_t_opt = 41, /* EDNS0 option (meta-RR) */
824 : : ns_t_apl = 42, /* Address prefix list (RFC 3123) */
825 : : ns_t_tkey = 249, /* Transaction key */
826 : : ns_t_tsig = 250, /* Transaction signature. */
827 : : ns_t_ixfr = 251, /* Incremental zone transfer. */
828 : : ns_t_axfr = 252, /* Transfer zone of authority. */
829 : : ns_t_mailb = 253, /* Transfer mailbox records. */
830 : : ns_t_maila = 254, /* Transfer mail agent records. */
831 : : ns_t_any = 255, /* Wildcard match. */
832 : : ns_t_zxfr = 256, /* BIND-specific, nonstandard. */
833 : : ns_t_max = 65536
834 : : } ns_type;
835 : :
836 : : #endif /* __BIONIC__ */
837 : :
838 : : /* Wrapper around dn_expand() which does associated length checks and returns
839 : : * errors as #GError. */
840 : : static gboolean
841 : 15 : expand_name (const gchar *rrname,
842 : : const guint8 *answer,
843 : : const guint8 *end,
844 : : const guint8 **p,
845 : : gchar *namebuf,
846 : : gsize namebuf_len,
847 : : GError **error)
848 : : {
849 : : int expand_result;
850 : :
851 : 15 : expand_result = dn_expand (answer, end, *p, namebuf, namebuf_len);
852 : 15 : if (expand_result < 0 || end - *p < expand_result)
853 : : {
854 : 7 : g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
855 : : /* Translators: the placeholder is a DNS record type, such as ‘MX’ or ‘SRV’ */
856 : : _("Error parsing DNS %s record: malformed DNS packet"), rrname);
857 : 7 : return FALSE;
858 : : }
859 : :
860 : 8 : *p += expand_result;
861 : :
862 : 8 : return TRUE;
863 : : }
864 : :
865 : : static GVariant *
866 : 4 : parse_res_srv (const guint8 *answer,
867 : : const guint8 *end,
868 : : const guint8 **p,
869 : : GError **error)
870 : : {
871 : : gchar namebuf[1024];
872 : : guint16 priority, weight, port;
873 : :
874 : 4 : if (end - *p < 6)
875 : : {
876 : 1 : g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
877 : : /* Translators: the placeholder is a DNS record type, such as ‘MX’ or ‘SRV’ */
878 : : _("Error parsing DNS %s record: malformed DNS packet"), "SRV");
879 : 1 : return NULL;
880 : : }
881 : :
882 : 3 : GETSHORT (priority, *p);
883 : 3 : GETSHORT (weight, *p);
884 : 3 : GETSHORT (port, *p);
885 : :
886 : : /* RFC 2782 says (on page 4) that “Unless and until permitted by future
887 : : * standards action, name compression is not to be used for this field.”, so
888 : : * technically we shouldn’t be expanding names here for SRV records.
889 : : *
890 : : * However, other DNS resolvers (such as systemd[1]) do, and it seems in
891 : : * keeping with the principle of being liberal in what you accept and strict
892 : : * in what you emit. It also seems harmless.
893 : : *
894 : : * An earlier version of the RFC, RFC 2052 (now obsolete) specified that name
895 : : * compression *was* to be used for SRV targets[2].
896 : : *
897 : : * See discussion on https://gitlab.gnome.org/GNOME/glib/-/issues/2622.
898 : : *
899 : : * [1]: https://github.com/yuwata/systemd/blob/2d23cc3c07c49722ce93170737b3efd2692a2d08/src/resolve/resolved-dns-packet.c#L1674
900 : : * [2]: https://datatracker.ietf.org/doc/html/rfc2052#page-3
901 : : */
902 : 3 : if (!expand_name ("SRV", answer, end, p, namebuf, sizeof (namebuf), error))
903 : 2 : return NULL;
904 : :
905 : 1 : return g_variant_new ("(qqqs)",
906 : : priority,
907 : : weight,
908 : : port,
909 : : namebuf);
910 : : }
911 : :
912 : : static GVariant *
913 : 4 : parse_res_soa (const guint8 *answer,
914 : : const guint8 *end,
915 : : const guint8 **p,
916 : : GError **error)
917 : : {
918 : : gchar mnamebuf[1024];
919 : : gchar rnamebuf[1024];
920 : : guint32 serial, refresh, retry, expire, ttl;
921 : :
922 : 4 : if (!expand_name ("SOA", answer, end, p, mnamebuf, sizeof (mnamebuf), error))
923 : 1 : return NULL;
924 : :
925 : 3 : if (!expand_name ("SOA", answer, end, p, rnamebuf, sizeof (rnamebuf), error))
926 : 1 : return NULL;
927 : :
928 : 2 : if (end - *p < 20)
929 : : {
930 : 1 : g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
931 : : /* Translators: the placeholder is a DNS record type, such as ‘MX’ or ‘SRV’ */
932 : : _("Error parsing DNS %s record: malformed DNS packet"), "SOA");
933 : 1 : return NULL;
934 : : }
935 : :
936 : 1 : GETLONG (serial, *p);
937 : 1 : GETLONG (refresh, *p);
938 : 1 : GETLONG (retry, *p);
939 : 1 : GETLONG (expire, *p);
940 : 1 : GETLONG (ttl, *p);
941 : :
942 : 1 : return g_variant_new ("(ssuuuuu)",
943 : : mnamebuf,
944 : : rnamebuf,
945 : : serial,
946 : : refresh,
947 : : retry,
948 : : expire,
949 : : ttl);
950 : : }
951 : :
952 : : static GVariant *
953 : 2 : parse_res_ns (const guint8 *answer,
954 : : const guint8 *end,
955 : : const guint8 **p,
956 : : GError **error)
957 : : {
958 : : gchar namebuf[1024];
959 : :
960 : 2 : if (!expand_name ("NS", answer, end, p, namebuf, sizeof (namebuf), error))
961 : 1 : return NULL;
962 : :
963 : 1 : return g_variant_new ("(s)", namebuf);
964 : : }
965 : :
966 : : static GVariant *
967 : 4 : parse_res_mx (const guint8 *answer,
968 : : const guint8 *end,
969 : : const guint8 **p,
970 : : GError **error)
971 : : {
972 : : gchar namebuf[1024];
973 : : guint16 preference;
974 : :
975 : 4 : if (end - *p < 2)
976 : : {
977 : 1 : g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
978 : : /* Translators: the placeholder is a DNS record type, such as ‘MX’ or ‘SRV’ */
979 : : _("Error parsing DNS %s record: malformed DNS packet"), "MX");
980 : 1 : return NULL;
981 : : }
982 : :
983 : 3 : GETSHORT (preference, *p);
984 : :
985 : 3 : if (!expand_name ("MX", answer, end, p, namebuf, sizeof (namebuf), error))
986 : 2 : return NULL;
987 : :
988 : 1 : return g_variant_new ("(qs)",
989 : : preference,
990 : : namebuf);
991 : : }
992 : :
993 : : static GVariant *
994 : 5 : parse_res_txt (const guint8 *answer,
995 : : const guint8 *end,
996 : : const guint8 **p,
997 : : GError **error)
998 : : {
999 : : GVariant *record;
1000 : : GPtrArray *array;
1001 : 5 : const guint8 *at = *p;
1002 : : gsize len;
1003 : :
1004 : 5 : if (end - *p == 0)
1005 : : {
1006 : 1 : g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
1007 : : /* Translators: the placeholder is a DNS record type, such as ‘MX’ or ‘SRV’ */
1008 : : _("Error parsing DNS %s record: malformed DNS packet"), "TXT");
1009 : 1 : return NULL;
1010 : : }
1011 : :
1012 : 4 : array = g_ptr_array_new_with_free_func (g_free);
1013 : 8 : while (at < end)
1014 : : {
1015 : 5 : len = *(at++);
1016 : 5 : if (len > (gsize) (end - at))
1017 : : {
1018 : 1 : g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
1019 : : /* Translators: the placeholder is a DNS record type, such as ‘MX’ or ‘SRV’ */
1020 : : _("Error parsing DNS %s record: malformed DNS packet"), "TXT");
1021 : 1 : g_ptr_array_free (array, TRUE);
1022 : 1 : return NULL;
1023 : : }
1024 : :
1025 : 4 : g_ptr_array_add (array, g_strndup ((gchar *)at, len));
1026 : 4 : at += len;
1027 : : }
1028 : :
1029 : 3 : *p = at;
1030 : 3 : record = g_variant_new ("(@as)",
1031 : 3 : g_variant_new_strv ((const gchar **)array->pdata, array->len));
1032 : 3 : g_ptr_array_free (array, TRUE);
1033 : 3 : return record;
1034 : : }
1035 : :
1036 : : gint
1037 : 41 : g_resolver_record_type_to_rrtype (GResolverRecordType type)
1038 : : {
1039 : 41 : switch (type)
1040 : : {
1041 : 8 : case G_RESOLVER_RECORD_SRV:
1042 : 8 : return T_SRV;
1043 : 9 : case G_RESOLVER_RECORD_TXT:
1044 : 9 : return T_TXT;
1045 : 8 : case G_RESOLVER_RECORD_SOA:
1046 : 8 : return T_SOA;
1047 : 8 : case G_RESOLVER_RECORD_NS:
1048 : 8 : return T_NS;
1049 : 8 : case G_RESOLVER_RECORD_MX:
1050 : 8 : return T_MX;
1051 : : }
1052 : : g_return_val_if_reached (-1);
1053 : : }
1054 : :
1055 : : GList *
1056 : 30 : g_resolver_records_from_res_query (const gchar *rrname,
1057 : : gint rrtype,
1058 : : const guint8 *answer,
1059 : : gssize len,
1060 : : gint herr,
1061 : : GError **error)
1062 : : {
1063 : : uint16_t count;
1064 : : gchar namebuf[1024];
1065 : : const guint8 *end, *p;
1066 : : guint16 type, qclass, rdlength;
1067 : : const HEADER *header;
1068 : : GList *records;
1069 : : GVariant *record;
1070 : : gsize len_unsigned;
1071 : 30 : GError *parsing_error = NULL;
1072 : :
1073 : 30 : if (len <= 0)
1074 : : {
1075 : 1 : if (len == 0 || herr == HOST_NOT_FOUND || herr == NO_DATA)
1076 : : {
1077 : 1 : g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND,
1078 : : _("No DNS record of the requested type for “%s”"), rrname);
1079 : : }
1080 : 0 : else if (herr == TRY_AGAIN)
1081 : : {
1082 : 0 : g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_TEMPORARY_FAILURE,
1083 : : _("Temporarily unable to resolve “%s”"), rrname);
1084 : : }
1085 : : else
1086 : : {
1087 : 0 : g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
1088 : : _("Error resolving “%s”"), rrname);
1089 : : }
1090 : :
1091 : 1 : return NULL;
1092 : : }
1093 : :
1094 : : /* We know len ≥ 0 now. */
1095 : 29 : len_unsigned = (gsize) len;
1096 : :
1097 : 29 : if (len_unsigned < sizeof (HEADER))
1098 : : {
1099 : 2 : g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
1100 : : /* Translators: the first placeholder is a domain name, the
1101 : : * second is an error message */
1102 : : _("Error resolving “%s”: %s"), rrname, _("Malformed DNS packet"));
1103 : 2 : return NULL;
1104 : : }
1105 : :
1106 : 27 : records = NULL;
1107 : :
1108 : 27 : header = (HEADER *)answer;
1109 : 27 : p = answer + sizeof (HEADER);
1110 : 27 : end = answer + len_unsigned;
1111 : :
1112 : : /* Skip query */
1113 : 27 : count = ntohs (header->qdcount);
1114 : 27 : while (count-- && p < end)
1115 : : {
1116 : : int expand_result;
1117 : :
1118 : 6 : expand_result = dn_expand (answer, end, p, namebuf, sizeof (namebuf));
1119 : 6 : if (expand_result < 0 || end - p < expand_result + 4)
1120 : : {
1121 : : /* Not possible to recover parsing as the length of the rest of the
1122 : : * record is unknown or is too short. */
1123 : 6 : g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
1124 : : /* Translators: the first placeholder is a domain name, the
1125 : : * second is an error message */
1126 : : _("Error resolving “%s”: %s"), rrname, _("Malformed DNS packet"));
1127 : 6 : return NULL;
1128 : : }
1129 : :
1130 : 0 : p += expand_result;
1131 : 0 : p += 4; /* skip TYPE and CLASS */
1132 : :
1133 : : /* To silence gcc warnings */
1134 : 0 : namebuf[0] = namebuf[1];
1135 : : }
1136 : :
1137 : : /* Read answers */
1138 : 21 : count = ntohs (header->ancount);
1139 : 29 : while (count-- && p < end)
1140 : : {
1141 : : int expand_result;
1142 : :
1143 : 20 : expand_result = dn_expand (answer, end, p, namebuf, sizeof (namebuf));
1144 : 20 : if (expand_result < 0 || end - p < expand_result + 10)
1145 : : {
1146 : : /* Not possible to recover parsing as the length of the rest of the
1147 : : * record is unknown or is too short. */
1148 : 0 : g_set_error (&parsing_error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
1149 : : /* Translators: the first placeholder is a domain name, the
1150 : : * second is an error message */
1151 : : _("Error resolving “%s”: %s"), rrname, _("Malformed DNS packet"));
1152 : 0 : break;
1153 : : }
1154 : :
1155 : 20 : p += expand_result;
1156 : 20 : GETSHORT (type, p);
1157 : 20 : GETSHORT (qclass, p);
1158 : 20 : p += 4; /* ignore the ttl (type=long) value */
1159 : 20 : GETSHORT (rdlength, p);
1160 : :
1161 : 20 : if (end - p < rdlength)
1162 : : {
1163 : 0 : g_set_error (&parsing_error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
1164 : : /* Translators: the first placeholder is a domain name, the
1165 : : * second is an error message */
1166 : : _("Error resolving “%s”: %s"), rrname, _("Malformed DNS packet"));
1167 : 0 : break;
1168 : : }
1169 : :
1170 : 20 : if (type != rrtype || qclass != C_IN)
1171 : : {
1172 : 0 : p += rdlength;
1173 : 0 : continue;
1174 : : }
1175 : :
1176 : 20 : switch (rrtype)
1177 : : {
1178 : 4 : case T_SRV:
1179 : 4 : record = parse_res_srv (answer, p + rdlength, &p, &parsing_error);
1180 : 4 : break;
1181 : 4 : case T_MX:
1182 : 4 : record = parse_res_mx (answer, p + rdlength, &p, &parsing_error);
1183 : 4 : break;
1184 : 4 : case T_SOA:
1185 : 4 : record = parse_res_soa (answer, p + rdlength, &p, &parsing_error);
1186 : 4 : break;
1187 : 2 : case T_NS:
1188 : 2 : record = parse_res_ns (answer, p + rdlength, &p, &parsing_error);
1189 : 2 : break;
1190 : 5 : case T_TXT:
1191 : 5 : record = parse_res_txt (answer, p + rdlength, &p, &parsing_error);
1192 : 5 : break;
1193 : 1 : default:
1194 : 1 : g_debug ("Unrecognized DNS record type %u", rrtype);
1195 : 1 : record = NULL;
1196 : 1 : break;
1197 : : }
1198 : :
1199 : 20 : if (record != NULL)
1200 : 7 : records = g_list_prepend (records, g_variant_ref_sink (record));
1201 : :
1202 : 20 : if (parsing_error != NULL)
1203 : 12 : break;
1204 : : }
1205 : :
1206 : 21 : if (parsing_error != NULL)
1207 : : {
1208 : 12 : g_propagate_prefixed_error (error, parsing_error, _("Failed to parse DNS response for “%s”: "), rrname);
1209 : 12 : g_list_free_full (records, (GDestroyNotify)g_variant_unref);
1210 : 12 : return NULL;
1211 : : }
1212 : 9 : else if (records == NULL)
1213 : : {
1214 : 2 : g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND,
1215 : : _("No DNS record of the requested type for “%s”"), rrname);
1216 : :
1217 : 2 : return NULL;
1218 : : }
1219 : : else
1220 : 7 : return records;
1221 : : }
1222 : :
1223 : : #elif defined(G_OS_WIN32)
1224 : :
1225 : : static GVariant *
1226 : : parse_dns_srv (DNS_RECORDA *rec)
1227 : : {
1228 : : return g_variant_new ("(qqqs)",
1229 : : (guint16)rec->Data.SRV.wPriority,
1230 : : (guint16)rec->Data.SRV.wWeight,
1231 : : (guint16)rec->Data.SRV.wPort,
1232 : : rec->Data.SRV.pNameTarget);
1233 : : }
1234 : :
1235 : : static GVariant *
1236 : : parse_dns_soa (DNS_RECORDA *rec)
1237 : : {
1238 : : return g_variant_new ("(ssuuuuu)",
1239 : : rec->Data.SOA.pNamePrimaryServer,
1240 : : rec->Data.SOA.pNameAdministrator,
1241 : : (guint32)rec->Data.SOA.dwSerialNo,
1242 : : (guint32)rec->Data.SOA.dwRefresh,
1243 : : (guint32)rec->Data.SOA.dwRetry,
1244 : : (guint32)rec->Data.SOA.dwExpire,
1245 : : (guint32)rec->Data.SOA.dwDefaultTtl);
1246 : : }
1247 : :
1248 : : static GVariant *
1249 : : parse_dns_ns (DNS_RECORDA *rec)
1250 : : {
1251 : : return g_variant_new ("(s)", rec->Data.NS.pNameHost);
1252 : : }
1253 : :
1254 : : static GVariant *
1255 : : parse_dns_mx (DNS_RECORDA *rec)
1256 : : {
1257 : : return g_variant_new ("(qs)",
1258 : : (guint16)rec->Data.MX.wPreference,
1259 : : rec->Data.MX.pNameExchange);
1260 : : }
1261 : :
1262 : : static GVariant *
1263 : : parse_dns_txt (DNS_RECORDA *rec)
1264 : : {
1265 : : GVariant *record;
1266 : : GPtrArray *array;
1267 : : DWORD i;
1268 : :
1269 : : array = g_ptr_array_new ();
1270 : : for (i = 0; i < rec->Data.TXT.dwStringCount; i++)
1271 : : g_ptr_array_add (array, rec->Data.TXT.pStringArray[i]);
1272 : : record = g_variant_new ("(@as)",
1273 : : g_variant_new_strv ((const gchar **)array->pdata, array->len));
1274 : : g_ptr_array_free (array, TRUE);
1275 : : return record;
1276 : : }
1277 : :
1278 : : static WORD
1279 : : g_resolver_record_type_to_dnstype (GResolverRecordType type)
1280 : : {
1281 : : switch (type)
1282 : : {
1283 : : case G_RESOLVER_RECORD_SRV:
1284 : : return DNS_TYPE_SRV;
1285 : : case G_RESOLVER_RECORD_TXT:
1286 : : return DNS_TYPE_TEXT;
1287 : : case G_RESOLVER_RECORD_SOA:
1288 : : return DNS_TYPE_SOA;
1289 : : case G_RESOLVER_RECORD_NS:
1290 : : return DNS_TYPE_NS;
1291 : : case G_RESOLVER_RECORD_MX:
1292 : : return DNS_TYPE_MX;
1293 : : }
1294 : : g_return_val_if_reached (-1);
1295 : : }
1296 : :
1297 : : static GList *
1298 : : g_resolver_records_from_DnsQuery (const gchar *rrname,
1299 : : WORD dnstype,
1300 : : DNS_STATUS status,
1301 : : DNS_RECORDA *results,
1302 : : GError **error)
1303 : : {
1304 : : DNS_RECORDA *rec;
1305 : : gpointer record;
1306 : : GList *records;
1307 : :
1308 : : if (status != ERROR_SUCCESS)
1309 : : {
1310 : : if (status == DNS_ERROR_RCODE_NAME_ERROR)
1311 : : {
1312 : : g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND,
1313 : : _("No DNS record of the requested type for “%s”"), rrname);
1314 : : }
1315 : : else if (status == DNS_ERROR_RCODE_SERVER_FAILURE)
1316 : : {
1317 : : g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_TEMPORARY_FAILURE,
1318 : : _("Temporarily unable to resolve “%s”"), rrname);
1319 : : }
1320 : : else
1321 : : {
1322 : : g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
1323 : : _("Error resolving “%s”"), rrname);
1324 : : }
1325 : :
1326 : : return NULL;
1327 : : }
1328 : :
1329 : : records = NULL;
1330 : : for (rec = results; rec; rec = rec->pNext)
1331 : : {
1332 : : if (rec->wType != dnstype)
1333 : : continue;
1334 : : switch (dnstype)
1335 : : {
1336 : : case DNS_TYPE_SRV:
1337 : : record = parse_dns_srv (rec);
1338 : : break;
1339 : : case DNS_TYPE_SOA:
1340 : : record = parse_dns_soa (rec);
1341 : : break;
1342 : : case DNS_TYPE_NS:
1343 : : record = parse_dns_ns (rec);
1344 : : break;
1345 : : case DNS_TYPE_MX:
1346 : : record = parse_dns_mx (rec);
1347 : : break;
1348 : : case DNS_TYPE_TEXT:
1349 : : record = parse_dns_txt (rec);
1350 : : break;
1351 : : default:
1352 : : g_warn_if_reached ();
1353 : : record = NULL;
1354 : : break;
1355 : : }
1356 : : if (record != NULL)
1357 : : records = g_list_prepend (records, g_variant_ref_sink (record));
1358 : : }
1359 : :
1360 : : if (records == NULL)
1361 : : {
1362 : : g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND,
1363 : : _("No DNS record of the requested type for “%s”"), rrname);
1364 : :
1365 : : return NULL;
1366 : : }
1367 : : else
1368 : : return records;
1369 : : }
1370 : :
1371 : : #endif
1372 : :
1373 : : static void
1374 : 0 : free_records (GList *records)
1375 : : {
1376 : 0 : g_list_free_full (records, (GDestroyNotify) g_variant_unref);
1377 : 0 : }
1378 : :
1379 : : #if defined(G_OS_UNIX)
1380 : : #ifdef __BIONIC__
1381 : : #ifndef C_IN
1382 : : #define C_IN 1
1383 : : #endif
1384 : : int res_query(const char *, int, int, u_char *, int);
1385 : : #endif
1386 : : #endif
1387 : :
1388 : : static GList *
1389 : 0 : do_lookup_records (const gchar *rrname,
1390 : : GResolverRecordType record_type,
1391 : : GCancellable *cancellable,
1392 : : GError **error)
1393 : : {
1394 : : GList *records;
1395 : :
1396 : : #if defined(G_OS_UNIX)
1397 : 0 : gint len = 512;
1398 : : gint herr;
1399 : : GByteArray *answer;
1400 : : gint rrtype;
1401 : :
1402 : : #ifdef HAVE_RES_NQUERY
1403 : : /* Load the resolver state. This is done once per worker thread, and the
1404 : : * #GResolver::reload signal is ignored (since we always reload). This could
1405 : : * be improved by having an explicit worker thread pool, with each thread
1406 : : * containing some state which is initialised at thread creation time and
1407 : : * updated in response to #GResolver::reload.
1408 : : *
1409 : : * What we have currently is not particularly worse than using res_query() in
1410 : : * worker threads, since it would transparently call res_init() for each new
1411 : : * worker thread. (Although the workers would get reused by the
1412 : : * #GThreadPool.)
1413 : : *
1414 : : * FreeBSD requires the state to be zero-filled before calling res_ninit(). */
1415 : 0 : struct __res_state res = { 0, };
1416 : 0 : if (res_ninit (&res) != 0)
1417 : : {
1418 : 0 : g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
1419 : : _("Error resolving “%s”"), rrname);
1420 : 0 : return NULL;
1421 : : }
1422 : : #endif
1423 : :
1424 : 0 : rrtype = g_resolver_record_type_to_rrtype (record_type);
1425 : 0 : answer = g_byte_array_new ();
1426 : : for (;;)
1427 : : {
1428 : 0 : g_byte_array_set_size (answer, len * 2);
1429 : : #if defined(HAVE_RES_NQUERY)
1430 : 0 : len = res_nquery (&res, rrname, C_IN, rrtype, answer->data, answer->len);
1431 : : #else
1432 : : len = res_query (rrname, C_IN, rrtype, answer->data, answer->len);
1433 : : #endif
1434 : :
1435 : : /* If answer fit in the buffer then we're done */
1436 : 0 : if (len < 0 || len < (gint)answer->len)
1437 : : break;
1438 : :
1439 : : /*
1440 : : * On overflow some res_query's return the length needed, others
1441 : : * return the full length entered. This code works in either case.
1442 : : */
1443 : : }
1444 : :
1445 : 0 : herr = h_errno;
1446 : 0 : records = g_resolver_records_from_res_query (rrname, rrtype, answer->data, len, herr, error);
1447 : 0 : g_byte_array_free (answer, TRUE);
1448 : :
1449 : : #ifdef HAVE_RES_NQUERY
1450 : :
1451 : : #if defined(HAVE_RES_NDESTROY)
1452 : : res_ndestroy (&res);
1453 : : #elif defined(HAVE_RES_NCLOSE)
1454 : 0 : res_nclose (&res);
1455 : : #elif defined(HAVE_RES_NINIT)
1456 : : #error "Your platform has res_ninit() but not res_nclose() or res_ndestroy(). Please file a bug at https://gitlab.gnome.org/GNOME/glib/issues/new"
1457 : : #endif
1458 : :
1459 : : #endif /* HAVE_RES_NQUERY */
1460 : :
1461 : : #else
1462 : :
1463 : : DNS_STATUS status;
1464 : : DNS_RECORDA *results = NULL;
1465 : : WORD dnstype;
1466 : :
1467 : : /* Work around differences in Windows SDK and mingw-w64 headers */
1468 : : #ifdef _MSC_VER
1469 : : typedef DNS_RECORDW * PDNS_RECORD_UTF8_;
1470 : : #else
1471 : : typedef DNS_RECORDA * PDNS_RECORD_UTF8_;
1472 : : #endif
1473 : :
1474 : : dnstype = g_resolver_record_type_to_dnstype (record_type);
1475 : : status = DnsQuery_UTF8 (rrname, dnstype, DNS_QUERY_STANDARD, NULL, (PDNS_RECORD_UTF8_*)&results, NULL);
1476 : : records = g_resolver_records_from_DnsQuery (rrname, dnstype, status, results, error);
1477 : : if (results != NULL)
1478 : : DnsRecordListFree (results, DnsFreeRecordList);
1479 : :
1480 : : #endif
1481 : :
1482 : 0 : return g_steal_pointer (&records);
1483 : : }
1484 : :
1485 : : static GList *
1486 : 0 : lookup_records (GResolver *resolver,
1487 : : const gchar *rrname,
1488 : : GResolverRecordType record_type,
1489 : : GCancellable *cancellable,
1490 : : GError **error)
1491 : : {
1492 : 0 : GThreadedResolver *self = G_THREADED_RESOLVER (resolver);
1493 : : GTask *task;
1494 : : GList *records;
1495 : 0 : LookupData *data = NULL;
1496 : :
1497 : 0 : task = g_task_new (resolver, cancellable, NULL, NULL);
1498 : 0 : g_task_set_source_tag (task, lookup_records);
1499 : 0 : g_task_set_name (task, "[gio] resolver lookup records");
1500 : :
1501 : 0 : data = lookup_data_new_records (rrname, record_type);
1502 : 0 : g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) lookup_data_free);
1503 : :
1504 : 0 : run_task_in_thread_pool_sync (self, task);
1505 : :
1506 : 0 : records = g_task_propagate_pointer (task, error);
1507 : 0 : g_object_unref (task);
1508 : :
1509 : 0 : return records;
1510 : : }
1511 : :
1512 : : static void
1513 : 0 : lookup_records_async (GResolver *resolver,
1514 : : const char *rrname,
1515 : : GResolverRecordType record_type,
1516 : : GCancellable *cancellable,
1517 : : GAsyncReadyCallback callback,
1518 : : gpointer user_data)
1519 : : {
1520 : 0 : GThreadedResolver *self = G_THREADED_RESOLVER (resolver);
1521 : : GTask *task;
1522 : 0 : LookupData *data = NULL;
1523 : :
1524 : 0 : task = g_task_new (resolver, cancellable, callback, user_data);
1525 : 0 : g_task_set_source_tag (task, lookup_records_async);
1526 : 0 : g_task_set_name (task, "[gio] resolver lookup records");
1527 : :
1528 : 0 : data = lookup_data_new_records (rrname, record_type);
1529 : 0 : g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) lookup_data_free);
1530 : :
1531 : 0 : run_task_in_thread_pool_async (self, task);
1532 : :
1533 : 0 : g_object_unref (task);
1534 : 0 : }
1535 : :
1536 : : static GList *
1537 : 0 : lookup_records_finish (GResolver *resolver,
1538 : : GAsyncResult *result,
1539 : : GError **error)
1540 : : {
1541 : 0 : g_return_val_if_fail (g_task_is_valid (result, resolver), NULL);
1542 : :
1543 : 0 : return g_task_propagate_pointer (G_TASK (result), error);
1544 : : }
1545 : :
1546 : : /* Will be called in the GLib worker thread, so must lock all accesses to shared
1547 : : * data. */
1548 : : static gboolean
1549 : 0 : timeout_cb (gpointer user_data)
1550 : : {
1551 : 0 : GWeakRef *weak_task = user_data;
1552 : 0 : GTask *task = NULL; /* (owned) */
1553 : : LookupData *data;
1554 : : gboolean should_return;
1555 : :
1556 : 0 : task = g_weak_ref_get (weak_task);
1557 : 0 : if (task == NULL)
1558 : 0 : return G_SOURCE_REMOVE;
1559 : :
1560 : 0 : data = g_task_get_task_data (task);
1561 : :
1562 : 0 : g_mutex_lock (&data->lock);
1563 : :
1564 : 0 : should_return = g_atomic_int_compare_and_exchange (&data->will_return, NOT_YET, TIMED_OUT);
1565 : 0 : g_clear_pointer (&data->timeout_source, g_source_unref);
1566 : :
1567 : 0 : g_mutex_unlock (&data->lock);
1568 : :
1569 : 0 : if (should_return)
1570 : : {
1571 : 0 : g_task_return_new_error_literal (task, G_IO_ERROR, G_IO_ERROR_TIMED_OUT,
1572 : 0 : _("Socket I/O timed out"));
1573 : : }
1574 : :
1575 : : /* Signal completion of the task. */
1576 : 0 : g_mutex_lock (&data->lock);
1577 : 0 : data->has_returned = TRUE;
1578 : 0 : g_cond_broadcast (&data->cond);
1579 : 0 : g_mutex_unlock (&data->lock);
1580 : :
1581 : 0 : g_object_unref (task);
1582 : :
1583 : 0 : return G_SOURCE_REMOVE;
1584 : : }
1585 : :
1586 : : /* Will be called in the GLib worker thread, so must lock all accesses to shared
1587 : : * data. */
1588 : : static gboolean
1589 : 0 : cancelled_cb (GCancellable *cancellable,
1590 : : gpointer user_data)
1591 : : {
1592 : 0 : GWeakRef *weak_task = user_data;
1593 : 0 : GTask *task = NULL; /* (owned) */
1594 : : LookupData *data;
1595 : : gboolean should_return;
1596 : :
1597 : 0 : task = g_weak_ref_get (weak_task);
1598 : 0 : if (task == NULL)
1599 : 0 : return G_SOURCE_REMOVE;
1600 : :
1601 : 0 : data = g_task_get_task_data (task);
1602 : :
1603 : 0 : g_mutex_lock (&data->lock);
1604 : :
1605 : 0 : g_assert (g_cancellable_is_cancelled (cancellable));
1606 : 0 : should_return = g_atomic_int_compare_and_exchange (&data->will_return, NOT_YET, CANCELLED);
1607 : 0 : g_clear_pointer (&data->cancellable_source, g_source_unref);
1608 : :
1609 : 0 : g_mutex_unlock (&data->lock);
1610 : :
1611 : 0 : if (should_return)
1612 : 0 : g_task_return_error_if_cancelled (task);
1613 : :
1614 : : /* Signal completion of the task. */
1615 : 0 : g_mutex_lock (&data->lock);
1616 : 0 : data->has_returned = TRUE;
1617 : 0 : g_cond_broadcast (&data->cond);
1618 : 0 : g_mutex_unlock (&data->lock);
1619 : :
1620 : 0 : g_object_unref (task);
1621 : :
1622 : 0 : return G_SOURCE_REMOVE;
1623 : : }
1624 : :
1625 : : static void
1626 : 0 : weak_ref_clear_and_free (GWeakRef *weak_ref)
1627 : : {
1628 : 0 : g_weak_ref_clear (weak_ref);
1629 : 0 : g_free (weak_ref);
1630 : 0 : }
1631 : :
1632 : : static void
1633 : 3 : run_task_in_thread_pool_async (GThreadedResolver *self,
1634 : : GTask *task)
1635 : : {
1636 : 3 : LookupData *data = g_task_get_task_data (task);
1637 : 3 : guint timeout_ms = g_resolver_get_timeout (G_RESOLVER (self));
1638 : 3 : GCancellable *cancellable = g_task_get_cancellable (task);
1639 : :
1640 : 3 : g_mutex_lock (&data->lock);
1641 : :
1642 : 3 : g_thread_pool_push (self->thread_pool, g_object_ref (task), NULL);
1643 : :
1644 : 3 : if (timeout_ms != 0)
1645 : : {
1646 : 3 : GWeakRef *weak_task = g_new0 (GWeakRef, 1);
1647 : 3 : g_weak_ref_set (weak_task, task);
1648 : :
1649 : 3 : data->timeout_source = g_timeout_source_new (timeout_ms);
1650 : 3 : g_source_set_static_name (data->timeout_source, "[gio] threaded resolver timeout");
1651 : 3 : g_source_set_callback (data->timeout_source, G_SOURCE_FUNC (timeout_cb), g_steal_pointer (&weak_task), (GDestroyNotify) weak_ref_clear_and_free);
1652 : 3 : g_source_attach (data->timeout_source, GLIB_PRIVATE_CALL (g_get_worker_context) ());
1653 : : }
1654 : :
1655 : 3 : if (cancellable != NULL)
1656 : : {
1657 : 0 : GWeakRef *weak_task = g_new0 (GWeakRef, 1);
1658 : 0 : g_weak_ref_set (weak_task, task);
1659 : :
1660 : 0 : data->cancellable_source = g_cancellable_source_new (cancellable);
1661 : 0 : g_source_set_static_name (data->cancellable_source, "[gio] threaded resolver cancellable");
1662 : 0 : g_source_set_callback (data->cancellable_source, G_SOURCE_FUNC (cancelled_cb), g_steal_pointer (&weak_task), (GDestroyNotify) weak_ref_clear_and_free);
1663 : 0 : g_source_attach (data->cancellable_source, GLIB_PRIVATE_CALL (g_get_worker_context) ());
1664 : : }
1665 : :
1666 : 3 : g_mutex_unlock (&data->lock);
1667 : 3 : }
1668 : :
1669 : : static void
1670 : 3 : run_task_in_thread_pool_sync (GThreadedResolver *self,
1671 : : GTask *task)
1672 : : {
1673 : 3 : LookupData *data = g_task_get_task_data (task);
1674 : :
1675 : 3 : run_task_in_thread_pool_async (self, task);
1676 : :
1677 : 3 : g_mutex_lock (&data->lock);
1678 : 6 : while (!data->has_returned)
1679 : 3 : g_cond_wait (&data->cond, &data->lock);
1680 : 3 : g_mutex_unlock (&data->lock);
1681 : 3 : }
1682 : :
1683 : : static void
1684 : 3 : threaded_resolver_worker_cb (gpointer task_data,
1685 : : gpointer user_data)
1686 : : {
1687 : 3 : GTask *task = G_TASK (g_steal_pointer (&task_data));
1688 : 3 : LookupData *data = g_task_get_task_data (task);
1689 : 3 : GCancellable *cancellable = g_task_get_cancellable (task);
1690 : 3 : GThreadedResolver *resolver = G_THREADED_RESOLVER (user_data);
1691 : 3 : GError *local_error = NULL;
1692 : : gboolean should_return;
1693 : :
1694 : 3 : switch (data->lookup_type) {
1695 : 3 : case LOOKUP_BY_NAME:
1696 : : {
1697 : 6 : GList *addresses = do_lookup_by_name (resolver,
1698 : 3 : data->lookup_by_name.hostname,
1699 : : data->lookup_by_name.address_family,
1700 : : cancellable,
1701 : : &local_error);
1702 : :
1703 : 3 : g_mutex_lock (&data->lock);
1704 : 3 : should_return = g_atomic_int_compare_and_exchange (&data->will_return, NOT_YET, COMPLETED);
1705 : 3 : g_mutex_unlock (&data->lock);
1706 : :
1707 : 3 : if (should_return)
1708 : : {
1709 : 3 : if (addresses != NULL)
1710 : 0 : g_task_return_pointer (task, g_steal_pointer (&addresses), (GDestroyNotify) g_resolver_free_addresses);
1711 : : else
1712 : 3 : g_task_return_error (task, g_steal_pointer (&local_error));
1713 : : }
1714 : :
1715 : 3 : g_clear_pointer (&addresses, g_resolver_free_addresses);
1716 : 3 : g_clear_error (&local_error);
1717 : : }
1718 : 3 : break;
1719 : 0 : case LOOKUP_BY_ADDRESS:
1720 : : {
1721 : 0 : gchar *name = do_lookup_by_address (data->lookup_by_address.address,
1722 : : cancellable,
1723 : : &local_error);
1724 : :
1725 : 0 : g_mutex_lock (&data->lock);
1726 : 0 : should_return = g_atomic_int_compare_and_exchange (&data->will_return, NOT_YET, COMPLETED);
1727 : 0 : g_mutex_unlock (&data->lock);
1728 : :
1729 : 0 : if (should_return)
1730 : : {
1731 : 0 : if (name != NULL)
1732 : 0 : g_task_return_pointer (task, g_steal_pointer (&name), g_free);
1733 : : else
1734 : 0 : g_task_return_error (task, g_steal_pointer (&local_error));
1735 : : }
1736 : :
1737 : 0 : g_clear_pointer (&name, g_free);
1738 : 0 : g_clear_error (&local_error);
1739 : : }
1740 : 0 : break;
1741 : 0 : case LOOKUP_RECORDS:
1742 : : {
1743 : 0 : GList *records = do_lookup_records (data->lookup_records.rrname,
1744 : : data->lookup_records.record_type,
1745 : : cancellable,
1746 : : &local_error);
1747 : :
1748 : 0 : g_mutex_lock (&data->lock);
1749 : 0 : should_return = g_atomic_int_compare_and_exchange (&data->will_return, NOT_YET, COMPLETED);
1750 : 0 : g_mutex_unlock (&data->lock);
1751 : :
1752 : 0 : if (should_return)
1753 : : {
1754 : 0 : if (records != NULL)
1755 : 0 : g_task_return_pointer (task, g_steal_pointer (&records), (GDestroyNotify) free_records);
1756 : : else
1757 : 0 : g_task_return_error (task, g_steal_pointer (&local_error));
1758 : : }
1759 : :
1760 : 0 : g_clear_pointer (&records, free_records);
1761 : 0 : g_clear_error (&local_error);
1762 : : }
1763 : 0 : break;
1764 : 0 : default:
1765 : : g_assert_not_reached ();
1766 : : }
1767 : :
1768 : : /* Signal completion of a task. */
1769 : 3 : g_mutex_lock (&data->lock);
1770 : 3 : data->has_returned = TRUE;
1771 : 3 : g_cond_broadcast (&data->cond);
1772 : 3 : g_mutex_unlock (&data->lock);
1773 : :
1774 : 3 : g_object_unref (task);
1775 : 3 : }
1776 : :
1777 : : static void
1778 : 6 : g_threaded_resolver_class_init (GThreadedResolverClass *threaded_class)
1779 : : {
1780 : 6 : GObjectClass *object_class = G_OBJECT_CLASS (threaded_class);
1781 : 6 : GResolverClass *resolver_class = G_RESOLVER_CLASS (threaded_class);
1782 : :
1783 : 6 : object_class->finalize = g_threaded_resolver_finalize;
1784 : :
1785 : 6 : resolver_class->lookup_by_name = lookup_by_name;
1786 : 6 : resolver_class->lookup_by_name_async = lookup_by_name_async;
1787 : 6 : resolver_class->lookup_by_name_finish = lookup_by_name_finish;
1788 : 6 : resolver_class->lookup_by_name_with_flags = lookup_by_name_with_flags;
1789 : 6 : resolver_class->lookup_by_name_with_flags_async = lookup_by_name_with_flags_async;
1790 : 6 : resolver_class->lookup_by_name_with_flags_finish = lookup_by_name_with_flags_finish;
1791 : 6 : resolver_class->lookup_by_address = lookup_by_address;
1792 : 6 : resolver_class->lookup_by_address_async = lookup_by_address_async;
1793 : 6 : resolver_class->lookup_by_address_finish = lookup_by_address_finish;
1794 : 6 : resolver_class->lookup_records = lookup_records;
1795 : 6 : resolver_class->lookup_records_async = lookup_records_async;
1796 : 6 : resolver_class->lookup_records_finish = lookup_records_finish;
1797 : 6 : }
|