summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Larsson <alexl@redhat.com>2009-05-11 09:01:13 (GMT)
committerAlexander Larsson <alexl@redhat.com>2009-05-11 09:01:13 (GMT)
commitb9553adad9f63fb94eea481dec70ccbbd112ab6f (patch)
tree030451b42184b8fb8ce1c5e15f7d7c819597f639
parentf4d0712cf8bc8225a5c52ceb3679118cbc2e6303 (diff)
downloadgnio-b9553adad9f63fb94eea481dec70ccbbd112ab6f.tar.gz
gnio-b9553adad9f63fb94eea481dec70ccbbd112ab6f.tar.xz
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.
-rw-r--r--gio/gsocketclient.c203
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 *