[glib/gresolver: 4/4] GResolver wrappers: GNetworkAddress, GNetworkService, GSocketConnectable



commit 6b25e58e61624d0965fef7ee7426977bcb0f82d7
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 http://bugzilla.gnome.org/show_bug.cgi?id=548466
---
 docs/reference/gio/gio-docs.xml     |    3 +
 docs/reference/gio/gio-sections.txt |   61 ++++
 docs/reference/gio/gio.types        |    3 +
 gio/Makefile.am                     |    6 +
 gio/gio.h                           |    3 +
 gio/gio.symbols                     |   29 ++
 gio/giotypes.h                      |    3 +
 gio/gnetworkaddress.c               |  465 ++++++++++++++++++++++++++
 gio/gnetworkaddress.h               |   65 ++++
 gio/gnetworkservice.c               |  610 +++++++++++++++++++++++++++++++++++
 gio/gnetworkservice.h               |   69 ++++
 gio/gresolver.c                     |   12 +
 gio/gsocketaddress.c                |   45 +++-
 gio/gsocketconnectable.c            |  269 +++++++++++++++
 gio/gsocketconnectable.h            |   94 ++++++
 gio/tests/.gitignore                |    1 +
 gio/tests/resolver.c                |  137 ++++++++-
 17 files changed, 1868 insertions(+), 7 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 d0f744d..8f81a27 100644
--- a/docs/reference/gio/gio-sections.txt
+++ b/docs/reference/gio/gio-sections.txt
@@ -1454,3 +1454,64 @@ G_TYPE_SRV_TARGET
 <SUBSECTION Private>
 g_srv_target_get_type
 </SECTION>
+
+<SECTION>
+<FILE>gsocketconnectable</FILE>
+<TITLE>GSocketConnectable</TITLE>
+GSocketConnectable
+GSocketConnectableIface
+g_socket_connectable_get_next
+g_socket_connectable_get_next_async
+g_socket_connectable_get_next_finish
+g_socket_connectable_reset
+<SUBSECTION Standard>
+G_IS_SOCKET_CONNECTABLE
+G_SOCKET_CONNECTABLE
+G_SOCKET_CONNECTABLE_GET_IFACE
+G_TYPE_SOCKET_CONNECTABLE
+<SUBSECTION Private>
+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..8fb70f5 100644
--- a/docs/reference/gio/gio.types
+++ b/docs/reference/gio/gio.types
@@ -52,6 +52,8 @@ 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
@@ -59,6 +61,7 @@ g_resolver_get_type
 g_seekable_get_type
 g_simple_async_result_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..7e7c2d5 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,7 @@ libgio_2_0_la_SOURCES =		\
 	gseekable.c 		\
 	gsimpleasyncresult.c 	\
 	gsocketaddress.c	\
+	gsocketconnectable.c	\
 	gsrvtarget.c		\
 	gthemedicon.c 		\
 	gthreadedresolver.c	\
@@ -328,11 +331,14 @@ gio_headers =			\
 	gmemoryoutputstream.h 	\
 	gmountoperation.h 	\
 	gnativevolumemonitor.h 	\
+	gnetworkaddress.h	\
+	gnetworkservice.h	\
 	goutputstream.h 	\
 	gresolver.h		\
 	gseekable.h 		\
 	gsimpleasyncresult.h 	\
 	gsocketaddress.h	\
+	gsocketconnectable.h	\
 	gsrvtarget.h		\
 	gthemedicon.h 		\
 	gvfs.h 			\
diff --git a/gio/gio.h b/gio/gio.h
index 779ce40..e61b8e4 100644
--- a/gio/gio.h
+++ b/gio/gio.h
@@ -63,11 +63,14 @@
 #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/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 f792a8a..6e9207a 100644
--- a/gio/gio.symbols
+++ b/gio/gio.symbols
@@ -963,3 +963,32 @@ 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_get_next
+g_socket_connectable_get_next_async
+g_socket_connectable_get_next_finish
+g_socket_connectable_get_type G_GNUC_CONST
+g_socket_connectable_reset
+#endif
+#endif
diff --git a/gio/giotypes.h b/gio/giotypes.h
index 1e44e4c..6b08331 100644
--- a/gio/giotypes.h
+++ b/gio/giotypes.h
@@ -99,11 +99,14 @@ 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 _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..9548df6
--- /dev/null
+++ b/gio/gnetworkaddress.c
@@ -0,0 +1,465 @@
+/* -*- 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 "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.
+ *
+ * |[
+ * MyConnectionType *
+ * connect_to_host (const char    *hostname,
+ *                  guint16        port,
+ *                  GCancellable  *cancellable,
+ *                  GError       **error)
+ * {
+ *   MyConnection *conn = NULL;
+ *   GSocketConnectable *addr;
+ *   GSocketAddress *sockaddr;
+ *   GError *conn_error = NULL;
+ *
+ *   addr = g_network_address_new ("www.gnome.org", 80);
+ *
+ *   /<!-- -->* 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_connectable_get_next (addr, cancellable, error))
+ *     {
+ *       conn = connect_to_sockaddr (sockaddr, conn_error ? NULL : &conn_error);
+ *       g_object_unref (sockaddr);
+ *     }
+ *   g_object_unref (addr);
+ *
+ *   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;
+ *     }
+ * }
+ * ]|
+ **/
+
+/**
+ * GNetworkAddress:
+ *
+ * A #GSocketConnectable for resolving a hostname and connecting to
+ * that host.
+ **/
+
+struct _GNetworkAddressPrivate {
+  gchar *hostname;
+  guint16 port;
+
+  GList *sockaddrs, *iter;
+};
+
+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 GSocketAddress *g_network_address_connectable_get_next        (GSocketConnectable  *connectable,
+                                                                      GCancellable        *cancellable,
+                                                                      GError             **error);
+static void            g_network_address_connectable_get_next_async  (GSocketConnectable   *connectable,
+                                                                      GCancellable         *cancellable,
+                                                                      GAsyncReadyCallback   callback,
+                                                                      gpointer              user_data);
+static GSocketAddress *g_network_address_connectable_get_next_finish (GSocketConnectable   *connectable,
+                                                                      GAsyncResult         *result,
+                                                                      GError              **error);
+static void            g_network_address_connectable_reset           (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 resolver"),
+                                                        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->get_next        = g_network_address_connectable_get_next;
+  connectable_iface->get_next_async  = g_network_address_connectable_get_next_async;
+  connectable_iface->get_next_finish = g_network_address_connectable_get_next_finish;
+  connectable_iface->reset           = g_network_address_connectable_reset;
+}
+
+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.20
+ **/
+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.20
+ **/
+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.20
+ **/
+guint16
+g_network_address_get_port (GNetworkAddress *addr)
+{
+  g_return_val_if_fail (G_IS_NETWORK_ADDRESS (addr), 0);
+
+  return addr->priv->port;
+}
+
+static GSocketAddress *
+g_network_address_connectable_get_next (GSocketConnectable  *connectable,
+                                        GCancellable        *cancellable,
+                                        GError             **error)
+{
+  GNetworkAddress *addr = G_NETWORK_ADDRESS (connectable);
+  GSocketAddress *sockaddr;
+
+  if (!addr->priv->sockaddrs)
+    {
+      GResolver *resolver = g_resolver_get_default ();
+      GList *addresses;
+
+      addresses = g_resolver_lookup_by_name (resolver,
+                                             addr->priv->hostname,
+                                             cancellable, error);
+      g_object_unref (resolver);
+
+      if (!addresses)
+        return NULL;
+
+      g_network_address_set_addresses (addr, addresses);
+      addr->priv->iter = addr->priv->sockaddrs;
+    }
+
+  if (!addr->priv->iter)
+    return NULL;
+  else
+    {
+      sockaddr = addr->priv->iter->data;
+      addr->priv->iter = addr->priv->iter->next;
+      return g_object_ref (sockaddr);
+    }
+}
+
+static void
+got_addresses (GObject      *source_object,
+               GAsyncResult *result,
+               gpointer      user_data)
+{
+  GResolver *resolver = G_RESOLVER (source_object);
+  GSimpleAsyncResult *simple = user_data;
+  GNetworkAddress *addr;
+  GList *addresses;
+  GError *error = NULL;
+  GSocketAddress *sockaddr;
+
+  addr = (GNetworkAddress *)g_async_result_get_source_object (G_ASYNC_RESULT (simple));
+  /* get_source_object adds a ref */
+  g_object_unref (addr);
+
+  addresses = g_resolver_lookup_by_name_finish (resolver, result, &error);
+  if (error)
+    {
+      g_simple_async_result_set_from_error (simple, error);
+      g_error_free (error);
+    }
+  else
+    {
+      g_network_address_set_addresses (addr, addresses);
+      addr->priv->iter = addr->priv->sockaddrs;
+    }
+
+  sockaddr = g_network_address_connectable_get_next (G_SOCKET_CONNECTABLE (addr), NULL, NULL);
+  if (sockaddr)
+    g_simple_async_result_set_op_res_gpointer (simple, sockaddr, g_object_unref);
+
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+}
+
+static void
+g_network_address_connectable_get_next_async (GSocketConnectable   *connectable,
+                                              GCancellable         *cancellable,
+                                              GAsyncReadyCallback   callback,
+                                              gpointer              user_data)
+{
+  GNetworkAddress *addr = G_NETWORK_ADDRESS (connectable);
+  GSimpleAsyncResult *simple;
+  GSocketAddress *sockaddr;
+
+  simple = g_simple_async_result_new (G_OBJECT (connectable),
+                                      callback, user_data,
+                                      g_network_address_connectable_get_next_async);
+
+  if (!addr->priv->sockaddrs)
+    {
+      GResolver *resolver = g_resolver_get_default ();
+
+      g_resolver_lookup_by_name_async (resolver,
+                                       addr->priv->hostname,
+                                       cancellable,
+                                       got_addresses, simple);
+      g_object_unref (resolver);
+    }
+  else
+    {
+      sockaddr = g_network_address_connectable_get_next (connectable, 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_connectable_get_next_finish (GSocketConnectable  *connectable,
+                                               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_address_connectable_reset (GSocketConnectable  *connectable)
+{
+  GNetworkAddress *addr = G_NETWORK_ADDRESS (connectable);
+
+  addr->priv->iter = addr->priv->sockaddrs;
+}
+
+#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..b23619b
--- /dev/null
+++ b/gio/gnetworkservice.c
@@ -0,0 +1,610 @@
+/* -*- 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 "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.
+ **/
+
+/**
+ * GNetworkService:
+ *
+ * A #GSocketConnectable for resolving a SRV record and connecting to
+ * that service.
+ **/
+
+struct _GNetworkServicePrivate
+{
+  gchar *service, *protocol, *domain;
+
+  GResolver *resolver;
+  GList *targets, *target_iter;
+  GList *addrs, *addr_iter;
+
+  GSimpleAsyncResult *next_result;
+  GCancellable *cancellable;
+  GError *iter_error;
+};
+
+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 GSocketAddress *g_network_service_connectable_get_next        (GSocketConnectable  *connectable,
+                                                                      GCancellable        *cancellable,
+                                                                      GError             **error);
+static void            g_network_service_connectable_get_next_async  (GSocketConnectable   *connectable,
+                                                                      GCancellable         *cancellable,
+                                                                      GAsyncReadyCallback   callback,
+                                                                      gpointer              user_data);
+static GSocketAddress *g_network_service_connectable_get_next_finish (GSocketConnectable   *connectable,
+                                                                      GAsyncResult         *result,
+                                                                      GError              **error);
+static void            g_network_service_connectable_reset           (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_unref (srv->priv->resolver);
+  g_network_service_connectable_reset ((GSocketConnectable *)srv);
+
+  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->get_next        = g_network_service_connectable_get_next;
+  connectable_iface->get_next_async  = g_network_service_connectable_get_next_async;
+  connectable_iface->get_next_finish = g_network_service_connectable_get_next_finish;
+  connectable_iface->reset           = g_network_service_connectable_reset;
+}
+
+static void
+g_network_service_init (GNetworkService *srv)
+{
+  srv->priv = G_TYPE_INSTANCE_GET_PRIVATE (srv, G_TYPE_NETWORK_SERVICE,
+                                           GNetworkServicePrivate);
+  srv->priv->resolver = g_resolver_get_default ();
+}
+
+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.20
+ **/
+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.20
+ **/
+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.20
+ **/
+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.20
+ **/
+const gchar *
+g_network_service_get_domain (GNetworkService *srv)
+{
+  g_return_val_if_fail (G_IS_NETWORK_SERVICE (srv), NULL);
+
+  return srv->priv->domain;
+}
+
+static GSocketAddress *
+g_network_service_connectable_get_next (GSocketConnectable  *connectable,
+                                        GCancellable        *cancellable,
+                                        GError             **error)
+{
+  GNetworkService *srv = G_NETWORK_SERVICE (connectable);
+  GSrvTarget *target;
+  GSocketAddress *sockaddr;
+
+  /* If we haven't yet resolved srv, do that */
+  if (!srv->priv->targets)
+    {
+      GList *targets;
+
+      targets = g_resolver_lookup_service (srv->priv->resolver,
+                                           srv->priv->service,
+                                           srv->priv->protocol,
+                                           srv->priv->domain,
+                                           cancellable, error);
+      if (!targets)
+        return NULL;
+
+      srv->priv->targets = targets;
+      srv->priv->target_iter = 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->priv->target_iter)
+        {
+          if (srv->priv->iter_error)
+            {
+              g_propagate_error (error, srv->priv->iter_error);
+              srv->priv->iter_error = NULL;
+            }
+          return NULL;
+        }
+      target = srv->priv->target_iter->data;
+
+      /* If we haven't resolved the addrs for the current target, do that */
+      if (!srv->priv->addrs)
+        {
+          GError **error_p;
+
+          if (srv->priv->target_iter == srv->priv->targets)
+            error_p = &srv->priv->iter_error;
+          else
+            error_p = NULL;
+          srv->priv->addrs = g_resolver_lookup_by_name (srv->priv->resolver,
+                                                        g_srv_target_get_hostname (target),
+                                                        cancellable, error_p);
+          if (g_cancellable_set_error_if_cancelled (cancellable, error))
+            return NULL;
+
+          if (srv->priv->addrs)
+            {
+              srv->priv->addr_iter = srv->priv->addrs;
+              if (srv->priv->iter_error)
+                {
+                  g_error_free (srv->priv->iter_error);
+                  srv->priv->iter_error = NULL;
+                }
+            }
+          else
+            {
+              /* Try the next target */
+              srv->priv->target_iter = srv->priv->target_iter->next;
+            }
+        }
+    }
+  while (!srv->priv->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->priv->addr_iter->data,
+                                        g_srv_target_get_port (target));
+  srv->priv->addr_iter = srv->priv->addr_iter->next;
+
+  if (!srv->priv->addr_iter)
+    {
+      g_resolver_free_addresses (srv->priv->addrs);
+      srv->priv->addrs = NULL;
+      srv->priv->target_iter = srv->priv->target_iter->next;
+    }
+
+  return sockaddr;
+}
+
+static void get_next_async_resolved_targets   (GObject         *source_object,
+                                               GAsyncResult    *result,
+                                               gpointer         user_data);
+static void get_next_async_have_targets       (GNetworkService *srv);
+static void get_next_async_resolved_addresses (GObject         *source_object,
+                                               GAsyncResult    *result,
+                                               gpointer         user_data);
+static void get_next_async_have_addresses     (GNetworkService *srv);
+
+/* The async version is basically the same as the sync, except we have
+ * to split it into multiple functions.
+ */
+static void
+g_network_service_connectable_get_next_async (GSocketConnectable  *connectable,
+                                              GCancellable        *cancellable,
+                                              GAsyncReadyCallback  callback,
+                                              gpointer             user_data)
+{
+  GNetworkService *srv = G_NETWORK_SERVICE (connectable);
+
+  g_return_if_fail (srv->priv->next_result == NULL);
+
+  srv->priv->next_result = g_simple_async_result_new (G_OBJECT (connectable),
+                                                      callback, user_data,
+                                                      g_network_service_connectable_get_next_async);
+  srv->priv->cancellable = cancellable;
+
+  /* If we haven't yet resolved srv, do that */
+  if (!srv->priv->targets)
+    {
+      g_resolver_lookup_service_async (srv->priv->resolver,
+                                       srv->priv->service,
+                                       srv->priv->protocol,
+                                       srv->priv->domain,
+                                       cancellable,
+                                       get_next_async_resolved_targets,
+                                       srv);
+    }
+  else
+    get_next_async_have_targets (srv);
+}
+
+static void
+get_next_async_resolved_targets (GObject      *source_object,
+                                 GAsyncResult *result,
+                                 gpointer      user_data)
+{
+  GNetworkService *srv = user_data;
+  GError *error = NULL;
+
+  srv->priv->targets = g_resolver_lookup_service_finish (srv->priv->resolver,
+                                                         result, &error);
+  if (error)
+    {
+      GSimpleAsyncResult *simple = srv->priv->next_result;
+
+      srv->priv->next_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->priv->target_iter = srv->priv->targets;
+  get_next_async_have_targets (srv);
+}
+
+static void
+get_next_async_have_targets (GNetworkService *srv)
+{
+  GSrvTarget *target;
+
+  /* Get the current target, check if we're already done. */
+  if (!srv->priv->target_iter)
+    {
+      if (srv->priv->iter_error)
+        {
+          g_simple_async_result_set_from_error (srv->priv->next_result, srv->priv->iter_error);
+          g_error_free (srv->priv->iter_error);
+          srv->priv->iter_error = NULL;
+        }
+      g_simple_async_result_complete_in_idle (srv->priv->next_result);
+      g_object_unref (srv->priv->next_result);
+      srv->priv->next_result = NULL;
+      return;
+    }
+  target = srv->priv->target_iter->data;
+
+  /* If we haven't resolved the addrs for the current target, do that */
+  if (!srv->priv->addrs)
+    {
+      g_resolver_lookup_by_name_async (srv->priv->resolver,
+                                       g_srv_target_get_hostname (target),
+                                       srv->priv->cancellable,
+                                       get_next_async_resolved_addresses,
+                                       srv);
+    }
+  else
+    get_next_async_have_addresses (srv);
+}
+
+static void
+get_next_async_resolved_addresses (GObject      *source_object,
+                                   GAsyncResult *result,
+                                   gpointer      user_data)
+{
+  GNetworkService *srv = user_data;
+  GError *error = NULL;
+
+  srv->priv->addrs = g_resolver_lookup_by_name_finish (srv->priv->resolver, result, &error);
+  if (srv->priv->addrs)
+    {
+      srv->priv->addr_iter = srv->priv->addrs;
+      if (srv->priv->iter_error)
+        {
+          g_error_free (srv->priv->iter_error);
+          srv->priv->iter_error = NULL;
+        }
+      get_next_async_have_addresses (srv);
+    }
+  else
+    {
+      if (g_cancellable_is_cancelled (srv->priv->cancellable))
+        {
+          GSimpleAsyncResult *simple = srv->priv->next_result;
+
+          srv->priv->next_result = NULL;
+          g_simple_async_result_set_from_error (srv->priv->next_result, error);
+          g_error_free (error);
+          g_simple_async_result_complete (simple);
+          g_object_unref (simple);
+        }
+      else
+        {
+          if (srv->priv->target_iter == srv->priv->targets)
+            srv->priv->iter_error = error;
+          else
+            g_error_free (error);
+
+          /* Try the next target */
+          srv->priv->target_iter = srv->priv->target_iter->next;
+          get_next_async_have_targets (srv);
+        }
+    }
+}
+
+static void
+get_next_async_have_addresses (GNetworkService *srv)
+{
+  GSocketAddress *sockaddr;
+  GSimpleAsyncResult *simple = srv->priv->next_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->priv->addr_iter->data,
+                                        g_srv_target_get_port (srv->priv->target_iter->data));
+
+  srv->priv->addr_iter = srv->priv->addr_iter->next;
+  if (!srv->priv->addr_iter)
+    {
+      g_resolver_free_addresses (srv->priv->addrs);
+      srv->priv->addrs = NULL;
+      srv->priv->target_iter = srv->priv->target_iter->next;
+    }
+
+  srv->priv->next_result = NULL;
+  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_service_connectable_get_next_finish (GSocketConnectable  *connectable,
+                                               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_connectable_reset (GSocketConnectable  *connectable)
+{
+  GNetworkService *srv = G_NETWORK_SERVICE (connectable);
+
+  g_resolver_free_addresses (srv->priv->addrs);
+  srv->priv->addrs = NULL;
+  srv->priv->target_iter = srv->priv->targets;
+  if (srv->priv->iter_error)
+    {
+      g_error_free (srv->priv->iter_error);
+      srv->priv->iter_error = NULL;
+    }
+}
+
+#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 f529e5c..335be2b 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.
  **/
 
 /**
@@ -168,6 +172,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.)
@@ -467,6 +475,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 4d311a9..a7e7cfb 100644
--- a/gio/gsocketaddress.c
+++ b/gio/gsocketaddress.c
@@ -28,6 +28,7 @@
 #include "ginetaddress.h"
 #include "ginetsocketaddress.h"
 #include "gnetworkingprivate.h"
+#include "gsocketconnectable.h"
 #include "glibintl.h"
 #include "gioenumtypes.h"
 
@@ -61,7 +62,15 @@ 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 GSocketAddress *g_socket_address_connectable_get_next   (GSocketConnectable  *connectable,
+								GCancellable        *cancellable,
+								GError             **error);
+static void            g_socket_address_connectable_reset      (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:
@@ -113,6 +122,13 @@ g_socket_address_class_init (GSocketAddressClass *klass)
 }
 
 static void
+g_socket_address_connectable_iface_init (GSocketConnectableIface *connectable_iface)
+{
+  connectable_iface->get_next = g_socket_address_connectable_get_next;
+  connectable_iface->reset    = g_socket_address_connectable_reset;
+}
+
+static void
 g_socket_address_init (GSocketAddress *address)
 {
 
@@ -221,5 +237,32 @@ g_socket_address_new_from_native (gpointer native,
   return NULL;
 }
 
+static GSocketAddress *
+g_socket_address_connectable_get_next (GSocketConnectable  *connectable,
+				       GCancellable        *cancellable,
+				       GError             **error)
+{
+  /* Rather than creating a GSocketAddressPrivate for just 1 bit of
+   * data, we cheat and use GObject data.
+   */
+  if (g_object_get_data (G_OBJECT (connectable), "GSocketConnectable-iterated"))
+    return NULL;
+  else
+    {
+      g_object_set_data (G_OBJECT (connectable),
+			 "GSocketConnectable-iterated",
+			 GINT_TO_POINTER (TRUE));
+      return g_object_ref (connectable);
+    }
+}
+
+static void
+g_socket_address_connectable_reset (GSocketConnectable  *connectable)
+{
+  g_object_set_data (G_OBJECT (connectable),
+		     "GSocketConnectable-iterated",
+		     NULL);
+}
+
 #define __G_SOCKET_ADDRESS_C__
 #include "gioaliasdef.c"
diff --git a/gio/gsocketconnectable.c b/gio/gsocketconnectable.c
new file mode 100644
index 0000000..c72dd7c
--- /dev/null
+++ b/gio/gsocketconnectable.c
@@ -0,0 +1,269 @@
+/* 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 "gsimpleasyncresult.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. The caller can then use
+ * g_socket_connectable_get_next() or
+ * g_socket_connectable_get_next_async() to try out each
+ * #GSocketAddress in turn until it suceeds in connecting to one of
+ * them.
+ **/
+
+static void g_socket_connectable_base_init  (gpointer g_class);
+static void g_socket_connectable_class_init (gpointer g_class,
+					     gpointer class_data);
+
+static void            g_socket_connectable_real_get_next_async  (GSocketConnectable   *connectable,
+								  GCancellable         *cancellable,
+								  GAsyncReadyCallback   callback,
+								  gpointer              user_data);
+static GSocketAddress *g_socket_connectable_real_get_next_finish (GSocketConnectable   *connectable,
+								  GAsyncResult         *result,
+								  GError              **error);
+
+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 */
+	g_socket_connectable_base_init,		/* base_init */
+	NULL,		/* base_finalize */
+	g_socket_connectable_class_init,
+	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;
+}
+
+static void
+g_socket_connectable_class_init (gpointer g_class,
+				 gpointer class_data)
+{
+  GSocketConnectableIface *iface = g_class;
+
+  iface->get_next_async = g_socket_connectable_real_get_next_async;
+  iface->get_next_finish = g_socket_connectable_real_get_next_finish;
+}
+
+static void
+g_socket_connectable_base_init (gpointer g_class)
+{
+}
+
+/**
+ * g_socket_connectable_get_next:
+ * @connectable: a #GSocketConnectable
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @error: a #GError.
+ *
+ * Retrieves the next #GSocketAddress from @connectable. Note that
+ * this may block for some amount of time. (Eg, @connectable may need
+ * to do a DNS lookup before it can return an address.) Use
+ * g_socket_connectable_get_next_async() if you need to avoid
+ * blocking.
+ *
+ * If @connectable is unable to yield any addresses (eg, because of a
+ * DNS error), then the first call to g_socket_connectable_get_next()
+ * will return an appropriate error in * error  However, if the first
+ * call to g_socket_connectable_get_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_connectable_get_next (GSocketConnectable  *connectable,
+			       GCancellable        *cancellable,
+			       GError             **error)
+{
+  GSocketConnectableIface *iface;
+
+  g_return_val_if_fail (G_IS_SOCKET_CONNECTABLE (connectable), NULL);
+
+  iface = G_SOCKET_CONNECTABLE_GET_IFACE (connectable);
+
+  return (* iface->get_next) (connectable, cancellable, error);
+}
+
+/* Default implementation of get_next_async 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_connectable_real_get_next_async (GSocketConnectable  *connectable,
+					  GCancellable        *cancellable,
+					  GAsyncReadyCallback  callback,
+					  gpointer             user_data)
+{
+  GSimpleAsyncResult *result;
+  GSocketAddress *address;
+  GError *error = NULL;
+
+  result = g_simple_async_result_new (G_OBJECT (connectable),
+				      callback, user_data,
+				      g_socket_connectable_real_get_next_async);
+  address = g_socket_connectable_get_next (connectable, cancellable, &error);
+  if (address)
+    g_simple_async_result_set_op_res_gpointer (result, address, g_object_unref);
+  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_connectable_get_next_async:
+ * @connectable: a #GSocketConnectable
+ * @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 @connectable
+ * and then calls @callback, which must call
+ * g_socket_connectable_get_next_finish() to get the result.
+ **/
+void
+g_socket_connectable_get_next_async (GSocketConnectable  *connectable,
+				     GCancellable        *cancellable,
+				     GAsyncReadyCallback  callback,
+				     gpointer             user_data)
+{
+  GSocketConnectableIface *iface;
+
+  g_return_if_fail (G_IS_SOCKET_CONNECTABLE (connectable));
+
+  iface = G_SOCKET_CONNECTABLE_GET_IFACE (connectable);
+
+  (* iface->get_next_async) (connectable, cancellable, callback, user_data);
+}
+
+static GSocketAddress *
+g_socket_connectable_real_get_next_finish (GSocketConnectable  *connectable,
+					   GAsyncResult        *result,
+					   GError             **error)
+{
+  GSimpleAsyncResult *simple;
+  GSocketAddress *sockaddr;
+
+  g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (connectable), g_socket_connectable_real_get_next_async), NULL);
+  simple = G_SIMPLE_ASYNC_RESULT (result);
+
+  sockaddr = g_simple_async_result_get_op_res_gpointer (simple);
+  return sockaddr ? g_object_ref (sockaddr) : NULL;
+}
+
+/**
+ * g_socket_connectable_get_next_finish:
+ * @connectable: a #GSocketConnectable
+ * @result: a #GAsyncResult.
+ * @error: a #GError.
+ *
+ * Retrieves the result of a completed call to
+ * g_socket_connectable_get_next_async(). See
+ * g_socket_connectable_get_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_connectable_get_next_finish (GSocketConnectable  *connectable,
+				      GAsyncResult        *result,
+				      GError             **error)
+{
+  GSocketConnectableIface *iface;
+
+  g_return_val_if_fail (G_IS_SOCKET_CONNECTABLE (connectable), NULL);
+
+  if (G_IS_SIMPLE_ASYNC_RESULT (result))
+    {
+      GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
+
+      if (g_simple_async_result_propagate_error (simple, error))
+	return NULL;
+    }
+
+  iface = G_SOCKET_CONNECTABLE_GET_IFACE (connectable);
+
+  return (* iface->get_next_finish) (connectable, result, error);
+}
+
+/**
+ * g_socket_connectable_reset:
+ * @connectable: a #GSocketConnectable
+ *
+ * Resets @connectable's iterator state, so that the next call to
+ * g_socket_connectable_get_next() or
+ * g_socket_connectable_get_next_async() will return the first
+ * available #GSocketAddress again.
+ *
+ * You can only call this after successfully enumerating all
+ * addresses. Resetting a connectable at any other time may not
+ * necessarily work correctly.
+ **/
+void
+g_socket_connectable_reset (GSocketConnectable *connectable)
+{
+  GSocketConnectableIface *iface;
+
+  g_return_if_fail (G_IS_SOCKET_CONNECTABLE (connectable));
+
+  iface = G_SOCKET_CONNECTABLE_GET_IFACE (connectable);
+
+  if (iface->reset)
+    (* iface->reset) (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..115d6e3
--- /dev/null
+++ b/gio/gsocketconnectable.h
@@ -0,0 +1,94 @@
+/* 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 enumerate #GSocketAddress<!-- -->es.
+ **/
+typedef struct _GSocketConnectableIface GSocketConnectableIface;
+
+/**
+ * GSocketConnectableIface:
+ * @g_iface: The parent interface.
+ * @get_next: Synchronously gets the next #GSocketAddress
+ * @get_next_async: Begins asynchronously getting the next #GSocketAddress
+ * @get_next_finish: Finishes asynchronously getting a #GSocketAddress
+ * @reset: Resets the enumerator
+ *
+ * Provides an interface for enumerating #GSocketAddress<!-- -->es.
+ **/
+struct _GSocketConnectableIface
+{
+  GTypeInterface g_iface;
+
+  /* Virtual Table */
+
+  GSocketAddress * (* get_next)        (GSocketConnectable   *connectable,
+		  			GCancellable         *cancellable,
+		  			GError              **error);
+
+  void             (* get_next_async)  (GSocketConnectable   *connectable,
+		  			GCancellable         *cancellable,
+		  			GAsyncReadyCallback   callback,
+		  			gpointer              user_data);
+  GSocketAddress * (* get_next_finish) (GSocketConnectable   *connectable,
+					GAsyncResult         *result,
+					GError              **error);
+
+  void             (* reset)           (GSocketConnectable   *connectable);
+};
+
+GType           g_socket_connectable_get_type        (void) G_GNUC_CONST;
+
+GSocketAddress *g_socket_connectable_get_next        (GSocketConnectable   *connectable,
+						      GCancellable         *cancellable,
+						      GError              **error);
+
+void            g_socket_connectable_get_next_async  (GSocketConnectable   *connectable,
+						      GCancellable         *cancellable,
+						      GAsyncReadyCallback   callback,
+						      gpointer              user_data);
+GSocketAddress *g_socket_connectable_get_next_finish (GSocketConnectable   *connectable,
+						      GAsyncResult         *result,
+						      GError              **error);
+
+void            g_socket_connectable_reset           (GSocketConnectable   *connectable);
+
+G_END_DECLS
+
+
+#endif /* __G_SOCKET_CONNECTABLE_H__ */
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..4f0b71b 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,121 @@ 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 (GSocketConnectable *connectable)
+{
+  GSocketAddress *sockaddr;
+  GError *error = NULL;
+
+  while ((sockaddr = g_socket_connectable_get_next (connectable, cancellable, &error)))
+    print_connectable_sockaddr (sockaddr, error);
+
+  g_object_unref (connectable);
+  done_lookup ();
+}
+
+static void do_async_connectable (GSocketConnectable *connectable);
+
+static void
+got_next_async (GObject *source, GAsyncResult *result, gpointer user_data)
+{
+  GSocketConnectable *connectable = G_SOCKET_CONNECTABLE (source);
+  GSocketAddress *sockaddr;
+  GError *error = NULL;
+
+  sockaddr = g_socket_connectable_get_next_finish (connectable, result, &error);
+  if (sockaddr || error)
+    print_connectable_sockaddr (sockaddr, error);
+  if (sockaddr)
+    do_async_connectable (connectable);
+  else
+    {
+      g_object_unref (connectable);
+      done_lookup ();
+    }
+}
+
+static void
+do_async_connectable (GSocketConnectable *connectable)
+{
+  g_socket_connectable_get_next_async (connectable, cancellable,
+                                       got_next_async, NULL);
+}
+
+static void
+do_connectable (const char *arg, gboolean synchronous)
+{
+  char **parts;
+  GSocketConnectable *connectable;
+
+  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);
+    }
+
+  if (synchronous)
+    do_sync_connectable (connectable);
+  else
+    do_async_connectable (connectable);
+}
+
 #ifdef G_OS_UNIX
 static int cancel_fds[2];
 
@@ -307,6 +424,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 +442,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 +452,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 +478,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]