Hooked proxy enumeration into GSocketClient

This functionnallity can be disabled using property enable-proxy. It
enumerates addresses using GSocketConnectable::proxy_enumerate() instead of
enumerate(). When the returned address is of type GProxyAddress (a type
based on GInetSocketAddress), it gets the proxy protocol handler using
g_proxy_get_default_for_protocol() and call connect() on it.

Reviewed-by: Dan Winship <danw@gnome.org>
This commit is contained in:
Nicolas Dufresne 2010-08-10 16:48:45 -04:00
parent ee3dbf747e
commit a6c3820f46
4 changed files with 283 additions and 33 deletions

View File

@ -1798,6 +1798,8 @@ g_socket_client_get_local_address
g_socket_client_get_protocol
g_socket_client_get_socket_type
g_socket_client_get_timeout
g_socket_client_get_enable_proxy
g_socket_client_set_enable_proxy
<SUBSECTION Standard>
GSocketClientClass
G_IS_SOCKET_CLIENT

View File

@ -1359,12 +1359,14 @@ g_socket_client_get_local_address
g_socket_client_get_protocol
g_socket_client_get_socket_type
g_socket_client_get_timeout
g_socket_client_get_enable_proxy
g_socket_client_new
g_socket_client_set_family
g_socket_client_set_local_address
g_socket_client_set_protocol
g_socket_client_set_socket_type
g_socket_client_set_timeout
g_socket_client_set_enable_proxy
#endif
#endif

View File

@ -26,18 +26,24 @@
#include "gsocketclient.h"
#include <stdlib.h>
#include <string.h>
#include <gio/gioenumtypes.h>
#include <gio/gsocketaddressenumerator.h>
#include <gio/gsocketconnectable.h>
#include <gio/gsocketconnection.h>
#include <gio/gproxyaddressenumerator.h>
#include <gio/gproxyaddress.h>
#include <gio/gproxyconnection.h>
#include <gio/gsimpleasyncresult.h>
#include <gio/gcancellable.h>
#include <gio/gioerror.h>
#include <gio/gsocket.h>
#include <gio/gnetworkaddress.h>
#include <gio/gnetworkservice.h>
#include <gio/gproxy.h>
#include <gio/gsocketaddress.h>
#include <gio/gtcpconnection.h>
#include "glibintl.h"
@ -71,7 +77,8 @@ enum
PROP_TYPE,
PROP_PROTOCOL,
PROP_LOCAL_ADDRESS,
PROP_TIMEOUT
PROP_TIMEOUT,
PROP_ENABLE_PROXY,
};
struct _GSocketClientPrivate
@ -81,6 +88,7 @@ struct _GSocketClientPrivate
GSocketProtocol protocol;
GSocketAddress *local_address;
guint timeout;
gboolean enable_proxy;
};
static GSocket *
@ -123,6 +131,15 @@ create_socket (GSocketClient *client,
return socket;
}
gboolean
can_use_proxy (GSocketClient *client)
{
GSocketClientPrivate *priv = client->priv;
return priv->enable_proxy
&& priv->type == G_SOCKET_TYPE_STREAM;
}
static void
g_socket_client_init (GSocketClient *client)
{
@ -190,6 +207,10 @@ g_socket_client_get_property (GObject *object,
g_value_set_uint (value, client->priv->timeout);
break;
case PROP_ENABLE_PROXY:
g_value_set_boolean (value, client->priv->enable_proxy);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
@ -225,6 +246,10 @@ g_socket_client_set_property (GObject *object,
g_socket_client_set_timeout (client, g_value_get_uint (value));
break;
case PROP_ENABLE_PROXY:
g_socket_client_set_enable_proxy (client, g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
@ -399,7 +424,7 @@ g_socket_client_set_local_address (GSocketClient *client,
GSocketAddress *address)
{
if (address)
g_object_ref (address);
g_object_ref (address);
if (client->priv->local_address)
{
@ -453,6 +478,46 @@ g_socket_client_set_timeout (GSocketClient *client,
g_object_notify (G_OBJECT (client), "timeout");
}
/**
* g_socket_client_get_enable_proxy:
* @client: a #GSocketClient.
*
* Gets the proxy enable state; see g_socket_client_set_enable_proxy()
*
* Returns: whether proxying is enabled
*
* Since: 2.26
*/
gboolean
g_socket_client_get_enable_proxy (GSocketClient *client)
{
return client->priv->enable_proxy;
}
/**
* g_socket_client_set_enable_proxy:
* @client: a #GSocketClient.
* @enable: whether to enable proxies
*
* Sets whether or not @client attempts to make connections via a
* proxy server. When enabled (the default), #GSocketClient will use a
* #GProxyResolver to determine if a proxy protocol such as SOCKS is
* needed, and automatically do the necessary proxy negotiation.
*
* Since: 2.26
*/
void
g_socket_client_set_enable_proxy (GSocketClient *client,
gboolean enable)
{
enable = !!enable;
if (client->priv->enable_proxy == enable)
return;
client->priv->enable_proxy = enable;
g_object_notify (G_OBJECT (client), "enable-proxy");
}
static void
g_socket_client_class_init (GSocketClientClass *class)
{
@ -512,6 +577,15 @@ g_socket_client_class_init (GSocketClientClass *class)
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_ENABLE_PROXY,
g_param_spec_boolean ("enable-proxy",
P_("Enable proxy"),
P_("Enable proxy support"),
TRUE,
G_PARAM_CONSTRUCT |
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
}
/**
@ -551,14 +625,19 @@ g_socket_client_connect (GSocketClient *client,
GError **error)
{
GSocketConnection *connection = NULL;
GSocketAddressEnumerator *enumerator;
GSocketAddressEnumerator *enumerator = NULL;
GError *last_error, *tmp_error;
last_error = NULL;
enumerator = g_socket_connectable_enumerate (connectable);
if (can_use_proxy (client))
enumerator = g_socket_connectable_proxy_enumerate (connectable);
else
enumerator = g_socket_connectable_enumerate (connectable);
while (connection == NULL)
{
GSocketAddress *address;
GSocketAddress *address = NULL;
GSocket *socket;
if (g_cancellable_is_cancelled (cancellable))
@ -570,7 +649,8 @@ g_socket_client_connect (GSocketClient *client,
tmp_error = NULL;
address = g_socket_address_enumerator_next (enumerator, cancellable,
&tmp_error);
&tmp_error);
if (address == NULL)
{
if (tmp_error)
@ -600,6 +680,71 @@ g_socket_client_connect (GSocketClient *client,
g_object_unref (socket);
}
if (connection &&
G_IS_PROXY_ADDRESS (address) &&
client->priv->enable_proxy)
{
GProxyAddress *proxy_addr = G_PROXY_ADDRESS (address);
const gchar *protocol;
GProxy *proxy;
protocol = g_proxy_address_get_protocol (proxy_addr);
proxy = g_proxy_get_default_for_protocol (protocol);
/* The connection should not be anything else then TCP Connection,
* but let's put a safety guard in case
*/
if (!G_IS_TCP_CONNECTION (connection))
{
g_critical ("Trying to proxy over non-TCP connection, this is "
"most likely a bug in GLib IO library.");
g_set_error_literal (&last_error,
G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
_("Trying to proxy over non-TCP connection is not supported."));
g_object_unref (connection);
connection = NULL;
}
else if (proxy)
{
GIOStream *io_stream;
GTcpConnection *old_connection = G_TCP_CONNECTION (connection);
io_stream = g_proxy_connect (proxy,
G_IO_STREAM (old_connection),
proxy_addr,
cancellable,
&last_error);
if (io_stream)
{
if (G_IS_SOCKET_CONNECTION (io_stream))
connection = G_SOCKET_CONNECTION (g_object_ref (io_stream));
else
connection = _g_proxy_connection_new (old_connection,
io_stream);
g_object_unref (io_stream);
}
else
{
connection = NULL;
}
g_object_unref (old_connection);
g_object_unref (proxy);
}
else
{
g_set_error (&last_error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
_("Proxy protocol '%s' is not supported."),
protocol);
g_object_unref (connection);
connection = NULL;
}
}
g_object_unref (address);
}
g_object_unref (enumerator);
@ -720,7 +865,9 @@ typedef struct
GSocketClient *client;
GSocketAddressEnumerator *enumerator;
GProxyAddress *proxy_addr;
GSocket *current_socket;
GSocketConnection *connection;
GError *last_error;
} GSocketClientAsyncConnectData;
@ -728,8 +875,6 @@ typedef struct
static void
g_socket_client_async_connect_complete (GSocketClientAsyncConnectData *data)
{
GSocketConnection *connection;
if (data->last_error)
{
g_simple_async_result_set_from_error (data->result, data->last_error);
@ -737,14 +882,10 @@ g_socket_client_async_connect_complete (GSocketClientAsyncConnectData *data)
}
else
{
g_assert (data->current_socket);
g_assert (data->connection);
g_socket_set_blocking (data->current_socket, TRUE);
connection = g_socket_connection_factory_create_connection (data->current_socket);
g_object_unref (data->current_socket);
g_simple_async_result_set_op_res_gpointer (data->result,
connection,
data->connection,
g_object_unref);
}
@ -753,6 +894,10 @@ g_socket_client_async_connect_complete (GSocketClientAsyncConnectData *data)
g_object_unref (data->enumerator);
if (data->cancellable)
g_object_unref (data->cancellable);
if (data->current_socket)
g_object_unref (data->current_socket);
if (data->proxy_addr)
g_object_unref (data->proxy_addr);
g_slice_free (GSocketClientAsyncConnectData, data);
}
@ -770,6 +915,97 @@ set_last_error (GSocketClientAsyncConnectData *data,
data->last_error = error;
}
static void
enumerator_next_async (GSocketClientAsyncConnectData *data)
{
g_socket_address_enumerator_next_async (data->enumerator,
data->cancellable,
g_socket_client_enumerator_callback,
data);
}
static void
g_socket_client_proxy_connect_callback (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
GSocketClientAsyncConnectData *data = user_data;
GIOStream *io_stream;
GTcpConnection *old_connection = G_TCP_CONNECTION (data->connection);
io_stream = g_proxy_connect_finish (G_PROXY (object),
result,
&data->last_error);
if (io_stream)
{
if (G_IS_SOCKET_CONNECTION (io_stream))
data->connection = G_SOCKET_CONNECTION (g_object_ref (io_stream));
else
data->connection = _g_proxy_connection_new (old_connection,
io_stream);
g_object_unref (io_stream);
}
else
{
data->connection = NULL;
}
g_object_unref (old_connection);
g_socket_client_async_connect_complete (data);
}
static void
g_socket_client_proxy_connect (GSocketClientAsyncConnectData *data)
{
GProxy *proxy;
const gchar *protocol = g_proxy_address_get_protocol (data->proxy_addr);
proxy = g_proxy_get_default_for_protocol (protocol);
/* The connection should not be anything else then TCP Connection,
* but let's put a safety guard in case
*/
if (!G_IS_TCP_CONNECTION (data->connection))
{
g_critical ("Trying to proxy over non-TCP connection, this is "
"most likely a bug in GLib IO library.");
g_set_error_literal (&data->last_error,
G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
_("Trying to proxy over non-TCP connection is not supported."));
g_object_unref (data->connection);
data->connection = NULL;
enumerator_next_async (data);
}
else if (proxy)
{
g_proxy_connect_async (proxy,
G_IO_STREAM (data->connection),
data->proxy_addr,
data->cancellable,
g_socket_client_proxy_connect_callback,
data);
g_object_unref (proxy);
}
else
{
g_clear_error (&data->last_error);
g_set_error (&data->last_error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
_("Proxy protocol '%s' is not supported."),
protocol);
g_object_unref (data->connection);
data->connection = NULL;
enumerator_next_async (data);
}
}
static gboolean
g_socket_client_socket_callback (GSocket *socket,
GIOCondition condition,
@ -796,16 +1032,23 @@ g_socket_client_socket_callback (GSocket *socket,
data->current_socket = NULL;
/* try next one */
g_socket_address_enumerator_next_async (data->enumerator,
data->cancellable,
g_socket_client_enumerator_callback,
data);
enumerator_next_async (data);
return FALSE;
}
}
g_socket_client_async_connect_complete (data);
g_socket_set_blocking (data->current_socket, TRUE);
data->connection =
g_socket_connection_factory_create_connection (data->current_socket);
g_object_unref (data->current_socket);
data->current_socket = NULL;
if (data->proxy_addr)
g_socket_client_proxy_connect (data);
else
g_socket_client_async_connect_complete (data);
return FALSE;
}
@ -816,7 +1059,7 @@ g_socket_client_enumerator_callback (GObject *object,
gpointer user_data)
{
GSocketClientAsyncConnectData *data = user_data;
GSocketAddress *address;
GSocketAddress *address = NULL;
GSocket *socket;
GError *tmp_error = NULL;
@ -843,6 +1086,10 @@ g_socket_client_enumerator_callback (GObject *object,
return;
}
if (G_IS_PROXY_ADDRESS (address) &&
data->client->priv->enable_proxy)
data->proxy_addr = g_object_ref (G_PROXY_ADDRESS (address));
g_clear_error (&data->last_error);
socket = create_socket (data->client, address, &data->last_error);
@ -880,13 +1127,10 @@ g_socket_client_enumerator_callback (GObject *object,
data->last_error = tmp_error;
g_object_unref (socket);
}
g_object_unref (address);
}
g_socket_address_enumerator_next_async (data->enumerator,
data->cancellable,
g_socket_client_enumerator_callback,
data);
g_object_unref (address);
enumerator_next_async (data);
}
/**
@ -916,7 +1160,7 @@ g_socket_client_connect_async (GSocketClient *client,
g_return_if_fail (G_IS_SOCKET_CLIENT (client));
data = g_slice_new (GSocketClientAsyncConnectData);
data = g_slice_new0 (GSocketClientAsyncConnectData);
data->result = g_simple_async_result_new (G_OBJECT (client),
callback, user_data,
@ -924,14 +1168,13 @@ g_socket_client_connect_async (GSocketClient *client,
data->client = client;
if (cancellable)
data->cancellable = g_object_ref (cancellable);
else
data->cancellable = NULL;
data->last_error = NULL;
data->enumerator = g_socket_connectable_enumerate (connectable);
g_socket_address_enumerator_next_async (data->enumerator, cancellable,
g_socket_client_enumerator_callback,
data);
if (can_use_proxy (client))
data->enumerator = g_socket_connectable_proxy_enumerate (connectable);
else
data->enumerator = g_socket_connectable_enumerate (connectable);
enumerator_next_async (data);
}
/**

View File

@ -85,6 +85,9 @@ void g_socket_client_set_local_address (GSocket
guint g_socket_client_get_timeout (GSocketClient *client);
void g_socket_client_set_timeout (GSocketClient *client,
guint timeout);
gboolean g_socket_client_get_enable_proxy (GSocketClient *client);
void g_socket_client_set_enable_proxy (GSocketClient *client,
gboolean enable);
GSocketConnection * g_socket_client_connect (GSocketClient *client,
GSocketConnectable *connectable,