* 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 <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "gnetworkingprivate.h"
#include "gproxyaddressenumerator.h"
#include "gresolver.h"
-#include "gsimpleasyncresult.h"
+#include "gtask.h"
#include "gsocketaddressenumerator.h"
#include "gioerror.h"
#include "gsocketconnectable.h"
guint16 port;
GList *sockaddrs;
gchar *scheme;
+
+ gint64 resolver_serial;
};
enum {
static GSocketAddressEnumerator *g_network_address_connectable_proxy_enumerate (GSocketConnectable *connectable);
G_DEFINE_TYPE_WITH_CODE (GNetworkAddress, g_network_address, G_TYPE_OBJECT,
+ G_ADD_PRIVATE (GNetworkAddress)
G_IMPLEMENT_INTERFACE (G_TYPE_SOCKET_CONNECTABLE,
g_network_address_connectable_iface_init))
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
- g_type_class_add_private (klass, sizeof (GNetworkAddressPrivate));
-
gobject_class->set_property = g_network_address_set_property;
gobject_class->get_property = g_network_address_get_property;
gobject_class->finalize = g_network_address_finalize;
static void
g_network_address_init (GNetworkAddress *addr)
{
- addr->priv = G_TYPE_INSTANCE_GET_PRIVATE (addr, G_TYPE_NETWORK_ADDRESS,
- GNetworkAddressPrivate);
+ addr->priv = g_network_address_get_instance_private (addr);
}
static void
static void
g_network_address_set_addresses (GNetworkAddress *addr,
- GList *addresses)
+ GList *addresses,
+ guint64 resolver_serial)
{
GList *a;
GSocketAddress *sockaddr;
}
g_list_free (addresses);
addr->priv->sockaddrs = g_list_reverse (addr->priv->sockaddrs);
+
+ addr->priv->resolver_serial = resolver_serial;
+}
+
+static gboolean
+g_network_address_parse_sockaddr (GNetworkAddress *addr)
+{
+ GSocketAddress *sockaddr;
+
+ sockaddr = g_inet_socket_address_new_from_string (addr->priv->hostname,
+ addr->priv->port);
+ if (sockaddr)
+ {
+ addr->priv->sockaddrs = g_list_prepend (addr->priv->sockaddrs, sockaddr);
+ return TRUE;
+ }
+ else
+ return FALSE;
}
/**
* Creates a new #GSocketConnectable for connecting to the given
* @hostname and @port.
*
- * Return value: (transfer full): the new #GNetworkAddress
+ * Returns: (transfer full) (type GNetworkAddress): the new #GNetworkAddress
*
* Since: 2.22
*/
* address, an IPv4 address, or a domain name (in which case a DNS
* lookup is performed). Quoting with [] is supported for all address
* types. A port override may be specified in the usual way with a
- * colon. Ports may be given as decimal numbers or symbolic names (in
- * which case an /etc/services lookup is performed).
+ * colon.
*
* If no port is specified in @host_and_port then @default_port will be
* used as the port number to connect to.
* (allowing them to give the hostname, and a port overide if necessary)
* and @default_port is expected to be provided by the application.
*
- * Return value: (transfer full): the new #GNetworkAddress, or %NULL on error
+ * (The port component of @host_and_port can also be specified as a
+ * service name rather than as a numeric port, but this functionality
+ * is deprecated, because it depends on the contents of /etc/services,
+ * which is generally quite sparse on platforms other than Linux.)
+ *
+ * Returns: (transfer full): the new #GNetworkAddress, or %NULL on error
*
* Since: 2.22
*/
char **userinfo)
{
char *tmp_str;
- const char *start, *p;
+ const char *start, *p, *at, *delim;
char c;
g_return_val_if_fail (uri != NULL, FALSE);
return FALSE;
start += 2;
- p = strchr (start, '@');
- if (p != NULL)
+ /* check if the @ sign is part of the authority before attempting to
+ * decode the userinfo */
+ delim = strpbrk (start, "/?#[]");
+ at = strchr (start, '@');
+ if (at && delim && at > delim)
+ at = NULL;
+
+ if (at != NULL)
{
/* Decode userinfo:
* userinfo = *( unreserved / pct-encoded / sub-delims / ":" )
* unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
* pct-encoded = "%" HEXDIG HEXDIG
*/
+ p = start;
while (1)
{
c = *p++;
}
/* unreserved / sub-delims / : */
- if (!(g_ascii_isalnum(c) ||
+ if (!(g_ascii_isalnum (c) ||
strchr (G_URI_OTHER_UNRESERVED, c) ||
strchr (G_URI_RESERVED_CHARS_SUBCOMPONENT_DELIMITERS, c) ||
c == ':'))
/* If IPv6 or IPvFuture */
if (*p == '[')
{
+ gboolean has_scope_id = FALSE, has_bad_scope_id = FALSE;
+
start++;
p++;
while (1)
if (c == ']')
break;
+ if (c == '%' && !has_scope_id)
+ {
+ has_scope_id = TRUE;
+ if (p[0] != '2' || p[1] != '5')
+ has_bad_scope_id = TRUE;
+ continue;
+ }
+
/* unreserved / sub-delims */
- if (!(g_ascii_isalnum(c) ||
+ if (!(g_ascii_isalnum (c) ||
strchr (G_URI_OTHER_UNRESERVED, c) ||
strchr (G_URI_RESERVED_CHARS_SUBCOMPONENT_DELIMITERS, c) ||
c == ':' ||
c == '.'))
goto error;
}
+
+ if (host)
+ {
+ if (has_bad_scope_id)
+ *host = g_strndup (start, p - start - 1);
+ else
+ *host = g_uri_unescape_segment (start, p - 1, NULL);
+ }
+
+ c = *p++;
}
else
{
}
/* unreserved / sub-delims */
- if (!(g_ascii_isalnum(c) ||
+ if (!(g_ascii_isalnum (c) ||
strchr (G_URI_OTHER_UNRESERVED, c) ||
strchr (G_URI_RESERVED_CHARS_SUBCOMPONENT_DELIMITERS, c)))
goto error;
}
- }
- if (host)
- *host = g_uri_unescape_segment (start, p - 1, NULL);
+ if (host)
+ *host = g_uri_unescape_segment (start, p - 1, NULL);
+ }
if (c == ':')
{
- /* Decode pot:
+ /* Decode port:
* port = *DIGIT
*/
guint tmp = 0;
* @uri. May fail and return %NULL in case parsing @uri fails.
*
* Using this rather than g_network_address_new() or
- * g_network_address_parse_host() allows #GSocketClient to determine
+ * g_network_address_parse() allows #GSocketClient to determine
* when to use application-specific proxy protocols.
*
- * Return value: (transfer full): the new #GNetworkAddress, or %NULL on error
+ * Returns: (transfer full): the new #GNetworkAddress, or %NULL on error
*
* Since: 2.26
*/
* Gets @addr's hostname. This might be either UTF-8 or ASCII-encoded,
* depending on what @addr was created with.
*
- * Return value: @addr's hostname
+ * Returns: @addr's hostname
*
* Since: 2.22
*/
*
* Gets @addr's port number
*
- * Return value: @addr's port (which may be 0)
+ * Returns: @addr's port (which may be 0)
*
* Since: 2.22
*/
*
* Gets @addr's scheme
*
- * Return value: @addr's scheme (%NULL if not built from URI)
+ * Returns: @addr's scheme (%NULL if not built from URI)
*
* Since: 2.26
*/
} GNetworkAddressAddressEnumeratorClass;
+static GType _g_network_address_address_enumerator_get_type (void);
G_DEFINE_TYPE (GNetworkAddressAddressEnumerator, _g_network_address_address_enumerator, G_TYPE_SOCKET_ADDRESS_ENUMERATOR)
static void
if (addr_enum->addresses == NULL)
{
- if (!addr_enum->addr->priv->sockaddrs)
+ GNetworkAddress *addr = addr_enum->addr;
+ GResolver *resolver = g_resolver_get_default ();
+ gint64 serial = g_resolver_get_serial (resolver);
+
+ if (addr->priv->resolver_serial != 0 &&
+ addr->priv->resolver_serial != serial)
+ {
+ /* Resolver has reloaded, discard cached addresses */
+ g_list_free_full (addr->priv->sockaddrs, g_object_unref);
+ addr->priv->sockaddrs = NULL;
+ }
+
+ if (!addr->priv->sockaddrs)
+ g_network_address_parse_sockaddr (addr);
+ if (!addr->priv->sockaddrs)
{
- GResolver *resolver = g_resolver_get_default ();
GList *addresses;
addresses = g_resolver_lookup_by_name (resolver,
- addr_enum->addr->priv->hostname,
+ addr->priv->hostname,
cancellable, error);
- g_object_unref (resolver);
-
if (!addresses)
- return NULL;
+ {
+ g_object_unref (resolver);
+ return NULL;
+ }
- g_network_address_set_addresses (addr_enum->addr, addresses);
+ g_network_address_set_addresses (addr, addresses, serial);
}
- addr_enum->addresses = addr_enum->addr->priv->sockaddrs;
+ addr_enum->addresses = addr->priv->sockaddrs;
addr_enum->next = addr_enum->addresses;
+ g_object_unref (resolver);
}
if (addr_enum->next == NULL)
}
static void
+have_addresses (GNetworkAddressAddressEnumerator *addr_enum,
+ GTask *task, GError *error)
+{
+ GSocketAddress *sockaddr;
+
+ addr_enum->addresses = addr_enum->addr->priv->sockaddrs;
+ addr_enum->next = addr_enum->addresses;
+
+ if (addr_enum->next)
+ {
+ sockaddr = g_object_ref (addr_enum->next->data);
+ addr_enum->next = addr_enum->next->next;
+ }
+ else
+ sockaddr = NULL;
+
+ if (error)
+ g_task_return_error (task, error);
+ else
+ g_task_return_pointer (task, sockaddr, g_object_unref);
+ g_object_unref (task);
+}
+
+static void
got_addresses (GObject *source_object,
GAsyncResult *result,
gpointer user_data)
{
- GSimpleAsyncResult *simple = user_data;
- GNetworkAddressAddressEnumerator *addr_enum =
- g_simple_async_result_get_op_res_gpointer (simple);
+ GTask *task = user_data;
+ GNetworkAddressAddressEnumerator *addr_enum = g_task_get_source_object (task);
GResolver *resolver = G_RESOLVER (source_object);
GList *addresses;
GError *error = NULL;
{
addresses = g_resolver_lookup_by_name_finish (resolver, result, &error);
- if (error)
- g_simple_async_result_take_error (simple, error);
- else
- g_network_address_set_addresses (addr_enum->addr, addresses);
+ if (!error)
+ {
+ g_network_address_set_addresses (addr_enum->addr, addresses,
+ g_resolver_get_serial (resolver));
+ }
}
-
- g_object_unref (resolver);
-
- addr_enum->addresses = addr_enum->addr->priv->sockaddrs;
- addr_enum->next = addr_enum->addresses;
-
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ have_addresses (addr_enum, task, error);
}
static void
{
GNetworkAddressAddressEnumerator *addr_enum =
G_NETWORK_ADDRESS_ADDRESS_ENUMERATOR (enumerator);
- GSimpleAsyncResult *simple;
+ GSocketAddress *sockaddr;
+ GTask *task;
- simple = g_simple_async_result_new (G_OBJECT (enumerator),
- callback, user_data,
- g_network_address_address_enumerator_next_async);
+ task = g_task_new (addr_enum, cancellable, callback, user_data);
if (addr_enum->addresses == NULL)
{
- if (!addr_enum->addr->priv->sockaddrs)
+ GNetworkAddress *addr = addr_enum->addr;
+ GResolver *resolver = g_resolver_get_default ();
+ gint64 serial = g_resolver_get_serial (resolver);
+
+ if (addr->priv->resolver_serial != 0 &&
+ addr->priv->resolver_serial != serial)
{
- GResolver *resolver = g_resolver_get_default ();
+ /* Resolver has reloaded, discard cached addresses */
+ g_list_free_full (addr->priv->sockaddrs, g_object_unref);
+ addr->priv->sockaddrs = NULL;
+ }
- g_simple_async_result_set_op_res_gpointer (simple, g_object_ref (addr_enum), g_object_unref);
- g_resolver_lookup_by_name_async (resolver,
- addr_enum->addr->priv->hostname,
- cancellable,
- got_addresses, simple);
+ if (!addr->priv->sockaddrs)
+ {
+ if (g_network_address_parse_sockaddr (addr))
+ have_addresses (addr_enum, task, NULL);
+ else
+ {
+ g_resolver_lookup_by_name_async (resolver,
+ addr->priv->hostname,
+ cancellable,
+ got_addresses, task);
+ }
+ g_object_unref (resolver);
return;
}
- addr_enum->addresses = addr_enum->addr->priv->sockaddrs;
+ addr_enum->addresses = addr->priv->sockaddrs;
addr_enum->next = addr_enum->addresses;
+ g_object_unref (resolver);
+ }
+
+ if (addr_enum->next)
+ {
+ sockaddr = g_object_ref (addr_enum->next->data);
+ addr_enum->next = addr_enum->next->next;
}
+ else
+ sockaddr = NULL;
- g_simple_async_result_complete_in_idle (simple);
- g_object_unref (simple);
+ g_task_return_pointer (task, sockaddr, g_object_unref);
+ g_object_unref (task);
}
static GSocketAddress *
GAsyncResult *result,
GError **error)
{
- GNetworkAddressAddressEnumerator *addr_enum =
- G_NETWORK_ADDRESS_ADDRESS_ENUMERATOR (enumerator);
- GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
- GSocketAddress *sockaddr;
+ g_return_val_if_fail (g_task_is_valid (result, enumerator), NULL);
- if (g_simple_async_result_propagate_error (simple, error))
- return NULL;
- else if (!addr_enum->next)
- return NULL;
- else
- {
- sockaddr = addr_enum->next->data;
- addr_enum->next = addr_enum->next->next;
- return g_object_ref (sockaddr);
- }
+ return g_task_propagate_pointer (G_TASK (result), error);
}
static void