Coverage Report

Created: 2025-11-16 06:24

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