X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gio%2Fgresolver.c;h=4c29d587399a8f2a268bc7c11223d954b556217e;hb=HEAD;hp=6aad5179149c188f5c09a648376794dd5931c895;hpb=25fa94eb82a8b55fd9279f5faa67c02c4738b8d8;p=platform%2Fupstream%2Fglib.git diff --git a/gio/gresolver.c b/gio/gresolver.c index 6aad517..4c29d58 100644 --- a/gio/gresolver.c +++ b/gio/gresolver.c @@ -3,11 +3,14 @@ /* 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 of the License, or (at your option) any later version. + * 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 @@ -15,9 +18,7 @@ * 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, write to the - * Free Software Foundation, Inc., 59 Temple Place, Suite 330, - * Boston, MA 02111-1307, USA. + * Public License along with this library; if not, see . */ #include "config.h" @@ -28,10 +29,11 @@ #include "gnetworkingprivate.h" #include "gasyncresult.h" #include "ginetaddress.h" -#include "gsimpleasyncresult.h" #include "gtask.h" #include "gsrvtarget.h" #include "gthreadedresolver.h" +#include "gioerror.h" +#include "gcancellable.h" #ifdef G_OS_UNIX #include @@ -53,8 +55,18 @@ * #GNetworkAddress and #GNetworkService provide wrappers around * #GResolver functionality that also implement #GSocketConnectable, * making it easy to connect to a remote host/service. + * + * The default resolver (see g_resolver_get_default()) has a timeout of 30s set + * on it since GLib 2.78. Earlier versions of GLib did not support resolver + * timeouts. */ +typedef enum { + PROP_TIMEOUT = 1, +} GResolverProperty; + +static GParamSpec *props[PROP_TIMEOUT + 1] = { NULL, }; + enum { RELOAD, LAST_SIGNAL @@ -63,10 +75,11 @@ enum { static guint signals[LAST_SIGNAL] = { 0 }; struct _GResolverPrivate { + unsigned timeout_ms; + #ifdef G_OS_UNIX - time_t resolv_conf_timestamp; -#else - int dummy; + GMutex mutex; + time_t resolv_conf_timestamp; /* protected by @mutex */ #endif }; @@ -75,9 +88,13 @@ struct _GResolverPrivate { * * The object that handles DNS resolution. Use g_resolver_get_default() * to get the default resolver. + * + * This is an abstract type; subclasses of it implement different resolvers for + * different platforms and situations. */ -G_DEFINE_TYPE_WITH_CODE (GResolver, g_resolver, G_TYPE_OBJECT, - g_networking_init ();) +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) @@ -145,14 +162,91 @@ g_resolver_real_lookup_service_finish (GResolver *resolver, } 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; - g_type_class_add_private (resolver_class, sizeof (GResolverPrivate)); + /** + * 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", + P_("Timeout"), + P_("Timeout (ms) applied to all resolver lookups"), + 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: @@ -167,7 +261,7 @@ g_resolver_class_init (GResolverClass *resolver_class) G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GResolverClass, reload), NULL, NULL, - g_cclosure_marshal_VOID__VOID, + NULL, G_TYPE_NONE, 0); } @@ -178,14 +272,17 @@ g_resolver_init (GResolver *resolver) struct stat st; #endif - resolver->priv = G_TYPE_INSTANCE_GET_PRIVATE (resolver, G_TYPE_RESOLVER, GResolverPrivate); + 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; /** @@ -195,17 +292,24 @@ static GResolver *default_resolver; * with it. #GResolver may use its reference count as a hint about how * many threads it should allocate for concurrent DNS resolutions. * - * Return value: (transfer full): the default #GResolver. + * 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, NULL); + default_resolver = g_object_new (G_TYPE_THREADED_RESOLVER, + "timeout", 30000, + NULL); + ret = g_object_ref (default_resolver); + G_UNLOCK (default_resolver); - return g_object_ref (default_resolver); + return ret; } /** @@ -227,32 +331,30 @@ g_resolver_get_default (void) 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); } -/* Bionic has res_init() but it's not in any header */ -#ifdef __BIONIC__ -int res_init (void); -#endif - static void -g_resolver_maybe_reload (GResolver *resolver) +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; -#ifdef HAVE_RES_INIT - res_init (); -#endif + g_mutex_unlock (&resolver->priv->mutex); g_signal_emit (resolver, signals[RELOAD], 0); } + else + g_mutex_unlock (&resolver->priv->mutex); } #endif } @@ -286,12 +388,184 @@ remove_duplicates (GList *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: (allow-none): a #GCancellable, or %NULL + * @cancellable: (nullable): a #GCancellable, or %NULL * @error: return location for a #GError, or %NULL * * Synchronously resolves @hostname to determine its associated IP @@ -299,7 +573,7 @@ remove_duplicates (GList *addrs) * 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 #GList of + * 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 @@ -308,7 +582,7 @@ remove_duplicates (GList *addrs) * 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. + * 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 @@ -318,7 +592,7 @@ remove_duplicates (GList *addrs) * address, it may be easier to create a #GNetworkAddress and use its * #GSocketConnectable interface. * - * Return value: (element-type GInetAddress) (transfer full): a #GList + * 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.) @@ -331,38 +605,197 @@ g_resolver_lookup_by_name (GResolver *resolver, GCancellable *cancellable, GError **error) { - GInetAddress *addr; - GList *addrs; + 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_val_if_fail (G_IS_RESOLVER (resolver), NULL); - g_return_val_if_fail (hostname != NULL, 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 */ - addr = g_inet_address_new_from_string (hostname); - if (addr) - return g_list_append (NULL, addr); + 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); - g_resolver_maybe_reload (resolver); - addrs = G_RESOLVER_GET_CLASS (resolver)-> - lookup_by_name (resolver, hostname, cancellable, error); + if (!hostname) + { + GTask *task; - remove_duplicates (addrs); + 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: (allow-none): a #GCancellable, or %NULL - * @callback: (scope async): callback to call after resolution completes - * @user_data: (closure): data for @callback + * @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 @@ -378,34 +811,12 @@ g_resolver_lookup_by_name_async (GResolver *resolver, GAsyncReadyCallback callback, gpointer user_data) { - GInetAddress *addr; - gchar *ascii_hostname = NULL; - - g_return_if_fail (G_IS_RESOLVER (resolver)); - g_return_if_fail (hostname != NULL); - - /* Check if @hostname is just an IP address */ - addr = g_inet_address_new_from_string (hostname); - if (addr) - { - GTask *task; - - task = g_task_new (resolver, cancellable, callback, user_data); - g_task_set_source_tag (task, g_resolver_lookup_by_name_async); - g_task_return_pointer (task, g_list_append (NULL, addr), - (GDestroyNotify) g_resolver_free_addresses); - g_object_unref (task); - return; - } - - if (g_hostname_is_non_ascii (hostname)) - hostname = ascii_hostname = g_hostname_to_ascii (hostname); - - g_resolver_maybe_reload (resolver); - G_RESOLVER_GET_CLASS (resolver)-> - lookup_by_name_async (resolver, hostname, cancellable, callback, user_data); - - g_free (ascii_hostname); + lookup_by_name_async_real (resolver, + hostname, + 0, + cancellable, + callback, + user_data); } /** @@ -421,7 +832,7 @@ g_resolver_lookup_by_name_async (GResolver *resolver, * a value from #GResolverError. If the operation was cancelled, * @error will be set to %G_IO_ERROR_CANCELLED. * - * Return value: (element-type GInetAddress) (transfer full): a #GList + * Returns: (element-type GInetAddress) (transfer full): a #GList * of #GInetAddress, or %NULL on error. See g_resolver_lookup_by_name() * for more details. * @@ -432,24 +843,40 @@ g_resolver_lookup_by_name_finish (GResolver *resolver, GAsyncResult *result, GError **error) { - GList *addrs; - - g_return_val_if_fail (G_IS_RESOLVER (resolver), NULL); - - if (g_async_result_legacy_propagate_error (result, error)) - return NULL; - else if (g_async_result_is_tagged (result, g_resolver_lookup_by_name_async)) - { - /* Handle the stringified-IP-addr case */ - return g_task_propagate_pointer (G_TASK (result), error); - } - - addrs = G_RESOLVER_GET_CLASS (resolver)-> - lookup_by_name_finish (resolver, result, error); - - remove_duplicates (addrs); + return lookup_by_name_finish_real (resolver, + result, + error, + FALSE); +} - return addrs; +/** + * 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); } /** @@ -477,7 +904,7 @@ g_resolver_free_addresses (GList *addresses) * g_resolver_lookup_by_address: * @resolver: a #GResolver * @address: the address to reverse-resolve - * @cancellable: (allow-none): a #GCancellable, or %NULL + * @cancellable: (nullable): a #GCancellable, or %NULL * @error: return location for a #GError, or %NULL * * Synchronously reverse-resolves @address to determine its @@ -490,7 +917,7 @@ g_resolver_free_addresses (GList *addresses) * operation, in which case @error (if non-%NULL) will be set to * %G_IO_ERROR_CANCELLED. * - * Return value: a hostname (either ASCII-only, or in ASCII-encoded + * Returns: a hostname (either ASCII-only, or in ASCII-encoded * form), or %NULL on error. * * Since: 2.22 @@ -504,7 +931,7 @@ g_resolver_lookup_by_address (GResolver *resolver, g_return_val_if_fail (G_IS_RESOLVER (resolver), NULL); g_return_val_if_fail (G_IS_INET_ADDRESS (address), NULL); - g_resolver_maybe_reload (resolver); + maybe_emit_reload (resolver); return G_RESOLVER_GET_CLASS (resolver)-> lookup_by_address (resolver, address, cancellable, error); } @@ -513,9 +940,9 @@ g_resolver_lookup_by_address (GResolver *resolver, * g_resolver_lookup_by_address_async: * @resolver: a #GResolver * @address: the address to reverse-resolve - * @cancellable: (allow-none): a #GCancellable, or %NULL - * @callback: (scope async): callback to call after resolution completes - * @user_data: (closure): data for @callback + * @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 @@ -533,7 +960,7 @@ g_resolver_lookup_by_address_async (GResolver *resolver, g_return_if_fail (G_IS_RESOLVER (resolver)); g_return_if_fail (G_IS_INET_ADDRESS (address)); - g_resolver_maybe_reload (resolver); + maybe_emit_reload (resolver); G_RESOLVER_GET_CLASS (resolver)-> lookup_by_address_async (resolver, address, cancellable, callback, user_data); } @@ -551,7 +978,7 @@ g_resolver_lookup_by_address_async (GResolver *resolver, * a value from #GResolverError. If the operation was cancelled, * @error will be set to %G_IO_ERROR_CANCELLED. * - * Return value: a hostname (either ASCII-only, or in ASCII-encoded + * Returns: a hostname (either ASCII-only, or in ASCII-encoded * form), or %NULL on error. * * Since: 2.22 @@ -579,6 +1006,8 @@ g_resolver_get_service_rrname (const char *service, 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); @@ -592,23 +1021,22 @@ g_resolver_get_service_rrname (const char *service, * @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: (allow-none): a #GCancellable, or %NULL + * @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. + * @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 #GList of + * 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. + * 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 @@ -618,9 +1046,10 @@ g_resolver_get_service_rrname (const char *service, * to create a #GNetworkService and use its #GSocketConnectable * interface. * - * Return value: (element-type GSrvTarget) (transfer full): a #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.) + * 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 */ @@ -641,8 +1070,14 @@ g_resolver_lookup_service (GResolver *resolver, 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; + } - g_resolver_maybe_reload (resolver); + maybe_emit_reload (resolver); targets = G_RESOLVER_GET_CLASS (resolver)-> lookup_service (resolver, rrname, cancellable, error); @@ -656,9 +1091,9 @@ g_resolver_lookup_service (GResolver *resolver, * @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: (allow-none): a #GCancellable, or %NULL - * @callback: (scope async): callback to call after resolution completes - * @user_data: (closure): data for @callback + * @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 @@ -685,8 +1120,16 @@ g_resolver_lookup_service_async (GResolver *resolver, 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; + } - g_resolver_maybe_reload (resolver); + maybe_emit_reload (resolver); G_RESOLVER_GET_CLASS (resolver)-> lookup_service_async (resolver, rrname, cancellable, callback, user_data); @@ -706,8 +1149,9 @@ g_resolver_lookup_service_async (GResolver *resolver, * a value from #GResolverError. If the operation was cancelled, * @error will be set to %G_IO_ERROR_CANCELLED. * - * Return value: (element-type GSrvTarget) (transfer full): a #GList of #GSrvTarget, - * or %NULL on error. See g_resolver_lookup_service() for more details. + * 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 */ @@ -749,9 +1193,9 @@ g_resolver_free_targets (GList *targets) /** * g_resolver_lookup_records: * @resolver: a #GResolver - * @rrname: the DNS name to lookup the record for - * @record_type: the type of DNS record to lookup - * @cancellable: (allow-none): a #GCancellable, or %NULL + * @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 @@ -759,15 +1203,16 @@ g_resolver_free_targets (GList *targets) * 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. + * 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. * - * Return value: (element-type GVariant) (transfer full): a #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.) + * 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 */ @@ -783,7 +1228,7 @@ g_resolver_lookup_records (GResolver *resolver, g_return_val_if_fail (G_IS_RESOLVER (resolver), NULL); g_return_val_if_fail (rrname != NULL, NULL); - g_resolver_maybe_reload (resolver); + maybe_emit_reload (resolver); records = G_RESOLVER_GET_CLASS (resolver)-> lookup_records (resolver, rrname, record_type, cancellable, error); @@ -793,11 +1238,11 @@ g_resolver_lookup_records (GResolver *resolver, /** * g_resolver_lookup_records_async: * @resolver: a #GResolver - * @rrname: the DNS name to lookup the record for - * @record_type: the type of DNS record to lookup - * @cancellable: (allow-none): a #GCancellable, or %NULL - * @callback: (scope async): callback to call after resolution completes - * @user_data: (closure): data for @callback + * @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 @@ -817,7 +1262,7 @@ g_resolver_lookup_records_async (GResolver *resolver, g_return_if_fail (G_IS_RESOLVER (resolver)); g_return_if_fail (rrname != NULL); - g_resolver_maybe_reload (resolver); + maybe_emit_reload (resolver); G_RESOLVER_GET_CLASS (resolver)-> lookup_records_async (resolver, rrname, record_type, cancellable, callback, user_data); } @@ -829,16 +1274,18 @@ g_resolver_lookup_records_async (GResolver *resolver, * @error: return location for a #GError, or %NULL * * Retrieves the result of a previous call to - * g_resolver_lookup_records_async(). Returns a list of records as #GVariant - * tuples. See #GResolverRecordType for information on what the records contain. + * 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. * - * Return value: (element-type GVariant) (transfer full): a #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.) + * 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 */ @@ -855,15 +1302,65 @@ g_resolver_lookup_records_finish (GResolver *resolver, guint64 g_resolver_get_serial (GResolver *resolver) { + guint64 result; + g_return_val_if_fail (G_IS_RESOLVER (resolver), 0); - g_resolver_maybe_reload (resolver); + maybe_emit_reload (resolver); #ifdef G_OS_UNIX - return (guint64) resolver->priv->resolv_conf_timestamp; + g_mutex_lock (&resolver->priv->mutex); + result = resolver->priv->resolv_conf_timestamp; + g_mutex_unlock (&resolver->priv->mutex); #else - return 1; + 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]); } /** @@ -871,7 +1368,7 @@ g_resolver_get_serial (GResolver *resolver) * * Gets the #GResolver Error Quark. * - * Return value: a #GQuark. + * Returns: a #GQuark. * * Since: 2.22 */