Fix old wiki links
[platform/upstream/glib.git] / gio / gnetworkaddress.c
index b6a25ff..0519713 100644 (file)
  * 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 <glib.h>
 #include "glibintl.h"
 
+#include <stdlib.h>
 #include "gnetworkaddress.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"
 
 #include <string.h>
 
-#include "gioalias.h"
 
 /**
  * SECTION:gnetworkaddress
- * @short_description: a #GSocketConnectable for resolving hostnames
+ * @short_description: GSocketConnectable for resolving hostnames
  * @include: gio/gio.h
  *
  * #GNetworkAddress provides an easy way to resolve a hostname and
  *
  * See #GSocketConnectable for and example of using the connectable
  * interface.
- **/
+ */
 
 /**
  * GNetworkAddress:
  *
  * A #GSocketConnectable for resolving a hostname and connecting to
  * that host.
- **/
+ */
 
 struct _GNetworkAddressPrivate {
   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,
@@ -78,10 +83,12 @@ static void g_network_address_get_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))
 
@@ -91,6 +98,7 @@ g_network_address_finalize (GObject *object)
   GNetworkAddress *addr = G_NETWORK_ADDRESS (object);
 
   g_free (addr->priv->hostname);
+  g_free (addr->priv->scheme);
 
   if (addr->priv->sockaddrs)
     {
@@ -109,8 +117,6 @@ g_network_address_class_init (GNetworkAddressClass *klass)
 {
   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;
@@ -120,26 +126,39 @@ g_network_address_class_init (GNetworkAddressClass *klass)
                                                         P_("Hostname"),
                                                         P_("Hostname to resolve"),
                                                         NULL,
-                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+                                                        G_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT_ONLY |
+                                                        G_PARAM_STATIC_STRINGS));
   g_object_class_install_property (gobject_class, PROP_PORT,
                                    g_param_spec_uint ("port",
                                                       P_("Port"),
                                                       P_("Network port"),
                                                       0, 65535, 0,
-                                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+                                                      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
@@ -150,11 +169,10 @@ g_network_address_set_property (GObject      *object,
 {
   GNetworkAddress *addr = G_NETWORK_ADDRESS (object);
 
-  switch (prop_id) 
+  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;
 
@@ -162,6 +180,12 @@ g_network_address_set_property (GObject      *object,
       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;
@@ -178,7 +202,7 @@ g_network_address_get_property (GObject    *object,
   GNetworkAddress *addr = G_NETWORK_ADDRESS (object);
 
   switch (prop_id)
-    { 
+    {
     case PROP_HOSTNAME:
       g_value_set_string (value, addr->priv->hostname);
       break;
@@ -187,6 +211,10 @@ g_network_address_get_property (GObject    *object,
       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;
@@ -196,7 +224,8 @@ g_network_address_get_property (GObject    *object,
 
 static void
 g_network_address_set_addresses (GNetworkAddress *addr,
-                                 GList           *addresses)
+                                 GList           *addresses,
+                                 guint64          resolver_serial)
 {
   GList *a;
   GSocketAddress *sockaddr;
@@ -211,6 +240,24 @@ g_network_address_set_addresses (GNetworkAddress *addr,
     }
   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;
 }
 
 /**
@@ -221,10 +268,10 @@ g_network_address_set_addresses (GNetworkAddress *addr,
  * 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
- **/
+ */
 GSocketConnectable *
 g_network_address_new (const gchar *hostname,
                        guint16      port)
@@ -236,16 +283,501 @@ g_network_address_new (const gchar *hostname,
 }
 
 /**
+ * g_network_address_parse:
+ * @host_and_port: the hostname and optionally a port
+ * @default_port: the default port if not in @host_and_port
+ * @error: a pointer to a #GError, or %NULL
+ *
+ * Creates a new #GSocketConnectable for connecting to the given
+ * @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
+ * 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.
+ *
+ * If no port is specified in @host_and_port then @default_port will be
+ * used as the port number to connect to.
+ *
+ * In general, @host_and_port is expected to be provided by the user
+ * (allowing them to give the hostname, and a port overide if necessary)
+ * and @default_port is expected to be provided by the application.
+ *
+ * (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
+ */
+GSocketConnectable *
+g_network_address_parse (const gchar  *host_and_port,
+                         guint16       default_port,
+                         GError      **error)
+{
+  GSocketConnectable *connectable;
+  const gchar *port;
+  guint16 portnum;
+  gchar *name;
+
+  g_return_val_if_fail (host_and_port != NULL, NULL);
+
+  port = NULL;
+  if (host_and_port[0] == '[')
+    /* escaped host part (to allow, eg. "[2001:db8::1]:888") */
+    {
+      const gchar *end;
+
+      end = strchr (host_and_port, ']');
+      if (end == NULL)
+        {
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+                       _("Hostname '%s' contains '[' but not ']'"), host_and_port);
+          return NULL;
+        }
+
+      if (end[1] == '\0')
+        port = NULL;
+      else if (end[1] == ':')
+        port = &end[2];
+      else
+        {
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+                       "The ']' character (in hostname '%s') must come at the"
+                       " end or be immediately followed by ':' and a port",
+                       host_and_port);
+          return NULL;
+        }
+
+      name = g_strndup (host_and_port + 1, end - host_and_port - 1);
+    }
+
+  else if ((port = strchr (host_and_port, ':')))
+    /* string has a ':' in it */
+    {
+      /* skip ':' */
+      port++;
+
+      if (strchr (port, ':'))
+        /* more than one ':' in string */
+        {
+          /* this is actually an unescaped IPv6 address */
+          name = g_strdup (host_and_port);
+          port = NULL;
+        }
+      else
+        name = g_strndup (host_and_port, port - host_and_port - 1);
+    }
+
+  else
+    /* plain hostname, no port */
+    name = g_strdup (host_and_port);
+
+  if (port != NULL)
+    {
+      if (port[0] == '\0')
+        {
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+                       "If a ':' character is given, it must be followed by a "
+                       "port (in hostname '%s').", host_and_port);
+          g_free (name);
+          return NULL;
+        }
+
+      else if ('0' <= port[0] && port[0] <= '9')
+        {
+          char *end;
+          long value;
+
+          value = strtol (port, &end, 10);
+          if (*end != '\0' || value < 0 || value > G_MAXUINT16)
+            {
+              g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+                           "Invalid numeric port '%s' specified in hostname '%s'",
+                           port, host_and_port);
+              g_free (name);
+              return NULL;
+            }
+
+          portnum = value;
+        }
+
+      else
+        {
+          struct servent *entry;
+
+          entry = getservbyname (port, "tcp");
+          if (entry == NULL)
+            {
+              g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+                           "Unknown service '%s' specified in hostname '%s'",
+                           port, host_and_port);
+#ifdef HAVE_ENDSERVENT
+              endservent ();
+#endif
+              g_free (name);
+              return NULL;
+            }
+
+          portnum = g_ntohs (entry->s_port);
+
+#ifdef HAVE_ENDSERVENT
+          endservent ();
+#endif
+        }
+    }
+  else
+    {
+      /* No port in host_and_port */
+      portnum = default_port;
+    }
+
+  connectable = g_network_address_new (name, portnum);
+  g_free (name);
+
+  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
- **/
+ */
 const gchar *
 g_network_address_get_hostname (GNetworkAddress *addr)
 {
@@ -260,10 +792,10 @@ g_network_address_get_hostname (GNetworkAddress *addr)
  *
  * 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
- **/
+ */
 guint16
 g_network_address_get_port (GNetworkAddress *addr)
 {
@@ -272,6 +804,24 @@ g_network_address_get_port (GNetworkAddress *addr)
   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))
 
@@ -279,7 +829,8 @@ typedef struct {
   GSocketAddressEnumerator parent_instance;
 
   GNetworkAddress *addr;
-  GList *a;
+  GList *addresses;
+  GList *next;
 } GNetworkAddressAddressEnumerator;
 
 typedef struct {
@@ -287,6 +838,7 @@ 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
@@ -309,31 +861,73 @@ g_network_address_address_enumerator_next (GSocketAddressEnumerator  *enumerator
     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;
-
-      addresses = g_resolver_lookup_by_name (resolver,
-                                             addr_enum->addr->priv->hostname,
-                                             cancellable, error);
-      g_object_unref (resolver);
+      gint64 serial = g_resolver_get_serial (resolver);
 
-      if (!addresses)
-        return NULL;
+      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;
+        }
 
-      g_network_address_set_addresses (addr_enum->addr, addresses);
-      addr_enum->a = addr_enum->addr->priv->sockaddrs;
+      if (!addr->priv->sockaddrs)
+        g_network_address_parse_sockaddr (addr);
+      if (!addr->priv->sockaddrs)
+        {
+          GList *addresses;
+
+          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, 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
@@ -341,34 +935,23 @@ 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 (!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
@@ -379,32 +962,55 @@ g_network_address_address_enumerator_next_async (GSocketAddressEnumerator  *enum
 {
   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 *
@@ -412,21 +1018,9 @@ g_network_address_address_enumerator_next_finish (GSocketAddressEnumerator  *enu
                                                   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
@@ -458,5 +1052,24 @@ g_network_address_connectable_enumerate (GSocketConnectable *connectable)
   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;
+}