Coverage Report

Created: 2025-06-13 06:55

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