Change LGPL-2.1+ to LGPL-2.1-or-later
[platform/upstream/glib.git] / gio / gthreadedresolver.c
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 {
102   self->thread_pool = g_thread_pool_new_full (threaded_resolver_worker_cb,
103                                               self,
104                                               (GDestroyNotify) g_object_unref,
105                                               20,
106                                               FALSE,
107                                               NULL);
108 }
109
110 static void
111 g_threaded_resolver_finalize (GObject *object)
112 {
113   GThreadedResolver *self = G_THREADED_RESOLVER (object);
114
115   g_thread_pool_free (self->thread_pool, TRUE, FALSE);
116   self->thread_pool = NULL;
117
118   G_OBJECT_CLASS (g_threaded_resolver_parent_class)->finalize (object);
119 }
120
121 static GResolverError
122 g_resolver_error_from_addrinfo_error (gint err)
123 {
124   switch (err)
125     {
126     case EAI_FAIL:
127 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
128     case EAI_NODATA:
129 #endif
130     case EAI_NONAME:
131       return G_RESOLVER_ERROR_NOT_FOUND;
132
133     case EAI_AGAIN:
134       return G_RESOLVER_ERROR_TEMPORARY_FAILURE;
135
136     default:
137       return G_RESOLVER_ERROR_INTERNAL;
138     }
139 }
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 {
189   LookupData *data = g_new0 (LookupData, 1);
190   data->lookup_type = LOOKUP_BY_NAME;
191   g_cond_init (&data->cond);
192   g_mutex_init (&data->lock);
193   data->lookup_by_name.hostname = g_strdup (hostname);
194   data->lookup_by_name.address_family = address_family;
195   return g_steal_pointer (&data);
196 }
197
198 static LookupData *
199 lookup_data_new_by_address (GInetAddress *address)
200 {
201   LookupData *data = g_new0 (LookupData, 1);
202   data->lookup_type = LOOKUP_BY_ADDRESS;
203   g_cond_init (&data->cond);
204   g_mutex_init (&data->lock);
205   data->lookup_by_address.address = g_object_ref (address);
206   return g_steal_pointer (&data);
207 }
208
209 static LookupData *
210 lookup_data_new_records (const gchar         *rrname,
211                          GResolverRecordType  record_type)
212 {
213   LookupData *data = g_new0 (LookupData, 1);
214   data->lookup_type = LOOKUP_RECORDS;
215   g_cond_init (&data->cond);
216   g_mutex_init (&data->lock);
217   data->lookup_records.rrname = g_strdup (rrname);
218   data->lookup_records.record_type = record_type;
219   return g_steal_pointer (&data);
220 }
221
222 static void
223 lookup_data_free (LookupData *data)
224 {
225   switch (data->lookup_type) {
226   case LOOKUP_BY_NAME:
227     g_free (data->lookup_by_name.hostname);
228     break;
229   case LOOKUP_BY_ADDRESS:
230     g_clear_object (&data->lookup_by_address.address);
231     break;
232   case LOOKUP_RECORDS:
233     g_free (data->lookup_records.rrname);
234     break;
235   default:
236     g_assert_not_reached ();
237   }
238
239   if (data->timeout_source != NULL)
240     {
241       g_source_destroy (data->timeout_source);
242       g_clear_pointer (&data->timeout_source, g_source_unref);
243     }
244
245   if (data->cancellable_source != NULL)
246     {
247       g_source_destroy (data->cancellable_source);
248       g_clear_pointer (&data->cancellable_source, g_source_unref);
249     }
250
251   g_mutex_clear (&data->lock);
252   g_cond_clear (&data->cond);
253
254   g_free (data);
255 }
256
257 static GList *
258 do_lookup_by_name (const gchar   *hostname,
259                    int            address_family,
260                    GCancellable  *cancellable,
261                    GError       **error)
262 {
263   struct addrinfo *res = NULL;
264   GList *addresses;
265   gint retval;
266   struct addrinfo addrinfo_hints = { 0 };
267
268 #ifdef AI_ADDRCONFIG
269   addrinfo_hints.ai_flags = AI_ADDRCONFIG;
270 #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   addrinfo_hints.ai_socktype = SOCK_STREAM;
276   addrinfo_hints.ai_protocol = IPPROTO_TCP;
277
278   addrinfo_hints.ai_family = address_family;
279   retval = getaddrinfo (hostname, NULL, &addrinfo_hints, &res);
280
281   if (retval == 0)
282     {
283       struct addrinfo *ai;
284       GSocketAddress *sockaddr;
285       GInetAddress *addr;
286
287       addresses = NULL;
288       for (ai = res; ai; ai = ai->ai_next)
289         {
290           sockaddr = g_socket_address_new_from_native (ai->ai_addr, ai->ai_addrlen);
291           if (!sockaddr)
292             continue;
293           if (!G_IS_INET_SOCKET_ADDRESS (sockaddr))
294             {
295               g_clear_object (&sockaddr);
296               continue;
297             }
298
299           addr = g_object_ref (g_inet_socket_address_get_address ((GInetSocketAddress *)sockaddr));
300           addresses = g_list_prepend (addresses, addr);
301           g_object_unref (sockaddr);
302         }
303
304       g_clear_pointer (&res, freeaddrinfo);
305
306       if (addresses != NULL)
307         {
308           addresses = g_list_reverse (addresses);
309           return g_steal_pointer (&addresses);
310         }
311       else
312         {
313           /* All addresses failed to be converted to GSocketAddresses. */
314           g_set_error (error,
315                        G_RESOLVER_ERROR,
316                        G_RESOLVER_ERROR_NOT_FOUND,
317                        _("Error resolving “%s”: %s"),
318                        hostname,
319                        _("No valid addresses were found"));
320           return NULL;
321         }
322     }
323   else
324     {
325 #ifdef G_OS_WIN32
326       gchar *error_message = g_win32_error_message (WSAGetLastError ());
327 #else
328       gchar *error_message = g_locale_to_utf8 (gai_strerror (retval), -1, NULL, NULL, NULL);
329       if (error_message == NULL)
330         error_message = g_strdup ("[Invalid UTF-8]");
331 #endif
332
333       g_clear_pointer (&res, freeaddrinfo);
334
335       g_set_error (error,
336                    G_RESOLVER_ERROR,
337                    g_resolver_error_from_addrinfo_error (retval),
338                    _("Error resolving “%s”: %s"),
339                    hostname, error_message);
340       g_free (error_message);
341
342       return NULL;
343     }
344 }
345
346 static GList *
347 lookup_by_name (GResolver     *resolver,
348                 const gchar   *hostname,
349                 GCancellable  *cancellable,
350                 GError       **error)
351 {
352   GThreadedResolver *self = G_THREADED_RESOLVER (resolver);
353   GTask *task;
354   GList *addresses;
355   LookupData *data;
356
357   data = lookup_data_new_by_name (hostname, AF_UNSPEC);
358   task = g_task_new (resolver, cancellable, NULL, NULL);
359   g_task_set_source_tag (task, lookup_by_name);
360   g_task_set_name (task, "[gio] resolver lookup");
361   g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) lookup_data_free);
362
363   run_task_in_thread_pool_sync (self, task);
364
365   addresses = g_task_propagate_pointer (task, error);
366   g_object_unref (task);
367
368   return addresses;
369 }
370
371 static int
372 flags_to_family (GResolverNameLookupFlags flags)
373 {
374   int address_family = AF_UNSPEC;
375
376   if (flags & G_RESOLVER_NAME_LOOKUP_FLAGS_IPV4_ONLY)
377     address_family = AF_INET;
378
379   if (flags & G_RESOLVER_NAME_LOOKUP_FLAGS_IPV6_ONLY)
380     {
381       address_family = AF_INET6;
382       /* You can only filter by one family at a time */
383       g_return_val_if_fail (!(flags & G_RESOLVER_NAME_LOOKUP_FLAGS_IPV4_ONLY), address_family);
384     }
385
386   return address_family;
387 }
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 {
396   GThreadedResolver *self = G_THREADED_RESOLVER (resolver);
397   GTask *task;
398   GList *addresses;
399   LookupData *data;
400
401   data = lookup_data_new_by_name (hostname, flags_to_family (flags));
402   task = g_task_new (resolver, cancellable, NULL, NULL);
403   g_task_set_source_tag (task, lookup_by_name_with_flags);
404   g_task_set_name (task, "[gio] resolver lookup");
405   g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) lookup_data_free);
406
407   run_task_in_thread_pool_sync (self, task);
408
409   addresses = g_task_propagate_pointer (task, error);
410   g_object_unref (task);
411
412   return addresses;
413 }
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 {
423   GThreadedResolver *self = G_THREADED_RESOLVER (resolver);
424   GTask *task;
425   LookupData *data;
426
427   data = lookup_data_new_by_name (hostname, flags_to_family (flags));
428   task = g_task_new (resolver, cancellable, callback, user_data);
429
430   g_debug ("%s: starting new lookup for %s with GTask %p, LookupData %p",
431            G_STRFUNC, hostname, task, data);
432
433   g_task_set_source_tag (task, lookup_by_name_with_flags_async);
434   g_task_set_name (task, "[gio] resolver lookup");
435   g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) lookup_data_free);
436
437   run_task_in_thread_pool_async (self, task);
438
439   g_object_unref (task);
440 }
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 {
449   lookup_by_name_with_flags_async (resolver,
450                                    hostname,
451                                    G_RESOLVER_NAME_LOOKUP_FLAGS_DEFAULT,
452                                    cancellable,
453                                    callback,
454                                    user_data);
455 }
456
457 static GList *
458 lookup_by_name_finish (GResolver     *resolver,
459                        GAsyncResult  *result,
460                        GError       **error)
461 {
462   g_return_val_if_fail (g_task_is_valid (result, resolver), NULL);
463
464   return g_task_propagate_pointer (G_TASK (result), error);
465 }
466
467 static GList *
468 lookup_by_name_with_flags_finish (GResolver     *resolver,
469                                   GAsyncResult  *result,
470                                   GError       **error)
471 {
472   g_return_val_if_fail (g_task_is_valid (result, resolver), NULL);
473
474   return g_task_propagate_pointer (G_TASK (result), error);
475 }
476
477 static gchar *
478 do_lookup_by_address (GInetAddress  *address,
479                       GCancellable  *cancellable,
480                       GError       **error)
481 {
482   struct sockaddr_storage sockaddr_address;
483   gsize sockaddr_address_size;
484   GSocketAddress *gsockaddr;
485   gchar name[NI_MAXHOST];
486   gint retval;
487
488   gsockaddr = g_inet_socket_address_new (address, 0);
489   g_socket_address_to_native (gsockaddr, (struct sockaddr *)&sockaddr_address,
490                               sizeof (sockaddr_address), NULL);
491   sockaddr_address_size = g_socket_address_get_native_size (gsockaddr);
492   g_object_unref (gsockaddr);
493
494   retval = getnameinfo ((struct sockaddr *) &sockaddr_address, sockaddr_address_size,
495                         name, sizeof (name), NULL, 0, NI_NAMEREQD);
496   if (retval == 0)
497     return g_strdup (name);
498   else
499     {
500       gchar *phys;
501
502 #ifdef G_OS_WIN32
503       gchar *error_message = g_win32_error_message (WSAGetLastError ());
504 #else
505       gchar *error_message = g_locale_to_utf8 (gai_strerror (retval), -1, NULL, NULL, NULL);
506       if (error_message == NULL)
507         error_message = g_strdup ("[Invalid UTF-8]");
508 #endif
509
510       phys = g_inet_address_to_string (address);
511       g_set_error (error,
512                    G_RESOLVER_ERROR,
513                    g_resolver_error_from_addrinfo_error (retval),
514                    _("Error reverse-resolving “%s”: %s"),
515                    phys ? phys : "(unknown)",
516                    error_message);
517       g_free (phys);
518       g_free (error_message);
519
520       return NULL;
521     }
522 }
523
524 static gchar *
525 lookup_by_address (GResolver        *resolver,
526                    GInetAddress     *address,
527                    GCancellable     *cancellable,
528                    GError          **error)
529 {
530   GThreadedResolver *self = G_THREADED_RESOLVER (resolver);
531   LookupData *data = NULL;
532   GTask *task;
533   gchar *name;
534
535   data = lookup_data_new_by_address (address);
536   task = g_task_new (resolver, cancellable, NULL, NULL);
537   g_task_set_source_tag (task, lookup_by_address);
538   g_task_set_name (task, "[gio] resolver lookup");
539   g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) lookup_data_free);
540
541   run_task_in_thread_pool_sync (self, task);
542
543   name = g_task_propagate_pointer (task, error);
544   g_object_unref (task);
545
546   return name;
547 }
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 {
556   GThreadedResolver *self = G_THREADED_RESOLVER (resolver);
557   LookupData *data = NULL;
558   GTask *task;
559
560   data = lookup_data_new_by_address (address);
561   task = g_task_new (resolver, cancellable, callback, user_data);
562   g_task_set_source_tag (task, lookup_by_address_async);
563   g_task_set_name (task, "[gio] resolver lookup");
564   g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) lookup_data_free);
565
566   run_task_in_thread_pool_async (self, task);
567
568   g_object_unref (task);
569 }
570
571 static gchar *
572 lookup_by_address_finish (GResolver     *resolver,
573                           GAsyncResult  *result,
574                           GError       **error)
575 {
576   g_return_val_if_fail (g_task_is_valid (result, resolver), NULL);
577
578   return g_task_propagate_pointer (G_TASK (result), error);
579 }
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 {
730   int expand_result;
731
732   expand_result = dn_expand (answer, end, *p, namebuf, namebuf_len);
733   if (expand_result < 0 || end - *p < expand_result)
734     {
735       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                    _("Error parsing DNS %s record: malformed DNS packet"), rrname);
738       return FALSE;
739     }
740
741   *p += expand_result;
742
743   return TRUE;
744 }
745
746 static GVariant *
747 parse_res_srv (const guint8  *answer,
748                const guint8  *end,
749                const guint8 **p,
750                GError       **error)
751 {
752   gchar namebuf[1024];
753   guint16 priority, weight, port;
754
755   if (end - *p < 6)
756     {
757       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                    _("Error parsing DNS %s record: malformed DNS packet"), "SRV");
760       return NULL;
761     }
762
763   GETSHORT (priority, *p);
764   GETSHORT (weight, *p);
765   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   if (!expand_name ("SRV", answer, end, p, namebuf, sizeof (namebuf), error))
784     return NULL;
785
786   return g_variant_new ("(qqqs)",
787                         priority,
788                         weight,
789                         port,
790                         namebuf);
791 }
792
793 static GVariant *
794 parse_res_soa (const guint8  *answer,
795                const guint8  *end,
796                const guint8 **p,
797                GError       **error)
798 {
799   gchar mnamebuf[1024];
800   gchar rnamebuf[1024];
801   guint32 serial, refresh, retry, expire, ttl;
802
803   if (!expand_name ("SOA", answer, end, p, mnamebuf, sizeof (mnamebuf), error))
804     return NULL;
805
806   if (!expand_name ("SOA", answer, end, p, rnamebuf, sizeof (rnamebuf), error))
807     return NULL;
808
809   if (end - *p < 20)
810     {
811       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                    _("Error parsing DNS %s record: malformed DNS packet"), "SOA");
814       return NULL;
815     }
816
817   GETLONG (serial, *p);
818   GETLONG (refresh, *p);
819   GETLONG (retry, *p);
820   GETLONG (expire, *p);
821   GETLONG (ttl, *p);
822
823   return g_variant_new ("(ssuuuuu)",
824                         mnamebuf,
825                         rnamebuf,
826                         serial,
827                         refresh,
828                         retry,
829                         expire,
830                         ttl);
831 }
832
833 static GVariant *
834 parse_res_ns (const guint8  *answer,
835               const guint8  *end,
836               const guint8 **p,
837               GError       **error)
838 {
839   gchar namebuf[1024];
840
841   if (!expand_name ("NS", answer, end, p, namebuf, sizeof (namebuf), error))
842     return NULL;
843
844   return g_variant_new ("(s)", namebuf);
845 }
846
847 static GVariant *
848 parse_res_mx (const guint8  *answer,
849               const guint8  *end,
850               const guint8 **p,
851               GError       **error)
852 {
853   gchar namebuf[1024];
854   guint16 preference;
855
856   if (end - *p < 2)
857     {
858       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                    _("Error parsing DNS %s record: malformed DNS packet"), "MX");
861       return NULL;
862     }
863
864   GETSHORT (preference, *p);
865
866   if (!expand_name ("MX", answer, end, p, namebuf, sizeof (namebuf), error))
867     return NULL;
868
869   return g_variant_new ("(qs)",
870                         preference,
871                         namebuf);
872 }
873
874 static GVariant *
875 parse_res_txt (const guint8  *answer,
876                const guint8  *end,
877                const guint8 **p,
878                GError       **error)
879 {
880   GVariant *record;
881   GPtrArray *array;
882   const guint8 *at = *p;
883   gsize len;
884
885   if (end - *p == 0)
886     {
887       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                    _("Error parsing DNS %s record: malformed DNS packet"), "TXT");
890       return NULL;
891     }
892
893   array = g_ptr_array_new_with_free_func (g_free);
894   while (at < end)
895     {
896       len = *(at++);
897       if (len > (gsize) (end - at))
898         {
899           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                        _("Error parsing DNS %s record: malformed DNS packet"), "TXT");
902           g_ptr_array_free (array, TRUE);
903           return NULL;
904         }
905
906       g_ptr_array_add (array, g_strndup ((gchar *)at, len));
907       at += len;
908     }
909
910   *p = at;
911   record = g_variant_new ("(@as)",
912                           g_variant_new_strv ((const gchar **)array->pdata, array->len));
913   g_ptr_array_free (array, TRUE);
914   return record;
915 }
916
917 gint
918 g_resolver_record_type_to_rrtype (GResolverRecordType type)
919 {
920   switch (type)
921   {
922     case G_RESOLVER_RECORD_SRV:
923       return T_SRV;
924     case G_RESOLVER_RECORD_TXT:
925       return T_TXT;
926     case G_RESOLVER_RECORD_SOA:
927       return T_SOA;
928     case G_RESOLVER_RECORD_NS:
929       return T_NS;
930     case G_RESOLVER_RECORD_MX:
931       return T_MX;
932   }
933   g_return_val_if_reached (-1);
934 }
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 {
944   uint16_t count;
945   gchar namebuf[1024];
946   const guint8 *end, *p;
947   guint16 type, qclass, rdlength;
948   const HEADER *header;
949   GList *records;
950   GVariant *record;
951   gsize len_unsigned;
952   GError *parsing_error = NULL;
953
954   if (len <= 0)
955     {
956       if (len == 0 || herr == HOST_NOT_FOUND || herr == NO_DATA)
957         {
958           g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND,
959                        _("No DNS record of the requested type for “%s”"), rrname);
960         }
961       else if (herr == TRY_AGAIN)
962         {
963           g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_TEMPORARY_FAILURE,
964                        _("Temporarily unable to resolve “%s”"), rrname);
965         }
966       else
967         {
968           g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
969                        _("Error resolving “%s”"), rrname);
970         }
971
972       return NULL;
973     }
974
975   /* We know len ≥ 0 now. */
976   len_unsigned = (gsize) len;
977
978   if (len_unsigned < sizeof (HEADER))
979     {
980       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                    _("Error resolving “%s”: %s"), rrname, _("Malformed DNS packet"));
984       return NULL;
985     }
986
987   records = NULL;
988
989   header = (HEADER *)answer;
990   p = answer + sizeof (HEADER);
991   end = answer + len_unsigned;
992
993   /* Skip query */
994   count = ntohs (header->qdcount);
995   while (count-- && p < end)
996     {
997       int expand_result;
998
999       expand_result = dn_expand (answer, end, p, namebuf, sizeof (namebuf));
1000       if (expand_result < 0 || end - p < expand_result + 4)
1001         {
1002           /* Not possible to recover parsing as the length of the rest of the
1003            * record is unknown or is too short. */
1004           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                        _("Error resolving “%s”: %s"), rrname, _("Malformed DNS packet"));
1008           return NULL;
1009         }
1010
1011       p += expand_result;
1012       p += 4;  /* skip TYPE and CLASS */
1013
1014       /* To silence gcc warnings */
1015       namebuf[0] = namebuf[1];
1016     }
1017
1018   /* Read answers */
1019   count = ntohs (header->ancount);
1020   while (count-- && p < end)
1021     {
1022       int expand_result;
1023
1024       expand_result = dn_expand (answer, end, p, namebuf, sizeof (namebuf));
1025       if (expand_result < 0 || end - p < expand_result + 10)
1026         {
1027           /* Not possible to recover parsing as the length of the rest of the
1028            * record is unknown or is too short. */
1029           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                        _("Error resolving “%s”: %s"), rrname, _("Malformed DNS packet"));
1033           break;
1034         }
1035
1036       p += expand_result;
1037       GETSHORT (type, p);
1038       GETSHORT (qclass, p);
1039       p += 4; /* ignore the ttl (type=long) value */
1040       GETSHORT (rdlength, p);
1041
1042       if (end - p < rdlength)
1043         {
1044           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                        _("Error resolving “%s”: %s"), rrname, _("Malformed DNS packet"));
1048           break;
1049         }
1050
1051       if (type != rrtype || qclass != C_IN)
1052         {
1053           p += rdlength;
1054           continue;
1055         }
1056
1057       switch (rrtype)
1058         {
1059         case T_SRV:
1060           record = parse_res_srv (answer, p + rdlength, &p, &parsing_error);
1061           break;
1062         case T_MX:
1063           record = parse_res_mx (answer, p + rdlength, &p, &parsing_error);
1064           break;
1065         case T_SOA:
1066           record = parse_res_soa (answer, p + rdlength, &p, &parsing_error);
1067           break;
1068         case T_NS:
1069           record = parse_res_ns (answer, p + rdlength, &p, &parsing_error);
1070           break;
1071         case T_TXT:
1072           record = parse_res_txt (answer, p + rdlength, &p, &parsing_error);
1073           break;
1074         default:
1075           g_debug ("Unrecognised DNS record type %u", rrtype);
1076           record = NULL;
1077           break;
1078         }
1079
1080       if (record != NULL)
1081         records = g_list_prepend (records, record);
1082
1083       if (parsing_error != NULL)
1084         break;
1085     }
1086
1087   if (parsing_error != NULL)
1088     {
1089       g_propagate_prefixed_error (error, parsing_error, _("Failed to parse DNS response for “%s”: "), rrname);
1090       g_list_free_full (records, (GDestroyNotify)g_variant_unref);
1091       return NULL;
1092     }
1093   else if (records == NULL)
1094     {
1095       g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND,
1096                    _("No DNS record of the requested type for “%s”"), rrname);
1097
1098       return NULL;
1099     }
1100   else
1101     return records;
1102 }
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 {
1257   g_list_free_full (records, (GDestroyNotify) g_variant_unref);
1258 }
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 {
1275   GList *records;
1276
1277 #if defined(G_OS_UNIX)
1278   gint len = 512;
1279   gint herr;
1280   GByteArray *answer;
1281   gint rrtype;
1282
1283 #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   struct __res_state res = { 0, };
1297   if (res_ninit (&res) != 0)
1298     {
1299       g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
1300                    _("Error resolving “%s”"), rrname);
1301       return NULL;
1302     }
1303 #endif
1304
1305   rrtype = g_resolver_record_type_to_rrtype (record_type);
1306   answer = g_byte_array_new ();
1307   for (;;)
1308     {
1309       g_byte_array_set_size (answer, len * 2);
1310 #if defined(HAVE_RES_NQUERY)
1311       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       if (len < 0 || len < (gint)answer->len)
1318         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     }
1325
1326   herr = h_errno;
1327   records = g_resolver_records_from_res_query (rrname, rrtype, answer->data, len, herr, error);
1328   g_byte_array_free (answer, TRUE);
1329
1330 #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 #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   return g_steal_pointer (&records);
1357 }
1358
1359 static GList *
1360 lookup_records (GResolver              *resolver,
1361                 const gchar            *rrname,
1362                 GResolverRecordType     record_type,
1363                 GCancellable           *cancellable,
1364                 GError                **error)
1365 {
1366   GThreadedResolver *self = G_THREADED_RESOLVER (resolver);
1367   GTask *task;
1368   GList *records;
1369   LookupData *data = NULL;
1370
1371   task = g_task_new (resolver, cancellable, NULL, NULL);
1372   g_task_set_source_tag (task, lookup_records);
1373   g_task_set_name (task, "[gio] resolver lookup records");
1374
1375   data = lookup_data_new_records (rrname, record_type);
1376   g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) lookup_data_free);
1377
1378   run_task_in_thread_pool_sync (self, task);
1379
1380   records = g_task_propagate_pointer (task, error);
1381   g_object_unref (task);
1382
1383   return records;
1384 }
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 {
1394   GThreadedResolver *self = G_THREADED_RESOLVER (resolver);
1395   GTask *task;
1396   LookupData *data = NULL;
1397
1398   task = g_task_new (resolver, cancellable, callback, user_data);
1399   g_task_set_source_tag (task, lookup_records_async);
1400   g_task_set_name (task, "[gio] resolver lookup records");
1401
1402   data = lookup_data_new_records (rrname, record_type);
1403   g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) lookup_data_free);
1404
1405   run_task_in_thread_pool_async (self, task);
1406
1407   g_object_unref (task);
1408 }
1409
1410 static GList *
1411 lookup_records_finish (GResolver     *resolver,
1412                        GAsyncResult  *result,
1413                        GError       **error)
1414 {
1415   g_return_val_if_fail (g_task_is_valid (result, resolver), NULL);
1416
1417   return g_task_propagate_pointer (G_TASK (result), error);
1418 }
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 {
1425   GWeakRef *weak_task = user_data;
1426   GTask *task = NULL;  /* (owned) */
1427   LookupData *data;
1428   gboolean should_return;
1429
1430   task = g_weak_ref_get (weak_task);
1431   if (task == NULL)
1432     return G_SOURCE_REMOVE;
1433
1434   data = g_task_get_task_data (task);
1435
1436   g_mutex_lock (&data->lock);
1437
1438   should_return = g_atomic_int_compare_and_exchange (&data->will_return, NOT_YET, TIMED_OUT);
1439   g_clear_pointer (&data->timeout_source, g_source_unref);
1440
1441   g_mutex_unlock (&data->lock);
1442
1443   if (should_return)
1444     g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_TIMED_OUT,
1445                              _("Socket I/O timed out"));
1446
1447   /* Signal completion of the task. */
1448   g_mutex_lock (&data->lock);
1449   data->has_returned = TRUE;
1450   g_cond_broadcast (&data->cond);
1451   g_mutex_unlock (&data->lock);
1452
1453   g_object_unref (task);
1454
1455   return G_SOURCE_REMOVE;
1456 }
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 {
1464   GWeakRef *weak_task = user_data;
1465   GTask *task = NULL;  /* (owned) */
1466   LookupData *data;
1467   gboolean should_return;
1468
1469   task = g_weak_ref_get (weak_task);
1470   if (task == NULL)
1471     return G_SOURCE_REMOVE;
1472
1473   data = g_task_get_task_data (task);
1474
1475   g_mutex_lock (&data->lock);
1476
1477   g_assert (g_cancellable_is_cancelled (cancellable));
1478   should_return = g_atomic_int_compare_and_exchange (&data->will_return, NOT_YET, CANCELLED);
1479   g_clear_pointer (&data->cancellable_source, g_source_unref);
1480
1481   g_mutex_unlock (&data->lock);
1482
1483   if (should_return)
1484     g_task_return_error_if_cancelled (task);
1485
1486   /* Signal completion of the task. */
1487   g_mutex_lock (&data->lock);
1488   data->has_returned = TRUE;
1489   g_cond_broadcast (&data->cond);
1490   g_mutex_unlock (&data->lock);
1491
1492   g_object_unref (task);
1493
1494   return G_SOURCE_REMOVE;
1495 }
1496
1497 static void
1498 weak_ref_clear_and_free (GWeakRef *weak_ref)
1499 {
1500   g_weak_ref_clear (weak_ref);
1501   g_free (weak_ref);
1502 }
1503
1504 static void
1505 run_task_in_thread_pool_async (GThreadedResolver *self,
1506                                GTask             *task)
1507 {
1508   LookupData *data = g_task_get_task_data (task);
1509   guint timeout_ms = g_resolver_get_timeout (G_RESOLVER (self));
1510   GCancellable *cancellable = g_task_get_cancellable (task);
1511
1512   g_mutex_lock (&data->lock);
1513
1514   g_thread_pool_push (self->thread_pool, g_object_ref (task), NULL);
1515
1516   if (timeout_ms != 0)
1517     {
1518       GWeakRef *weak_task = g_new0 (GWeakRef, 1);
1519       g_weak_ref_set (weak_task, task);
1520
1521       data->timeout_source = g_timeout_source_new (timeout_ms);
1522       g_source_set_static_name (data->timeout_source, "[gio] threaded resolver timeout");
1523       g_source_set_callback (data->timeout_source, G_SOURCE_FUNC (timeout_cb), g_steal_pointer (&weak_task), (GDestroyNotify) weak_ref_clear_and_free);
1524       g_source_attach (data->timeout_source, GLIB_PRIVATE_CALL (g_get_worker_context) ());
1525     }
1526
1527   if (cancellable != NULL)
1528     {
1529       GWeakRef *weak_task = g_new0 (GWeakRef, 1);
1530       g_weak_ref_set (weak_task, task);
1531
1532       data->cancellable_source = g_cancellable_source_new (cancellable);
1533       g_source_set_static_name (data->cancellable_source, "[gio] threaded resolver cancellable");
1534       g_source_set_callback (data->cancellable_source, G_SOURCE_FUNC (cancelled_cb), g_steal_pointer (&weak_task), (GDestroyNotify) weak_ref_clear_and_free);
1535       g_source_attach (data->cancellable_source, GLIB_PRIVATE_CALL (g_get_worker_context) ());
1536     }
1537
1538   g_mutex_unlock (&data->lock);
1539 }
1540
1541 static void
1542 run_task_in_thread_pool_sync (GThreadedResolver *self,
1543                               GTask             *task)
1544 {
1545   LookupData *data = g_task_get_task_data (task);
1546
1547   run_task_in_thread_pool_async (self, task);
1548
1549   g_mutex_lock (&data->lock);
1550   while (!data->has_returned)
1551     g_cond_wait (&data->cond, &data->lock);
1552   g_mutex_unlock (&data->lock);
1553 }
1554
1555 static void
1556 threaded_resolver_worker_cb (gpointer task_data,
1557                              gpointer user_data)
1558 {
1559   GTask *task = G_TASK (g_steal_pointer (&task_data));
1560   LookupData *data = g_task_get_task_data (task);
1561   GCancellable *cancellable = g_task_get_cancellable (task);
1562   GError *local_error = NULL;
1563   gboolean should_return;
1564
1565   switch (data->lookup_type) {
1566   case LOOKUP_BY_NAME:
1567     {
1568       GList *addresses = do_lookup_by_name (data->lookup_by_name.hostname,
1569                                             data->lookup_by_name.address_family,
1570                                             cancellable,
1571                                             &local_error);
1572
1573       g_mutex_lock (&data->lock);
1574       should_return = g_atomic_int_compare_and_exchange (&data->will_return, NOT_YET, COMPLETED);
1575       g_mutex_unlock (&data->lock);
1576
1577       if (should_return)
1578         {
1579           if (addresses != NULL)
1580             g_task_return_pointer (task, g_steal_pointer (&addresses), (GDestroyNotify) g_resolver_free_addresses);
1581           else
1582             g_task_return_error (task, g_steal_pointer (&local_error));
1583         }
1584
1585       g_clear_pointer (&addresses, g_resolver_free_addresses);
1586     }
1587     break;
1588   case LOOKUP_BY_ADDRESS:
1589     {
1590       gchar *name = do_lookup_by_address (data->lookup_by_address.address,
1591                                           cancellable,
1592                                           &local_error);
1593
1594       g_mutex_lock (&data->lock);
1595       should_return = g_atomic_int_compare_and_exchange (&data->will_return, NOT_YET, COMPLETED);
1596       g_mutex_unlock (&data->lock);
1597
1598       if (should_return)
1599         {
1600           if (name != NULL)
1601             g_task_return_pointer (task, g_steal_pointer (&name), g_free);
1602           else
1603             g_task_return_error (task, g_steal_pointer (&local_error));
1604         }
1605
1606       g_clear_pointer (&name, g_free);
1607     }
1608     break;
1609   case LOOKUP_RECORDS:
1610     {
1611       GList *records = do_lookup_records (data->lookup_records.rrname,
1612                                           data->lookup_records.record_type,
1613                                           cancellable,
1614                                           &local_error);
1615
1616       g_mutex_lock (&data->lock);
1617       should_return = g_atomic_int_compare_and_exchange (&data->will_return, NOT_YET, COMPLETED);
1618       g_mutex_unlock (&data->lock);
1619
1620       if (should_return)
1621         {
1622           if (records != NULL)
1623             g_task_return_pointer (task, g_steal_pointer (&records), (GDestroyNotify) free_records);
1624           else
1625             g_task_return_error (task, g_steal_pointer (&local_error));
1626         }
1627
1628       g_clear_pointer (&records, free_records);
1629     }
1630     break;
1631   default:
1632     g_assert_not_reached ();
1633   }
1634
1635   /* Signal completion of a task. */
1636   g_mutex_lock (&data->lock);
1637   data->has_returned = TRUE;
1638   g_cond_broadcast (&data->cond);
1639   g_mutex_unlock (&data->lock);
1640
1641   g_object_unref (task);
1642 }
1643
1644 static void
1645 g_threaded_resolver_class_init (GThreadedResolverClass *threaded_class)
1646 {
1647   GObjectClass *object_class = G_OBJECT_CLASS (threaded_class);
1648   GResolverClass *resolver_class = G_RESOLVER_CLASS (threaded_class);
1649
1650   object_class->finalize = g_threaded_resolver_finalize;
1651
1652   resolver_class->lookup_by_name                   = lookup_by_name;
1653   resolver_class->lookup_by_name_async             = lookup_by_name_async;
1654   resolver_class->lookup_by_name_finish            = lookup_by_name_finish;
1655   resolver_class->lookup_by_name_with_flags        = lookup_by_name_with_flags;
1656   resolver_class->lookup_by_name_with_flags_async  = lookup_by_name_with_flags_async;
1657   resolver_class->lookup_by_name_with_flags_finish = lookup_by_name_with_flags_finish;
1658   resolver_class->lookup_by_address                = lookup_by_address;
1659   resolver_class->lookup_by_address_async          = lookup_by_address_async;
1660   resolver_class->lookup_by_address_finish         = lookup_by_address_finish;
1661   resolver_class->lookup_records                   = lookup_records;
1662   resolver_class->lookup_records_async             = lookup_records_async;
1663   resolver_class->lookup_records_finish            = lookup_records_finish;
1664 }