mirror of
				https://gitlab.gnome.org/GNOME/glib.git
				synced 2025-11-04 01:58:54 +01:00 
			
		
		
		
	Nicks and blurbs don't have any practical use for gio/gobject libraries. Leaving tests untouched since this features is still used by other libraries. Closes #2991
		
			
				
	
	
		
			1370 lines
		
	
	
		
			44 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1370 lines
		
	
	
		
			44 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
 | 
						||
 | 
						||
/* GIO - GLib Input, Output and Streaming Library
 | 
						||
 *
 | 
						||
 * Copyright (C) 2008 Red Hat, Inc.
 | 
						||
 * Copyright (C) 2018 Igalia S.L.
 | 
						||
 *
 | 
						||
 * SPDX-License-Identifier: LGPL-2.1-or-later
 | 
						||
 *
 | 
						||
 * This library is free software; you can redistribute it and/or
 | 
						||
 * modify it under the terms of the GNU Lesser General Public
 | 
						||
 * License as published by the Free Software Foundation; either
 | 
						||
 * version 2.1 of the License, or (at your option) any later version.
 | 
						||
 *
 | 
						||
 * This library is distributed in the hope that it will be useful,
 | 
						||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
						||
 * Lesser General Public License for more details.
 | 
						||
 *
 | 
						||
 * You should have received a copy of the GNU Lesser General
 | 
						||
 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
 | 
						||
 */
 | 
						||
 | 
						||
#include "config.h"
 | 
						||
#include <glib.h>
 | 
						||
#include "glibintl.h"
 | 
						||
 | 
						||
#include "gresolver.h"
 | 
						||
#include "gnetworkingprivate.h"
 | 
						||
#include "gasyncresult.h"
 | 
						||
#include "ginetaddress.h"
 | 
						||
#include "gtask.h"
 | 
						||
#include "gsrvtarget.h"
 | 
						||
#include "gthreadedresolver.h"
 | 
						||
#include "gioerror.h"
 | 
						||
#include "gcancellable.h"
 | 
						||
 | 
						||
#ifdef G_OS_UNIX
 | 
						||
#include <sys/stat.h>
 | 
						||
#endif
 | 
						||
 | 
						||
#include <stdlib.h>
 | 
						||
 | 
						||
 | 
						||
/**
 | 
						||
 * GResolver:
 | 
						||
 *
 | 
						||
 * The object that handles DNS resolution. Use [func@Gio.Resolver.get_default]
 | 
						||
 * to get the default resolver.
 | 
						||
 *
 | 
						||
 * `GResolver` provides cancellable synchronous and asynchronous DNS
 | 
						||
 * resolution, for hostnames ([method@Gio.Resolver.lookup_by_address],
 | 
						||
 * [method@Gio.Resolver.lookup_by_name] and their async variants) and SRV
 | 
						||
 * (service) records ([method@Gio.Resolver.lookup_service]).
 | 
						||
 *
 | 
						||
 * [class@Gio.NetworkAddress] and [class@Gio.NetworkService] provide wrappers
 | 
						||
 * around `GResolver` functionality that also implement
 | 
						||
 * [iface@Gio.SocketConnectable], making it easy to connect to a remote
 | 
						||
 * host/service.
 | 
						||
 *
 | 
						||
 * The default resolver (see [func@Gio.Resolver.get_default]) has a timeout of
 | 
						||
 * 30s set on it since GLib 2.78. Earlier versions of GLib did not support
 | 
						||
 * resolver timeouts.
 | 
						||
 *
 | 
						||
 * This is an abstract type; subclasses of it implement different resolvers for
 | 
						||
 * different platforms and situations.
 | 
						||
 */
 | 
						||
 | 
						||
typedef enum {
 | 
						||
  PROP_TIMEOUT = 1,
 | 
						||
} GResolverProperty;
 | 
						||
 | 
						||
static GParamSpec *props[PROP_TIMEOUT + 1] = { NULL, };
 | 
						||
 | 
						||
enum {
 | 
						||
  RELOAD,
 | 
						||
  LAST_SIGNAL
 | 
						||
};
 | 
						||
 | 
						||
static guint signals[LAST_SIGNAL] = { 0 };
 | 
						||
 | 
						||
struct _GResolverPrivate {
 | 
						||
  unsigned timeout_ms;
 | 
						||
 | 
						||
#ifdef G_OS_UNIX
 | 
						||
  GMutex mutex;
 | 
						||
  time_t resolv_conf_timestamp;  /* protected by @mutex */
 | 
						||
#endif
 | 
						||
};
 | 
						||
 | 
						||
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GResolver, g_resolver, G_TYPE_OBJECT,
 | 
						||
                                  G_ADD_PRIVATE (GResolver)
 | 
						||
                                  g_networking_init ();)
 | 
						||
 | 
						||
static GList *
 | 
						||
srv_records_to_targets (GList *records)
 | 
						||
{
 | 
						||
  const gchar *hostname;
 | 
						||
  guint16 port, priority, weight;
 | 
						||
  GSrvTarget *target;
 | 
						||
  GList *l;
 | 
						||
 | 
						||
  for (l = records; l != NULL; l = g_list_next (l))
 | 
						||
    {
 | 
						||
      g_variant_get (l->data, "(qqq&s)", &priority, &weight, &port, &hostname);
 | 
						||
      target = g_srv_target_new (hostname, port, priority, weight);
 | 
						||
      g_variant_unref (l->data);
 | 
						||
      l->data = target;
 | 
						||
    }
 | 
						||
 | 
						||
  return g_srv_target_list_sort (records);
 | 
						||
}
 | 
						||
 | 
						||
static GList *
 | 
						||
g_resolver_real_lookup_service (GResolver            *resolver,
 | 
						||
                                const gchar          *rrname,
 | 
						||
                                GCancellable         *cancellable,
 | 
						||
                                GError              **error)
 | 
						||
{
 | 
						||
  GList *records;
 | 
						||
 | 
						||
  records = G_RESOLVER_GET_CLASS (resolver)->lookup_records (resolver,
 | 
						||
                                                             rrname,
 | 
						||
                                                             G_RESOLVER_RECORD_SRV,
 | 
						||
                                                             cancellable,
 | 
						||
                                                             error);
 | 
						||
 | 
						||
  return srv_records_to_targets (records);
 | 
						||
}
 | 
						||
 | 
						||
static void
 | 
						||
g_resolver_real_lookup_service_async (GResolver            *resolver,
 | 
						||
                                      const gchar          *rrname,
 | 
						||
                                      GCancellable         *cancellable,
 | 
						||
                                      GAsyncReadyCallback   callback,
 | 
						||
                                      gpointer              user_data)
 | 
						||
{
 | 
						||
  G_RESOLVER_GET_CLASS (resolver)->lookup_records_async (resolver,
 | 
						||
                                                         rrname,
 | 
						||
                                                         G_RESOLVER_RECORD_SRV,
 | 
						||
                                                         cancellable,
 | 
						||
                                                         callback,
 | 
						||
                                                         user_data);
 | 
						||
}
 | 
						||
 | 
						||
static GList *
 | 
						||
g_resolver_real_lookup_service_finish (GResolver            *resolver,
 | 
						||
                                       GAsyncResult         *result,
 | 
						||
                                       GError              **error)
 | 
						||
{
 | 
						||
  GList *records;
 | 
						||
 | 
						||
  records = G_RESOLVER_GET_CLASS (resolver)->lookup_records_finish (resolver,
 | 
						||
                                                                    result,
 | 
						||
                                                                    error);
 | 
						||
 | 
						||
  return srv_records_to_targets (records);
 | 
						||
}
 | 
						||
 | 
						||
static void
 | 
						||
g_resolver_get_property (GObject    *object,
 | 
						||
                         guint       prop_id,
 | 
						||
                         GValue     *value,
 | 
						||
                         GParamSpec *pspec)
 | 
						||
{
 | 
						||
  GResolver *self = G_RESOLVER (object);
 | 
						||
 | 
						||
  switch ((GResolverProperty) prop_id)
 | 
						||
    {
 | 
						||
    case PROP_TIMEOUT:
 | 
						||
      g_value_set_uint (value, g_resolver_get_timeout (self));
 | 
						||
      break;
 | 
						||
    default:
 | 
						||
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
static void
 | 
						||
g_resolver_set_property (GObject      *object,
 | 
						||
                         guint         prop_id,
 | 
						||
                         const GValue *value,
 | 
						||
                         GParamSpec   *pspec)
 | 
						||
{
 | 
						||
  GResolver *self = G_RESOLVER (object);
 | 
						||
 | 
						||
  switch ((GResolverProperty) prop_id)
 | 
						||
    {
 | 
						||
    case PROP_TIMEOUT:
 | 
						||
      g_resolver_set_timeout (self, g_value_get_uint (value));
 | 
						||
      break;
 | 
						||
    default:
 | 
						||
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
static void
 | 
						||
g_resolver_finalize (GObject *object)
 | 
						||
{
 | 
						||
#ifdef G_OS_UNIX
 | 
						||
  GResolver *resolver = G_RESOLVER (object);
 | 
						||
 | 
						||
  g_mutex_clear (&resolver->priv->mutex);
 | 
						||
#endif
 | 
						||
 | 
						||
  G_OBJECT_CLASS (g_resolver_parent_class)->finalize (object);
 | 
						||
}
 | 
						||
 | 
						||
static void
 | 
						||
g_resolver_class_init (GResolverClass *resolver_class)
 | 
						||
{
 | 
						||
  GObjectClass *object_class = G_OBJECT_CLASS (resolver_class);
 | 
						||
 | 
						||
  object_class->get_property = g_resolver_get_property;
 | 
						||
  object_class->set_property = g_resolver_set_property;
 | 
						||
  object_class->finalize = g_resolver_finalize;
 | 
						||
 | 
						||
  /* Automatically pass these over to the lookup_records methods */
 | 
						||
  resolver_class->lookup_service = g_resolver_real_lookup_service;
 | 
						||
  resolver_class->lookup_service_async = g_resolver_real_lookup_service_async;
 | 
						||
  resolver_class->lookup_service_finish = g_resolver_real_lookup_service_finish;
 | 
						||
 | 
						||
  /**
 | 
						||
   * GResolver:timeout:
 | 
						||
   *
 | 
						||
   * The timeout applied to all resolver lookups, in milliseconds.
 | 
						||
   *
 | 
						||
   * This may be changed through the lifetime of the #GResolver. The new value
 | 
						||
   * will apply to any lookups started after the change, but not to any
 | 
						||
   * already-ongoing lookups.
 | 
						||
   *
 | 
						||
   * If this is `0`, no timeout is applied to lookups.
 | 
						||
   *
 | 
						||
   * No timeout was applied to lookups before this property was added in
 | 
						||
   * GLib 2.78.
 | 
						||
   *
 | 
						||
   * Since: 2.78
 | 
						||
   */
 | 
						||
  props[PROP_TIMEOUT] =
 | 
						||
    g_param_spec_uint ("timeout", NULL, NULL,
 | 
						||
                       0, G_MAXUINT, 0,
 | 
						||
                       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
 | 
						||
 | 
						||
  g_object_class_install_properties (object_class, G_N_ELEMENTS (props), props);
 | 
						||
 | 
						||
  /**
 | 
						||
   * GResolver::reload:
 | 
						||
   * @resolver: a #GResolver
 | 
						||
   *
 | 
						||
   * Emitted when the resolver notices that the system resolver
 | 
						||
   * configuration has changed.
 | 
						||
   **/
 | 
						||
  signals[RELOAD] =
 | 
						||
    g_signal_new (I_("reload"),
 | 
						||
		  G_TYPE_RESOLVER,
 | 
						||
		  G_SIGNAL_RUN_LAST,
 | 
						||
		  G_STRUCT_OFFSET (GResolverClass, reload),
 | 
						||
		  NULL, NULL,
 | 
						||
		  NULL,
 | 
						||
		  G_TYPE_NONE, 0);
 | 
						||
}
 | 
						||
 | 
						||
static void
 | 
						||
g_resolver_init (GResolver *resolver)
 | 
						||
{
 | 
						||
#ifdef G_OS_UNIX
 | 
						||
  struct stat st;
 | 
						||
#endif
 | 
						||
 | 
						||
  resolver->priv = g_resolver_get_instance_private (resolver);
 | 
						||
 | 
						||
#ifdef G_OS_UNIX
 | 
						||
  if (stat (_PATH_RESCONF, &st) == 0)
 | 
						||
    resolver->priv->resolv_conf_timestamp = st.st_mtime;
 | 
						||
 | 
						||
  g_mutex_init (&resolver->priv->mutex);
 | 
						||
#endif
 | 
						||
}
 | 
						||
 | 
						||
G_LOCK_DEFINE_STATIC (default_resolver);
 | 
						||
static GResolver *default_resolver;
 | 
						||
 | 
						||
/**
 | 
						||
 * g_resolver_get_default:
 | 
						||
 *
 | 
						||
 * Gets the default #GResolver. You should unref it when you are done
 | 
						||
 * with it. #GResolver may use its reference count as a hint about how
 | 
						||
 * many threads it should allocate for concurrent DNS resolutions.
 | 
						||
 *
 | 
						||
 * Returns: (transfer full): the default #GResolver.
 | 
						||
 *
 | 
						||
 * Since: 2.22
 | 
						||
 */
 | 
						||
GResolver *
 | 
						||
g_resolver_get_default (void)
 | 
						||
{
 | 
						||
  GResolver *ret;
 | 
						||
 | 
						||
  G_LOCK (default_resolver);
 | 
						||
  if (!default_resolver)
 | 
						||
    default_resolver = g_object_new (G_TYPE_THREADED_RESOLVER,
 | 
						||
                                     "timeout", 30000,
 | 
						||
                                     NULL);
 | 
						||
  ret = g_object_ref (default_resolver);
 | 
						||
  G_UNLOCK (default_resolver);
 | 
						||
 | 
						||
  return ret;
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * g_resolver_set_default:
 | 
						||
 * @resolver: the new default #GResolver
 | 
						||
 *
 | 
						||
 * Sets @resolver to be the application's default resolver (reffing
 | 
						||
 * @resolver, and unreffing the previous default resolver, if any).
 | 
						||
 * Future calls to g_resolver_get_default() will return this resolver.
 | 
						||
 *
 | 
						||
 * This can be used if an application wants to perform any sort of DNS
 | 
						||
 * caching or "pinning"; it can implement its own #GResolver that
 | 
						||
 * calls the original default resolver for DNS operations, and
 | 
						||
 * implements its own cache policies on top of that, and then set
 | 
						||
 * itself as the default resolver for all later code to use.
 | 
						||
 *
 | 
						||
 * Since: 2.22
 | 
						||
 */
 | 
						||
void
 | 
						||
g_resolver_set_default (GResolver *resolver)
 | 
						||
{
 | 
						||
  G_LOCK (default_resolver);
 | 
						||
  if (default_resolver)
 | 
						||
    g_object_unref (default_resolver);
 | 
						||
  default_resolver = g_object_ref (resolver);
 | 
						||
  G_UNLOCK (default_resolver);
 | 
						||
}
 | 
						||
 | 
						||
static void
 | 
						||
maybe_emit_reload (GResolver *resolver)
 | 
						||
{
 | 
						||
#ifdef G_OS_UNIX
 | 
						||
  struct stat st;
 | 
						||
 | 
						||
  if (stat (_PATH_RESCONF, &st) == 0)
 | 
						||
    {
 | 
						||
      g_mutex_lock (&resolver->priv->mutex);
 | 
						||
      if (st.st_mtime != resolver->priv->resolv_conf_timestamp)
 | 
						||
        {
 | 
						||
          resolver->priv->resolv_conf_timestamp = st.st_mtime;
 | 
						||
          g_mutex_unlock (&resolver->priv->mutex);
 | 
						||
          g_signal_emit (resolver, signals[RELOAD], 0);
 | 
						||
        }
 | 
						||
      else
 | 
						||
        g_mutex_unlock (&resolver->priv->mutex);
 | 
						||
    }
 | 
						||
#endif
 | 
						||
}
 | 
						||
 | 
						||
/* filter out duplicates, cf. https://bugzilla.gnome.org/show_bug.cgi?id=631379 */
 | 
						||
static void
 | 
						||
remove_duplicates (GList *addrs)
 | 
						||
{
 | 
						||
  GList *l;
 | 
						||
  GList *ll;
 | 
						||
  GList *lll;
 | 
						||
 | 
						||
  /* TODO: if this is too slow (it's O(n^2) but n is typically really
 | 
						||
   * small), we can do something more clever but note that we must not
 | 
						||
   * change the order of elements...
 | 
						||
   */
 | 
						||
  for (l = addrs; l != NULL; l = l->next)
 | 
						||
    {
 | 
						||
      GInetAddress *address = G_INET_ADDRESS (l->data);
 | 
						||
      for (ll = l->next; ll != NULL; ll = lll)
 | 
						||
        {
 | 
						||
          GInetAddress *other_address = G_INET_ADDRESS (ll->data);
 | 
						||
          lll = ll->next;
 | 
						||
          if (g_inet_address_equal (address, other_address))
 | 
						||
            {
 | 
						||
              g_object_unref (other_address);
 | 
						||
              /* we never return the first element */
 | 
						||
              g_warn_if_fail (g_list_delete_link (addrs, ll) == addrs);
 | 
						||
            }
 | 
						||
        }
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
static gboolean
 | 
						||
hostname_is_localhost (const char *hostname)
 | 
						||
{
 | 
						||
  size_t len = strlen (hostname);
 | 
						||
  const char *p;
 | 
						||
 | 
						||
  /* Match "localhost", "localhost.", "*.localhost" and "*.localhost." */
 | 
						||
  if (len < strlen ("localhost"))
 | 
						||
    return FALSE;
 | 
						||
 | 
						||
  if (hostname[len - 1] == '.')
 | 
						||
      len--;
 | 
						||
 | 
						||
  /* Scan backwards in @hostname to find the right-most dot (excluding the final dot, if it exists, as it was chopped off above).
 | 
						||
   * We can’t use strrchr() because because we need to operate with string lengths.
 | 
						||
   * End with @p pointing to the character after the right-most dot. */
 | 
						||
  p = hostname + len - 1;
 | 
						||
  while (p >= hostname)
 | 
						||
    {
 | 
						||
      if (*p == '.')
 | 
						||
       {
 | 
						||
         p++;
 | 
						||
         break;
 | 
						||
       }
 | 
						||
      else if (p == hostname)
 | 
						||
        break;
 | 
						||
      p--;
 | 
						||
    }
 | 
						||
 | 
						||
  len -= p - hostname;
 | 
						||
 | 
						||
  return g_ascii_strncasecmp (p, "localhost", MAX (len, strlen ("localhost"))) == 0;
 | 
						||
}
 | 
						||
 | 
						||
/* Note that this does not follow the "FALSE means @error is set"
 | 
						||
 * convention. The return value tells the caller whether it should
 | 
						||
 * return @addrs and @error to the caller right away, or if it should
 | 
						||
 * continue and trying to resolve the name as a hostname.
 | 
						||
 */
 | 
						||
static gboolean
 | 
						||
handle_ip_address_or_localhost (const char                *hostname,
 | 
						||
                                GList                    **addrs,
 | 
						||
                                GResolverNameLookupFlags   flags,
 | 
						||
                                GError                   **error)
 | 
						||
{
 | 
						||
  GInetAddress *addr;
 | 
						||
 | 
						||
#ifndef G_OS_WIN32
 | 
						||
  struct in_addr ip4addr;
 | 
						||
#endif
 | 
						||
 | 
						||
  addr = g_inet_address_new_from_string (hostname);
 | 
						||
  if (addr)
 | 
						||
    {
 | 
						||
      *addrs = g_list_append (NULL, addr);
 | 
						||
      return TRUE;
 | 
						||
    }
 | 
						||
 | 
						||
  *addrs = NULL;
 | 
						||
 | 
						||
#ifdef G_OS_WIN32
 | 
						||
 | 
						||
  /* Reject IPv6 addresses that have brackets ('[' or ']') and/or port numbers,
 | 
						||
   * as no valid addresses should contain these at this point.
 | 
						||
   * Non-standard IPv4 addresses would be rejected during the call to
 | 
						||
   * getaddrinfo() later.
 | 
						||
   */
 | 
						||
  if (strrchr (hostname, '[') != NULL ||
 | 
						||
      strrchr (hostname, ']') != NULL)
 | 
						||
#else
 | 
						||
 | 
						||
  /* Reject non-standard IPv4 numbers-and-dots addresses.
 | 
						||
   * g_inet_address_new_from_string() will have accepted any "real" IP
 | 
						||
   * address, so if inet_aton() succeeds, then it's an address we want
 | 
						||
   * to reject.
 | 
						||
   */
 | 
						||
  if (inet_aton (hostname, &ip4addr))
 | 
						||
#endif
 | 
						||
    {
 | 
						||
#ifdef G_OS_WIN32
 | 
						||
      gchar *error_message = g_win32_error_message (WSAHOST_NOT_FOUND);
 | 
						||
#else
 | 
						||
      gchar *error_message = g_locale_to_utf8 (gai_strerror (EAI_NONAME), -1, NULL, NULL, NULL);
 | 
						||
      if (error_message == NULL)
 | 
						||
        error_message = g_strdup ("[Invalid UTF-8]");
 | 
						||
#endif
 | 
						||
      g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND,
 | 
						||
                   _("Error resolving “%s”: %s"),
 | 
						||
                   hostname, error_message);
 | 
						||
      g_free (error_message);
 | 
						||
 | 
						||
      return TRUE;
 | 
						||
    }
 | 
						||
 | 
						||
  /* Always resolve localhost to a loopback address so it can be reliably considered secure.
 | 
						||
     This behavior is being adopted by browsers:
 | 
						||
     - https://w3c.github.io/webappsec-secure-contexts/
 | 
						||
     - https://groups.google.com/a/chromium.org/forum/#!msg/blink-dev/RC9dSw-O3fE/E3_0XaT0BAAJ
 | 
						||
     - https://chromium.googlesource.com/chromium/src.git/+/8da2a80724a9b896890602ff77ef2216cb951399
 | 
						||
     - https://bugs.webkit.org/show_bug.cgi?id=171934
 | 
						||
     - https://tools.ietf.org/html/draft-west-let-localhost-be-localhost-06
 | 
						||
  */
 | 
						||
  if (hostname_is_localhost (hostname))
 | 
						||
    {
 | 
						||
      if (flags & G_RESOLVER_NAME_LOOKUP_FLAGS_IPV6_ONLY)
 | 
						||
        *addrs = g_list_append (*addrs, g_inet_address_new_loopback (G_SOCKET_FAMILY_IPV6)); 
 | 
						||
      if (flags & G_RESOLVER_NAME_LOOKUP_FLAGS_IPV4_ONLY)
 | 
						||
        *addrs = g_list_append (*addrs, g_inet_address_new_loopback (G_SOCKET_FAMILY_IPV4));
 | 
						||
      if (*addrs == NULL)
 | 
						||
        {
 | 
						||
          *addrs = g_list_append (*addrs, g_inet_address_new_loopback (G_SOCKET_FAMILY_IPV6));
 | 
						||
          *addrs = g_list_append (*addrs, g_inet_address_new_loopback (G_SOCKET_FAMILY_IPV4));
 | 
						||
        }
 | 
						||
      return TRUE;
 | 
						||
    }
 | 
						||
 | 
						||
  return FALSE;
 | 
						||
}
 | 
						||
 | 
						||
static GList *
 | 
						||
lookup_by_name_real (GResolver                 *resolver,
 | 
						||
                     const gchar               *hostname,
 | 
						||
                     GResolverNameLookupFlags   flags,
 | 
						||
                     GCancellable              *cancellable,
 | 
						||
                     GError                    **error)
 | 
						||
{
 | 
						||
  GList *addrs;
 | 
						||
  gchar *ascii_hostname = NULL;
 | 
						||
 | 
						||
  g_return_val_if_fail (G_IS_RESOLVER (resolver), NULL);
 | 
						||
  g_return_val_if_fail (hostname != NULL, NULL);
 | 
						||
  g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
 | 
						||
  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
 | 
						||
 | 
						||
  /* Check if @hostname is just an IP address */
 | 
						||
  if (handle_ip_address_or_localhost (hostname, &addrs, flags, error))
 | 
						||
    return addrs;
 | 
						||
 | 
						||
  if (g_hostname_is_non_ascii (hostname))
 | 
						||
    hostname = ascii_hostname = g_hostname_to_ascii (hostname);
 | 
						||
 | 
						||
  if (!hostname)
 | 
						||
    {
 | 
						||
      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
 | 
						||
                           _("Invalid hostname"));
 | 
						||
      return NULL;
 | 
						||
    }
 | 
						||
 | 
						||
  maybe_emit_reload (resolver);
 | 
						||
 | 
						||
  if (flags != G_RESOLVER_NAME_LOOKUP_FLAGS_DEFAULT)
 | 
						||
    {
 | 
						||
      if (!G_RESOLVER_GET_CLASS (resolver)->lookup_by_name_with_flags)
 | 
						||
        {
 | 
						||
          g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
 | 
						||
                       /* Translators: The placeholder is for a function name. */
 | 
						||
                       _("%s not implemented"), "lookup_by_name_with_flags");
 | 
						||
          g_free (ascii_hostname);
 | 
						||
          return NULL;
 | 
						||
        }
 | 
						||
      addrs = G_RESOLVER_GET_CLASS (resolver)->
 | 
						||
        lookup_by_name_with_flags (resolver, hostname, flags, cancellable, error);
 | 
						||
    }
 | 
						||
  else
 | 
						||
    addrs = G_RESOLVER_GET_CLASS (resolver)->
 | 
						||
      lookup_by_name (resolver, hostname, cancellable, error);
 | 
						||
 | 
						||
  remove_duplicates (addrs);
 | 
						||
 | 
						||
  g_free (ascii_hostname);
 | 
						||
  return addrs;
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * g_resolver_lookup_by_name:
 | 
						||
 * @resolver: a #GResolver
 | 
						||
 * @hostname: the hostname to look up
 | 
						||
 * @cancellable: (nullable): a #GCancellable, or %NULL
 | 
						||
 * @error: return location for a #GError, or %NULL
 | 
						||
 *
 | 
						||
 * Synchronously resolves @hostname to determine its associated IP
 | 
						||
 * address(es). @hostname may be an ASCII-only or UTF-8 hostname, or
 | 
						||
 * the textual form of an IP address (in which case this just becomes
 | 
						||
 * a wrapper around g_inet_address_new_from_string()).
 | 
						||
 *
 | 
						||
 * On success, g_resolver_lookup_by_name() will return a non-empty #GList of
 | 
						||
 * #GInetAddress, sorted in order of preference and guaranteed to not
 | 
						||
 * contain duplicates. That is, if using the result to connect to
 | 
						||
 * @hostname, you should attempt to connect to the first address
 | 
						||
 * first, then the second if the first fails, etc. If you are using
 | 
						||
 * the result to listen on a socket, it is appropriate to add each
 | 
						||
 * result using e.g. g_socket_listener_add_address().
 | 
						||
 *
 | 
						||
 * If the DNS resolution fails, @error (if non-%NULL) will be set to a
 | 
						||
 * value from #GResolverError and %NULL will be returned.
 | 
						||
 *
 | 
						||
 * If @cancellable is non-%NULL, it can be used to cancel the
 | 
						||
 * operation, in which case @error (if non-%NULL) will be set to
 | 
						||
 * %G_IO_ERROR_CANCELLED.
 | 
						||
 *
 | 
						||
 * If you are planning to connect to a socket on the resolved IP
 | 
						||
 * address, it may be easier to create a #GNetworkAddress and use its
 | 
						||
 * #GSocketConnectable interface.
 | 
						||
 *
 | 
						||
 * Returns: (element-type GInetAddress) (transfer full): a non-empty #GList
 | 
						||
 * of #GInetAddress, or %NULL on error. You
 | 
						||
 * must unref each of the addresses and free the list when you are
 | 
						||
 * done with it. (You can use g_resolver_free_addresses() to do this.)
 | 
						||
 *
 | 
						||
 * Since: 2.22
 | 
						||
 */
 | 
						||
GList *
 | 
						||
g_resolver_lookup_by_name (GResolver     *resolver,
 | 
						||
                           const gchar   *hostname,
 | 
						||
                           GCancellable  *cancellable,
 | 
						||
                           GError       **error)
 | 
						||
{
 | 
						||
  return lookup_by_name_real (resolver,
 | 
						||
                              hostname,
 | 
						||
                              G_RESOLVER_NAME_LOOKUP_FLAGS_DEFAULT,
 | 
						||
                              cancellable,
 | 
						||
                              error);
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * g_resolver_lookup_by_name_with_flags:
 | 
						||
 * @resolver: a #GResolver
 | 
						||
 * @hostname: the hostname to look up
 | 
						||
 * @flags: extra #GResolverNameLookupFlags for the lookup
 | 
						||
 * @cancellable: (nullable): a #GCancellable, or %NULL
 | 
						||
 * @error: (nullable): return location for a #GError, or %NULL
 | 
						||
 *
 | 
						||
 * This differs from g_resolver_lookup_by_name() in that you can modify
 | 
						||
 * the lookup behavior with @flags. For example this can be used to limit
 | 
						||
 * results with %G_RESOLVER_NAME_LOOKUP_FLAGS_IPV4_ONLY.
 | 
						||
 *
 | 
						||
 * Returns: (element-type GInetAddress) (transfer full): a non-empty #GList
 | 
						||
 * of #GInetAddress, or %NULL on error. You
 | 
						||
 * must unref each of the addresses and free the list when you are
 | 
						||
 * done with it. (You can use g_resolver_free_addresses() to do this.)
 | 
						||
 *
 | 
						||
 * Since: 2.60
 | 
						||
 */
 | 
						||
GList *
 | 
						||
g_resolver_lookup_by_name_with_flags (GResolver                 *resolver,
 | 
						||
                                      const gchar               *hostname,
 | 
						||
                                      GResolverNameLookupFlags   flags,
 | 
						||
                                      GCancellable              *cancellable,
 | 
						||
                                      GError                   **error)
 | 
						||
{
 | 
						||
  return lookup_by_name_real (resolver,
 | 
						||
                              hostname,
 | 
						||
                              flags,
 | 
						||
                              cancellable,
 | 
						||
                              error);
 | 
						||
}
 | 
						||
 | 
						||
static void
 | 
						||
lookup_by_name_async_real (GResolver                *resolver,
 | 
						||
                           const gchar              *hostname,
 | 
						||
                           GResolverNameLookupFlags  flags,
 | 
						||
                           GCancellable             *cancellable,
 | 
						||
                           GAsyncReadyCallback       callback,
 | 
						||
                           gpointer                  user_data)
 | 
						||
{
 | 
						||
  gchar *ascii_hostname = NULL;
 | 
						||
  GList *addrs;
 | 
						||
  GError *error = NULL;
 | 
						||
 | 
						||
  g_return_if_fail (G_IS_RESOLVER (resolver));
 | 
						||
  g_return_if_fail (hostname != NULL);
 | 
						||
  g_return_if_fail (!(flags & G_RESOLVER_NAME_LOOKUP_FLAGS_IPV4_ONLY && flags & G_RESOLVER_NAME_LOOKUP_FLAGS_IPV6_ONLY));
 | 
						||
 | 
						||
  /* Check if @hostname is just an IP address */
 | 
						||
  if (handle_ip_address_or_localhost (hostname, &addrs, flags, &error))
 | 
						||
    {
 | 
						||
      GTask *task;
 | 
						||
 | 
						||
      task = g_task_new (resolver, cancellable, callback, user_data);
 | 
						||
      g_task_set_source_tag (task, lookup_by_name_async_real);
 | 
						||
      g_task_set_name (task, "[gio] resolver lookup");
 | 
						||
      if (addrs)
 | 
						||
        g_task_return_pointer (task, addrs, (GDestroyNotify) g_resolver_free_addresses);
 | 
						||
      else
 | 
						||
        g_task_return_error (task, error);
 | 
						||
      g_object_unref (task);
 | 
						||
      return;
 | 
						||
    }
 | 
						||
 | 
						||
  if (g_hostname_is_non_ascii (hostname))
 | 
						||
    hostname = ascii_hostname = g_hostname_to_ascii (hostname);
 | 
						||
 | 
						||
  if (!hostname)
 | 
						||
    {
 | 
						||
      GTask *task;
 | 
						||
 | 
						||
      g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_FAILED,
 | 
						||
                           _("Invalid hostname"));
 | 
						||
      task = g_task_new (resolver, cancellable, callback, user_data);
 | 
						||
      g_task_set_source_tag (task, lookup_by_name_async_real);
 | 
						||
      g_task_set_name (task, "[gio] resolver lookup");
 | 
						||
      g_task_return_error (task, error);
 | 
						||
      g_object_unref (task);
 | 
						||
      return;
 | 
						||
    }
 | 
						||
 | 
						||
  maybe_emit_reload (resolver);
 | 
						||
 | 
						||
  if (flags != G_RESOLVER_NAME_LOOKUP_FLAGS_DEFAULT)
 | 
						||
    {
 | 
						||
      if (G_RESOLVER_GET_CLASS (resolver)->lookup_by_name_with_flags_async == NULL)
 | 
						||
        {
 | 
						||
          GTask *task;
 | 
						||
 | 
						||
          g_set_error (&error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
 | 
						||
                       /* Translators: The placeholder is for a function name. */
 | 
						||
                       _("%s not implemented"), "lookup_by_name_with_flags_async");
 | 
						||
          task = g_task_new (resolver, cancellable, callback, user_data);
 | 
						||
          g_task_set_source_tag (task, lookup_by_name_async_real);
 | 
						||
          g_task_set_name (task, "[gio] resolver lookup");
 | 
						||
          g_task_return_error (task, error);
 | 
						||
          g_object_unref (task);
 | 
						||
        }
 | 
						||
      else
 | 
						||
        G_RESOLVER_GET_CLASS (resolver)->
 | 
						||
          lookup_by_name_with_flags_async (resolver, hostname, flags, cancellable, callback, user_data);
 | 
						||
    }
 | 
						||
  else
 | 
						||
    G_RESOLVER_GET_CLASS (resolver)->
 | 
						||
      lookup_by_name_async (resolver, hostname, cancellable, callback, user_data);
 | 
						||
 | 
						||
  g_free (ascii_hostname);
 | 
						||
}
 | 
						||
 | 
						||
static GList *
 | 
						||
lookup_by_name_finish_real (GResolver     *resolver,
 | 
						||
                            GAsyncResult  *result,
 | 
						||
                            GError       **error,
 | 
						||
                            gboolean       with_flags)
 | 
						||
{
 | 
						||
  GList *addrs;
 | 
						||
 | 
						||
  g_return_val_if_fail (G_IS_RESOLVER (resolver), NULL);
 | 
						||
  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
 | 
						||
  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
 | 
						||
 | 
						||
  if (g_async_result_legacy_propagate_error (result, error))
 | 
						||
    return NULL;
 | 
						||
  else if (g_async_result_is_tagged (result, lookup_by_name_async_real))
 | 
						||
    {
 | 
						||
      /* Handle the stringified-IP-addr case */
 | 
						||
      return g_task_propagate_pointer (G_TASK (result), error);
 | 
						||
    }
 | 
						||
 | 
						||
  if (with_flags)
 | 
						||
    {
 | 
						||
      g_assert (G_RESOLVER_GET_CLASS (resolver)->lookup_by_name_with_flags_finish != NULL);
 | 
						||
      addrs = G_RESOLVER_GET_CLASS (resolver)->
 | 
						||
        lookup_by_name_with_flags_finish (resolver, result, error);
 | 
						||
    }
 | 
						||
  else
 | 
						||
    addrs = G_RESOLVER_GET_CLASS (resolver)->
 | 
						||
      lookup_by_name_finish (resolver, result, error);
 | 
						||
 | 
						||
  remove_duplicates (addrs);
 | 
						||
 | 
						||
  return addrs;
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * g_resolver_lookup_by_name_with_flags_async:
 | 
						||
 * @resolver: a #GResolver
 | 
						||
 * @hostname: the hostname to look up the address of
 | 
						||
 * @flags: extra #GResolverNameLookupFlags for the lookup
 | 
						||
 * @cancellable: (nullable): a #GCancellable, or %NULL
 | 
						||
 * @callback: (scope async) (closure user_data): callback to call after resolution completes
 | 
						||
 * @user_data: data for @callback
 | 
						||
 *
 | 
						||
 * Begins asynchronously resolving @hostname to determine its
 | 
						||
 * associated IP address(es), and eventually calls @callback, which
 | 
						||
 * must call g_resolver_lookup_by_name_with_flags_finish() to get the result.
 | 
						||
 * See g_resolver_lookup_by_name() for more details.
 | 
						||
 *
 | 
						||
 * Since: 2.60
 | 
						||
 */
 | 
						||
void
 | 
						||
g_resolver_lookup_by_name_with_flags_async (GResolver                *resolver,
 | 
						||
                                            const gchar              *hostname,
 | 
						||
                                            GResolverNameLookupFlags  flags,
 | 
						||
                                            GCancellable             *cancellable,
 | 
						||
                                            GAsyncReadyCallback       callback,
 | 
						||
                                            gpointer                  user_data)
 | 
						||
{
 | 
						||
  lookup_by_name_async_real (resolver,
 | 
						||
                             hostname,
 | 
						||
                             flags,
 | 
						||
                             cancellable,
 | 
						||
                             callback,
 | 
						||
                             user_data);
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * g_resolver_lookup_by_name_async:
 | 
						||
 * @resolver: a #GResolver
 | 
						||
 * @hostname: the hostname to look up the address of
 | 
						||
 * @cancellable: (nullable): a #GCancellable, or %NULL
 | 
						||
 * @callback: (scope async) (closure user_data): callback to call after resolution completes
 | 
						||
 * @user_data: data for @callback
 | 
						||
 *
 | 
						||
 * Begins asynchronously resolving @hostname to determine its
 | 
						||
 * associated IP address(es), and eventually calls @callback, which
 | 
						||
 * must call g_resolver_lookup_by_name_finish() to get the result.
 | 
						||
 * See g_resolver_lookup_by_name() for more details.
 | 
						||
 *
 | 
						||
 * Since: 2.22
 | 
						||
 */
 | 
						||
void
 | 
						||
g_resolver_lookup_by_name_async (GResolver           *resolver,
 | 
						||
                                 const gchar         *hostname,
 | 
						||
                                 GCancellable        *cancellable,
 | 
						||
                                 GAsyncReadyCallback  callback,
 | 
						||
                                 gpointer             user_data)
 | 
						||
{
 | 
						||
  lookup_by_name_async_real (resolver,
 | 
						||
                             hostname,
 | 
						||
                             0,
 | 
						||
                             cancellable,
 | 
						||
                             callback,
 | 
						||
                             user_data);
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * g_resolver_lookup_by_name_finish:
 | 
						||
 * @resolver: a #GResolver
 | 
						||
 * @result: the result passed to your #GAsyncReadyCallback
 | 
						||
 * @error: return location for a #GError, or %NULL
 | 
						||
 *
 | 
						||
 * Retrieves the result of a call to
 | 
						||
 * g_resolver_lookup_by_name_async().
 | 
						||
 *
 | 
						||
 * If the DNS resolution failed, @error (if non-%NULL) will be set to
 | 
						||
 * a value from #GResolverError. If the operation was cancelled,
 | 
						||
 * @error will be set to %G_IO_ERROR_CANCELLED.
 | 
						||
 *
 | 
						||
 * Returns: (element-type GInetAddress) (transfer full): a #GList
 | 
						||
 * of #GInetAddress, or %NULL on error. See g_resolver_lookup_by_name()
 | 
						||
 * for more details.
 | 
						||
 *
 | 
						||
 * Since: 2.22
 | 
						||
 */
 | 
						||
GList *
 | 
						||
g_resolver_lookup_by_name_finish (GResolver     *resolver,
 | 
						||
                                  GAsyncResult  *result,
 | 
						||
                                  GError       **error)
 | 
						||
{
 | 
						||
  return lookup_by_name_finish_real (resolver,
 | 
						||
                                     result,
 | 
						||
                                     error,
 | 
						||
                                     FALSE);
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * g_resolver_lookup_by_name_with_flags_finish:
 | 
						||
 * @resolver: a #GResolver
 | 
						||
 * @result: the result passed to your #GAsyncReadyCallback
 | 
						||
 * @error: return location for a #GError, or %NULL
 | 
						||
 *
 | 
						||
 * Retrieves the result of a call to
 | 
						||
 * g_resolver_lookup_by_name_with_flags_async().
 | 
						||
 *
 | 
						||
 * If the DNS resolution failed, @error (if non-%NULL) will be set to
 | 
						||
 * a value from #GResolverError. If the operation was cancelled,
 | 
						||
 * @error will be set to %G_IO_ERROR_CANCELLED.
 | 
						||
 *
 | 
						||
 * Returns: (element-type GInetAddress) (transfer full): a #GList
 | 
						||
 * of #GInetAddress, or %NULL on error. See g_resolver_lookup_by_name()
 | 
						||
 * for more details.
 | 
						||
 *
 | 
						||
 * Since: 2.60
 | 
						||
 */
 | 
						||
GList *
 | 
						||
g_resolver_lookup_by_name_with_flags_finish (GResolver     *resolver,
 | 
						||
                                             GAsyncResult  *result,
 | 
						||
                                             GError       **error)
 | 
						||
{
 | 
						||
  return lookup_by_name_finish_real (resolver,
 | 
						||
                                     result,
 | 
						||
                                     error,
 | 
						||
                                     TRUE);
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * g_resolver_free_addresses: (skip)
 | 
						||
 * @addresses: a #GList of #GInetAddress
 | 
						||
 *
 | 
						||
 * Frees @addresses (which should be the return value from
 | 
						||
 * g_resolver_lookup_by_name() or g_resolver_lookup_by_name_finish()).
 | 
						||
 * (This is a convenience method; you can also simply free the results
 | 
						||
 * by hand.)
 | 
						||
 *
 | 
						||
 * Since: 2.22
 | 
						||
 */
 | 
						||
void
 | 
						||
g_resolver_free_addresses (GList *addresses)
 | 
						||
{
 | 
						||
  GList *a;
 | 
						||
 | 
						||
  for (a = addresses; a; a = a->next)
 | 
						||
    g_object_unref (a->data);
 | 
						||
  g_list_free (addresses);
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * g_resolver_lookup_by_address:
 | 
						||
 * @resolver: a #GResolver
 | 
						||
 * @address: the address to reverse-resolve
 | 
						||
 * @cancellable: (nullable): a #GCancellable, or %NULL
 | 
						||
 * @error: return location for a #GError, or %NULL
 | 
						||
 *
 | 
						||
 * Synchronously reverse-resolves @address to determine its
 | 
						||
 * associated hostname.
 | 
						||
 *
 | 
						||
 * If the DNS resolution fails, @error (if non-%NULL) will be set to
 | 
						||
 * a value from #GResolverError.
 | 
						||
 *
 | 
						||
 * If @cancellable is non-%NULL, it can be used to cancel the
 | 
						||
 * operation, in which case @error (if non-%NULL) will be set to
 | 
						||
 * %G_IO_ERROR_CANCELLED.
 | 
						||
 *
 | 
						||
 * Returns: a hostname (either ASCII-only, or in ASCII-encoded
 | 
						||
 *     form), or %NULL on error.
 | 
						||
 *
 | 
						||
 * Since: 2.22
 | 
						||
 */
 | 
						||
gchar *
 | 
						||
g_resolver_lookup_by_address (GResolver     *resolver,
 | 
						||
                              GInetAddress  *address,
 | 
						||
                              GCancellable  *cancellable,
 | 
						||
                              GError       **error)
 | 
						||
{
 | 
						||
  g_return_val_if_fail (G_IS_RESOLVER (resolver), NULL);
 | 
						||
  g_return_val_if_fail (G_IS_INET_ADDRESS (address), NULL);
 | 
						||
 | 
						||
  maybe_emit_reload (resolver);
 | 
						||
  return G_RESOLVER_GET_CLASS (resolver)->
 | 
						||
    lookup_by_address (resolver, address, cancellable, error);
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * g_resolver_lookup_by_address_async:
 | 
						||
 * @resolver: a #GResolver
 | 
						||
 * @address: the address to reverse-resolve
 | 
						||
 * @cancellable: (nullable): a #GCancellable, or %NULL
 | 
						||
 * @callback: (scope async) (closure user_data): callback to call after resolution completes
 | 
						||
 * @user_data: data for @callback
 | 
						||
 *
 | 
						||
 * Begins asynchronously reverse-resolving @address to determine its
 | 
						||
 * associated hostname, and eventually calls @callback, which must
 | 
						||
 * call g_resolver_lookup_by_address_finish() to get the final result.
 | 
						||
 *
 | 
						||
 * Since: 2.22
 | 
						||
 */
 | 
						||
void
 | 
						||
g_resolver_lookup_by_address_async (GResolver           *resolver,
 | 
						||
                                    GInetAddress        *address,
 | 
						||
                                    GCancellable        *cancellable,
 | 
						||
                                    GAsyncReadyCallback  callback,
 | 
						||
                                    gpointer             user_data)
 | 
						||
{
 | 
						||
  g_return_if_fail (G_IS_RESOLVER (resolver));
 | 
						||
  g_return_if_fail (G_IS_INET_ADDRESS (address));
 | 
						||
 | 
						||
  maybe_emit_reload (resolver);
 | 
						||
  G_RESOLVER_GET_CLASS (resolver)->
 | 
						||
    lookup_by_address_async (resolver, address, cancellable, callback, user_data);
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * g_resolver_lookup_by_address_finish:
 | 
						||
 * @resolver: a #GResolver
 | 
						||
 * @result: the result passed to your #GAsyncReadyCallback
 | 
						||
 * @error: return location for a #GError, or %NULL
 | 
						||
 *
 | 
						||
 * Retrieves the result of a previous call to
 | 
						||
 * g_resolver_lookup_by_address_async().
 | 
						||
 *
 | 
						||
 * If the DNS resolution failed, @error (if non-%NULL) will be set to
 | 
						||
 * a value from #GResolverError. If the operation was cancelled,
 | 
						||
 * @error will be set to %G_IO_ERROR_CANCELLED.
 | 
						||
 *
 | 
						||
 * Returns: a hostname (either ASCII-only, or in ASCII-encoded
 | 
						||
 * form), or %NULL on error.
 | 
						||
 *
 | 
						||
 * Since: 2.22
 | 
						||
 */
 | 
						||
gchar *
 | 
						||
g_resolver_lookup_by_address_finish (GResolver     *resolver,
 | 
						||
                                     GAsyncResult  *result,
 | 
						||
                                     GError       **error)
 | 
						||
{
 | 
						||
  g_return_val_if_fail (G_IS_RESOLVER (resolver), NULL);
 | 
						||
 | 
						||
  if (g_async_result_legacy_propagate_error (result, error))
 | 
						||
    return NULL;
 | 
						||
 | 
						||
  return G_RESOLVER_GET_CLASS (resolver)->
 | 
						||
    lookup_by_address_finish (resolver, result, error);
 | 
						||
}
 | 
						||
 | 
						||
static gchar *
 | 
						||
g_resolver_get_service_rrname (const char *service,
 | 
						||
                               const char *protocol,
 | 
						||
                               const char *domain)
 | 
						||
{
 | 
						||
  gchar *rrname, *ascii_domain = NULL;
 | 
						||
 | 
						||
  if (g_hostname_is_non_ascii (domain))
 | 
						||
    domain = ascii_domain = g_hostname_to_ascii (domain);
 | 
						||
  if (!domain)
 | 
						||
    return NULL;
 | 
						||
 | 
						||
  rrname = g_strdup_printf ("_%s._%s.%s", service, protocol, domain);
 | 
						||
 | 
						||
  g_free (ascii_domain);
 | 
						||
  return rrname;
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * g_resolver_lookup_service:
 | 
						||
 * @resolver: a #GResolver
 | 
						||
 * @service: the service type to look up (eg, "ldap")
 | 
						||
 * @protocol: the networking protocol to use for @service (eg, "tcp")
 | 
						||
 * @domain: the DNS domain to look up the service in
 | 
						||
 * @cancellable: (nullable): a #GCancellable, or %NULL
 | 
						||
 * @error: return location for a #GError, or %NULL
 | 
						||
 *
 | 
						||
 * Synchronously performs a DNS SRV lookup for the given @service and
 | 
						||
 * @protocol in the given @domain and returns an array of #GSrvTarget.
 | 
						||
 * @domain may be an ASCII-only or UTF-8 hostname. Note also that the
 | 
						||
 * @service and @protocol arguments do not include the leading underscore
 | 
						||
 * that appears in the actual DNS entry.
 | 
						||
 *
 | 
						||
 * On success, g_resolver_lookup_service() will return a non-empty #GList of
 | 
						||
 * #GSrvTarget, sorted in order of preference. (That is, you should
 | 
						||
 * attempt to connect to the first target first, then the second if
 | 
						||
 * the first fails, etc.)
 | 
						||
 *
 | 
						||
 * If the DNS resolution fails, @error (if non-%NULL) will be set to
 | 
						||
 * a value from #GResolverError and %NULL will be returned.
 | 
						||
 *
 | 
						||
 * If @cancellable is non-%NULL, it can be used to cancel the
 | 
						||
 * operation, in which case @error (if non-%NULL) will be set to
 | 
						||
 * %G_IO_ERROR_CANCELLED.
 | 
						||
 *
 | 
						||
 * If you are planning to connect to the service, it is usually easier
 | 
						||
 * to create a #GNetworkService and use its #GSocketConnectable
 | 
						||
 * interface.
 | 
						||
 *
 | 
						||
 * Returns: (element-type GSrvTarget) (transfer full): a non-empty #GList of
 | 
						||
 * #GSrvTarget, or %NULL on error. You must free each of the targets and the
 | 
						||
 * list when you are done with it. (You can use g_resolver_free_targets() to do
 | 
						||
 * this.)
 | 
						||
 *
 | 
						||
 * Since: 2.22
 | 
						||
 */
 | 
						||
GList *
 | 
						||
g_resolver_lookup_service (GResolver     *resolver,
 | 
						||
                           const gchar   *service,
 | 
						||
                           const gchar   *protocol,
 | 
						||
                           const gchar   *domain,
 | 
						||
                           GCancellable  *cancellable,
 | 
						||
                           GError       **error)
 | 
						||
{
 | 
						||
  GList *targets;
 | 
						||
  gchar *rrname;
 | 
						||
 | 
						||
  g_return_val_if_fail (G_IS_RESOLVER (resolver), NULL);
 | 
						||
  g_return_val_if_fail (service != NULL, NULL);
 | 
						||
  g_return_val_if_fail (protocol != NULL, NULL);
 | 
						||
  g_return_val_if_fail (domain != NULL, NULL);
 | 
						||
 | 
						||
  rrname = g_resolver_get_service_rrname (service, protocol, domain);
 | 
						||
  if (!rrname)
 | 
						||
    {
 | 
						||
      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
 | 
						||
                           _("Invalid domain"));
 | 
						||
      return NULL;
 | 
						||
    }
 | 
						||
 | 
						||
  maybe_emit_reload (resolver);
 | 
						||
  targets = G_RESOLVER_GET_CLASS (resolver)->
 | 
						||
    lookup_service (resolver, rrname, cancellable, error);
 | 
						||
 | 
						||
  g_free (rrname);
 | 
						||
  return targets;
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * g_resolver_lookup_service_async:
 | 
						||
 * @resolver: a #GResolver
 | 
						||
 * @service: the service type to look up (eg, "ldap")
 | 
						||
 * @protocol: the networking protocol to use for @service (eg, "tcp")
 | 
						||
 * @domain: the DNS domain to look up the service in
 | 
						||
 * @cancellable: (nullable): a #GCancellable, or %NULL
 | 
						||
 * @callback: (scope async) (closure user_data): callback to call after resolution completes
 | 
						||
 * @user_data: data for @callback
 | 
						||
 *
 | 
						||
 * Begins asynchronously performing a DNS SRV lookup for the given
 | 
						||
 * @service and @protocol in the given @domain, and eventually calls
 | 
						||
 * @callback, which must call g_resolver_lookup_service_finish() to
 | 
						||
 * get the final result. See g_resolver_lookup_service() for more
 | 
						||
 * details.
 | 
						||
 *
 | 
						||
 * Since: 2.22
 | 
						||
 */
 | 
						||
void
 | 
						||
g_resolver_lookup_service_async (GResolver           *resolver,
 | 
						||
                                 const gchar         *service,
 | 
						||
                                 const gchar         *protocol,
 | 
						||
                                 const gchar         *domain,
 | 
						||
                                 GCancellable        *cancellable,
 | 
						||
                                 GAsyncReadyCallback  callback,
 | 
						||
                                 gpointer             user_data)
 | 
						||
{
 | 
						||
  gchar *rrname;
 | 
						||
 | 
						||
  g_return_if_fail (G_IS_RESOLVER (resolver));
 | 
						||
  g_return_if_fail (service != NULL);
 | 
						||
  g_return_if_fail (protocol != NULL);
 | 
						||
  g_return_if_fail (domain != NULL);
 | 
						||
 | 
						||
  rrname = g_resolver_get_service_rrname (service, protocol, domain);
 | 
						||
  if (!rrname)
 | 
						||
    {
 | 
						||
      g_task_report_new_error (resolver, callback, user_data,
 | 
						||
                               g_resolver_lookup_service_async,
 | 
						||
                               G_IO_ERROR, G_IO_ERROR_FAILED,
 | 
						||
                               _("Invalid domain"));
 | 
						||
      return;
 | 
						||
    }
 | 
						||
 | 
						||
  maybe_emit_reload (resolver);
 | 
						||
  G_RESOLVER_GET_CLASS (resolver)->
 | 
						||
    lookup_service_async (resolver, rrname, cancellable, callback, user_data);
 | 
						||
 | 
						||
  g_free (rrname);
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * g_resolver_lookup_service_finish:
 | 
						||
 * @resolver: a #GResolver
 | 
						||
 * @result: the result passed to your #GAsyncReadyCallback
 | 
						||
 * @error: return location for a #GError, or %NULL
 | 
						||
 *
 | 
						||
 * Retrieves the result of a previous call to
 | 
						||
 * g_resolver_lookup_service_async().
 | 
						||
 *
 | 
						||
 * If the DNS resolution failed, @error (if non-%NULL) will be set to
 | 
						||
 * a value from #GResolverError. If the operation was cancelled,
 | 
						||
 * @error will be set to %G_IO_ERROR_CANCELLED.
 | 
						||
 *
 | 
						||
 * Returns: (element-type GSrvTarget) (transfer full): a non-empty #GList of
 | 
						||
 * #GSrvTarget, or %NULL on error. See g_resolver_lookup_service() for more
 | 
						||
 * details.
 | 
						||
 *
 | 
						||
 * Since: 2.22
 | 
						||
 */
 | 
						||
GList *
 | 
						||
g_resolver_lookup_service_finish (GResolver     *resolver,
 | 
						||
                                  GAsyncResult  *result,
 | 
						||
                                  GError       **error)
 | 
						||
{
 | 
						||
  g_return_val_if_fail (G_IS_RESOLVER (resolver), NULL);
 | 
						||
 | 
						||
  if (g_async_result_legacy_propagate_error (result, error))
 | 
						||
    return NULL;
 | 
						||
 | 
						||
  return G_RESOLVER_GET_CLASS (resolver)->
 | 
						||
    lookup_service_finish (resolver, result, error);
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * g_resolver_free_targets: (skip)
 | 
						||
 * @targets: a #GList of #GSrvTarget
 | 
						||
 *
 | 
						||
 * Frees @targets (which should be the return value from
 | 
						||
 * g_resolver_lookup_service() or g_resolver_lookup_service_finish()).
 | 
						||
 * (This is a convenience method; you can also simply free the
 | 
						||
 * results by hand.)
 | 
						||
 *
 | 
						||
 * Since: 2.22
 | 
						||
 */
 | 
						||
void
 | 
						||
g_resolver_free_targets (GList *targets)
 | 
						||
{
 | 
						||
  GList *t;
 | 
						||
 | 
						||
  for (t = targets; t; t = t->next)
 | 
						||
    g_srv_target_free (t->data);
 | 
						||
  g_list_free (targets);
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * g_resolver_lookup_records:
 | 
						||
 * @resolver: a #GResolver
 | 
						||
 * @rrname: the DNS name to look up the record for
 | 
						||
 * @record_type: the type of DNS record to look up
 | 
						||
 * @cancellable: (nullable): a #GCancellable, or %NULL
 | 
						||
 * @error: return location for a #GError, or %NULL
 | 
						||
 *
 | 
						||
 * Synchronously performs a DNS record lookup for the given @rrname and returns
 | 
						||
 * a list of records as #GVariant tuples. See #GResolverRecordType for
 | 
						||
 * information on what the records contain for each @record_type.
 | 
						||
 *
 | 
						||
 * If the DNS resolution fails, @error (if non-%NULL) will be set to
 | 
						||
 * a value from #GResolverError and %NULL will be returned.
 | 
						||
 *
 | 
						||
 * If @cancellable is non-%NULL, it can be used to cancel the
 | 
						||
 * operation, in which case @error (if non-%NULL) will be set to
 | 
						||
 * %G_IO_ERROR_CANCELLED.
 | 
						||
 *
 | 
						||
 * Returns: (element-type GVariant) (transfer full): a non-empty #GList of
 | 
						||
 * #GVariant, or %NULL on error. You must free each of the records and the list
 | 
						||
 * when you are done with it. (You can use g_list_free_full() with
 | 
						||
 * g_variant_unref() to do this.)
 | 
						||
 *
 | 
						||
 * Since: 2.34
 | 
						||
 */
 | 
						||
GList *
 | 
						||
g_resolver_lookup_records (GResolver            *resolver,
 | 
						||
                           const gchar          *rrname,
 | 
						||
                           GResolverRecordType   record_type,
 | 
						||
                           GCancellable         *cancellable,
 | 
						||
                           GError              **error)
 | 
						||
{
 | 
						||
  GList *records;
 | 
						||
 | 
						||
  g_return_val_if_fail (G_IS_RESOLVER (resolver), NULL);
 | 
						||
  g_return_val_if_fail (rrname != NULL, NULL);
 | 
						||
 | 
						||
  maybe_emit_reload (resolver);
 | 
						||
  records = G_RESOLVER_GET_CLASS (resolver)->
 | 
						||
    lookup_records (resolver, rrname, record_type, cancellable, error);
 | 
						||
 | 
						||
  return records;
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * g_resolver_lookup_records_async:
 | 
						||
 * @resolver: a #GResolver
 | 
						||
 * @rrname: the DNS name to look up the record for
 | 
						||
 * @record_type: the type of DNS record to look up
 | 
						||
 * @cancellable: (nullable): a #GCancellable, or %NULL
 | 
						||
 * @callback: (scope async) (closure user_data): callback to call after resolution completes
 | 
						||
 * @user_data: data for @callback
 | 
						||
 *
 | 
						||
 * Begins asynchronously performing a DNS lookup for the given
 | 
						||
 * @rrname, and eventually calls @callback, which must call
 | 
						||
 * g_resolver_lookup_records_finish() to get the final result. See
 | 
						||
 * g_resolver_lookup_records() for more details.
 | 
						||
 *
 | 
						||
 * Since: 2.34
 | 
						||
 */
 | 
						||
void
 | 
						||
g_resolver_lookup_records_async (GResolver           *resolver,
 | 
						||
                                 const gchar         *rrname,
 | 
						||
                                 GResolverRecordType  record_type,
 | 
						||
                                 GCancellable        *cancellable,
 | 
						||
                                 GAsyncReadyCallback  callback,
 | 
						||
                                 gpointer             user_data)
 | 
						||
{
 | 
						||
  g_return_if_fail (G_IS_RESOLVER (resolver));
 | 
						||
  g_return_if_fail (rrname != NULL);
 | 
						||
 | 
						||
  maybe_emit_reload (resolver);
 | 
						||
  G_RESOLVER_GET_CLASS (resolver)->
 | 
						||
    lookup_records_async (resolver, rrname, record_type, cancellable, callback, user_data);
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * g_resolver_lookup_records_finish:
 | 
						||
 * @resolver: a #GResolver
 | 
						||
 * @result: the result passed to your #GAsyncReadyCallback
 | 
						||
 * @error: return location for a #GError, or %NULL
 | 
						||
 *
 | 
						||
 * Retrieves the result of a previous call to
 | 
						||
 * g_resolver_lookup_records_async(). Returns a non-empty list of records as
 | 
						||
 * #GVariant tuples. See #GResolverRecordType for information on what the
 | 
						||
 * records contain.
 | 
						||
 *
 | 
						||
 * If the DNS resolution failed, @error (if non-%NULL) will be set to
 | 
						||
 * a value from #GResolverError. If the operation was cancelled,
 | 
						||
 * @error will be set to %G_IO_ERROR_CANCELLED.
 | 
						||
 *
 | 
						||
 * Returns: (element-type GVariant) (transfer full): a non-empty #GList of
 | 
						||
 * #GVariant, or %NULL on error. You must free each of the records and the list
 | 
						||
 * when you are done with it. (You can use g_list_free_full() with
 | 
						||
 * g_variant_unref() to do this.)
 | 
						||
 *
 | 
						||
 * Since: 2.34
 | 
						||
 */
 | 
						||
GList *
 | 
						||
g_resolver_lookup_records_finish (GResolver     *resolver,
 | 
						||
                                  GAsyncResult  *result,
 | 
						||
                                  GError       **error)
 | 
						||
{
 | 
						||
  g_return_val_if_fail (G_IS_RESOLVER (resolver), NULL);
 | 
						||
  return G_RESOLVER_GET_CLASS (resolver)->
 | 
						||
    lookup_records_finish (resolver, result, error);
 | 
						||
}
 | 
						||
 | 
						||
guint64
 | 
						||
g_resolver_get_serial (GResolver *resolver)
 | 
						||
{
 | 
						||
  guint64 result;
 | 
						||
 | 
						||
  g_return_val_if_fail (G_IS_RESOLVER (resolver), 0);
 | 
						||
 | 
						||
  maybe_emit_reload (resolver);
 | 
						||
 | 
						||
#ifdef G_OS_UNIX
 | 
						||
  g_mutex_lock (&resolver->priv->mutex);
 | 
						||
  result = resolver->priv->resolv_conf_timestamp;
 | 
						||
  g_mutex_unlock (&resolver->priv->mutex);
 | 
						||
#else
 | 
						||
  result = 1;
 | 
						||
#endif
 | 
						||
 | 
						||
  return result;
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * g_resolver_get_timeout:
 | 
						||
 * @resolver: a #GResolver
 | 
						||
 *
 | 
						||
 * Get the timeout applied to all resolver lookups. See #GResolver:timeout.
 | 
						||
 *
 | 
						||
 * Returns: the resolver timeout, in milliseconds, or `0` for no timeout
 | 
						||
 *
 | 
						||
 * Since: 2.78
 | 
						||
 */
 | 
						||
unsigned
 | 
						||
g_resolver_get_timeout (GResolver *resolver)
 | 
						||
{
 | 
						||
  GResolverPrivate *priv = g_resolver_get_instance_private (resolver);
 | 
						||
 | 
						||
  g_return_val_if_fail (G_IS_RESOLVER (resolver), 0);
 | 
						||
 | 
						||
  return priv->timeout_ms;
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * g_resolver_set_timeout:
 | 
						||
 * @resolver: a #GResolver
 | 
						||
 * @timeout_ms: timeout in milliseconds, or `0` for no timeouts
 | 
						||
 *
 | 
						||
 * Set the timeout applied to all resolver lookups. See #GResolver:timeout.
 | 
						||
 *
 | 
						||
 * Since: 2.78
 | 
						||
 */
 | 
						||
void
 | 
						||
g_resolver_set_timeout (GResolver *resolver,
 | 
						||
                        unsigned   timeout_ms)
 | 
						||
{
 | 
						||
  GResolverPrivate *priv = g_resolver_get_instance_private (resolver);
 | 
						||
 | 
						||
  g_return_if_fail (G_IS_RESOLVER (resolver));
 | 
						||
 | 
						||
  if (priv->timeout_ms == timeout_ms)
 | 
						||
    return;
 | 
						||
 | 
						||
  priv->timeout_ms = timeout_ms;
 | 
						||
  g_object_notify_by_pspec (G_OBJECT (resolver), props[PROP_TIMEOUT]);
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * g_resolver_error_quark:
 | 
						||
 *
 | 
						||
 * Gets the #GResolver Error Quark.
 | 
						||
 *
 | 
						||
 * Returns: a #GQuark.
 | 
						||
 *
 | 
						||
 * Since: 2.22
 | 
						||
 */
 | 
						||
G_DEFINE_QUARK (g-resolver-error-quark, g_resolver_error)
 |