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

Generated by: LCOV version 1.14