[glib] GResolver wrappers: GNetworkAddress, GNetworkService, GSocketConnectable



commit 9a3d18d2a652f9f1567e09bdb1055e6cb462f710
Author: Dan Winship <danw gnome org>
Date:   Mon Dec 29 13:38:28 2008 -0500

    GResolver wrappers: GNetworkAddress, GNetworkService, GSocketConnectable
    
    Higher-level wrappers around GResolver. GSocketConnectable provides an
    interface for synchronously or asynchronously iterating multiple
    socket addresses, with GNetworkAddress and GNetworkService providing
    interfaces based on hostname and SRV record resolution.
    Part of #548466.
---
 docs/reference/gio/gio-docs.xml     |    3 +
 docs/reference/gio/gio-sections.txt |   71 ++++
 docs/reference/gio/gio.types        |    4 +
 gio/Makefile.am                     |    8 +
 gio/gio.h                           |    4 +
 gio/gio.symbols                     |   35 ++
 gio/giotypes.h                      |    4 +
 gio/gnetworkaddress.c               |  462 ++++++++++++++++++++++++
 gio/gnetworkaddress.h               |   65 ++++
 gio/gnetworkservice.c               |  658 +++++++++++++++++++++++++++++++++++
 gio/gnetworkservice.h               |   69 ++++
 gio/gresolver.c                     |   12 +
 gio/gsocketaddress.c                |   90 +++++-
 gio/gsocketaddressenumerator.c      |  191 ++++++++++
 gio/gsocketaddressenumerator.h      |   89 +++++
 gio/gsocketconnectable.c            |  148 ++++++++
 gio/gsocketconnectable.h            |   68 ++++
 gio/gsrvtarget.c                    |    7 +-
 gio/tests/.gitignore                |    1 +
 gio/tests/resolver.c                |  141 +++++++-
 20 files changed, 2121 insertions(+), 9 deletions(-)

diff --git a/docs/reference/gio/gio-docs.xml b/docs/reference/gio/gio-docs.xml
index 223127c..0b0208a 100644
--- a/docs/reference/gio/gio-docs.xml
+++ b/docs/reference/gio/gio-docs.xml
@@ -98,6 +98,9 @@
         <xi:include href="xml/ginetsocketaddress.xml"/>
         <xi:include href="xml/gunixsocketaddress.xml"/>
         <xi:include href="xml/gsrvtarget.xml"/>
+        <xi:include href="xml/gsocketconnectable.xml"/>
+        <xi:include href="xml/gnetworkaddress.xml"/>
+        <xi:include href="xml/gnetworkservice.xml"/>
     </chapter>
     <chapter id="utils">   
     	<title>Utilities</title>
diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt
index ae97ca5..88b757f 100644
--- a/docs/reference/gio/gio-sections.txt
+++ b/docs/reference/gio/gio-sections.txt
@@ -1456,3 +1456,74 @@ G_TYPE_SRV_TARGET
 <SUBSECTION Private>
 g_srv_target_get_type
 </SECTION>
+
+<SECTION>
+<FILE>gsocketconnectable</FILE>
+<TITLE>GSocketConnectable</TITLE>
+GSocketConnectable
+GSocketConnectableIface
+g_socket_connectable_enumerate
+<SUBSECTION>
+GSocketAddressEnumerator
+g_socket_address_enumerator_next
+g_socket_address_enumerator_next_async
+g_socket_address_enumerator_next_finish
+<SUBSECTION Standard>
+G_IS_SOCKET_CONNECTABLE
+G_SOCKET_CONNECTABLE
+G_SOCKET_CONNECTABLE_GET_IFACE
+G_TYPE_SOCKET_CONNECTABLE
+GSocketAddressEnumeratorClass
+G_IS_SOCKET_ADDRESS_ENUMERATOR
+G_IS_SOCKET_ADDRESS_ENUMERATOR_CLASS
+G_SOCKET_ADDRESS_ENUMERATOR
+G_SOCKET_ADDRESS_ENUMERATOR_CLASS
+G_SOCKET_ADDRESS_ENUMERATOR_GET_CLASS
+G_TYPE_SOCKET_ADDRESS_ENUMERATOR
+<SUBSECTION Private>
+g_socket_address_enumerator_get_type
+g_socket_connectable_get_type
+</SECTION>
+
+<SECTION>
+<FILE>gnetworkaddress</FILE>
+<TITLE>GNetworkAddress</TITLE>
+GNetworkAddress
+g_network_address_new
+g_network_address_get_hostname
+g_network_address_get_ascii_name
+g_network_address_get_port
+<SUBSECTION Standard>
+GNetworkAddressClass
+GNetworkAddressPrivate
+G_IS_NETWORK_ADDRESS
+G_IS_NETWORK_ADDRESS_CLASS
+G_NETWORK_ADDRESS
+G_NETWORK_ADDRESS_CLASS
+G_NETWORK_ADDRESS_GET_CLASS
+G_TYPE_NETWORK_ADDRESS
+<SUBSECTION Private>
+g_network_address_get_type
+</SECTION>
+
+<SECTION>
+<FILE>gnetworkservice</FILE>
+<TITLE>GNetworkService</TITLE>
+GNetworkService
+g_network_service_new
+g_network_service_get_service
+g_network_service_get_protocol
+g_network_service_get_domain
+g_network_service_get_expires
+<SUBSECTION Standard>
+GNetworkServiceClass
+GNetworkServicePrivate
+G_IS_NETWORK_SERVICE
+G_IS_NETWORK_SERVICE_CLASS
+G_NETWORK_SERVICE
+G_NETWORK_SERVICE_CLASS
+G_NETWORK_SERVICE_GET_CLASS
+G_TYPE_NETWORK_SERVICE
+<SUBSECTION Private>
+g_network_service_get_type
+</SECTION>
diff --git a/docs/reference/gio/gio.types b/docs/reference/gio/gio.types
index 54d834d..2fe81cd 100644
--- a/docs/reference/gio/gio.types
+++ b/docs/reference/gio/gio.types
@@ -52,13 +52,17 @@ g_mount_operation_get_type
 g_mount_operation_result_get_type
 g_mount_unmount_flags_get_type
 g_native_volume_monitor_get_type
+g_network_address_get_type
+g_network_service_get_type
 g_output_stream_get_type
 g_output_stream_splice_flags_get_type
 g_password_save_get_type
 g_resolver_get_type
 g_seekable_get_type
 g_simple_async_result_get_type
+g_socket_address_enumerator_get_type
 g_socket_address_get_type
+g_socket_connectable_get_type
 g_themed_icon_get_type
 g_unix_input_stream_get_type
 g_unix_mount_monitor_get_type
diff --git a/gio/Makefile.am b/gio/Makefile.am
index e889ecb..9a87508 100644
--- a/gio/Makefile.am
+++ b/gio/Makefile.am
@@ -217,7 +217,9 @@ libgio_2_0_la_SOURCES =		\
 	gmountoperation.c 	\
 	gnativevolumemonitor.c 	\
 	gnativevolumemonitor.h 	\
+	gnetworkaddress.c	\
 	gnetworkingprivate.h	\
+	gnetworkservice.c	\
 	goutputstream.c 	\
 	gpollfilemonitor.c 	\
 	gpollfilemonitor.h 	\
@@ -225,6 +227,8 @@ libgio_2_0_la_SOURCES =		\
 	gseekable.c 		\
 	gsimpleasyncresult.c 	\
 	gsocketaddress.c	\
+	gsocketaddressenumerator.c \
+	gsocketconnectable.c	\
 	gsrvtarget.c		\
 	gthemedicon.c 		\
 	gthreadedresolver.c	\
@@ -328,11 +332,15 @@ gio_headers =			\
 	gmemoryoutputstream.h 	\
 	gmountoperation.h 	\
 	gnativevolumemonitor.h 	\
+	gnetworkaddress.h	\
+	gnetworkservice.h	\
 	goutputstream.h 	\
 	gresolver.h		\
 	gseekable.h 		\
 	gsimpleasyncresult.h 	\
 	gsocketaddress.h	\
+	gsocketaddressenumerator.h \
+	gsocketconnectable.h	\
 	gsrvtarget.h		\
 	gthemedicon.h 		\
 	gvfs.h 			\
diff --git a/gio/gio.h b/gio/gio.h
index 779ce40..9539a60 100644
--- a/gio/gio.h
+++ b/gio/gio.h
@@ -63,11 +63,15 @@
 #include <gio/gmount.h>
 #include <gio/gmountoperation.h>
 #include <gio/gnativevolumemonitor.h>
+#include <gio/gnetworkaddress.h>
+#include <gio/gnetworkservice.h>
 #include <gio/goutputstream.h>
 #include <gio/gresolver.h>
 #include <gio/gseekable.h>
 #include <gio/gsimpleasyncresult.h>
 #include <gio/gsocketaddress.h>
+#include <gio/gsocketaddressenumerator.h>
+#include <gio/gsocketconnectable.h>
 #include <gio/gsrvtarget.h>
 #include <gio/gthemedicon.h>
 #include <gio/gvfs.h>
diff --git a/gio/gio.symbols b/gio/gio.symbols
index 4a8350a..b3bedd1 100644
--- a/gio/gio.symbols
+++ b/gio/gio.symbols
@@ -965,3 +965,38 @@ g_srv_target_get_weight
 g_srv_target_list_sort
 #endif
 #endif
+
+#if IN_HEADER(__G_NETWORK_ADDRESS_H__)
+#if IN_FILE(__G_NETWORK_ADDRESS_C__)
+g_network_address_get_type G_GNUC_CONST
+g_network_address_get_hostname
+g_network_address_get_port
+g_network_address_new
+#endif
+#endif
+
+#if IN_HEADER(__G_NETWORK_SERVICE_H__)
+#if IN_FILE(__G_NETWORK_SERVICE_C__)
+g_network_service_get_type G_GNUC_CONST
+g_network_service_get_service
+g_network_service_get_protocol
+g_network_service_get_domain
+g_network_service_new
+#endif
+#endif
+
+#if IN_HEADER(__G_SOCKET_CONNECTABLE_H__)
+#if IN_FILE(__G_SOCKET_CONNECTABLE_C__)
+g_socket_connectable_enumerate
+g_socket_connectable_get_type G_GNUC_CONST
+#endif
+#endif
+
+#if IN_HEADER(__G_SOCKET_ADDRESS_ENUMERATOR_H__)
+#if IN_FILE(__G_SOCKET_ADDRESS_ENUMERATOR_C__)
+g_socket_address_enumerator_get_type G_GNUC_CONST
+g_socket_address_enumerator_next
+g_socket_address_enumerator_next_async
+g_socket_address_enumerator_next_finish
+#endif
+#endif
diff --git a/gio/giotypes.h b/gio/giotypes.h
index 1e44e4c..41da274 100644
--- a/gio/giotypes.h
+++ b/gio/giotypes.h
@@ -99,11 +99,15 @@ typedef struct _GMemoryOutputStream           GMemoryOutputStream;
  **/
 typedef struct _GMount                        GMount; /* Dummy typedef */
 typedef struct _GMountOperation               GMountOperation;
+typedef struct _GNetworkAddress               GNetworkAddress;
+typedef struct _GNetworkService               GNetworkService;
 typedef struct _GOutputStream                 GOutputStream;
 typedef struct _GResolver                     GResolver;
 typedef struct _GSeekable                     GSeekable;
 typedef struct _GSimpleAsyncResult            GSimpleAsyncResult;
 typedef struct _GSocketAddress                GSocketAddress;
+typedef struct _GSocketAddressEnumerator      GSocketAddressEnumerator;
+typedef struct _GSocketConnectable            GSocketConnectable;
 typedef struct _GSrvTarget                    GSrvTarget;
 typedef struct _GThemedIcon                   GThemedIcon;
 typedef struct _GVfs                          GVfs; /* Dummy typedef */
diff --git a/gio/gnetworkaddress.c b/gio/gnetworkaddress.c
new file mode 100644
index 0000000..b6a25ff
--- /dev/null
+++ b/gio/gnetworkaddress.c
@@ -0,0 +1,462 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * 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.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include <glib.h>
+#include "glibintl.h"
+
+#include "gnetworkaddress.h"
+#include "gasyncresult.h"
+#include "ginetaddress.h"
+#include "ginetsocketaddress.h"
+#include "gresolver.h"
+#include "gsimpleasyncresult.h"
+#include "gsocketaddressenumerator.h"
+#include "gsocketconnectable.h"
+
+#include <string.h>
+
+#include "gioalias.h"
+
+/**
+ * SECTION:gnetworkaddress
+ * @short_description: a #GSocketConnectable for resolving hostnames
+ * @include: gio/gio.h
+ *
+ * #GNetworkAddress provides an easy way to resolve a hostname and
+ * then attempt to connect to that host, handling the possibility of
+ * multiple IP addresses and multiple address families.
+ *
+ * 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;
+};
+
+enum {
+  PROP_0,
+  PROP_HOSTNAME,
+  PROP_PORT,
+};
+
+static void g_network_address_set_property (GObject      *object,
+                                            guint         prop_id,
+                                            const GValue *value,
+                                            GParamSpec   *pspec);
+static void g_network_address_get_property (GObject      *object,
+                                            guint         prop_id,
+                                            GValue       *value,
+                                            GParamSpec   *pspec);
+
+static void                      g_network_address_connectable_iface_init (GSocketConnectableIface *iface);
+static GSocketAddressEnumerator *g_network_address_connectable_enumerate  (GSocketConnectable      *connectable);
+
+G_DEFINE_TYPE_WITH_CODE (GNetworkAddress, g_network_address, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (G_TYPE_SOCKET_CONNECTABLE,
+                                                g_network_address_connectable_iface_init))
+
+static void
+g_network_address_finalize (GObject *object)
+{
+  GNetworkAddress *addr = G_NETWORK_ADDRESS (object);
+
+  g_free (addr->priv->hostname);
+
+  if (addr->priv->sockaddrs)
+    {
+      GList *a;
+
+      for (a = addr->priv->sockaddrs; a; a = a->next)
+        g_object_unref (a->data);
+      g_list_free (addr->priv->sockaddrs);
+    }
+
+  G_OBJECT_CLASS (g_network_address_parent_class)->finalize (object);
+}
+
+static void
+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;
+
+  g_object_class_install_property (gobject_class, PROP_HOSTNAME,
+                                   g_param_spec_string ("hostname",
+                                                        P_("Hostname"),
+                                                        P_("Hostname to resolve"),
+                                                        NULL,
+                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+  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));
+}
+
+static void
+g_network_address_connectable_iface_init (GSocketConnectableIface *connectable_iface)
+{
+  connectable_iface->enumerate  = g_network_address_connectable_enumerate;
+}
+
+static void
+g_network_address_init (GNetworkAddress *addr)
+{
+  addr->priv = G_TYPE_INSTANCE_GET_PRIVATE (addr, G_TYPE_NETWORK_ADDRESS,
+                                            GNetworkAddressPrivate);
+}
+
+static void
+g_network_address_set_property (GObject      *object,
+                                guint         prop_id,
+                                const GValue *value,
+                                GParamSpec   *pspec)
+{
+  GNetworkAddress *addr = G_NETWORK_ADDRESS (object);
+
+  switch (prop_id) 
+    {
+    case PROP_HOSTNAME:
+      if (addr->priv->hostname)
+        g_free (addr->priv->hostname);
+      addr->priv->hostname = g_value_dup_string (value);
+      break;
+
+    case PROP_PORT:
+      addr->priv->port = g_value_get_uint (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+
+}
+
+static void
+g_network_address_get_property (GObject    *object,
+                                guint       prop_id,
+                                GValue     *value,
+                                GParamSpec *pspec)
+{
+  GNetworkAddress *addr = G_NETWORK_ADDRESS (object);
+
+  switch (prop_id)
+    { 
+    case PROP_HOSTNAME:
+      g_value_set_string (value, addr->priv->hostname);
+      break;
+
+    case PROP_PORT:
+      g_value_set_uint (value, addr->priv->port);
+      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 *a;
+  GSocketAddress *sockaddr;
+
+  g_return_if_fail (addresses != NULL && addr->priv->sockaddrs == NULL);
+
+  for (a = addresses; a; a = a->next)
+    {
+      sockaddr = g_inet_socket_address_new (a->data, addr->priv->port);
+      addr->priv->sockaddrs = g_list_prepend (addr->priv->sockaddrs, sockaddr);
+      g_object_unref (a->data);
+    }
+  g_list_free (addresses);
+  addr->priv->sockaddrs = g_list_reverse (addr->priv->sockaddrs);
+}
+
+/**
+ * g_network_address_new:
+ * @hostname: the hostname
+ * @port: the port
+ *
+ * Creates a new #GSocketConnectable for connecting to the given
+ * @hostname and @port.
+ *
+ * Return value: the new #GNetworkAddress
+ *
+ * Since: 2.22
+ **/
+GSocketConnectable *
+g_network_address_new (const gchar *hostname,
+                       guint16      port)
+{
+  return g_object_new (G_TYPE_NETWORK_ADDRESS,
+                       "hostname", hostname,
+                       "port", port,
+                       NULL);
+}
+
+/**
+ * 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
+ *
+ * Since: 2.22
+ **/
+const gchar *
+g_network_address_get_hostname (GNetworkAddress *addr)
+{
+  g_return_val_if_fail (G_IS_NETWORK_ADDRESS (addr), NULL);
+
+  return addr->priv->hostname;
+}
+
+/**
+ * g_network_address_get_port:
+ * @addr: a #GNetworkAddress
+ *
+ * Gets @addr's port number
+ *
+ * Return value: @addr's port (which may be %0)
+ *
+ * Since: 2.22
+ **/
+guint16
+g_network_address_get_port (GNetworkAddress *addr)
+{
+  g_return_val_if_fail (G_IS_NETWORK_ADDRESS (addr), 0);
+
+  return addr->priv->port;
+}
+
+#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))
+
+typedef struct {
+  GSocketAddressEnumerator parent_instance;
+
+  GNetworkAddress *addr;
+  GList *a;
+} GNetworkAddressAddressEnumerator;
+
+typedef struct {
+  GSocketAddressEnumeratorClass parent_class;
+
+} GNetworkAddressAddressEnumeratorClass;
+
+G_DEFINE_TYPE (GNetworkAddressAddressEnumerator, _g_network_address_address_enumerator, G_TYPE_SOCKET_ADDRESS_ENUMERATOR)
+
+static void
+g_network_address_address_enumerator_finalize (GObject *object)
+{
+  GNetworkAddressAddressEnumerator *addr_enum =
+    G_NETWORK_ADDRESS_ADDRESS_ENUMERATOR (object);
+
+  g_object_unref (addr_enum->addr);
+
+  G_OBJECT_CLASS (_g_network_address_address_enumerator_parent_class)->finalize (object);
+}
+
+static GSocketAddress *
+g_network_address_address_enumerator_next (GSocketAddressEnumerator  *enumerator,
+                                           GCancellable              *cancellable,
+                                           GError                   **error)
+{
+  GNetworkAddressAddressEnumerator *addr_enum =
+    G_NETWORK_ADDRESS_ADDRESS_ENUMERATOR (enumerator);
+  GSocketAddress *sockaddr;
+
+  if (!addr_enum->addr->priv->sockaddrs)
+    {
+      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);
+
+      if (!addresses)
+        return NULL;
+
+      g_network_address_set_addresses (addr_enum->addr, addresses);
+      addr_enum->a = addr_enum->addr->priv->sockaddrs;
+    }
+
+  if (!addr_enum->a)
+    return NULL;
+  else
+    {
+      sockaddr = addr_enum->a->data;
+      addr_enum->a = addr_enum->a->next;
+      return g_object_ref (sockaddr);
+    }
+}
+
+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);
+  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
+        {
+          g_network_address_set_addresses (addr_enum->addr, addresses);
+          addr_enum->a = addr_enum->addr->priv->sockaddrs;
+        }
+    }
+  else if (error)
+    g_error_free (error);
+
+  g_object_unref (resolver);
+
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+}
+
+static void
+g_network_address_address_enumerator_next_async (GSocketAddressEnumerator  *enumerator,
+                                                 GCancellable              *cancellable,
+                                                 GAsyncReadyCallback        callback,
+                                                 gpointer                   user_data)
+{
+  GNetworkAddressAddressEnumerator *addr_enum =
+    G_NETWORK_ADDRESS_ADDRESS_ENUMERATOR (enumerator);
+  GSimpleAsyncResult *simple;
+  GSocketAddress *sockaddr;
+
+  simple = g_simple_async_result_new (G_OBJECT (enumerator),
+                                      callback, user_data,
+                                      g_network_address_address_enumerator_next_async);
+
+  if (!addr_enum->addr->priv->sockaddrs)
+    {
+      GResolver *resolver = g_resolver_get_default ();
+
+      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);
+    }
+  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);
+    }
+}
+
+static GSocketAddress *
+g_network_address_address_enumerator_next_finish (GSocketAddressEnumerator  *enumerator,
+                                                  GAsyncResult              *result,
+                                                  GError                   **error)
+{
+  GNetworkAddressAddressEnumerator *addr_enum =
+    G_NETWORK_ADDRESS_ADDRESS_ENUMERATOR (enumerator);
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
+  GSocketAddress *sockaddr;
+
+  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);
+    }
+}
+
+static void
+_g_network_address_address_enumerator_init (GNetworkAddressAddressEnumerator *enumerator)
+{
+}
+
+static void
+_g_network_address_address_enumerator_class_init (GNetworkAddressAddressEnumeratorClass *addrenum_class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (addrenum_class);
+  GSocketAddressEnumeratorClass *enumerator_class =
+    G_SOCKET_ADDRESS_ENUMERATOR_CLASS (addrenum_class);
+
+  enumerator_class->next = g_network_address_address_enumerator_next;
+  enumerator_class->next_async = g_network_address_address_enumerator_next_async;
+  enumerator_class->next_finish = g_network_address_address_enumerator_next_finish;
+  object_class->finalize = g_network_address_address_enumerator_finalize;
+}
+
+static GSocketAddressEnumerator *
+g_network_address_connectable_enumerate (GSocketConnectable *connectable)
+{
+  GNetworkAddressAddressEnumerator *addr_enum;
+
+  addr_enum = g_object_new (G_TYPE_NETWORK_ADDRESS_ADDRESS_ENUMERATOR, NULL);
+  addr_enum->addr = g_object_ref (connectable);
+
+  return (GSocketAddressEnumerator *)addr_enum;
+}
+
+#define __G_NETWORK_ADDRESS_C__
+#include "gioaliasdef.c"
diff --git a/gio/gnetworkaddress.h b/gio/gnetworkaddress.h
new file mode 100644
index 0000000..47dba10
--- /dev/null
+++ b/gio/gnetworkaddress.h
@@ -0,0 +1,65 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * 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.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
+#error "Only <gio/gio.h> can be included directly."
+#endif
+
+#ifndef __G_NETWORK_ADDRESS_H__
+#define __G_NETWORK_ADDRESS_H__
+
+#include <gio/giotypes.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_NETWORK_ADDRESS         (g_network_address_get_type ())
+#define G_NETWORK_ADDRESS(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_NETWORK_ADDRESS, GNetworkAddress))
+#define G_NETWORK_ADDRESS_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_NETWORK_ADDRESS, GNetworkAddressClass))
+#define G_IS_NETWORK_ADDRESS(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_NETWORK_ADDRESS))
+#define G_IS_NETWORK_ADDRESS_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_NETWORK_ADDRESS))
+#define G_NETWORK_ADDRESS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_NETWORK_ADDRESS, GNetworkAddressClass))
+
+typedef struct _GNetworkAddressClass   GNetworkAddressClass;
+typedef struct _GNetworkAddressPrivate GNetworkAddressPrivate;
+
+struct _GNetworkAddress
+{
+  GObject parent_instance;
+
+  /*< private >*/
+  GNetworkAddressPrivate *priv;
+};
+
+struct _GNetworkAddressClass
+{
+  GObjectClass parent_class;
+
+};
+
+GType               g_network_address_get_type       (void) G_GNUC_CONST;
+
+GSocketConnectable *g_network_address_new            (const gchar      *hostname,
+						      guint16           port);
+const gchar        *g_network_address_get_hostname   (GNetworkAddress  *addr);
+guint16             g_network_address_get_port       (GNetworkAddress  *addr);
+
+G_END_DECLS
+
+#endif /* __G_NETWORK_ADDRESS_H__ */
diff --git a/gio/gnetworkservice.c b/gio/gnetworkservice.c
new file mode 100644
index 0000000..2c3b87b
--- /dev/null
+++ b/gio/gnetworkservice.c
@@ -0,0 +1,658 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * 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.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include <glib.h>
+#include "glibintl.h"
+
+#include "gnetworkservice.h"
+#include "gcancellable.h"
+#include "ginetaddress.h"
+#include "ginetsocketaddress.h"
+#include "gresolver.h"
+#include "gsimpleasyncresult.h"
+#include "gsocketaddressenumerator.h"
+#include "gsocketconnectable.h"
+#include "gsrvtarget.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "gioalias.h"
+
+/**
+ * SECTION:gnetworkservice
+ * @short_description: a #GSocketConnectable for resolving SRV records
+ * @include: gio/gio.h
+ *
+ * Like #GNetworkAddress does with hostnames, #GNetworkService
+ * provides an easy way to resolve a SRV record, and then attempt to
+ * connect to one of the hosts that implements that service, handling
+ * service priority/weighting, multiple IP addresses, and multiple
+ * address families.
+ *
+ * See #GSrvTarget for more information about SRV records, and see
+ * #GSocketConnectable for and example of using the connectable
+ * interface.
+ **/
+
+/**
+ * GNetworkService:
+ *
+ * A #GSocketConnectable for resolving a SRV record and connecting to
+ * that service.
+ **/
+
+struct _GNetworkServicePrivate
+{
+  gchar *service, *protocol, *domain;
+  GList *targets;
+};
+
+enum {
+  PROP_0,
+  PROP_SERVICE,
+  PROP_PROTOCOL,
+  PROP_DOMAIN,
+};
+
+static void g_network_service_set_property (GObject      *object,
+                                            guint         prop_id,
+                                            const GValue *value,
+                                            GParamSpec   *pspec);
+static void g_network_service_get_property (GObject      *object,
+                                            guint         prop_id,
+                                            GValue       *value,
+                                            GParamSpec   *pspec);
+
+static void                      g_network_service_connectable_iface_init (GSocketConnectableIface *iface);
+static GSocketAddressEnumerator *g_network_service_connectable_enumerate  (GSocketConnectable      *connectable);
+
+G_DEFINE_TYPE_WITH_CODE (GNetworkService, g_network_service, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (G_TYPE_SOCKET_CONNECTABLE,
+                                                g_network_service_connectable_iface_init))
+
+static void
+g_network_service_finalize (GObject *object)
+{
+  GNetworkService *srv = G_NETWORK_SERVICE (object);
+
+  g_free (srv->priv->service);
+  g_free (srv->priv->protocol);
+  g_free (srv->priv->domain);
+
+  if (srv->priv->targets)
+    g_resolver_free_targets (srv->priv->targets);
+
+  G_OBJECT_CLASS (g_network_service_parent_class)->finalize (object);
+}
+
+static void
+g_network_service_class_init (GNetworkServiceClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (GNetworkServicePrivate));
+
+  gobject_class->set_property = g_network_service_set_property;
+  gobject_class->get_property = g_network_service_get_property;
+  gobject_class->finalize = g_network_service_finalize;
+
+  g_object_class_install_property (gobject_class, PROP_SERVICE,
+                                   g_param_spec_string ("service",
+                                                        P_("Service"),
+                                                        P_("Service name, eg \"ldap\""),
+                                                        NULL,
+                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+  g_object_class_install_property (gobject_class, PROP_PROTOCOL,
+                                   g_param_spec_string ("protocol",
+                                                        P_("Protocol"),
+                                                        P_("Network protocol, eg \"tcp\""),
+                                                        NULL,
+                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+  g_object_class_install_property (gobject_class, PROP_DOMAIN,
+                                   g_param_spec_string ("domain",
+                                                        P_("domain"),
+                                                        P_("Network domain, eg, \"example.com\""),
+                                                        NULL,
+                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+g_network_service_connectable_iface_init (GSocketConnectableIface *connectable_iface)
+{
+  connectable_iface->enumerate = g_network_service_connectable_enumerate;
+}
+
+static void
+g_network_service_init (GNetworkService *srv)
+{
+  srv->priv = G_TYPE_INSTANCE_GET_PRIVATE (srv, G_TYPE_NETWORK_SERVICE,
+                                           GNetworkServicePrivate);
+}
+
+static void
+g_network_service_set_property (GObject      *object,
+                                guint         prop_id,
+                                const GValue *value,
+                                GParamSpec   *pspec)
+{
+  GNetworkService *srv = G_NETWORK_SERVICE (object);
+
+  switch (prop_id) 
+    {
+    case PROP_SERVICE:
+      srv->priv->service = g_value_dup_string (value);
+      break;
+
+    case PROP_PROTOCOL:
+      srv->priv->protocol = g_value_dup_string (value);
+      break;
+
+    case PROP_DOMAIN:
+      srv->priv->domain = g_value_dup_string (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+
+}
+
+static void
+g_network_service_get_property (GObject    *object,
+                                guint       prop_id,
+                                GValue     *value,
+                                GParamSpec *pspec)
+{
+  GNetworkService *srv = G_NETWORK_SERVICE (object);
+
+  switch (prop_id)
+    { 
+    case PROP_SERVICE:
+      g_value_set_string (value, g_network_service_get_service (srv));
+      break;
+
+    case PROP_PROTOCOL:
+      g_value_set_string (value, g_network_service_get_protocol (srv));
+      break;
+
+    case PROP_DOMAIN:
+      g_value_set_string (value, g_network_service_get_domain (srv));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+
+}
+
+/**
+ * g_network_service_new:
+ * @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
+ *
+ * Creates a new #GNetworkService representing the given @service,
+ * @protocol, and @domain. This will initially be unresolved; use the
+ * #GSocketConnectable interface to resolve it.
+ *
+ * Return value: a new #GNetworkService
+ *
+ * Since: 2.22
+ **/
+GSocketConnectable *
+g_network_service_new (const gchar *service,
+                       const gchar *protocol,
+                       const gchar *domain)
+{
+  return g_object_new (G_TYPE_NETWORK_SERVICE,
+                       "service", service,
+                       "protocol", protocol,
+                       "domain", domain,
+                       NULL);
+}
+
+/**
+ * g_network_service_get_service:
+ * @srv: a #GNetworkService
+ *
+ * Gets @srv's service name (eg, "ldap").
+ *
+ * Return value: @srv's service name
+ *
+ * Since: 2.22
+ **/
+const gchar *
+g_network_service_get_service (GNetworkService *srv)
+{
+  g_return_val_if_fail (G_IS_NETWORK_SERVICE (srv), NULL);
+
+  return srv->priv->service;
+}
+
+/**
+ * g_network_service_get_protocol:
+ * @srv: a #GNetworkService
+ *
+ * Gets @srv's protocol name (eg, "tcp").
+ *
+ * Return value: @srv's protocol name
+ *
+ * Since: 2.22
+ **/
+const gchar *
+g_network_service_get_protocol (GNetworkService *srv)
+{
+  g_return_val_if_fail (G_IS_NETWORK_SERVICE (srv), NULL);
+
+  return srv->priv->protocol;
+}
+
+/**
+ * g_network_service_get_domain:
+ * @srv: a #GNetworkService
+ *
+ * Gets the domain that @srv serves. This might be either UTF-8 or
+ * ASCII-encoded, depending on what @srv was created with.
+ *
+ * Return value: @srv's domain name
+ *
+ * Since: 2.22
+ **/
+const gchar *
+g_network_service_get_domain (GNetworkService *srv)
+{
+  g_return_val_if_fail (G_IS_NETWORK_SERVICE (srv), NULL);
+
+  return srv->priv->domain;
+}
+
+#define G_TYPE_NETWORK_SERVICE_ADDRESS_ENUMERATOR (_g_network_service_address_enumerator_get_type ())
+#define G_NETWORK_SERVICE_ADDRESS_ENUMERATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_NETWORK_SERVICE_ADDRESS_ENUMERATOR, GNetworkServiceAddressEnumerator))
+
+typedef struct {
+  GSocketAddressEnumerator parent_instance;
+
+  GResolver *resolver;
+  GNetworkService *srv;
+  GList *addrs, *a, *t;
+
+  GError *error;
+
+  /* For async operation */
+  GCancellable *cancellable;
+  GSimpleAsyncResult *result;
+} GNetworkServiceAddressEnumerator;
+
+typedef struct {
+  GSocketAddressEnumeratorClass parent_class;
+
+} GNetworkServiceAddressEnumeratorClass;
+
+G_DEFINE_TYPE (GNetworkServiceAddressEnumerator, _g_network_service_address_enumerator, G_TYPE_SOCKET_ADDRESS_ENUMERATOR)
+
+static void
+g_network_service_address_enumerator_finalize (GObject *object)
+{
+  GNetworkServiceAddressEnumerator *srv_enum =
+    G_NETWORK_SERVICE_ADDRESS_ENUMERATOR (object);
+
+  g_object_unref (srv_enum->srv);
+  if (srv_enum->addrs)
+    {
+      while (srv_enum->a)
+        {
+          g_object_unref (srv_enum->a->data);
+          srv_enum->a = srv_enum->a->next;
+        }
+      g_list_free (srv_enum->addrs);
+    }
+  g_object_unref (srv_enum->resolver);
+  if (srv_enum->error)
+    g_error_free (srv_enum->error);
+
+  G_OBJECT_CLASS (_g_network_service_address_enumerator_parent_class)->finalize (object);
+}
+
+static GSocketAddress *
+g_network_service_address_enumerator_next (GSocketAddressEnumerator  *enumerator,
+                                           GCancellable              *cancellable,
+                                           GError                   **error)
+{
+  GNetworkServiceAddressEnumerator *srv_enum =
+    G_NETWORK_SERVICE_ADDRESS_ENUMERATOR (enumerator);
+  GSrvTarget *target;
+  GSocketAddress *sockaddr;
+
+  /* If we haven't yet resolved srv, do that */
+  if (!srv_enum->srv->priv->targets)
+    {
+      GList *targets;
+
+      targets = g_resolver_lookup_service (srv_enum->resolver,
+                                           srv_enum->srv->priv->service,
+                                           srv_enum->srv->priv->protocol,
+                                           srv_enum->srv->priv->domain,
+                                           cancellable, error);
+      if (!targets)
+        return NULL;
+
+      if (!srv_enum->srv->priv->targets)
+        srv_enum->srv->priv->targets = targets;
+      srv_enum->t = srv_enum->srv->priv->targets;
+    }
+
+  /* Make sure we have a set of resolved addresses for the current
+   * target. When resolving the first target, we save the GError, if
+   * any. If any later target succeeds, we'll free the earlier error,
+   * but if we get to the last target without any of them resolving,
+   * we return that initial error.
+   */
+  do
+    {
+      /* Return if we're out of targets. */
+      if (!srv_enum->t)
+        {
+          if (srv_enum->error)
+            {
+              g_propagate_error (error, srv_enum->error);
+              srv_enum->error = NULL;
+            }
+          return NULL;
+        }
+      target = srv_enum->t->data;
+
+      /* If we haven't resolved the addrs for the current target, do that */
+      if (!srv_enum->addrs)
+        {
+          GError **error_p;
+
+          error_p = (srv_enum->t == srv_enum->srv->priv->targets) ? &srv_enum->error : NULL;
+          srv_enum->addrs = g_resolver_lookup_by_name (srv_enum->resolver,
+                                                       g_srv_target_get_hostname (target),
+                                                       cancellable, error_p);
+          if (g_cancellable_set_error_if_cancelled (cancellable, error))
+            return NULL;
+
+          if (srv_enum->addrs)
+            {
+              srv_enum->a = srv_enum->addrs;
+              if (srv_enum->error)
+                {
+                  g_error_free (srv_enum->error);
+                  srv_enum->error = NULL;
+                }
+            }
+          else
+            {
+              /* Try the next target */
+              srv_enum->t = srv_enum->t->next;
+            }
+        }
+    }
+  while (!srv_enum->addrs);
+
+  /* Return the next address for this target. If it's the last one,
+   * advance the target counter.
+   */
+  sockaddr = g_inet_socket_address_new (srv_enum->a->data,
+                                        g_srv_target_get_port (target));
+  g_object_unref (srv_enum->a->data);
+  srv_enum->a = srv_enum->a->next;
+
+  if (!srv_enum->a)
+    {
+      g_list_free (srv_enum->addrs);
+      srv_enum->addrs = NULL;
+      srv_enum->t = srv_enum->t->next;
+    }
+
+  return sockaddr;
+}
+
+static void next_async_resolved_targets   (GObject                          *source_object,
+                                           GAsyncResult                     *result,
+                                           gpointer                          user_data);
+static void next_async_have_targets       (GNetworkServiceAddressEnumerator *srv_enum);
+static void next_async_resolved_addresses (GObject                          *source_object,
+                                           GAsyncResult                     *result,
+                                           gpointer                          user_data);
+static void next_async_have_addresses     (GNetworkServiceAddressEnumerator *srv_enum);
+
+/* The async version is basically the same as the sync, except we have
+ * to split it into multiple functions.
+ */
+static void
+g_network_service_address_enumerator_next_async (GSocketAddressEnumerator  *enumerator,
+                                                 GCancellable              *cancellable,
+                                                 GAsyncReadyCallback        callback,
+                                                 gpointer                   user_data)
+{
+  GNetworkServiceAddressEnumerator *srv_enum =
+    G_NETWORK_SERVICE_ADDRESS_ENUMERATOR (enumerator);
+
+  g_return_if_fail (srv_enum->result == NULL);
+
+  srv_enum->result = g_simple_async_result_new (G_OBJECT (enumerator),
+                                                callback, user_data,
+                                                g_network_service_address_enumerator_next_async);
+  srv_enum->cancellable = cancellable;
+
+  /* If we haven't yet resolved srv, do that */
+  if (!srv_enum->srv->priv->targets)
+    {
+      g_resolver_lookup_service_async (srv_enum->resolver,
+                                       srv_enum->srv->priv->service,
+                                       srv_enum->srv->priv->protocol,
+                                       srv_enum->srv->priv->domain,
+                                       cancellable,
+                                       next_async_resolved_targets,
+                                       srv_enum);
+    }
+  else
+    next_async_have_targets (srv_enum);
+}
+
+static void
+next_async_resolved_targets (GObject      *source_object,
+                             GAsyncResult *result,
+                             gpointer      user_data)
+{
+  GNetworkServiceAddressEnumerator *srv_enum = user_data;
+  GList *targets;
+  GError *error = NULL;
+
+  targets = g_resolver_lookup_service_finish (srv_enum->resolver, result, &error);
+  if (!srv_enum->srv->priv->targets)
+    {
+      if (error)
+        {
+          GSimpleAsyncResult *simple = srv_enum->result;
+
+          srv_enum->result = NULL;
+          g_simple_async_result_set_from_error (simple, error);
+          g_error_free (error);
+          g_simple_async_result_complete (simple);
+          g_object_unref (simple);
+          return;
+        }
+
+      srv_enum->srv->priv->targets = targets;
+      srv_enum->t = srv_enum->srv->priv->targets;
+    }
+
+  next_async_have_targets (srv_enum);
+}
+
+static void
+next_async_have_targets (GNetworkServiceAddressEnumerator *srv_enum)
+{
+  GSrvTarget *target;
+
+  /* Get the current target, check if we're already done. */
+  if (!srv_enum->t)
+    {
+      if (srv_enum->error)
+        {
+          g_simple_async_result_set_from_error (srv_enum->result, srv_enum->error);
+          g_error_free (srv_enum->error);
+          srv_enum->error = NULL;
+        }
+      g_simple_async_result_complete_in_idle (srv_enum->result);
+      g_object_unref (srv_enum->result);
+      srv_enum->result = NULL;
+      return;
+    }
+  target = srv_enum->t->data;
+
+  /* If we haven't resolved the addrs for the current target, do that */
+  if (!srv_enum->addrs)
+    {
+      g_resolver_lookup_by_name_async (srv_enum->resolver,
+                                       g_srv_target_get_hostname (target),
+                                       srv_enum->cancellable,
+                                       next_async_resolved_addresses,
+                                       srv_enum);
+    }
+  else
+    next_async_have_addresses (srv_enum);
+}
+
+static void
+next_async_resolved_addresses (GObject      *source_object,
+                               GAsyncResult *result,
+                               gpointer      user_data)
+{
+  GNetworkServiceAddressEnumerator *srv_enum = user_data;
+  GError *error = NULL;
+
+  srv_enum->addrs = g_resolver_lookup_by_name_finish (srv_enum->resolver, result, &error);
+  if (srv_enum->addrs)
+    {
+      srv_enum->a = srv_enum->addrs;
+      if (srv_enum->error)
+        {
+          g_error_free (srv_enum->error);
+          srv_enum->error = NULL;
+        }
+      next_async_have_addresses (srv_enum);
+    }
+  else
+    {
+      if (g_cancellable_is_cancelled (srv_enum->cancellable))
+        {
+          GSimpleAsyncResult *simple = srv_enum->result;
+
+          srv_enum->result = NULL;
+          g_simple_async_result_set_from_error (srv_enum->result, error);
+          g_error_free (error);
+          g_simple_async_result_complete (simple);
+          g_object_unref (simple);
+        }
+      else
+        {
+          if (srv_enum->t == srv_enum->srv->priv->targets)
+            srv_enum->error = error;
+          else
+            g_error_free (error);
+
+          /* Try the next target */
+          srv_enum->t = srv_enum->t->next;
+          next_async_have_targets (srv_enum);
+        }
+    }
+}
+
+static void
+next_async_have_addresses (GNetworkServiceAddressEnumerator *srv_enum)
+{
+  GSocketAddress *sockaddr;
+  GSimpleAsyncResult *simple = srv_enum->result;
+
+  /* Return the next address for this target. If it's the last one,
+   * advance the target counter.
+   */
+  sockaddr = g_inet_socket_address_new (srv_enum->a->data,
+                                        g_srv_target_get_port (srv_enum->t->data));
+  g_object_unref (srv_enum->a->data);
+
+  srv_enum->a = srv_enum->a->next;
+  if (!srv_enum->a)
+    {
+      g_list_free (srv_enum->addrs);
+      srv_enum->addrs = NULL;
+      srv_enum->t = srv_enum->t->next;
+    }
+
+  srv_enum->result = NULL;
+  g_simple_async_result_set_op_res_gpointer (simple, sockaddr, NULL);
+  g_simple_async_result_complete_in_idle (simple);
+  g_object_unref (simple);
+}
+
+static GSocketAddress *
+g_network_service_address_enumerator_next_finish (GSocketAddressEnumerator  *enumerator,
+                                                  GAsyncResult              *result,
+                                                  GError                   **error)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
+  GSocketAddress *sockaddr;
+
+  sockaddr = g_simple_async_result_get_op_res_gpointer (simple);
+  return sockaddr ? g_object_ref (sockaddr) : NULL;
+}
+
+static void
+_g_network_service_address_enumerator_init (GNetworkServiceAddressEnumerator *enumerator)
+{
+}
+
+static void
+_g_network_service_address_enumerator_class_init (GNetworkServiceAddressEnumeratorClass *addrenum_class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (addrenum_class);
+  GSocketAddressEnumeratorClass *enumerator_class =
+    G_SOCKET_ADDRESS_ENUMERATOR_CLASS (addrenum_class);
+
+  enumerator_class->next = g_network_service_address_enumerator_next;
+  enumerator_class->next_async = g_network_service_address_enumerator_next_async;
+  enumerator_class->next_finish = g_network_service_address_enumerator_next_finish;
+  object_class->finalize = g_network_service_address_enumerator_finalize;
+}
+
+static GSocketAddressEnumerator *
+g_network_service_connectable_enumerate (GSocketConnectable *connectable)
+{
+  GNetworkServiceAddressEnumerator *srv_enum;
+
+  srv_enum = g_object_new (G_TYPE_NETWORK_SERVICE_ADDRESS_ENUMERATOR, NULL);
+  srv_enum->srv = g_object_ref (connectable);
+  srv_enum->resolver = g_resolver_get_default ();
+
+  return (GSocketAddressEnumerator *)srv_enum;
+}
+
+#define __G_NETWORK_SERVICE_C__
+#include "gioaliasdef.c"
diff --git a/gio/gnetworkservice.h b/gio/gnetworkservice.h
new file mode 100644
index 0000000..b1a754b
--- /dev/null
+++ b/gio/gnetworkservice.h
@@ -0,0 +1,69 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * 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.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
+#error "Only <gio/gio.h> can be included directly."
+#endif
+
+#ifndef __G_NETWORK_SERVICE_H__
+#define __G_NETWORK_SERVICE_H__
+
+#include <gio/giotypes.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_NETWORK_SERVICE         (g_network_service_get_type ())
+#define G_NETWORK_SERVICE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_NETWORK_SERVICE, GNetworkService))
+#define G_NETWORK_SERVICE_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_NETWORK_SERVICE, GNetworkServiceClass))
+#define G_IS_NETWORK_SERVICE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_NETWORK_SERVICE))
+#define G_IS_NETWORK_SERVICE_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_NETWORK_SERVICE))
+#define G_NETWORK_SERVICE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_NETWORK_SERVICE, GNetworkServiceClass))
+
+typedef struct _GNetworkServiceClass   GNetworkServiceClass;
+typedef struct _GNetworkServicePrivate GNetworkServicePrivate;
+
+struct _GNetworkService
+{
+  GObject parent_instance;
+
+  /*< private >*/
+  GNetworkServicePrivate *priv;
+};
+
+struct _GNetworkServiceClass
+{
+  GObjectClass parent_class;
+
+};
+
+GType                g_network_service_get_type      (void) G_GNUC_CONST;
+
+GSocketConnectable  *g_network_service_new           (const gchar     *service,
+						      const gchar     *protocol,
+						      const gchar     *domain);
+
+const gchar         *g_network_service_get_service   (GNetworkService *srv);
+const gchar         *g_network_service_get_protocol  (GNetworkService *srv);
+const gchar         *g_network_service_get_domain    (GNetworkService *srv);
+
+G_END_DECLS
+
+#endif /* __G_NETWORK_SERVICE_H__ */
+
diff --git a/gio/gresolver.c b/gio/gresolver.c
index 32a3767..8b51015 100644
--- a/gio/gresolver.c
+++ b/gio/gresolver.c
@@ -52,6 +52,10 @@
  * resolution, for hostnames (g_resolver_lookup_by_address(),
  * g_resolver_lookup_by_name() and their async variants) and SRV
  * (service) records (g_resolver_lookup_service()).
+ *
+ * #GNetworkAddress and #GNetworkService provide wrappers around
+ * #GResolver functionality that also implement #GSocketConnectable,
+ * making it easy to connect to a remote host/service.
  **/
 
 /**
@@ -169,6 +173,10 @@ g_resolver_set_default (GResolver *resolver)
  * operation, in which case @error (if non-%NULL) will be set to
  * %G_IO_ERROR_CANCELLED.
  *
+ * If you are planning to connect to a socket on the resolved IP
+ * address, it may be easier to create a #GNetworkAddress and use its
+ * #GSocketConnectable interface.
+ *
  * Return value: a #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.)
@@ -470,6 +478,10 @@ g_resolver_get_service_rrname (const char *service,
  * operation, in which case @error (if non-%NULL) will be set to
  * %G_IO_ERROR_CANCELLED.
  *
+ * If you are planning to connect to the service, it is usually easier
+ * to create a #GNetworkService and use its #GSocketConnectable
+ * interface.
+ *
  * Return value: 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.)
diff --git a/gio/gsocketaddress.c b/gio/gsocketaddress.c
index 33e8ffd..f5458d4 100644
--- a/gio/gsocketaddress.c
+++ b/gio/gsocketaddress.c
@@ -28,6 +28,8 @@
 #include "ginetaddress.h"
 #include "ginetsocketaddress.h"
 #include "gnetworkingprivate.h"
+#include "gsocketaddressenumerator.h"
+#include "gsocketconnectable.h"
 #include "glibintl.h"
 #include "gioenumtypes.h"
 
@@ -61,7 +63,12 @@ enum
   PROP_FAMILY
 };
 
-G_DEFINE_ABSTRACT_TYPE (GSocketAddress, g_socket_address, G_TYPE_OBJECT);
+static void                      g_socket_address_connectable_iface_init (GSocketConnectableIface *iface);
+static GSocketAddressEnumerator *g_socket_address_connectable_enumerate  (GSocketConnectable      *connectable);
+
+G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GSocketAddress, g_socket_address, G_TYPE_OBJECT,
+				  G_IMPLEMENT_INTERFACE (G_TYPE_SOCKET_CONNECTABLE,
+							 g_socket_address_connectable_iface_init))
 
 /**
  * g_socket_address_get_family:
@@ -115,6 +122,12 @@ g_socket_address_class_init (GSocketAddressClass *klass)
 }
 
 static void
+g_socket_address_connectable_iface_init (GSocketConnectableIface *connectable_iface)
+{
+  connectable_iface->enumerate  = g_socket_address_connectable_enumerate;
+}
+
+static void
 g_socket_address_init (GSocketAddress *address)
 {
 
@@ -229,5 +242,80 @@ g_socket_address_new_from_native (gpointer native,
   return NULL;
 }
 
+
+#define G_TYPE_SOCKET_ADDRESS_ADDRESS_ENUMERATOR (_g_socket_address_address_enumerator_get_type ())
+#define G_SOCKET_ADDRESS_ADDRESS_ENUMERATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_SOCKET_ADDRESS_ADDRESS_ENUMERATOR, GSocketAddressAddressEnumerator))
+
+typedef struct {
+  GSocketAddressEnumerator parent_instance;
+
+  GSocketAddress *sockaddr;
+} GSocketAddressAddressEnumerator;
+
+typedef struct {
+  GSocketAddressEnumeratorClass parent_class;
+
+} GSocketAddressAddressEnumeratorClass;
+
+G_DEFINE_TYPE (GSocketAddressAddressEnumerator, _g_socket_address_address_enumerator, G_TYPE_SOCKET_ADDRESS_ENUMERATOR)
+
+static void
+g_socket_address_address_enumerator_finalize (GObject *object)
+{
+  GSocketAddressAddressEnumerator *sockaddr_enum =
+    G_SOCKET_ADDRESS_ADDRESS_ENUMERATOR (object);
+
+  if (sockaddr_enum->sockaddr)
+    g_object_unref (sockaddr_enum->sockaddr);
+
+  G_OBJECT_CLASS (_g_socket_address_address_enumerator_parent_class)->finalize (object);
+}
+
+static GSocketAddress *
+g_socket_address_address_enumerator_next (GSocketAddressEnumerator  *enumerator,
+					  GCancellable              *cancellable,
+					  GError                   **error)
+{
+  GSocketAddressAddressEnumerator *sockaddr_enum =
+    G_SOCKET_ADDRESS_ADDRESS_ENUMERATOR (enumerator);
+
+  if (sockaddr_enum->sockaddr)
+    {
+      GSocketAddress *ret = sockaddr_enum->sockaddr;
+
+      sockaddr_enum->sockaddr = NULL;
+      return ret;
+    }
+  else
+    return NULL;
+}
+
+static void
+_g_socket_address_address_enumerator_init (GSocketAddressAddressEnumerator *enumerator)
+{
+}
+
+static void
+_g_socket_address_address_enumerator_class_init (GSocketAddressAddressEnumeratorClass *sockaddrenum_class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (sockaddrenum_class);
+  GSocketAddressEnumeratorClass *enumerator_class =
+    G_SOCKET_ADDRESS_ENUMERATOR_CLASS (sockaddrenum_class);
+
+  enumerator_class->next = g_socket_address_address_enumerator_next;
+  object_class->finalize = g_socket_address_address_enumerator_finalize;
+}
+
+static GSocketAddressEnumerator *
+g_socket_address_connectable_enumerate (GSocketConnectable *connectable)
+{
+  GSocketAddressAddressEnumerator *sockaddr_enum;
+
+  sockaddr_enum = g_object_new (G_TYPE_SOCKET_ADDRESS_ADDRESS_ENUMERATOR, NULL);
+  sockaddr_enum->sockaddr = g_object_ref (connectable);
+
+  return (GSocketAddressEnumerator *)sockaddr_enum;
+}
+
 #define __G_SOCKET_ADDRESS_C__
 #include "gioaliasdef.c"
diff --git a/gio/gsocketaddressenumerator.c b/gio/gsocketaddressenumerator.c
new file mode 100644
index 0000000..7cf8a20
--- /dev/null
+++ b/gio/gsocketaddressenumerator.c
@@ -0,0 +1,191 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * 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.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "gsocketaddressenumerator.h"
+#include "glibintl.h"
+
+#include "gsimpleasyncresult.h"
+
+#include "gioalias.h"
+
+G_DEFINE_ABSTRACT_TYPE (GSocketAddressEnumerator, g_socket_address_enumerator, G_TYPE_OBJECT);
+
+static void            g_socket_address_enumerator_real_next_async  (GSocketAddressEnumerator  *enumerator,
+								     GCancellable              *cancellable,
+								     GAsyncReadyCallback        callback,
+								     gpointer                   user_data);
+static GSocketAddress *g_socket_address_enumerator_real_next_finish (GSocketAddressEnumerator  *enumerator,
+								     GAsyncResult              *result,
+								     GError                   **error);
+
+static void
+g_socket_address_enumerator_init (GSocketAddressEnumerator *enumerator)
+{
+}
+
+static void
+g_socket_address_enumerator_class_init (GSocketAddressEnumeratorClass *enumerator_class)
+{
+  enumerator_class->next_async = g_socket_address_enumerator_real_next_async;
+  enumerator_class->next_finish = g_socket_address_enumerator_real_next_finish;
+}
+
+/**
+ * g_socket_address_enumerator_next:
+ * @enumerator: a #GSocketAddressEnumerator
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @error: a #GError.
+ *
+ * Retrieves the next #GSocketAddress from @enumerator. Note that this
+ * may block for some amount of time. (Eg, a #GNetworkAddress may need
+ * to do a DNS lookup before it can return an address.) Use
+ * g_socket_address_enumerator_next_async() if you need to avoid
+ * blocking.
+ *
+ * If @enumerator is expected to yield addresses, but for some reason
+ * is unable to (eg, because of a DNS error), then the first call to
+ * g_socket_address_enumerator_next() will return an appropriate error
+ * in * error  However, if the first call to
+ * g_socket_address_enumerator_next() succeeds, then any further
+ * internal errors (other than @cancellable being triggered) will be
+ * ignored.
+ *
+ * Return value: a #GSocketAddress (owned by the caller), or %NULL on
+ * error (in which case * error will be set) or if there are no more
+ * addresses.
+ **/
+GSocketAddress *
+g_socket_address_enumerator_next (GSocketAddressEnumerator  *enumerator,
+				  GCancellable              *cancellable,
+				  GError                   **error)
+{
+  GSocketAddressEnumeratorClass *klass;
+
+  g_return_val_if_fail (G_IS_SOCKET_ADDRESS_ENUMERATOR (enumerator), NULL);
+
+  klass = G_SOCKET_ADDRESS_ENUMERATOR_GET_CLASS (enumerator);
+
+  return (* klass->next) (enumerator, cancellable, error);
+}
+
+/* Default implementation just calls the synchronous method; this can
+ * be used if the implementation already knows all of its addresses,
+ * and so the synchronous method will never block.
+ */
+static void
+g_socket_address_enumerator_real_next_async (GSocketAddressEnumerator  *enumerator,
+					     GCancellable              *cancellable,
+					     GAsyncReadyCallback        callback,
+					     gpointer                   user_data)
+{
+  GSimpleAsyncResult *result;
+  GSocketAddress *address;
+  GError *error = NULL;
+
+  result = g_simple_async_result_new (G_OBJECT (enumerator),
+				      callback, user_data,
+				      g_socket_address_enumerator_real_next_async);
+  address = g_socket_address_enumerator_next (enumerator, cancellable, &error);
+  if (address)
+    g_simple_async_result_set_op_res_gpointer (result, address, NULL);
+  else if (error)
+    {
+      g_simple_async_result_set_from_error (result, error);
+      g_error_free (error);
+    }
+  g_simple_async_result_complete_in_idle (result);
+  g_object_unref (result);
+}
+
+/**
+ * g_socket_address_enumerator_next_async:
+ * @enumerator: a #GSocketAddressEnumerator
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: a #GAsyncReadyCallback to call when the request is satisfied
+ * @user_data: the data to pass to callback function
+ *
+ * Asynchronously retrieves the next #GSocketAddress from @enumerator
+ * and then calls @callback, which must call
+ * g_socket_address_enumerator_next_finish() to get the result.
+ **/
+void
+g_socket_address_enumerator_next_async (GSocketAddressEnumerator  *enumerator,
+					GCancellable              *cancellable,
+					GAsyncReadyCallback        callback,
+					gpointer                   user_data)
+{
+  GSocketAddressEnumeratorClass *klass;
+
+  g_return_if_fail (G_IS_SOCKET_ADDRESS_ENUMERATOR (enumerator));
+
+  klass = G_SOCKET_ADDRESS_ENUMERATOR_GET_CLASS (enumerator);
+
+  (* klass->next_async) (enumerator, cancellable, callback, user_data);
+}
+
+static GSocketAddress *
+g_socket_address_enumerator_real_next_finish (GSocketAddressEnumerator  *enumerator,
+					      GAsyncResult              *result,
+					      GError                   **error)
+{
+  GSimpleAsyncResult *simple;
+
+  g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), NULL);
+  simple = G_SIMPLE_ASYNC_RESULT (result);
+  g_return_val_if_fail (g_simple_async_result_get_source_tag (simple) == g_socket_address_enumerator_real_next_async, NULL);
+
+  if (g_simple_async_result_propagate_error (simple, error))
+    return NULL;
+  else
+    return g_simple_async_result_get_op_res_gpointer (simple);
+}
+
+/**
+ * g_socket_address_enumerator_next_finish:
+ * @enumerator: a #GSocketAddressEnumerator
+ * @result: a #GAsyncResult.
+ * @error: a #GError.
+ *
+ * Retrieves the result of a completed call to
+ * g_socket_address_enumerator_next_async(). See
+ * g_socket_address_enumerator_next() for more information about
+ * error handling.
+ *
+ * Return value: a #GSocketAddress (owned by the caller), or %NULL on
+ * error (in which case * error will be set) or if there are no more
+ * addresses.
+ **/
+GSocketAddress *
+g_socket_address_enumerator_next_finish (GSocketAddressEnumerator  *enumerator,
+					 GAsyncResult              *result,
+					 GError                   **error)
+{
+  GSocketAddressEnumeratorClass *klass;
+
+  g_return_val_if_fail (G_IS_SOCKET_ADDRESS_ENUMERATOR (enumerator), NULL);
+
+  klass = G_SOCKET_ADDRESS_ENUMERATOR_GET_CLASS (enumerator);
+
+  return (* klass->next_finish) (enumerator, result, error);
+}
+
+#define __G_SOCKET_ADDRESS_ENUMERATOR_C__
+#include "gioaliasdef.c"
diff --git a/gio/gsocketaddressenumerator.h b/gio/gsocketaddressenumerator.h
new file mode 100644
index 0000000..cd09f6a
--- /dev/null
+++ b/gio/gsocketaddressenumerator.h
@@ -0,0 +1,89 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * 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.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
+#error "Only <gio/gio.h> can be included directly."
+#endif
+
+#ifndef __G_SOCKET_ADDRESS_ENUMERATOR_H__
+#define __G_SOCKET_ADDRESS_ENUMERATOR_H__
+
+#include <gio/giotypes.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_SOCKET_ADDRESS_ENUMERATOR         (g_socket_address_enumerator_get_type ())
+#define G_SOCKET_ADDRESS_ENUMERATOR(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_SOCKET_ADDRESS_ENUMERATOR, GSocketAddressEnumerator))
+#define G_SOCKET_ADDRESS_ENUMERATOR_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_SOCKET_ADDRESS_ENUMERATOR, GSocketAddressEnumeratorClass))
+#define G_IS_SOCKET_ADDRESS_ENUMERATOR(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_SOCKET_ADDRESS_ENUMERATOR))
+#define G_IS_SOCKET_ADDRESS_ENUMERATOR_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_SOCKET_ADDRESS_ENUMERATOR))
+#define G_SOCKET_ADDRESS_ENUMERATOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_SOCKET_ADDRESS_ENUMERATOR, GSocketAddressEnumeratorClass))
+
+/**
+ * GSocketAddressEnumerator:
+ *
+ * Enumerator type for objects that contain or generate
+ * #GSocketAddress<!-- -->es.
+ **/
+typedef struct _GSocketAddressEnumeratorClass GSocketAddressEnumeratorClass;
+
+struct _GSocketAddressEnumerator
+{
+  GObject parent_instance;
+
+};
+
+struct _GSocketAddressEnumeratorClass
+{
+  GObjectClass parent_class;
+
+  /* Virtual Table */
+
+  GSocketAddress * (* next)        (GSocketAddressEnumerator  *enumerator,
+				    GCancellable              *cancellable,
+				    GError                   **error);
+
+  void             (* next_async)  (GSocketAddressEnumerator  *enumerator,
+				    GCancellable              *cancellable,
+				    GAsyncReadyCallback        callback,
+				    gpointer                   user_data);
+  GSocketAddress * (* next_finish) (GSocketAddressEnumerator  *enumerator,
+				    GAsyncResult              *result,
+				    GError                   **error);
+};
+
+GType           g_socket_address_enumerator_get_type        (void) G_GNUC_CONST;
+
+GSocketAddress *g_socket_address_enumerator_next        (GSocketAddressEnumerator  *enumerator,
+							 GCancellable              *cancellable,
+							 GError                   **error);
+
+void            g_socket_address_enumerator_next_async  (GSocketAddressEnumerator  *enumerator,
+							 GCancellable              *cancellable,
+							 GAsyncReadyCallback        callback,
+							 gpointer                   user_data);
+GSocketAddress *g_socket_address_enumerator_next_finish (GSocketAddressEnumerator  *enumerator,
+							 GAsyncResult              *result,
+							 GError                   **error);
+
+G_END_DECLS
+
+
+#endif /* __G_SOCKET_ADDRESS_ENUMERATOR_H__ */
diff --git a/gio/gsocketconnectable.c b/gio/gsocketconnectable.c
new file mode 100644
index 0000000..419d57a
--- /dev/null
+++ b/gio/gsocketconnectable.c
@@ -0,0 +1,148 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * 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.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "gsocketconnectable.h"
+#include "glibintl.h"
+
+#include "gioalias.h"
+
+/**
+ * SECTION:gsocketconnectable
+ * @short_description: Interface for potential socket endpoints.
+ *
+ * Objects that describe one or more potential socket endpoints
+ * implement #GSocketConnectable. Callers can then use
+ * g_socket_connectable_enumerate() to get a #GSocketAddressEnumerator
+ * to try out each socket address in turn until one succeeds, as shown
+ * in the sample code below.
+ *
+ * |[
+ * MyConnectionType *
+ * connect_to_host (const char    *hostname,
+ *                  guint16        port,
+ *                  GCancellable  *cancellable,
+ *                  GError       **error)
+ * {
+ *   MyConnection *conn = NULL;
+ *   GSocketConnectable *addr;
+ *   GSocketAddressEnumerator *enumerator;
+ *   GSocketAddress *sockaddr;
+ *   GError *conn_error = NULL;
+ *
+ *   addr = g_network_address_new ("www.gnome.org", 80);
+ *   enumerator = g_socket_connectable_enumerate (addr);
+ *   g_object_unref (addr);
+ *
+ *   /<!-- -->* Try each sockaddr until we succeed. Record the first
+ *    * connection error, but not any further ones (since they'll probably
+ *    * be basically the same as the first).
+ *    *<!-- -->/
+ *   while (!conn && (sockaddr = g_socket_address_enumerator_next (enumerator, cancellable, error))
+ *     {
+ *       conn = connect_to_sockaddr (sockaddr, conn_error ? NULL : &conn_error);
+ *       g_object_unref (sockaddr);
+ *     }
+ *   g_object_unref (enumerator);
+ *
+ *   if (conn)
+ *     {
+ *       if (conn_error)
+ *         {
+ *           /<!-- -->* We couldn't connect to the first address, but we succeeded
+ *            * in connecting to a later address.
+ *            *<!-- -->/
+ *           g_error_free (conn_error);
+ *         }
+ *       return conn;
+ *     }
+ *   else if (error)
+ *     {
+ *       /<!-- -->* Either the initial lookup failed, or else the caller
+ *        * cancelled us.
+ *        *<!-- -->/
+ *       if (conn_error)
+ *         g_error_free (conn_error);
+ *       return NULL;
+ *     }
+ *   else
+ *     {
+ *       g_error_propagate (error, conn_error);
+ *       return NULL;
+ *     }
+ * }
+ * ]|
+ **/
+
+GType
+g_socket_connectable_get_type (void)
+{
+  static volatile gsize g_define_type_id__volatile = 0;
+
+  if (g_once_init_enter (&g_define_type_id__volatile))
+    {
+      const GTypeInfo connectable_info =
+      {
+        sizeof (GSocketConnectableIface), /* class_size */
+	NULL,		/* base_init */
+	NULL,		/* base_finalize */
+	NULL,
+	NULL,		/* class_finalize */
+	NULL,		/* class_data */
+	0,
+	0,              /* n_preallocs */
+	NULL
+      };
+      GType g_define_type_id =
+	g_type_register_static (G_TYPE_INTERFACE, I_("GSocketConnectable"),
+				&connectable_info, 0);
+
+      g_type_interface_add_prerequisite (g_define_type_id, G_TYPE_OBJECT);
+
+      g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
+    }
+
+  return g_define_type_id__volatile;
+}
+
+/**
+ * g_socket_connectable_enumerate:
+ * @connectable: a #GSocketConnectable
+ *
+ * Creates a #GSocketAddressEnumerator for @connectable.
+ *
+ * Return value: a new #GSocketAddressEnumerator.
+ *
+ * Since: 2.22
+ **/
+GSocketAddressEnumerator *
+g_socket_connectable_enumerate (GSocketConnectable *connectable)
+{
+  GSocketConnectableIface *iface;
+
+  g_return_val_if_fail (G_IS_SOCKET_CONNECTABLE (connectable), NULL);
+
+  iface = G_SOCKET_CONNECTABLE_GET_IFACE (connectable);
+
+  return (* iface->enumerate) (connectable);
+}
+
+#define __G_SOCKET_CONNECTABLE_C__
+#include "gioaliasdef.c"
diff --git a/gio/gsocketconnectable.h b/gio/gsocketconnectable.h
new file mode 100644
index 0000000..41eebea
--- /dev/null
+++ b/gio/gsocketconnectable.h
@@ -0,0 +1,68 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * 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.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
+#error "Only <gio/gio.h> can be included directly."
+#endif
+
+#ifndef __G_SOCKET_CONNECTABLE_H__
+#define __G_SOCKET_CONNECTABLE_H__
+
+#include <gio/giotypes.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_SOCKET_CONNECTABLE            (g_socket_connectable_get_type ())
+#define G_SOCKET_CONNECTABLE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_SOCKET_CONNECTABLE, GSocketConnectable))
+#define G_IS_SOCKET_CONNECTABLE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_SOCKET_CONNECTABLE))
+#define G_SOCKET_CONNECTABLE_GET_IFACE(obj)  (G_TYPE_INSTANCE_GET_INTERFACE ((obj), G_TYPE_SOCKET_CONNECTABLE, GSocketConnectableIface))
+
+/**
+ * GSocketConnectable:
+ *
+ * Interface for objects that contain or generate #GSocketAddress<!-- -->es.
+ **/
+typedef struct _GSocketConnectableIface GSocketConnectableIface;
+
+/**
+ * GSocketConnectableIface:
+ * @g_iface: The parent interface.
+ * @enumerate: Creates a #GSocketAddressEnumerator
+ *
+ * Provides an interface for returning a #GSocketAddressEnumerator
+ **/
+struct _GSocketConnectableIface
+{
+  GTypeInterface g_iface;
+
+  /* Virtual Table */
+
+  GSocketAddressEnumerator * (* enumerate) (GSocketConnectable *connectable);
+
+};
+
+GType                     g_socket_connectable_get_type  (void) G_GNUC_CONST;
+
+GSocketAddressEnumerator *g_socket_connectable_enumerate (GSocketConnectable *connectable);
+
+G_END_DECLS
+
+
+#endif /* __G_SOCKET_CONNECTABLE_H__ */
diff --git a/gio/gsrvtarget.c b/gio/gsrvtarget.c
index 537d816..87d132b 100644
--- a/gio/gsrvtarget.c
+++ b/gio/gsrvtarget.c
@@ -44,9 +44,12 @@
  * would look up the "xmpp-client" SRV record for "example.com", and
  * then connect to whatever host was pointed to by that record.
  *
- * Use g_resolver_lookup_service() or
+ * You can use g_resolver_lookup_service() or
  * g_resolver_lookup_service_async() to find the #GSrvTarget<!-- -->s
- * for a given service.
+ * for a given service. However, if you are simply planning to connect
+ * to the remote service, you can use #GNetworkService's
+ * #GSocketConnectable interface and not need to worry about
+ * #GSrvTarget at all.
  **/
 
 struct _GSrvTarget {
diff --git a/gio/tests/.gitignore b/gio/tests/.gitignore
index d9f5601..70f0f87 100644
--- a/gio/tests/.gitignore
+++ b/gio/tests/.gitignore
@@ -13,3 +13,4 @@ memory-output-stream
 filter-streams
 sleepy-stream
 resolver
+connectable
diff --git a/gio/tests/resolver.c b/gio/tests/resolver.c
index 8ee9166..205e41c 100644
--- a/gio/tests/resolver.c
+++ b/gio/tests/resolver.c
@@ -41,9 +41,11 @@ static void
 usage (void)
 {
 	fprintf (stderr, "Usage: resolver [-t] [-s] [hostname | IP | service/protocol/domain ] ...\n");
+	fprintf (stderr, "       resolver [-t] [-s] -c [hostname | IP | service/protocol/domain ]\n");
 	fprintf (stderr, "       Use -t to enable threading.\n");
 	fprintf (stderr, "       Use -s to do synchronous lookups.\n");
 	fprintf (stderr, "       Both together will result in simultaneous lookups in multiple threads\n");
+	fprintf (stderr, "       Use -c (and only a single resolvable argument) to test GSocketConnectable.\n");
 	exit (1);
 }
 
@@ -285,6 +287,125 @@ start_async_lookups (char **argv, int argc)
     }
 }
 
+static void
+print_connectable_sockaddr (GSocketAddress *sockaddr,
+                            GError         *error)
+{
+  char *phys;
+
+  if (error)
+    {
+      printf ("Error:   %s\n", error->message);
+      g_error_free (error);
+    }
+  else if (!G_IS_INET_SOCKET_ADDRESS (sockaddr))
+    {
+      printf ("Error:   Unexpected sockaddr type '%s'\n", g_type_name_from_instance ((GTypeInstance *)sockaddr));
+      g_object_unref (sockaddr);
+    }
+  else
+    {
+      GInetSocketAddress *isa = G_INET_SOCKET_ADDRESS (sockaddr);
+      phys = g_inet_address_to_string (g_inet_socket_address_get_address (isa));
+      printf ("Address: %s%s%s:%d\n",
+              strchr (phys, ':') ? "[" : "", phys, strchr (phys, ':') ? "]" : "",
+              g_inet_socket_address_get_port (isa));
+      g_free (phys);
+      g_object_unref (sockaddr);
+    }
+}
+
+static void
+do_sync_connectable (GSocketAddressEnumerator *enumerator)
+{
+  GSocketAddress *sockaddr;
+  GError *error = NULL;
+
+  while ((sockaddr = g_socket_address_enumerator_next (enumerator, cancellable, &error)))
+    print_connectable_sockaddr (sockaddr, error);
+
+  g_object_unref (enumerator);
+  done_lookup ();
+}
+
+static void do_async_connectable (GSocketAddressEnumerator *enumerator);
+
+static void
+got_next_async (GObject *source, GAsyncResult *result, gpointer user_data)
+{
+  GSocketAddressEnumerator *enumerator = G_SOCKET_ADDRESS_ENUMERATOR (source);
+  GSocketAddress *sockaddr;
+  GError *error = NULL;
+
+  sockaddr = g_socket_address_enumerator_next_finish (enumerator, result, &error);
+  if (sockaddr || error)
+    print_connectable_sockaddr (sockaddr, error);
+  if (sockaddr)
+    do_async_connectable (enumerator);
+  else
+    {
+      g_object_unref (enumerator);
+      done_lookup ();
+    }
+}
+
+static void
+do_async_connectable (GSocketAddressEnumerator *enumerator)
+{
+  g_socket_address_enumerator_next_async (enumerator, cancellable,
+                                          got_next_async, NULL);
+}
+
+static void
+do_connectable (const char *arg, gboolean synchronous)
+{
+  char **parts;
+  GSocketConnectable *connectable;
+  GSocketAddressEnumerator *enumerator;
+
+  if (strchr (arg, '/'))
+    {
+      /* service/protocol/domain */
+      parts = g_strsplit (arg, "/", 3);
+      if (!parts || !parts[2])
+	usage ();
+
+      connectable = g_network_service_new (parts[0], parts[1], parts[2]);
+    }
+  else
+    {
+      guint16 port;
+
+      parts = g_strsplit (arg, ":", 2);
+      if (parts && parts[1])
+	{
+	  arg = parts[0];
+	  port = strtoul (parts[1], NULL, 10);
+	}
+      else
+	port = 0;
+
+      if (g_hostname_is_ip_address (arg))
+	{
+	  GInetAddress *addr = g_inet_address_new_from_string (arg);
+	  GSocketAddress *sockaddr = g_inet_socket_address_new (addr, port);
+
+	  g_object_unref (addr);
+	  connectable = G_SOCKET_CONNECTABLE (sockaddr);
+	}
+      else
+        connectable = g_network_address_new (arg, port);
+    }
+
+  enumerator = g_socket_connectable_enumerate (connectable);
+  g_object_unref (connectable);
+
+  if (synchronous)
+    do_sync_connectable (enumerator);
+  else
+    do_async_connectable (enumerator);
+}
+
 #ifdef G_OS_UNIX
 static int cancel_fds[2];
 
@@ -307,6 +428,7 @@ int
 main (int argc, char **argv)
 {
   gboolean threaded = FALSE, synchronous = FALSE;
+  gboolean use_connectable = FALSE;
 #ifdef G_OS_UNIX
   GIOChannel *chan;
   guint watch;
@@ -324,6 +446,8 @@ main (int argc, char **argv)
         }
       else if (!strcmp (argv[1], "-s"))
         synchronous = TRUE;
+      else if (!strcmp (argv[1], "-c"))
+        use_connectable = TRUE;
       else
         usage ();
 
@@ -332,7 +456,7 @@ main (int argc, char **argv)
     }
   g_type_init ();
 
-  if (argc < 2)
+  if (argc < 2 || (argc > 2 && use_connectable))
     usage ();
 
   resolver = g_resolver_get_default ();
@@ -358,12 +482,17 @@ main (int argc, char **argv)
   nlookups = argc - 1;
   loop = g_main_loop_new (NULL, TRUE);
 
-  if (threaded && synchronous)
-    start_threaded_lookups (argv + 1, argc - 1);
-  else if (synchronous)
-    start_sync_lookups (argv + 1, argc - 1);
+  if (use_connectable)
+    do_connectable (argv[1], synchronous);
   else
-    start_async_lookups (argv + 1, argc - 1);
+    {
+      if (threaded && synchronous)
+        start_threaded_lookups (argv + 1, argc - 1);
+      else if (synchronous)
+        start_sync_lookups (argv + 1, argc - 1);
+      else
+        start_async_lookups (argv + 1, argc - 1);
+    }
 
   g_main_run (loop);
   g_main_loop_unref (loop);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]