1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
3 /* GIO - GLib Input, Output and Streaming Library
5 * Copyright (C) 2008 Red Hat, Inc.
6 * Copyright (C) 2018 Igalia S.L.
8 * SPDX-License-Identifier: LGPL-2.1-or-later
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.
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.
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/>.
31 #include "glib/glib-private.h"
32 #include "gthreadedresolver.h"
33 #include "gnetworkingprivate.h"
35 #include "gcancellable.h"
36 #include "ginetaddress.h"
37 #include "ginetsocketaddress.h"
39 #include "gsocketaddress.h"
40 #include "gsrvtarget.h"
43 * GThreadedResolver is a threaded wrapper around the system libc’s
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.
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).
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
62 * [using its own thread pool](https://github.com/bminor/glibc/blob/master/resolv/gai_misc.c),
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.
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).
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.
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.
83 struct _GThreadedResolver
85 GResolver parent_instance;
87 GThreadPool *thread_pool; /* (owned) */
90 G_DEFINE_TYPE (GThreadedResolver, g_threaded_resolver, G_TYPE_RESOLVER)
92 static void run_task_in_thread_pool_async (GThreadedResolver *self,
94 static void run_task_in_thread_pool_sync (GThreadedResolver *self,
96 static void threaded_resolver_worker_cb (gpointer task_data,
100 g_threaded_resolver_init (GThreadedResolver *self)
102 self->thread_pool = g_thread_pool_new_full (threaded_resolver_worker_cb,
104 (GDestroyNotify) g_object_unref,
111 g_threaded_resolver_finalize (GObject *object)
113 GThreadedResolver *self = G_THREADED_RESOLVER (object);
115 g_thread_pool_free (self->thread_pool, TRUE, FALSE);
116 self->thread_pool = NULL;
118 G_OBJECT_CLASS (g_threaded_resolver_parent_class)->finalize (object);
121 static GResolverError
122 g_resolver_error_from_addrinfo_error (gint err)
127 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
131 return G_RESOLVER_ERROR_NOT_FOUND;
134 return G_RESOLVER_ERROR_TEMPORARY_FAILURE;
137 return G_RESOLVER_ERROR_INTERNAL;
154 GInetAddress *address; /* (owned) */
158 GResolverRecordType record_type;
162 GCond cond; /* used for signalling completion of the task when running it sync */
165 GSource *timeout_source; /* (nullable) (owned) */
166 GSource *cancellable_source; /* (nullable) (owned) */
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. */
174 COMPLETED, /* libc lookup call has completed successfully or errored */
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;
186 lookup_data_new_by_name (const char *hostname,
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);
199 lookup_data_new_by_address (GInetAddress *address)
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);
210 lookup_data_new_records (const gchar *rrname,
211 GResolverRecordType record_type)
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);
223 lookup_data_free (LookupData *data)
225 switch (data->lookup_type) {
227 g_free (data->lookup_by_name.hostname);
229 case LOOKUP_BY_ADDRESS:
230 g_clear_object (&data->lookup_by_address.address);
233 g_free (data->lookup_records.rrname);
236 g_assert_not_reached ();
239 if (data->timeout_source != NULL)
241 g_source_destroy (data->timeout_source);
242 g_clear_pointer (&data->timeout_source, g_source_unref);
245 if (data->cancellable_source != NULL)
247 g_source_destroy (data->cancellable_source);
248 g_clear_pointer (&data->cancellable_source, g_source_unref);
251 g_mutex_clear (&data->lock);
252 g_cond_clear (&data->cond);
258 do_lookup_by_name (const gchar *hostname,
260 GCancellable *cancellable,
263 struct addrinfo *res = NULL;
266 struct addrinfo addrinfo_hints = { 0 };
269 addrinfo_hints.ai_flags = AI_ADDRCONFIG;
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.
275 addrinfo_hints.ai_socktype = SOCK_STREAM;
276 addrinfo_hints.ai_protocol = IPPROTO_TCP;
278 addrinfo_hints.ai_family = address_family;
279 retval = getaddrinfo (hostname, NULL, &addrinfo_hints, &res);
284 GSocketAddress *sockaddr;
288 for (ai = res; ai; ai = ai->ai_next)
290 sockaddr = g_socket_address_new_from_native (ai->ai_addr, ai->ai_addrlen);
293 if (!G_IS_INET_SOCKET_ADDRESS (sockaddr))
295 g_clear_object (&sockaddr);
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);
304 g_clear_pointer (&res, freeaddrinfo);
306 if (addresses != NULL)
308 addresses = g_list_reverse (addresses);
309 return g_steal_pointer (&addresses);
313 /* All addresses failed to be converted to GSocketAddresses. */
316 G_RESOLVER_ERROR_NOT_FOUND,
317 _("Error resolving “%s”: %s"),
319 _("No valid addresses were found"));
326 gchar *error_message = g_win32_error_message (WSAGetLastError ());
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]");
333 g_clear_pointer (&res, freeaddrinfo);
337 g_resolver_error_from_addrinfo_error (retval),
338 _("Error resolving “%s”: %s"),
339 hostname, error_message);
340 g_free (error_message);
347 lookup_by_name (GResolver *resolver,
348 const gchar *hostname,
349 GCancellable *cancellable,
352 GThreadedResolver *self = G_THREADED_RESOLVER (resolver);
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);
363 run_task_in_thread_pool_sync (self, task);
365 addresses = g_task_propagate_pointer (task, error);
366 g_object_unref (task);
372 flags_to_family (GResolverNameLookupFlags flags)
374 int address_family = AF_UNSPEC;
376 if (flags & G_RESOLVER_NAME_LOOKUP_FLAGS_IPV4_ONLY)
377 address_family = AF_INET;
379 if (flags & G_RESOLVER_NAME_LOOKUP_FLAGS_IPV6_ONLY)
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);
386 return address_family;
390 lookup_by_name_with_flags (GResolver *resolver,
391 const gchar *hostname,
392 GResolverNameLookupFlags flags,
393 GCancellable *cancellable,
396 GThreadedResolver *self = G_THREADED_RESOLVER (resolver);
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);
407 run_task_in_thread_pool_sync (self, task);
409 addresses = g_task_propagate_pointer (task, error);
410 g_object_unref (task);
416 lookup_by_name_with_flags_async (GResolver *resolver,
417 const gchar *hostname,
418 GResolverNameLookupFlags flags,
419 GCancellable *cancellable,
420 GAsyncReadyCallback callback,
423 GThreadedResolver *self = G_THREADED_RESOLVER (resolver);
427 data = lookup_data_new_by_name (hostname, flags_to_family (flags));
428 task = g_task_new (resolver, cancellable, callback, user_data);
430 g_debug ("%s: starting new lookup for %s with GTask %p, LookupData %p",
431 G_STRFUNC, hostname, task, data);
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);
437 run_task_in_thread_pool_async (self, task);
439 g_object_unref (task);
443 lookup_by_name_async (GResolver *resolver,
444 const gchar *hostname,
445 GCancellable *cancellable,
446 GAsyncReadyCallback callback,
449 lookup_by_name_with_flags_async (resolver,
451 G_RESOLVER_NAME_LOOKUP_FLAGS_DEFAULT,
458 lookup_by_name_finish (GResolver *resolver,
459 GAsyncResult *result,
462 g_return_val_if_fail (g_task_is_valid (result, resolver), NULL);
464 return g_task_propagate_pointer (G_TASK (result), error);
468 lookup_by_name_with_flags_finish (GResolver *resolver,
469 GAsyncResult *result,
472 g_return_val_if_fail (g_task_is_valid (result, resolver), NULL);
474 return g_task_propagate_pointer (G_TASK (result), error);
478 do_lookup_by_address (GInetAddress *address,
479 GCancellable *cancellable,
482 struct sockaddr_storage sockaddr_address;
483 gsize sockaddr_address_size;
484 GSocketAddress *gsockaddr;
485 gchar name[NI_MAXHOST];
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);
494 retval = getnameinfo ((struct sockaddr *) &sockaddr_address, sockaddr_address_size,
495 name, sizeof (name), NULL, 0, NI_NAMEREQD);
497 return g_strdup (name);
503 gchar *error_message = g_win32_error_message (WSAGetLastError ());
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]");
510 phys = g_inet_address_to_string (address);
513 g_resolver_error_from_addrinfo_error (retval),
514 _("Error reverse-resolving “%s”: %s"),
515 phys ? phys : "(unknown)",
518 g_free (error_message);
525 lookup_by_address (GResolver *resolver,
526 GInetAddress *address,
527 GCancellable *cancellable,
530 GThreadedResolver *self = G_THREADED_RESOLVER (resolver);
531 LookupData *data = NULL;
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);
541 run_task_in_thread_pool_sync (self, task);
543 name = g_task_propagate_pointer (task, error);
544 g_object_unref (task);
550 lookup_by_address_async (GResolver *resolver,
551 GInetAddress *address,
552 GCancellable *cancellable,
553 GAsyncReadyCallback callback,
556 GThreadedResolver *self = G_THREADED_RESOLVER (resolver);
557 LookupData *data = NULL;
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);
566 run_task_in_thread_pool_async (self, task);
568 g_object_unref (task);
572 lookup_by_address_finish (GResolver *resolver,
573 GAsyncResult *result,
576 g_return_val_if_fail (g_task_is_valid (result, resolver), NULL);
578 return g_task_propagate_pointer (G_TASK (result), error);
582 #if defined(G_OS_UNIX)
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 */
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 */
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 */
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 */
624 #define NS_INT32SZ 4 /* #/bytes of data in a uint32_t */
625 #define NS_INT16SZ 2 /* #/bytes of data in a uint16_t */
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]) \
632 (cp) += NS_INT16SZ; \
633 } while (/*CONSTCOND*/0)
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]) \
642 (cp) += NS_INT32SZ; \
643 } while (/*CONSTCOND*/0)
645 #define GETSHORT NS_GET16
646 #define GETLONG NS_GET32
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 *);
655 /* From bionic/libc/private/arpa_nameser_compat.h */
657 #define T_TXT ns_t_txt
658 #define T_SOA ns_t_soa
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. */
717 #endif /* __BIONIC__ */
719 /* Wrapper around dn_expand() which does associated length checks and returns
720 * errors as #GError. */
722 expand_name (const gchar *rrname,
723 const guint8 *answer,
732 expand_result = dn_expand (answer, end, *p, namebuf, namebuf_len);
733 if (expand_result < 0 || end - *p < expand_result)
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);
747 parse_res_srv (const guint8 *answer,
753 guint16 priority, weight, port;
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");
763 GETSHORT (priority, *p);
764 GETSHORT (weight, *p);
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.
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.
775 * An earlier version of the RFC, RFC 2052 (now obsolete) specified that name
776 * compression *was* to be used for SRV targets[2].
778 * See discussion on https://gitlab.gnome.org/GNOME/glib/-/issues/2622.
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
783 if (!expand_name ("SRV", answer, end, p, namebuf, sizeof (namebuf), error))
786 return g_variant_new ("(qqqs)",
794 parse_res_soa (const guint8 *answer,
799 gchar mnamebuf[1024];
800 gchar rnamebuf[1024];
801 guint32 serial, refresh, retry, expire, ttl;
803 if (!expand_name ("SOA", answer, end, p, mnamebuf, sizeof (mnamebuf), error))
806 if (!expand_name ("SOA", answer, end, p, rnamebuf, sizeof (rnamebuf), error))
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");
817 GETLONG (serial, *p);
818 GETLONG (refresh, *p);
820 GETLONG (expire, *p);
823 return g_variant_new ("(ssuuuuu)",
834 parse_res_ns (const guint8 *answer,
841 if (!expand_name ("NS", answer, end, p, namebuf, sizeof (namebuf), error))
844 return g_variant_new ("(s)", namebuf);
848 parse_res_mx (const guint8 *answer,
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");
864 GETSHORT (preference, *p);
866 if (!expand_name ("MX", answer, end, p, namebuf, sizeof (namebuf), error))
869 return g_variant_new ("(qs)",
875 parse_res_txt (const guint8 *answer,
882 const guint8 *at = *p;
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");
893 array = g_ptr_array_new_with_free_func (g_free);
897 if (len > (gsize) (end - at))
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);
906 g_ptr_array_add (array, g_strndup ((gchar *)at, len));
911 record = g_variant_new ("(@as)",
912 g_variant_new_strv ((const gchar **)array->pdata, array->len));
913 g_ptr_array_free (array, TRUE);
918 g_resolver_record_type_to_rrtype (GResolverRecordType type)
922 case G_RESOLVER_RECORD_SRV:
924 case G_RESOLVER_RECORD_TXT:
926 case G_RESOLVER_RECORD_SOA:
928 case G_RESOLVER_RECORD_NS:
930 case G_RESOLVER_RECORD_MX:
933 g_return_val_if_reached (-1);
937 g_resolver_records_from_res_query (const gchar *rrname,
939 const guint8 *answer,
946 const guint8 *end, *p;
947 guint16 type, qclass, rdlength;
948 const HEADER *header;
952 GError *parsing_error = NULL;
956 if (len == 0 || herr == HOST_NOT_FOUND || herr == NO_DATA)
958 g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND,
959 _("No DNS record of the requested type for “%s”"), rrname);
961 else if (herr == TRY_AGAIN)
963 g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_TEMPORARY_FAILURE,
964 _("Temporarily unable to resolve “%s”"), rrname);
968 g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
969 _("Error resolving “%s”"), rrname);
975 /* We know len ≥ 0 now. */
976 len_unsigned = (gsize) len;
978 if (len_unsigned < sizeof (HEADER))
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"));
989 header = (HEADER *)answer;
990 p = answer + sizeof (HEADER);
991 end = answer + len_unsigned;
994 count = ntohs (header->qdcount);
995 while (count-- && p < end)
999 expand_result = dn_expand (answer, end, p, namebuf, sizeof (namebuf));
1000 if (expand_result < 0 || end - p < expand_result + 4)
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"));
1012 p += 4; /* skip TYPE and CLASS */
1014 /* To silence gcc warnings */
1015 namebuf[0] = namebuf[1];
1019 count = ntohs (header->ancount);
1020 while (count-- && p < end)
1024 expand_result = dn_expand (answer, end, p, namebuf, sizeof (namebuf));
1025 if (expand_result < 0 || end - p < expand_result + 10)
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"));
1038 GETSHORT (qclass, p);
1039 p += 4; /* ignore the ttl (type=long) value */
1040 GETSHORT (rdlength, p);
1042 if (end - p < rdlength)
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"));
1051 if (type != rrtype || qclass != C_IN)
1060 record = parse_res_srv (answer, p + rdlength, &p, &parsing_error);
1063 record = parse_res_mx (answer, p + rdlength, &p, &parsing_error);
1066 record = parse_res_soa (answer, p + rdlength, &p, &parsing_error);
1069 record = parse_res_ns (answer, p + rdlength, &p, &parsing_error);
1072 record = parse_res_txt (answer, p + rdlength, &p, &parsing_error);
1075 g_debug ("Unrecognised DNS record type %u", rrtype);
1081 records = g_list_prepend (records, record);
1083 if (parsing_error != NULL)
1087 if (parsing_error != NULL)
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);
1093 else if (records == NULL)
1095 g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND,
1096 _("No DNS record of the requested type for “%s”"), rrname);
1104 #elif defined(G_OS_WIN32)
1107 parse_dns_srv (DNS_RECORD *rec)
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);
1117 parse_dns_soa (DNS_RECORD *rec)
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);
1130 parse_dns_ns (DNS_RECORD *rec)
1132 return g_variant_new ("(s)", rec->Data.NS.pNameHost);
1136 parse_dns_mx (DNS_RECORD *rec)
1138 return g_variant_new ("(qs)",
1139 (guint16)rec->Data.MX.wPreference,
1140 rec->Data.MX.pNameExchange);
1144 parse_dns_txt (DNS_RECORD *rec)
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);
1160 g_resolver_record_type_to_dnstype (GResolverRecordType type)
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:
1172 case G_RESOLVER_RECORD_MX:
1175 g_return_val_if_reached (-1);
1179 g_resolver_records_from_DnsQuery (const gchar *rrname,
1182 DNS_RECORD *results,
1189 if (status != ERROR_SUCCESS)
1191 if (status == DNS_ERROR_RCODE_NAME_ERROR)
1193 g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND,
1194 _("No DNS record of the requested type for “%s”"), rrname);
1196 else if (status == DNS_ERROR_RCODE_SERVER_FAILURE)
1198 g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_TEMPORARY_FAILURE,
1199 _("Temporarily unable to resolve “%s”"), rrname);
1203 g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
1204 _("Error resolving “%s”"), rrname);
1211 for (rec = results; rec; rec = rec->pNext)
1213 if (rec->wType != dnstype)
1218 record = parse_dns_srv (rec);
1221 record = parse_dns_soa (rec);
1224 record = parse_dns_ns (rec);
1227 record = parse_dns_mx (rec);
1230 record = parse_dns_txt (rec);
1233 g_warn_if_reached ();
1238 records = g_list_prepend (records, g_variant_ref_sink (record));
1241 if (records == NULL)
1243 g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND,
1244 _("No DNS record of the requested type for “%s”"), rrname);
1255 free_records (GList *records)
1257 g_list_free_full (records, (GDestroyNotify) g_variant_unref);
1260 #if defined(G_OS_UNIX)
1265 int res_query(const char *, int, int, u_char *, int);
1270 do_lookup_records (const gchar *rrname,
1271 GResolverRecordType record_type,
1272 GCancellable *cancellable,
1277 #if defined(G_OS_UNIX)
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.
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
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)
1299 g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
1300 _("Error resolving “%s”"), rrname);
1305 rrtype = g_resolver_record_type_to_rrtype (record_type);
1306 answer = g_byte_array_new ();
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);
1313 len = res_query (rrname, C_IN, rrtype, answer->data, answer->len);
1316 /* If answer fit in the buffer then we're done */
1317 if (len < 0 || len < (gint)answer->len)
1321 * On overflow some res_query's return the length needed, others
1322 * return the full length entered. This code works in either case.
1327 records = g_resolver_records_from_res_query (rrname, rrtype, answer->data, len, herr, error);
1328 g_byte_array_free (answer, TRUE);
1330 #ifdef HAVE_RES_NQUERY
1332 #if defined(HAVE_RES_NDESTROY)
1333 res_ndestroy (&res);
1334 #elif defined(HAVE_RES_NCLOSE)
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"
1340 #endif /* HAVE_RES_NQUERY */
1345 DNS_RECORD *results = NULL;
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);
1356 return g_steal_pointer (&records);
1360 lookup_records (GResolver *resolver,
1361 const gchar *rrname,
1362 GResolverRecordType record_type,
1363 GCancellable *cancellable,
1366 GThreadedResolver *self = G_THREADED_RESOLVER (resolver);
1369 LookupData *data = NULL;
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");
1375 data = lookup_data_new_records (rrname, record_type);
1376 g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) lookup_data_free);
1378 run_task_in_thread_pool_sync (self, task);
1380 records = g_task_propagate_pointer (task, error);
1381 g_object_unref (task);
1387 lookup_records_async (GResolver *resolver,
1389 GResolverRecordType record_type,
1390 GCancellable *cancellable,
1391 GAsyncReadyCallback callback,
1394 GThreadedResolver *self = G_THREADED_RESOLVER (resolver);
1396 LookupData *data = NULL;
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");
1402 data = lookup_data_new_records (rrname, record_type);
1403 g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) lookup_data_free);
1405 run_task_in_thread_pool_async (self, task);
1407 g_object_unref (task);
1411 lookup_records_finish (GResolver *resolver,
1412 GAsyncResult *result,
1415 g_return_val_if_fail (g_task_is_valid (result, resolver), NULL);
1417 return g_task_propagate_pointer (G_TASK (result), error);
1420 /* Will be called in the GLib worker thread, so must lock all accesses to shared
1423 timeout_cb (gpointer user_data)
1425 GWeakRef *weak_task = user_data;
1426 GTask *task = NULL; /* (owned) */
1428 gboolean should_return;
1430 task = g_weak_ref_get (weak_task);
1432 return G_SOURCE_REMOVE;
1434 data = g_task_get_task_data (task);
1436 g_mutex_lock (&data->lock);
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);
1441 g_mutex_unlock (&data->lock);
1444 g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_TIMED_OUT,
1445 _("Socket I/O timed out"));
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);
1453 g_object_unref (task);
1455 return G_SOURCE_REMOVE;
1458 /* Will be called in the GLib worker thread, so must lock all accesses to shared
1461 cancelled_cb (GCancellable *cancellable,
1464 GWeakRef *weak_task = user_data;
1465 GTask *task = NULL; /* (owned) */
1467 gboolean should_return;
1469 task = g_weak_ref_get (weak_task);
1471 return G_SOURCE_REMOVE;
1473 data = g_task_get_task_data (task);
1475 g_mutex_lock (&data->lock);
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);
1481 g_mutex_unlock (&data->lock);
1484 g_task_return_error_if_cancelled (task);
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);
1492 g_object_unref (task);
1494 return G_SOURCE_REMOVE;
1498 weak_ref_clear_and_free (GWeakRef *weak_ref)
1500 g_weak_ref_clear (weak_ref);
1505 run_task_in_thread_pool_async (GThreadedResolver *self,
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);
1512 g_mutex_lock (&data->lock);
1514 g_thread_pool_push (self->thread_pool, g_object_ref (task), NULL);
1516 if (timeout_ms != 0)
1518 GWeakRef *weak_task = g_new0 (GWeakRef, 1);
1519 g_weak_ref_set (weak_task, task);
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) ());
1527 if (cancellable != NULL)
1529 GWeakRef *weak_task = g_new0 (GWeakRef, 1);
1530 g_weak_ref_set (weak_task, task);
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) ());
1538 g_mutex_unlock (&data->lock);
1542 run_task_in_thread_pool_sync (GThreadedResolver *self,
1545 LookupData *data = g_task_get_task_data (task);
1547 run_task_in_thread_pool_async (self, task);
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);
1556 threaded_resolver_worker_cb (gpointer task_data,
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;
1565 switch (data->lookup_type) {
1566 case LOOKUP_BY_NAME:
1568 GList *addresses = do_lookup_by_name (data->lookup_by_name.hostname,
1569 data->lookup_by_name.address_family,
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);
1579 if (addresses != NULL)
1580 g_task_return_pointer (task, g_steal_pointer (&addresses), (GDestroyNotify) g_resolver_free_addresses);
1582 g_task_return_error (task, g_steal_pointer (&local_error));
1585 g_clear_pointer (&addresses, g_resolver_free_addresses);
1588 case LOOKUP_BY_ADDRESS:
1590 gchar *name = do_lookup_by_address (data->lookup_by_address.address,
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);
1601 g_task_return_pointer (task, g_steal_pointer (&name), g_free);
1603 g_task_return_error (task, g_steal_pointer (&local_error));
1606 g_clear_pointer (&name, g_free);
1609 case LOOKUP_RECORDS:
1611 GList *records = do_lookup_records (data->lookup_records.rrname,
1612 data->lookup_records.record_type,
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);
1622 if (records != NULL)
1623 g_task_return_pointer (task, g_steal_pointer (&records), (GDestroyNotify) free_records);
1625 g_task_return_error (task, g_steal_pointer (&local_error));
1628 g_clear_pointer (&records, free_records);
1632 g_assert_not_reached ();
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);
1641 g_object_unref (task);
1645 g_threaded_resolver_class_init (GThreadedResolverClass *threaded_class)
1647 GObjectClass *object_class = G_OBJECT_CLASS (threaded_class);
1648 GResolverClass *resolver_class = G_RESOLVER_CLASS (threaded_class);
1650 object_class->finalize = g_threaded_resolver_finalize;
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;