summaryrefslogtreecommitdiff
path: root/libpurple/proxy.c
diff options
context:
space:
mode:
authorMike Ruprecht <cmaiku@gmail.com>2016-01-12 20:41:51 -0600
committerMike Ruprecht <cmaiku@gmail.com>2016-01-12 20:41:51 -0600
commit9b9081c3a650bf847c30c8510392fdb09563381c (patch)
treee9bf2a998c8d2a0ced7b02d9065398ff15736e06 /libpurple/proxy.c
parent4882d992319d5c68dc4857aae4b60609ad7c43da (diff)
downloadpidgin-9b9081c3a650bf847c30c8510392fdb09563381c.tar.gz
Port purple_proxy_connect_socks5_account() to use Gio internally
Diffstat (limited to 'libpurple/proxy.c')
-rw-r--r--libpurple/proxy.c155
1 files changed, 126 insertions, 29 deletions
diff --git a/libpurple/proxy.c b/libpurple/proxy.c
index d708297f9a..be4aea41ec 100644
--- a/libpurple/proxy.c
+++ b/libpurple/proxy.c
@@ -2515,34 +2515,113 @@ purple_proxy_connect_udp(void *handle, PurpleAccount *account,
return connect_data;
}
+static void
+socks5_proxy_connect_cb(GObject *source, GAsyncResult *res, gpointer user_data)
+{
+ PurpleProxyConnectData *connect_data = user_data;
+ GIOStream *stream;
+ GError *error = NULL;
+ GSocket *socket;
+
+ stream = g_proxy_connect_finish(G_PROXY(source), res, &error);
+
+ if (stream == NULL) {
+ purple_debug_error("proxy",
+ "Unable to connect to destination host: %s\n",
+ error->message);
+ purple_proxy_connect_data_disconnect(connect_data,
+ "Unable to connecto to destination host.\n");
+ g_clear_error(&error);
+ return;
+ }
+
+ if (!G_IS_SOCKET_CONNECTION(stream)) {
+ purple_debug_error("proxy",
+ "GProxy didn't return a GSocketConnection.\n");
+ purple_proxy_connect_data_disconnect(connect_data,
+ "GProxy didn't return a GSocketConnection.\n");
+ g_object_unref(stream);
+ return;
+ }
+
+ socket = g_socket_connection_get_socket(G_SOCKET_CONNECTION(stream));
+
+ /* Duplicate the file descriptor, and then free the connection.
+ * libpurple's proxy code doesn't keep an object around for the
+ * lifetime of the connection. Therefore, in order to not leak
+ * memory, the GSocketConnection (aka GIOStream here) must be
+ * freed here. In order to avoid the double close/free of the
+ * file descriptor, the file descriptor is duplicated.
+ */
+ connect_data->fd = duplicate_fd(g_socket_get_fd(socket));
+ g_object_unref(stream);
+
+ purple_proxy_connect_data_connected(connect_data);
+}
+
/* This is called when we connect to the SOCKS5 proxy server (through any
* relevant account proxy)
*/
-static void socks5_connected_to_proxy(gpointer data, gint source,
- const gchar *error_message) {
- /* This is the PurpleProxyConnectData for the overall SOCKS5 connection */
- PurpleProxyConnectData *connect_data = data;
+static void
+socks5_connect_to_host_cb(GObject *source, GAsyncResult *res,
+ gpointer user_data)
+{
+ PurpleProxyConnectData *connect_data = user_data;
+ GSocketConnection *conn;
+ GError *error = NULL;
+ GProxy *proxy;
+ PurpleProxyInfo *info;
+ GSocketAddress *addr;
+ GInetSocketAddress *inet_addr;
+ GSocketAddress *proxy_addr;
- purple_debug_error("proxy", "Connect Data is %p\n", connect_data);
+ conn = g_socket_client_connect_to_host_finish(G_SOCKET_CLIENT(source),
+ res, &error);
+ if (conn == NULL) {
+ purple_debug_error("proxy",
+ "Unable to connect to SOCKS5 host: %s\n",
+ error->message);
+ purple_proxy_connect_data_disconnect(connect_data,
+ "Unable to connect to SOCKS5 host.\n");
+ g_clear_error(&error);
+ return;
+ }
- /* Check that the overall SOCKS5 connection wasn't cancelled while we were
- * connecting to it (we don't have a way of associating the process of
- * connecting to the SOCKS5 server to the overall PurpleProxyConnectData)
- */
- if (!PURPLE_PROXY_CONNECT_DATA_IS_VALID(connect_data)) {
- purple_debug_error("proxy", "Data had gone out of scope :(\n");
+ proxy = g_proxy_get_default_for_protocol("socks5");
+ if (proxy == NULL) {
+ purple_debug_error("proxy", "SOCKS5 proxy backend missing.\n");
+ purple_proxy_connect_data_disconnect(connect_data,
+ "SOCKS5 proxy backend missing.\n");
+ g_object_unref(conn);
return;
}
- /* Break the link between the two PurpleProxyConnectDatas */
- connect_data->child = NULL;
+ info = connect_data->gpi;
- if (error_message != NULL) {
- purple_debug_error("proxy", "Unable to connect to SOCKS5 host.\n");
- connect_data->connect_cb(connect_data->data, source, error_message);
+ addr = g_socket_connection_get_remote_address(conn, &error);
+ if (addr == NULL) {
+ purple_debug_error("proxy", "Unable to retrieve SOCKS5 host "
+ "address from connection: %s\n",
+ error->message);
+ purple_proxy_connect_data_disconnect(connect_data,
+ "Unable to retrieve SOCKS5 host address from "
+ "connection");
+ g_object_unref(conn);
+ g_object_unref(proxy);
+ g_clear_error(&error);
return;
}
+ inet_addr = G_INET_SOCKET_ADDRESS(addr);
+
+ proxy_addr = g_proxy_address_new(
+ g_inet_socket_address_get_address(inet_addr),
+ g_inet_socket_address_get_port(inet_addr),
+ "socks5", connect_data->host, connect_data->port,
+ purple_proxy_info_get_username(info),
+ purple_proxy_info_get_password(info));
+ g_object_unref(inet_addr);
+
purple_debug_info("proxy", "Initiating SOCKS5 negotiation.\n");
purple_debug_info("proxy",
@@ -2551,9 +2630,13 @@ static void socks5_connected_to_proxy(gpointer data, gint source,
purple_proxy_info_get_host(connect_data->gpi),
purple_proxy_info_get_port(connect_data->gpi));
- connect_data->fd = source;
-
- s5_canwrite(connect_data, connect_data->fd, PURPLE_INPUT_WRITE);
+ g_proxy_connect_async(proxy, G_IO_STREAM(conn),
+ G_PROXY_ADDRESS(proxy_addr),
+ connect_data->cancellable,
+ socks5_proxy_connect_cb, connect_data);
+ g_object_unref(proxy_addr);
+ g_object_unref(conn);
+ g_object_unref(proxy);
}
/*
@@ -2567,7 +2650,8 @@ purple_proxy_connect_socks5_account(void *handle, PurpleAccount *account,
gpointer data)
{
PurpleProxyConnectData *connect_data;
- PurpleProxyConnectData *account_proxy_conn_data;
+ GProxyResolver *resolver;
+ GSocketClient *client;
g_return_val_if_fail(host != NULL, NULL);
g_return_val_if_fail(port >= 0, NULL);
@@ -2587,19 +2671,32 @@ purple_proxy_connect_socks5_account(void *handle, PurpleAccount *account,
/* If there is an account proxy, use it to connect to the desired SOCKS5
* proxy.
*/
- account_proxy_conn_data = purple_proxy_connect(connect_data->handle,
- connect_data->account,
- purple_proxy_info_get_host(connect_data->gpi),
- purple_proxy_info_get_port(connect_data->gpi),
- socks5_connected_to_proxy, connect_data);
-
- if (account_proxy_conn_data == NULL) {
- purple_debug_error("proxy", "Unable to initiate connection to account proxy.\n");
+ resolver = purple_proxy_get_proxy_resolver(account);
+
+ if (resolver == NULL) {
+ /* purple_proxy_get_proxy_resolver already has debug output */
purple_proxy_connect_data_destroy(connect_data);
return NULL;
}
- connect_data->child = account_proxy_conn_data;
+ connect_data->cancellable = g_cancellable_new();
+
+ client = g_socket_client_new();
+ g_socket_client_set_proxy_resolver(client, resolver);
+ g_object_unref(resolver);
+
+ purple_debug_info("proxy",
+ "Connecting to %s:%d via %s:%d using SOCKS5\n",
+ connect_data->host, connect_data->port,
+ purple_proxy_info_get_host(connect_data->gpi),
+ purple_proxy_info_get_port(connect_data->gpi));
+
+ g_socket_client_connect_to_host_async(client,
+ purple_proxy_info_get_host(connect_data->gpi),
+ purple_proxy_info_get_port(connect_data->gpi),
+ connect_data->cancellable, socks5_connect_to_host_cb,
+ connect_data);
+ g_object_unref(client);
handles = g_slist_prepend(handles, connect_data);