* 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 "gasyncresult.h"
#include "ginetaddress.h"
#include "ginetsocketaddress.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"
-#ifdef HAVE_NETDB_H
-#include <netdb.h>
-#endif
-#ifdef HAVE_WINSOCK2_H
-#include <winsock2.h>
-#endif
-
#include <string.h>
-#include "gioalias.h"
/**
* SECTION:gnetworkaddress
- * @short_description: a #GSocketConnectable for resolving hostnames
+ * @short_description: A GSocketConnectable for resolving hostnames
* @include: gio/gio.h
*
* #GNetworkAddress provides an easy way to resolve a hostname and
gchar *hostname;
guint16 port;
GList *sockaddrs;
+ gchar *scheme;
+
+ gint64 resolver_serial;
};
enum {
PROP_0,
PROP_HOSTNAME,
PROP_PORT,
+ PROP_SCHEME,
};
static void g_network_address_set_property (GObject *object,
GValue *value,
GParamSpec *pspec);
-static void g_network_address_connectable_iface_init (GSocketConnectableIface *iface);
-static GSocketAddressEnumerator *g_network_address_connectable_enumerate (GSocketConnectable *connectable);
+static void g_network_address_connectable_iface_init (GSocketConnectableIface *iface);
+static GSocketAddressEnumerator *g_network_address_connectable_enumerate (GSocketConnectable *connectable);
+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))
GNetworkAddress *addr = G_NETWORK_ADDRESS (object);
g_free (addr->priv->hostname);
+ g_free (addr->priv->scheme);
if (addr->priv->sockaddrs)
{
{
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;
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class, PROP_SCHEME,
+ g_param_spec_string ("scheme",
+ P_("Scheme"),
+ P_("URI Scheme"),
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
}
static void
g_network_address_connectable_iface_init (GSocketConnectableIface *connectable_iface)
{
connectable_iface->enumerate = g_network_address_connectable_enumerate;
+ connectable_iface->proxy_enumerate = g_network_address_connectable_proxy_enumerate;
}
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
switch (prop_id)
{
case PROP_HOSTNAME:
- if (addr->priv->hostname)
- g_free (addr->priv->hostname);
+ g_free (addr->priv->hostname);
addr->priv->hostname = g_value_dup_string (value);
break;
addr->priv->port = g_value_get_uint (value);
break;
+ case PROP_SCHEME:
+ if (addr->priv->scheme)
+ g_free (addr->priv->scheme);
+ addr->priv->scheme = g_value_dup_string (value);
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
g_value_set_uint (value, addr->priv->port);
break;
+ case PROP_SCHEME:
+ g_value_set_string (value, addr->priv->scheme);
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
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: the new #GNetworkAddress
+ * Returns: (transfer full) (type GNetworkAddress): the new #GNetworkAddress
*
* Since: 2.22
*/
* @hostname and @port. May fail and return %NULL in case
* parsing @host_and_port fails.
*
- * @host_and_port may be in any of a number of recognised formats: an IPv6
+ * @host_and_port may be in any of a number of recognised formats; an IPv6
* 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: 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
*/
return connectable;
}
+/* Allowed characters outside alphanumeric for unreserved. */
+#define G_URI_OTHER_UNRESERVED "-._~"
+
+/* This or something equivalent will eventually go into glib/guri.h */
+gboolean
+_g_uri_parse_authority (const char *uri,
+ char **host,
+ guint16 *port,
+ char **userinfo)
+{
+ char *tmp_str;
+ const char *start, *p, *at, *delim;
+ char c;
+
+ g_return_val_if_fail (uri != NULL, FALSE);
+
+ if (host)
+ *host = NULL;
+
+ if (port)
+ *port = 0;
+
+ if (userinfo)
+ *userinfo = NULL;
+
+ /* From RFC 3986 Decodes:
+ * URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
+ * hier-part = "//" authority path-abempty
+ * path-abempty = *( "/" segment )
+ * authority = [ userinfo "@" ] host [ ":" port ]
+ */
+
+ /* Check we have a valid scheme */
+ tmp_str = g_uri_parse_scheme (uri);
+
+ if (tmp_str == NULL)
+ return FALSE;
+
+ g_free (tmp_str);
+
+ /* Decode hier-part:
+ * hier-part = "//" authority path-abempty
+ */
+ p = uri;
+ start = strstr (p, "//");
+
+ if (start == NULL)
+ return FALSE;
+
+ start += 2;
+
+ /* 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++;
+
+ if (c == '@')
+ break;
+
+ /* pct-encoded */
+ if (c == '%')
+ {
+ if (!(g_ascii_isxdigit (p[0]) ||
+ g_ascii_isxdigit (p[1])))
+ return FALSE;
+
+ p++;
+
+ continue;
+ }
+
+ /* unreserved / sub-delims / : */
+ if (!(g_ascii_isalnum (c) ||
+ strchr (G_URI_OTHER_UNRESERVED, c) ||
+ strchr (G_URI_RESERVED_CHARS_SUBCOMPONENT_DELIMITERS, c) ||
+ c == ':'))
+ return FALSE;
+ }
+
+ if (userinfo)
+ *userinfo = g_strndup (start, p - start - 1);
+
+ start = p;
+ }
+ else
+ {
+ p = start;
+ }
+
+
+ /* decode host:
+ * host = IP-literal / IPv4address / reg-name
+ * reg-name = *( unreserved / pct-encoded / sub-delims )
+ */
+
+ /* If IPv6 or IPvFuture */
+ if (*p == '[')
+ {
+ gboolean has_scope_id = FALSE, has_bad_scope_id = FALSE;
+
+ start++;
+ p++;
+ while (1)
+ {
+ c = *p++;
+
+ 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) ||
+ 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
+ {
+ while (1)
+ {
+ c = *p++;
+
+ if (c == ':' ||
+ c == '/' ||
+ c == '?' ||
+ c == '#' ||
+ c == '\0')
+ break;
+
+ /* pct-encoded */
+ if (c == '%')
+ {
+ if (!(g_ascii_isxdigit (p[0]) ||
+ g_ascii_isxdigit (p[1])))
+ goto error;
+
+ p++;
+
+ continue;
+ }
+
+ /* unreserved / sub-delims */
+ 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 (c == ':')
+ {
+ /* Decode port:
+ * port = *DIGIT
+ */
+ guint tmp = 0;
+
+ while (1)
+ {
+ c = *p++;
+
+ if (c == '/' ||
+ c == '?' ||
+ c == '#' ||
+ c == '\0')
+ break;
+
+ if (!g_ascii_isdigit (c))
+ goto error;
+
+ tmp = (tmp * 10) + (c - '0');
+
+ if (tmp > 65535)
+ goto error;
+ }
+ if (port)
+ *port = (guint16) tmp;
+ }
+
+ return TRUE;
+
+error:
+ if (host && *host)
+ {
+ g_free (*host);
+ *host = NULL;
+ }
+
+ if (userinfo && *userinfo)
+ {
+ g_free (*userinfo);
+ *userinfo = NULL;
+ }
+
+ return FALSE;
+}
+
+gchar *
+_g_uri_from_authority (const gchar *protocol,
+ const gchar *host,
+ guint port,
+ const gchar *userinfo)
+{
+ GString *uri;
+
+ uri = g_string_new (protocol);
+ g_string_append (uri, "://");
+
+ if (userinfo)
+ {
+ g_string_append_uri_escaped (uri, userinfo, G_URI_RESERVED_CHARS_ALLOWED_IN_USERINFO, FALSE);
+ g_string_append_c (uri, '@');
+ }
+
+ if (g_hostname_is_non_ascii (host))
+ {
+ gchar *ace_encoded = g_hostname_to_ascii (host);
+
+ if (!ace_encoded)
+ {
+ g_string_free (uri, TRUE);
+ return NULL;
+ }
+ g_string_append (uri, ace_encoded);
+ g_free (ace_encoded);
+ }
+ else if (strchr (host, ':'))
+ g_string_append_printf (uri, "[%s]", host);
+ else
+ g_string_append (uri, host);
+
+ if (port != 0)
+ g_string_append_printf (uri, ":%u", port);
+
+ return g_string_free (uri, FALSE);
+}
+
+/**
+ * g_network_address_parse_uri:
+ * @uri: the hostname and optionally a port
+ * @default_port: The default port if none is found in the URI
+ * @error: a pointer to a #GError, or %NULL
+ *
+ * Creates a new #GSocketConnectable for connecting to the given
+ * @uri. May fail and return %NULL in case parsing @uri fails.
+ *
+ * Using this rather than g_network_address_new() or
+ * g_network_address_parse() allows #GSocketClient to determine
+ * when to use application-specific proxy protocols.
+ *
+ * Returns: (transfer full): the new #GNetworkAddress, or %NULL on error
+ *
+ * Since: 2.26
+ */
+GSocketConnectable *
+g_network_address_parse_uri (const gchar *uri,
+ guint16 default_port,
+ GError **error)
+{
+ GSocketConnectable *conn;
+ gchar *scheme;
+ gchar *hostname;
+ guint16 port;
+
+ if (!_g_uri_parse_authority (uri, &hostname, &port, NULL))
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+ "Invalid URI '%s'",
+ uri);
+ return NULL;
+ }
+
+ if (port == 0)
+ port = default_port;
+
+ scheme = g_uri_parse_scheme (uri);
+
+ conn = g_object_new (G_TYPE_NETWORK_ADDRESS,
+ "hostname", hostname,
+ "port", port,
+ "scheme", scheme,
+ NULL);
+
+ g_free (scheme);
+ g_free (hostname);
+
+ return conn;
+}
+
/**
* g_network_address_get_hostname:
* @addr: a #GNetworkAddress
* 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
*/
return addr->priv->port;
}
+/**
+ * g_network_address_get_scheme:
+ * @addr: a #GNetworkAddress
+ *
+ * Gets @addr's scheme
+ *
+ * Returns: @addr's scheme (%NULL if not built from URI)
+ *
+ * Since: 2.26
+ */
+const gchar *
+g_network_address_get_scheme (GNetworkAddress *addr)
+{
+ g_return_val_if_fail (G_IS_NETWORK_ADDRESS (addr), NULL);
+
+ return addr->priv->scheme;
+}
+
#define G_TYPE_NETWORK_ADDRESS_ADDRESS_ENUMERATOR (_g_network_address_address_enumerator_get_type ())
#define G_NETWORK_ADDRESS_ADDRESS_ENUMERATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_NETWORK_ADDRESS_ADDRESS_ENUMERATOR, GNetworkAddressAddressEnumerator))
GSocketAddressEnumerator parent_instance;
GNetworkAddress *addr;
- GList *a;
+ GList *addresses;
+ GList *next;
} GNetworkAddressAddressEnumerator;
typedef struct {
} 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
G_NETWORK_ADDRESS_ADDRESS_ENUMERATOR (enumerator);
GSocketAddress *sockaddr;
- if (!addr_enum->addr->priv->sockaddrs)
+ if (addr_enum->addresses == NULL)
{
+ GNetworkAddress *addr = addr_enum->addr;
GResolver *resolver = g_resolver_get_default ();
- GList *addresses;
+ gint64 serial = g_resolver_get_serial (resolver);
- addresses = g_resolver_lookup_by_name (resolver,
- addr_enum->addr->priv->hostname,
- cancellable, error);
- g_object_unref (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)
+ {
+ GList *addresses;
- if (!addresses)
- return NULL;
+ addresses = g_resolver_lookup_by_name (resolver,
+ addr->priv->hostname,
+ cancellable, error);
+ if (!addresses)
+ {
+ g_object_unref (resolver);
+ return NULL;
+ }
- g_network_address_set_addresses (addr_enum->addr, addresses);
- addr_enum->a = addr_enum->addr->priv->sockaddrs;
+ g_network_address_set_addresses (addr, addresses, serial);
+ }
+
+ addr_enum->addresses = addr->priv->sockaddrs;
+ addr_enum->next = addr_enum->addresses;
+ g_object_unref (resolver);
}
- if (!addr_enum->a)
+ if (addr_enum->next == NULL)
return NULL;
- else
+
+ sockaddr = addr_enum->next->data;
+ addr_enum->next = addr_enum->next->next;
+ return g_object_ref (sockaddr);
+}
+
+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 = addr_enum->a->data;
- addr_enum->a = addr_enum->a->next;
- return g_object_ref (sockaddr);
+ 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
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 (!addr_enum->addr->priv->sockaddrs)
{
- if (error)
- {
- g_simple_async_result_set_from_error (simple, error);
- g_error_free (error);
- }
- else
+ addresses = g_resolver_lookup_by_name_finish (resolver, result, &error);
+
+ if (!error)
{
- g_network_address_set_addresses (addr_enum->addr, addresses);
- addr_enum->a = addr_enum->addr->priv->sockaddrs;
+ g_network_address_set_addresses (addr_enum->addr, addresses,
+ g_resolver_get_serial (resolver));
}
}
- else if (error)
- g_error_free (error);
-
- g_object_unref (resolver);
-
- 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->addr->priv->sockaddrs)
+ if (addr_enum->addresses == NULL)
{
+ 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)
+ {
+ 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;
+ }
- 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);
+ addr_enum->addresses = addr->priv->sockaddrs;
+ addr_enum->next = addr_enum->addresses;
+ g_object_unref (resolver);
}
- else
- {
- sockaddr = g_network_address_address_enumerator_next (enumerator, NULL, NULL);
- if (sockaddr)
- g_simple_async_result_set_op_res_gpointer (simple, sockaddr, g_object_unref);
- g_simple_async_result_complete_in_idle (simple);
- g_object_unref (simple);
+ if (addr_enum->next)
+ {
+ sockaddr = g_object_ref (addr_enum->next->data);
+ addr_enum->next = addr_enum->next->next;
}
+ else
+ sockaddr = NULL;
+
+ 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->a)
- return NULL;
- else
- {
- sockaddr = addr_enum->a->data;
- addr_enum->a = addr_enum->a->next;
- return g_object_ref (sockaddr);
- }
+ return g_task_propagate_pointer (G_TASK (result), error);
}
static void
return (GSocketAddressEnumerator *)addr_enum;
}
-#define __G_NETWORK_ADDRESS_C__
-#include "gioaliasdef.c"
+static GSocketAddressEnumerator *
+g_network_address_connectable_proxy_enumerate (GSocketConnectable *connectable)
+{
+ GNetworkAddress *self = G_NETWORK_ADDRESS (connectable);
+ GSocketAddressEnumerator *proxy_enum;
+ gchar *uri;
+
+ uri = _g_uri_from_authority (self->priv->scheme ? self->priv->scheme : "none",
+ self->priv->hostname,
+ self->priv->port,
+ NULL);
+
+ proxy_enum = g_object_new (G_TYPE_PROXY_ADDRESS_ENUMERATOR,
+ "connectable", connectable,
+ "uri", uri,
+ NULL);
+
+ g_free (uri);
+
+ return proxy_enum;
+}