[gnio/connection-factory] Add back support for host:port style hostnames



commit b9553adad9f63fb94eea481dec70ccbbd112ab6f
Author: Alexander Larsson <alexl redhat com>
Date:   Mon May 11 11:01:13 2009 +0200

    Add back support for host:port style hostnames
    
    g_socket_client_connect_to_host[_async] now supports passing
    port in the host string.
    
    This adds a local g_network_address_new_from_string() which should
    maybe be added to gnetworkaddress.c.
---
 gio/gsocketclient.c |  203 +++++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 187 insertions(+), 16 deletions(-)

diff --git a/gio/gsocketclient.c b/gio/gsocketclient.c
index 5c0a94c..a5060dd 100644
--- a/gio/gsocketclient.c
+++ b/gio/gsocketclient.c
@@ -23,6 +23,14 @@
 #include "config.h"
 #include "gsocketclient.h"
 
+#include <stdlib.h>
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_WINSOCK2_H
+#include <winsock2.h>
+#endif
+
 #include <gio/gioenumtypes.h>
 #include <gio/gsocketaddressenumerator.h>
 #include <gio/gsocketconnectable.h>
@@ -275,11 +283,149 @@ g_socket_client_connect (GSocketClient       *client,
   return connection;
 }
 
+static GSocketConnectable *
+g_network_address_new_from_string (const char *host_and_port,
+				   int default_port,
+				   GError **error)
+{
+  GSocketConnectable *connectable;
+  const gchar *port;
+  guint16 portnum;
+  gchar *name;
+
+  g_return_val_if_fail (host_and_port != NULL, NULL);
+
+  port = NULL;
+  if (host_and_port[0] == '[')
+    /* escaped host part (to allow, eg. "[2001:db8::1]:888") */
+    {
+      const gchar *end;
+
+      end = strchr (host_and_port, ']');
+
+      if (end == NULL)
+        {
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                       _("Hostname '%s' contains '[' but not ']'"), host_and_port);
+          return NULL;
+        }
+
+      if (end[1] == '\0')
+	port = NULL;
+      else if (end[1] == ':')
+	port = &end[2];
+      else
+        {
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                       "The ']' character (in hostname '%s') must come at the"
+                       " end or be immediately followed by ':' and a port",
+                       host_and_port);
+          return NULL;
+        }
+
+      name = g_strndup (host_and_port + 1, end - host_and_port - 1);
+    }
+
+  else if ((port = strchr (host_and_port, ':')))
+    /* string has a ':' in it */
+    {
+      /* skip ':' */
+      port++;
+
+      if (strchr (port, ':'))
+        /* more than one ':' in string */
+        {
+          /* this is actually an unescaped IPv6 address */
+          name = g_strdup (host_and_port);
+          port = NULL;
+        }
+      else
+        name = g_strndup (host_and_port, port - host_and_port - 1);
+    }
+
+  else
+    /* plain hostname, no port */
+    name = g_strdup (host_and_port);
+
+  if (port != NULL)
+    {
+      if (port[0] == '\0')
+	{
+	  g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+		       "If a ':' character is given, it must be followed by a "
+		       "port (in hostname '%s').", host_and_port);
+	  g_free (name);
+	  return NULL;
+	}
+
+      else if ('0' <= port[0] && port[0] <= '9')
+	{
+	  char *end;
+	  long value;
+
+	  value = strtol (port, &end, 10);
+	  if (*end != '\0' || value <= 0 || value > G_MAXUINT16)
+	    {
+	      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+			   "Invalid numeric port '%s' specified in hostname '%s'",
+			   port, host_and_port);
+	      g_free (name);
+	      return NULL;
+	    }
+
+	  portnum = value;
+	}
+
+      else
+	{
+	  struct servent *entry;
+
+	  entry = getservbyname (port, "tcp");
+	  if (entry == NULL)
+	    {
+	      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+			   "Unknown service '%s' specified in hostname '%s'",
+			   port, host_and_port);
+#ifdef HAVE_ENDSERVENT
+	      endservent ();
+#endif
+	      g_free (name);
+	      return NULL;
+	    }
+
+	  portnum = g_ntohs (entry->s_port);
+
+#ifdef HAVE_ENDSERVENT
+	  endservent ();
+#endif
+	}
+    }
+  else
+    {
+      /* No port in host_and_port */
+
+      if (default_port <= 0 || default_port > G_MAXUINT16)
+	{
+	  g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+		       "Invalid numeric default port '%d' specified",
+		       default_port);
+	  g_free (name);
+	  return NULL;
+	}
+      portnum = default_port;
+    }
+
+  connectable = g_network_address_new (name, portnum);
+  g_free (name);
+
+  return connectable;
+}
+
 /**
  * g_socket_client_connect_to_host:
  * @client: a #GTcpClient
- * @hostname: the name of the host to connect to
- * @port: the port to connect to
+ * @host_and_port: the name and optionally port of the host to connect to
+ * @default_port: the default port to connect to
  * @cancellable: a #GCancellable, or %NULL
  * @error: a pointer to a #GError, or %NULL
  * @returns: a #GSocketConnection if successful, or %NULL on error
@@ -288,10 +434,20 @@ g_socket_client_connect (GSocketClient       *client,
  *
  * Attempts to create a TCP connection to the named host.
  *
- * @host may be in any of a number of recognised formats: an IPv6
+ * @host_and_port may be in any of a number of recognised formats: an IPv6
  * address, an IPv4 address, or a domain name (in which case a DNS
- * lookup is performed).
+ * lookup is performed).  Quoting with [] is supported for all address
+ * types.  A port override may be specified in the usual way with a
+ * colon.  Ports may be given as decimal numbers or symbolic names (in
+ * which case an /etc/services lookup is performed).
  *
+ * If no port override is given in @host_and_port then @default_port will be
+ * used as the port number to connect to.
+ *
+ * In general, @host_and_port is expected to be provided by the user (allowing
+ * them to give the hostname, and a port overide if necessary) and
+ * @default_port is expected to be provided by the application.
+
  * In the case that an IP address is given, a single connection
  * attempt is made.  In the case that a name is given, multiple
  * connection attempts may be made, in turn and according to the
@@ -307,15 +463,18 @@ g_socket_client_connect (GSocketClient       *client,
  **/
 GSocketConnection *
 g_socket_client_connect_to_host (GSocketClient        *client,
-				 const char           *hostname,
-				 int                   port,
+				 const char           *host_and_port,
+				 int                   default_port,
 				 GCancellable         *cancellable,
 				 GError              **error)
 {
   GSocketConnectable *connectable;
   GSocketConnection *connection;
 
-  connectable = g_network_address_new (hostname, port);
+  connectable = g_network_address_new_from_string (host_and_port, default_port, error);
+  if (connectable == NULL)
+    return NULL;
+
   connection = g_socket_client_connect (client, connectable,
 					cancellable, error);
   g_object_unref (connectable);
@@ -524,8 +683,8 @@ g_socket_client_connect_async (GSocketClient       *client,
 /**
  * g_socket_client_connect_to_host_async:
  * @client: a #GTcpClient
- * @hostname: the name of the host to connect to
- * @port: the port to connect to
+ * @host_and_port: the name and optionally the port of the host to connect to
+ * @default_port: the default port to connect to
  * @cancellable: a #GCancellable, or %NULL
  * @callback: a #GAsyncReadyCallback
  * @user_data: user data for the callback
@@ -534,19 +693,31 @@ g_socket_client_connect_async (GSocketClient       *client,
  **/
 void
 g_socket_client_connect_to_host_async (GSocketClient        *client,
-				       const char           *hostname,
-				       int                   port,
+				       const char           *host_and_port,
+				       int                   default_port,
 				       GCancellable         *cancellable,
 				       GAsyncReadyCallback   callback,
 				       gpointer              user_data)
 {
   GSocketConnectable *connectable;
+  GError *error;
 
-  connectable = g_network_address_new (hostname, port);
-  g_socket_client_connect_async (client,
-				 connectable, cancellable,
-				 callback, user_data);
-  g_object_unref (connectable);
+  error = NULL;
+  connectable = g_network_address_new_from_string (host_and_port, default_port,
+						   &error);
+  if (connectable == NULL)
+    {
+      g_simple_async_report_gerror_in_idle (G_OBJECT (client),
+					    callback, user_data, error);
+      g_error_free (error);
+    }
+  else
+    {
+      g_socket_client_connect_async (client,
+				     connectable, cancellable,
+				     callback, user_data);
+      g_object_unref (connectable);
+    }
 }
 
 GSocketConnection *



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