Coverage Report

Created: 2026-02-14 06:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/glib/gio/gthreadedresolver.c
Line
Count
Source
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
0
G_DEFINE_TYPE (GThreadedResolver, g_threaded_resolver, G_TYPE_RESOLVER)
102
0
103
0
static void run_task_in_thread_pool_async (GThreadedResolver *self,
104
0
                                           GTask             *task);
105
0
static void run_task_in_thread_pool_sync (GThreadedResolver *self,
106
0
                                          GTask             *task);
107
0
static void threaded_resolver_worker_cb (gpointer task_data,
108
0
                                         gpointer user_data);
109
0
110
0
static void
111
0
g_threaded_resolver_init (GThreadedResolver *self)
112
0
{
113
0
  self->thread_pool = g_thread_pool_new_full (threaded_resolver_worker_cb,
114
0
                                              self,
115
0
                                              (GDestroyNotify) g_object_unref,
116
0
                                              20,
117
0
                                              FALSE,
118
0
                                              NULL);
119
120
0
  self->network_is_loopback_only = -1;
121
122
0
  g_mutex_init (&self->interface_mutex);
123
0
}
124
125
static void
126
g_threaded_resolver_finalize (GObject *object)
127
0
{
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
g_resolver_error_from_addrinfo_error (gint err)
144
0
{
145
0
  switch (err)
146
0
    {
147
0
    case EAI_FAIL:
148
0
#if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
149
0
    case EAI_NODATA:
150
0
#endif
151
0
    case EAI_NONAME:
152
0
      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
0
    }
160
0
}
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
lookup_data_new_by_name (const char *hostname,
208
                         int         address_family)
209
0
{
210
0
  LookupData *data = g_new0 (LookupData, 1);
211
0
  data->lookup_type = LOOKUP_BY_NAME;
212
0
  g_cond_init (&data->cond);
213
0
  g_mutex_init (&data->lock);
214
0
  data->lookup_by_name.hostname = g_strdup (hostname);
215
0
  data->lookup_by_name.address_family = address_family;
216
0
  return g_steal_pointer (&data);
217
0
}
218
219
static LookupData *
220
lookup_data_new_by_address (GInetAddress *address)
221
0
{
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
0
}
229
230
static LookupData *
231
lookup_data_new_records (const gchar         *rrname,
232
                         GResolverRecordType  record_type)
233
0
{
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
0
}
242
243
static void
244
lookup_data_free (LookupData *data)
245
0
{
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
0
    g_assert_not_reached ();
258
0
  }
259
260
0
  if (data->timeout_source != NULL)
261
0
    {
262
0
      g_source_destroy (data->timeout_source);
263
0
      g_clear_pointer (&data->timeout_source, g_source_unref);
264
0
    }
265
266
0
  if (data->cancellable_source != NULL)
267
0
    {
268
0
      g_source_destroy (data->cancellable_source);
269
0
      g_clear_pointer (&data->cancellable_source, g_source_unref);
270
0
    }
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
check_only_has_loopback_interfaces (void)
280
0
{
281
0
#if HAVE_GETIFADDRS
282
0
  struct ifaddrs *addrs;
283
0
  gboolean only_loopback = TRUE;
284
285
0
  if (getifaddrs (&addrs) != 0)
286
0
    {
287
0
      int saved_errno = errno;
288
0
      g_debug ("getifaddrs() failed: %s", g_strerror (saved_errno));
289
0
      return FALSE;
290
0
    }
291
292
0
  for (struct ifaddrs *addr = addrs; addr; addr = addr->ifa_next)
293
0
    {
294
0
      struct sockaddr *sa = addr->ifa_addr;
295
0
      size_t addrlen;
296
0
      GSocketAddress *saddr;
297
0
      if (!sa)
298
0
        continue;
299
300
0
      if (sa->sa_family == AF_INET)
301
0
        addrlen = sizeof (struct sockaddr_in);
302
0
      else if (sa->sa_family == AF_INET6)
303
0
        addrlen = sizeof (struct sockaddr_in6);
304
0
      else
305
0
        continue;
306
307
0
      saddr = g_socket_address_new_from_native (sa, addrlen);
308
0
      if (!saddr)
309
0
        continue;
310
311
0
      if (!G_IS_INET_SOCKET_ADDRESS (saddr))
312
0
        {
313
0
          g_object_unref (saddr);
314
0
          continue;
315
0
        }
316
317
0
      GInetAddress *inetaddr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (saddr));
318
0
      if (!g_inet_address_get_is_loopback (inetaddr))
319
0
        {
320
0
          only_loopback = FALSE;
321
0
          g_object_unref (saddr);
322
0
          break;
323
0
        }
324
325
0
      g_object_unref (saddr);
326
0
    }
327
328
0
  freeifaddrs (addrs);
329
0
  return only_loopback;
330
#else /* FIXME: Check GetAdaptersAddresses() on win32. */
331
  return FALSE;
332
#endif
333
0
}
334
335
static void
336
network_changed_cb (GNetworkMonitor   *monitor,
337
                    gboolean           network_available,
338
                    GThreadedResolver *resolver)
339
0
{
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
only_has_loopback_interfaces_cached (GThreadedResolver *resolver)
347
0
{
348
0
  g_mutex_lock (&resolver->interface_mutex);
349
350
0
  if (!resolver->network_monitor)
351
0
    {
352
0
      resolver->network_monitor = g_object_ref (g_network_monitor_get_default ());
353
0
      resolver->monitor_supports_caching = G_TYPE_FROM_INSTANCE (resolver->network_monitor) != G_TYPE_NETWORK_MONITOR_BASE;
354
0
      g_signal_connect_object (resolver->network_monitor, "network-changed", G_CALLBACK (network_changed_cb), resolver, G_CONNECT_DEFAULT);
355
0
    }
356
357
0
  if (!resolver->monitor_supports_caching || resolver->network_is_loopback_only == -1)
358
0
    resolver->network_is_loopback_only = check_only_has_loopback_interfaces ();
359
360
0
  g_mutex_unlock (&resolver->interface_mutex);
361
362
0
  return resolver->network_is_loopback_only;
363
0
}
364
365
static GList *
366
do_lookup_by_name (GThreadedResolver  *resolver,
367
                   const gchar        *hostname,
368
                   int                 address_family,
369
                   GCancellable       *cancellable,
370
                   GError            **error)
371
0
{
372
0
  struct addrinfo *res = NULL;
373
0
  GList *addresses;
374
0
  gint retval;
375
0
  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
0
  gboolean only_loopback = only_has_loopback_interfaces_cached (resolver);
381
382
0
#ifdef AI_ADDRCONFIG
383
0
  if (!only_loopback)
384
0
    addrinfo_hints.ai_flags = AI_ADDRCONFIG;
385
0
#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
0
  addrinfo_hints.ai_socktype = SOCK_STREAM;
391
0
  addrinfo_hints.ai_protocol = IPPROTO_TCP;
392
393
0
  addrinfo_hints.ai_family = address_family;
394
0
  retval = getaddrinfo (hostname, NULL, &addrinfo_hints, &res);
395
396
0
  if (retval == 0)
397
0
    {
398
0
      addresses = NULL;
399
0
      for (struct addrinfo *ai = res; ai; ai = ai->ai_next)
400
0
        {
401
0
          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
0
            {
408
0
              g_clear_object (&sockaddr);
409
0
              continue;
410
0
            }
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
0
            {
415
0
              g_object_unref (sockaddr);
416
0
              continue;
417
0
            }
418
419
0
          addresses = g_list_prepend (addresses, g_object_ref (addr));
420
0
          g_object_unref (sockaddr);
421
0
        }
422
423
0
      g_clear_pointer (&res, freeaddrinfo);
424
425
0
      if (addresses != NULL)
426
0
        {
427
0
          addresses = g_list_reverse (addresses);
428
0
          return g_steal_pointer (&addresses);
429
0
        }
430
0
      else
431
0
        {
432
          /* All addresses failed to be converted to GSocketAddresses. */
433
0
          g_set_error (error,
434
0
                       G_RESOLVER_ERROR,
435
0
                       G_RESOLVER_ERROR_NOT_FOUND,
436
0
                       _("Error resolving “%s”: %s"),
437
0
                       hostname,
438
0
                       _("No valid addresses were found"));
439
0
          return NULL;
440
0
        }
441
0
    }
442
0
  else
443
0
    {
444
#ifdef G_OS_WIN32
445
      gchar *error_message = g_win32_error_message (WSAGetLastError ());
446
#else
447
0
      gchar *error_message = g_locale_to_utf8 (gai_strerror (retval), -1, NULL, NULL, NULL);
448
0
      if (error_message == NULL)
449
0
        error_message = g_strdup ("[Invalid UTF-8]");
450
0
#endif
451
452
0
      g_clear_pointer (&res, freeaddrinfo);
453
454
0
      g_set_error (error,
455
0
                   G_RESOLVER_ERROR,
456
0
                   g_resolver_error_from_addrinfo_error (retval),
457
0
                   _("Error resolving “%s”: %s"),
458
0
                   hostname, error_message);
459
0
      g_free (error_message);
460
461
0
      return NULL;
462
0
    }
463
0
}
464
465
static GList *
466
lookup_by_name (GResolver     *resolver,
467
                const gchar   *hostname,
468
                GCancellable  *cancellable,
469
                GError       **error)
470
0
{
471
0
  GThreadedResolver *self = G_THREADED_RESOLVER (resolver);
472
0
  GTask *task;
473
0
  GList *addresses;
474
0
  LookupData *data;
475
476
0
  data = lookup_data_new_by_name (hostname, AF_UNSPEC);
477
0
  task = g_task_new (resolver, cancellable, NULL, NULL);
478
0
  g_task_set_source_tag (task, lookup_by_name);
479
0
  g_task_set_name (task, "[gio] resolver lookup");
480
0
  g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) lookup_data_free);
481
482
0
  run_task_in_thread_pool_sync (self, task);
483
484
0
  addresses = g_task_propagate_pointer (task, error);
485
0
  g_object_unref (task);
486
487
0
  return addresses;
488
0
}
489
490
static int
491
flags_to_family (GResolverNameLookupFlags flags)
492
0
{
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
0
    {
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
0
    }
504
505
0
  return address_family;
506
0
}
507
508
static GList *
509
lookup_by_name_with_flags (GResolver                 *resolver,
510
                           const gchar               *hostname,
511
                           GResolverNameLookupFlags   flags,
512
                           GCancellable              *cancellable,
513
                           GError                   **error)
514
0
{
515
0
  GThreadedResolver *self = G_THREADED_RESOLVER (resolver);
516
0
  GTask *task;
517
0
  GList *addresses;
518
0
  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
0
}
533
534
static void
535
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
0
{
542
0
  GThreadedResolver *self = G_THREADED_RESOLVER (resolver);
543
0
  GTask *task;
544
0
  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
0
           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
lookup_by_name_async (GResolver           *resolver,
563
                      const gchar         *hostname,
564
                      GCancellable        *cancellable,
565
                      GAsyncReadyCallback  callback,
566
                      gpointer             user_data)
567
0
{
568
0
  lookup_by_name_with_flags_async (resolver,
569
0
                                   hostname,
570
0
                                   G_RESOLVER_NAME_LOOKUP_FLAGS_DEFAULT,
571
0
                                   cancellable,
572
0
                                   callback,
573
0
                                   user_data);
574
0
}
575
576
static GList *
577
lookup_by_name_finish (GResolver     *resolver,
578
                       GAsyncResult  *result,
579
                       GError       **error)
580
0
{
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
0
}
585
586
static GList *
587
lookup_by_name_with_flags_finish (GResolver     *resolver,
588
                                  GAsyncResult  *result,
589
                                  GError       **error)
590
0
{
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
0
}
595
596
static gchar *
597
do_lookup_by_address (GInetAddress  *address,
598
                      GCancellable  *cancellable,
599
                      GError       **error)
600
0
{
601
0
  struct sockaddr_storage sockaddr_address;
602
0
  gsize sockaddr_address_size;
603
0
  GSocketAddress *gsockaddr;
604
0
  gchar name[NI_MAXHOST];
605
0
  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
0
                              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
0
                        name, sizeof (name), NULL, 0, NI_NAMEREQD);
615
0
  if (retval == 0)
616
0
    return g_strdup (name);
617
0
  else
618
0
    {
619
0
      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
0
#endif
628
629
0
      phys = g_inet_address_to_string (address);
630
0
      g_set_error (error,
631
0
                   G_RESOLVER_ERROR,
632
0
                   g_resolver_error_from_addrinfo_error (retval),
633
0
                   _("Error reverse-resolving “%s”: %s"),
634
0
                   phys ? phys : "(unknown)",
635
0
                   error_message);
636
0
      g_free (phys);
637
0
      g_free (error_message);
638
639
0
      return NULL;
640
0
    }
641
0
}
642
643
static gchar *
644
lookup_by_address (GResolver        *resolver,
645
                   GInetAddress     *address,
646
                   GCancellable     *cancellable,
647
                   GError          **error)
648
0
{
649
0
  GThreadedResolver *self = G_THREADED_RESOLVER (resolver);
650
0
  LookupData *data = NULL;
651
0
  GTask *task;
652
0
  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
0
}
667
668
static void
669
lookup_by_address_async (GResolver           *resolver,
670
                         GInetAddress        *address,
671
                         GCancellable        *cancellable,
672
                         GAsyncReadyCallback  callback,
673
                         gpointer             user_data)
674
0
{
675
0
  GThreadedResolver *self = G_THREADED_RESOLVER (resolver);
676
0
  LookupData *data = NULL;
677
0
  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
lookup_by_address_finish (GResolver     *resolver,
692
                          GAsyncResult  *result,
693
                          GError       **error)
694
0
{
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
0
}
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
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
6.50k
{
849
6.50k
  int expand_result;
850
851
6.50k
  expand_result = dn_expand (answer, end, *p, namebuf, namebuf_len);
852
6.50k
  if (expand_result < 0 || end - *p < expand_result)
853
63
    {
854
63
      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
63
                   _("Error parsing DNS %s record: malformed DNS packet"), rrname);
857
63
      return FALSE;
858
63
    }
859
860
6.44k
  *p += expand_result;
861
862
6.44k
  return TRUE;
863
6.50k
}
864
865
static GVariant *
866
parse_res_srv (const guint8  *answer,
867
               const guint8  *end,
868
               const guint8 **p,
869
               GError       **error)
870
610
{
871
610
  gchar namebuf[1024];
872
610
  guint16 priority, weight, port;
873
874
610
  if (end - *p < 6)
875
20
    {
876
20
      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
20
                   _("Error parsing DNS %s record: malformed DNS packet"), "SRV");
879
20
      return NULL;
880
20
    }
881
882
610
  GETSHORT (priority, *p);
883
590
  GETSHORT (weight, *p);
884
590
  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
590
  if (!expand_name ("SRV", answer, end, p, namebuf, sizeof (namebuf), error))
903
5
    return NULL;
904
905
585
  return g_variant_new ("(qqqs)",
906
585
                        priority,
907
585
                        weight,
908
585
                        port,
909
585
                        namebuf);
910
590
}
911
912
static GVariant *
913
parse_res_soa (const guint8  *answer,
914
               const guint8  *end,
915
               const guint8 **p,
916
               GError       **error)
917
1.26k
{
918
1.26k
  gchar mnamebuf[1024];
919
1.26k
  gchar rnamebuf[1024];
920
1.26k
  guint32 serial, refresh, retry, expire, ttl;
921
922
1.26k
  if (!expand_name ("SOA", answer, end, p, mnamebuf, sizeof (mnamebuf), error))
923
12
    return NULL;
924
925
1.25k
  if (!expand_name ("SOA", answer, end, p, rnamebuf, sizeof (rnamebuf), error))
926
17
    return NULL;
927
928
1.23k
  if (end - *p < 20)
929
28
    {
930
28
      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
28
                   _("Error parsing DNS %s record: malformed DNS packet"), "SOA");
933
28
      return NULL;
934
28
    }
935
936
1.23k
  GETLONG (serial, *p);
937
1.20k
  GETLONG (refresh, *p);
938
1.20k
  GETLONG (retry, *p);
939
1.20k
  GETLONG (expire, *p);
940
1.20k
  GETLONG (ttl, *p);
941
942
1.20k
  return g_variant_new ("(ssuuuuu)",
943
1.20k
                        mnamebuf,
944
1.20k
                        rnamebuf,
945
1.20k
                        serial,
946
1.20k
                        refresh,
947
1.20k
                        retry,
948
1.20k
                        expire,
949
1.20k
                        ttl);
950
1.23k
}
951
952
static GVariant *
953
parse_res_ns (const guint8  *answer,
954
              const guint8  *end,
955
              const guint8 **p,
956
              GError       **error)
957
2.12k
{
958
2.12k
  gchar namebuf[1024];
959
960
2.12k
  if (!expand_name ("NS", answer, end, p, namebuf, sizeof (namebuf), error))
961
27
    return NULL;
962
963
2.09k
  return g_variant_new ("(s)", namebuf);
964
2.12k
}
965
966
static GVariant *
967
parse_res_mx (const guint8  *answer,
968
              const guint8  *end,
969
              const guint8 **p,
970
              GError       **error)
971
1.29k
{
972
1.29k
  gchar namebuf[1024];
973
1.29k
  guint16 preference;
974
975
1.29k
  if (end - *p < 2)
976
16
    {
977
16
      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
16
                   _("Error parsing DNS %s record: malformed DNS packet"), "MX");
980
16
      return NULL;
981
16
    }
982
983
1.29k
  GETSHORT (preference, *p);
984
985
1.27k
  if (!expand_name ("MX", answer, end, p, namebuf, sizeof (namebuf), error))
986
2
    return NULL;
987
988
1.27k
  return g_variant_new ("(qs)",
989
1.27k
                        preference,
990
1.27k
                        namebuf);
991
1.27k
}
992
993
static GVariant *
994
parse_res_txt (const guint8  *answer,
995
               const guint8  *end,
996
               const guint8 **p,
997
               GError       **error)
998
129k
{
999
129k
  GVariant *record;
1000
129k
  GPtrArray *array;
1001
129k
  const guint8 *at = *p;
1002
129k
  gsize len;
1003
1004
129k
  if (end - *p == 0)
1005
25
    {
1006
25
      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
25
                   _("Error parsing DNS %s record: malformed DNS packet"), "TXT");
1009
25
      return NULL;
1010
25
    }
1011
1012
129k
  array = g_ptr_array_new_with_free_func (g_free);
1013
1.07M
  while (at < end)
1014
949k
    {
1015
949k
      len = *(at++);
1016
949k
      if (len > (gsize) (end - at))
1017
34
        {
1018
34
          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
34
                       _("Error parsing DNS %s record: malformed DNS packet"), "TXT");
1021
34
          g_ptr_array_free (array, TRUE);
1022
34
          return NULL;
1023
34
        }
1024
1025
949k
      g_ptr_array_add (array, g_strndup ((gchar *)at, len));
1026
949k
      at += len;
1027
949k
    }
1028
1029
129k
  *p = at;
1030
129k
  record = g_variant_new ("(@as)",
1031
129k
                          g_variant_new_strv ((const gchar **)array->pdata, array->len));
1032
129k
  g_ptr_array_free (array, TRUE);
1033
129k
  return record;
1034
129k
}
1035
1036
gint
1037
g_resolver_record_type_to_rrtype (GResolverRecordType type)
1038
0
{
1039
0
  switch (type)
1040
0
  {
1041
0
    case G_RESOLVER_RECORD_SRV:
1042
0
      return T_SRV;
1043
0
    case G_RESOLVER_RECORD_TXT:
1044
0
      return T_TXT;
1045
0
    case G_RESOLVER_RECORD_SOA:
1046
0
      return T_SOA;
1047
0
    case G_RESOLVER_RECORD_NS:
1048
0
      return T_NS;
1049
0
    case G_RESOLVER_RECORD_MX:
1050
0
      return T_MX;
1051
0
  }
1052
0
  g_return_val_if_reached (-1);
1053
0
}
1054
1055
GList *
1056
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
8.54k
{
1063
8.54k
  uint16_t count;
1064
8.54k
  gchar namebuf[1024];
1065
8.54k
  const guint8 *end, *p;
1066
8.54k
  guint16 type, qclass, rdlength;
1067
8.54k
  const HEADER *header;
1068
8.54k
  GList *records;
1069
8.54k
  GVariant *record;
1070
8.54k
  gsize len_unsigned;
1071
8.54k
  GError *parsing_error = NULL;
1072
1073
8.54k
  if (len <= 0)
1074
0
    {
1075
0
      if (len == 0 || herr == HOST_NOT_FOUND || herr == NO_DATA)
1076
0
        {
1077
0
          g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND,
1078
0
                       _("No DNS record of the requested type for “%s”"), rrname);
1079
0
        }
1080
0
      else if (herr == TRY_AGAIN)
1081
0
        {
1082
0
          g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_TEMPORARY_FAILURE,
1083
0
                       _("Temporarily unable to resolve “%s”"), rrname);
1084
0
        }
1085
0
      else
1086
0
        {
1087
0
          g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
1088
0
                       _("Error resolving “%s”"), rrname);
1089
0
        }
1090
1091
0
      return NULL;
1092
0
    }
1093
1094
  /* We know len ≥ 0 now. */
1095
8.54k
  len_unsigned = (gsize) len;
1096
1097
8.54k
  if (len_unsigned < sizeof (HEADER))
1098
66
    {
1099
66
      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
66
                   _("Error resolving “%s”: %s"), rrname, _("Malformed DNS packet"));
1103
66
      return NULL;
1104
66
    }
1105
1106
8.47k
  records = NULL;
1107
1108
8.47k
  header = (HEADER *)answer;
1109
8.47k
  p = answer + sizeof (HEADER);
1110
8.47k
  end = answer + len_unsigned;
1111
1112
  /* Skip query */
1113
8.47k
  count = ntohs (header->qdcount);
1114
1.36M
  while (count-- && p < end)
1115
1.35M
    {
1116
1.35M
      int expand_result;
1117
1118
1.35M
      expand_result = dn_expand (answer, end, p, namebuf, sizeof (namebuf));
1119
1.35M
      if (expand_result < 0 || end - p < expand_result + 4)
1120
324
        {
1121
          /* Not possible to recover parsing as the length of the rest of the
1122
           * record is unknown or is too short. */
1123
324
          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
324
                       _("Error resolving “%s”: %s"), rrname, _("Malformed DNS packet"));
1127
324
          return NULL;
1128
324
        }
1129
1130
1.35M
      p += expand_result;
1131
1.35M
      p += 4;  /* skip TYPE and CLASS */
1132
1133
      /* To silence gcc warnings */
1134
1.35M
      namebuf[0] = namebuf[1];
1135
1.35M
    }
1136
1137
  /* Read answers */
1138
8.15k
  count = ntohs (header->ancount);
1139
1.69M
  while (count-- && p < end)
1140
1.69M
    {
1141
1.69M
      int expand_result;
1142
1143
1.69M
      expand_result = dn_expand (answer, end, p, namebuf, sizeof (namebuf));
1144
1.69M
      if (expand_result < 0 || end - p < expand_result + 10)
1145
895
        {
1146
          /* Not possible to recover parsing as the length of the rest of the
1147
           * record is unknown or is too short. */
1148
895
          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
895
                       _("Error resolving “%s”: %s"), rrname, _("Malformed DNS packet"));
1152
895
          break;
1153
895
        }
1154
1155
1.69M
      p += expand_result;
1156
1.69M
      GETSHORT (type, p);
1157
1.69M
      GETSHORT (qclass, p);
1158
1.69M
      p += 4; /* ignore the ttl (type=long) value */
1159
1.69M
      GETSHORT (rdlength, p);
1160
1161
1.69M
      if (end - p < rdlength)
1162
653
        {
1163
653
          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
653
                       _("Error resolving “%s”: %s"), rrname, _("Malformed DNS packet"));
1167
653
          break;
1168
653
        }
1169
1170
1.69M
      if (type != rrtype || qclass != C_IN)
1171
1.55M
        {
1172
1.55M
          p += rdlength;
1173
1.55M
          continue;
1174
1.55M
        }
1175
1176
134k
      switch (rrtype)
1177
134k
        {
1178
610
        case T_SRV:
1179
610
          record = parse_res_srv (answer, p + rdlength, &p, &parsing_error);
1180
610
          break;
1181
1.29k
        case T_MX:
1182
1.29k
          record = parse_res_mx (answer, p + rdlength, &p, &parsing_error);
1183
1.29k
          break;
1184
1.26k
        case T_SOA:
1185
1.26k
          record = parse_res_soa (answer, p + rdlength, &p, &parsing_error);
1186
1.26k
          break;
1187
2.12k
        case T_NS:
1188
2.12k
          record = parse_res_ns (answer, p + rdlength, &p, &parsing_error);
1189
2.12k
          break;
1190
129k
        case T_TXT:
1191
129k
          record = parse_res_txt (answer, p + rdlength, &p, &parsing_error);
1192
129k
          break;
1193
325
        default:
1194
325
          g_debug ("Unrecognized DNS record type %u", rrtype);
1195
325
          record = NULL;
1196
325
          break;
1197
134k
        }
1198
1199
134k
      if (record != NULL)
1200
134k
        records = g_list_prepend (records, g_variant_ref_sink (record));
1201
1202
134k
      if (parsing_error != NULL)
1203
186
        break;
1204
134k
    }
1205
1206
8.15k
  if (parsing_error != NULL)
1207
1.73k
    {
1208
1.73k
      g_propagate_prefixed_error (error, parsing_error, _("Failed to parse DNS response for “%s”: "), rrname);
1209
1.73k
      g_list_free_full (records, (GDestroyNotify)g_variant_unref);
1210
1.73k
      return NULL;
1211
1.73k
    }
1212
6.42k
  else if (records == NULL)
1213
5.54k
    {
1214
5.54k
      g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND,
1215
5.54k
                   _("No DNS record of the requested type for “%s”"), rrname);
1216
1217
5.54k
      return NULL;
1218
5.54k
    }
1219
879
  else
1220
879
    return records;
1221
8.15k
}
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
free_records (GList *records)
1375
0
{
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
do_lookup_records (const gchar          *rrname,
1390
                   GResolverRecordType   record_type,
1391
                   GCancellable         *cancellable,
1392
                   GError              **error)
1393
0
{
1394
0
  GList *records;
1395
1396
0
#if defined(G_OS_UNIX)
1397
0
  gint len = 512;
1398
0
  gint herr;
1399
0
  GByteArray *answer;
1400
0
  gint rrtype;
1401
1402
0
#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
0
    {
1418
0
      g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
1419
0
                   _("Error resolving “%s”"), rrname);
1420
0
      return NULL;
1421
0
    }
1422
0
#endif
1423
1424
0
  rrtype = g_resolver_record_type_to_rrtype (record_type);
1425
0
  answer = g_byte_array_new ();
1426
0
  for (;;)
1427
0
    {
1428
0
      g_byte_array_set_size (answer, len * 2);
1429
0
#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
0
        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
0
    }
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
0
#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
0
#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
0
}
1484
1485
static GList *
1486
lookup_records (GResolver              *resolver,
1487
                const gchar            *rrname,
1488
                GResolverRecordType     record_type,
1489
                GCancellable           *cancellable,
1490
                GError                **error)
1491
0
{
1492
0
  GThreadedResolver *self = G_THREADED_RESOLVER (resolver);
1493
0
  GTask *task;
1494
0
  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
0
}
1511
1512
static void
1513
lookup_records_async (GResolver           *resolver,
1514
                      const char          *rrname,
1515
                      GResolverRecordType  record_type,
1516
                      GCancellable        *cancellable,
1517
                      GAsyncReadyCallback  callback,
1518
                      gpointer             user_data)
1519
0
{
1520
0
  GThreadedResolver *self = G_THREADED_RESOLVER (resolver);
1521
0
  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
lookup_records_finish (GResolver     *resolver,
1538
                       GAsyncResult  *result,
1539
                       GError       **error)
1540
0
{
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
0
}
1545
1546
/* Will be called in the GLib worker thread, so must lock all accesses to shared
1547
 * data. */
1548
static gboolean
1549
timeout_cb (gpointer user_data)
1550
0
{
1551
0
  GWeakRef *weak_task = user_data;
1552
0
  GTask *task = NULL;  /* (owned) */
1553
0
  LookupData *data;
1554
0
  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
0
    {
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
0
    }
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
0
}
1585
1586
/* Will be called in the GLib worker thread, so must lock all accesses to shared
1587
 * data. */
1588
static gboolean
1589
cancelled_cb (GCancellable *cancellable,
1590
              gpointer      user_data)
1591
0
{
1592
0
  GWeakRef *weak_task = user_data;
1593
0
  GTask *task = NULL;  /* (owned) */
1594
0
  LookupData *data;
1595
0
  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
0
}
1624
1625
static void
1626
weak_ref_clear_and_free (GWeakRef *weak_ref)
1627
0
{
1628
0
  g_weak_ref_clear (weak_ref);
1629
0
  g_free (weak_ref);
1630
0
}
1631
1632
static void
1633
run_task_in_thread_pool_async (GThreadedResolver *self,
1634
                               GTask             *task)
1635
0
{
1636
0
  LookupData *data = g_task_get_task_data (task);
1637
0
  guint timeout_ms = g_resolver_get_timeout (G_RESOLVER (self));
1638
0
  GCancellable *cancellable = g_task_get_cancellable (task);
1639
1640
0
  g_mutex_lock (&data->lock);
1641
1642
0
  g_thread_pool_push (self->thread_pool, g_object_ref (task), NULL);
1643
1644
0
  if (timeout_ms != 0)
1645
0
    {
1646
0
      GWeakRef *weak_task = g_new0 (GWeakRef, 1);
1647
0
      g_weak_ref_set (weak_task, task);
1648
1649
0
      data->timeout_source = g_timeout_source_new (timeout_ms);
1650
0
      g_source_set_static_name (data->timeout_source, "[gio] threaded resolver timeout");
1651
0
      g_source_set_callback (data->timeout_source, G_SOURCE_FUNC (timeout_cb), g_steal_pointer (&weak_task), (GDestroyNotify) weak_ref_clear_and_free);
1652
0
      g_source_attach (data->timeout_source, GLIB_PRIVATE_CALL (g_get_worker_context) ());
1653
0
    }
1654
1655
0
  if (cancellable != NULL)
1656
0
    {
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
0
    }
1665
1666
0
  g_mutex_unlock (&data->lock);
1667
0
}
1668
1669
static void
1670
run_task_in_thread_pool_sync (GThreadedResolver *self,
1671
                              GTask             *task)
1672
0
{
1673
0
  LookupData *data = g_task_get_task_data (task);
1674
1675
0
  run_task_in_thread_pool_async (self, task);
1676
1677
0
  g_mutex_lock (&data->lock);
1678
0
  while (!data->has_returned)
1679
0
    g_cond_wait (&data->cond, &data->lock);
1680
0
  g_mutex_unlock (&data->lock);
1681
0
}
1682
1683
static void
1684
threaded_resolver_worker_cb (gpointer task_data,
1685
                             gpointer user_data)
1686
0
{
1687
0
  GTask *task = G_TASK (g_steal_pointer (&task_data));
1688
0
  LookupData *data = g_task_get_task_data (task);
1689
0
  GCancellable *cancellable = g_task_get_cancellable (task);
1690
0
  GThreadedResolver *resolver = G_THREADED_RESOLVER (user_data);
1691
0
  GError *local_error = NULL;
1692
0
  gboolean should_return;
1693
1694
0
  switch (data->lookup_type) {
1695
0
  case LOOKUP_BY_NAME:
1696
0
    {
1697
0
      GList *addresses = do_lookup_by_name (resolver,
1698
0
                                            data->lookup_by_name.hostname,
1699
0
                                            data->lookup_by_name.address_family,
1700
0
                                            cancellable,
1701
0
                                            &local_error);
1702
1703
0
      g_mutex_lock (&data->lock);
1704
0
      should_return = g_atomic_int_compare_and_exchange (&data->will_return, NOT_YET, COMPLETED);
1705
0
      g_mutex_unlock (&data->lock);
1706
1707
0
      if (should_return)
1708
0
        {
1709
0
          if (addresses != NULL)
1710
0
            g_task_return_pointer (task, g_steal_pointer (&addresses), (GDestroyNotify) g_resolver_free_addresses);
1711
0
          else
1712
0
            g_task_return_error (task, g_steal_pointer (&local_error));
1713
0
        }
1714
1715
0
      g_clear_pointer (&addresses, g_resolver_free_addresses);
1716
0
      g_clear_error (&local_error);
1717
0
    }
1718
0
    break;
1719
0
  case LOOKUP_BY_ADDRESS:
1720
0
    {
1721
0
      gchar *name = do_lookup_by_address (data->lookup_by_address.address,
1722
0
                                          cancellable,
1723
0
                                          &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
0
        {
1731
0
          if (name != NULL)
1732
0
            g_task_return_pointer (task, g_steal_pointer (&name), g_free);
1733
0
          else
1734
0
            g_task_return_error (task, g_steal_pointer (&local_error));
1735
0
        }
1736
1737
0
      g_clear_pointer (&name, g_free);
1738
0
      g_clear_error (&local_error);
1739
0
    }
1740
0
    break;
1741
0
  case LOOKUP_RECORDS:
1742
0
    {
1743
0
      GList *records = do_lookup_records (data->lookup_records.rrname,
1744
0
                                          data->lookup_records.record_type,
1745
0
                                          cancellable,
1746
0
                                          &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
0
        {
1754
0
          if (records != NULL)
1755
0
            g_task_return_pointer (task, g_steal_pointer (&records), (GDestroyNotify) free_records);
1756
0
          else
1757
0
            g_task_return_error (task, g_steal_pointer (&local_error));
1758
0
        }
1759
1760
0
      g_clear_pointer (&records, free_records);
1761
0
      g_clear_error (&local_error);
1762
0
    }
1763
0
    break;
1764
0
  default:
1765
0
    g_assert_not_reached ();
1766
0
  }
1767
1768
  /* Signal completion of a task. */
1769
0
  g_mutex_lock (&data->lock);
1770
0
  data->has_returned = TRUE;
1771
0
  g_cond_broadcast (&data->cond);
1772
0
  g_mutex_unlock (&data->lock);
1773
1774
0
  g_object_unref (task);
1775
0
}
1776
1777
static void
1778
g_threaded_resolver_class_init (GThreadedResolverClass *threaded_class)
1779
0
{
1780
0
  GObjectClass *object_class = G_OBJECT_CLASS (threaded_class);
1781
0
  GResolverClass *resolver_class = G_RESOLVER_CLASS (threaded_class);
1782
1783
0
  object_class->finalize = g_threaded_resolver_finalize;
1784
1785
0
  resolver_class->lookup_by_name                   = lookup_by_name;
1786
0
  resolver_class->lookup_by_name_async             = lookup_by_name_async;
1787
0
  resolver_class->lookup_by_name_finish            = lookup_by_name_finish;
1788
0
  resolver_class->lookup_by_name_with_flags        = lookup_by_name_with_flags;
1789
0
  resolver_class->lookup_by_name_with_flags_async  = lookup_by_name_with_flags_async;
1790
0
  resolver_class->lookup_by_name_with_flags_finish = lookup_by_name_with_flags_finish;
1791
0
  resolver_class->lookup_by_address                = lookup_by_address;
1792
0
  resolver_class->lookup_by_address_async          = lookup_by_address_async;
1793
0
  resolver_class->lookup_by_address_finish         = lookup_by_address_finish;
1794
0
  resolver_class->lookup_records                   = lookup_records;
1795
0
  resolver_class->lookup_records_async             = lookup_records_async;
1796
0
  resolver_class->lookup_records_finish            = lookup_records_finish;
1797
0
}