LCOV - code coverage report
Current view: top level - gio - gthreadedresolver.c (source / functions) Coverage Total Hit
Test: unnamed Lines: 48.4 % 581 281
Test Date: 2026-02-10 05:15:13 Functions: 48.9 % 45 22
Branches: - 0 0

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

Generated by: LCOV version 2.0-1