changeset 27032:4c3b1bb3ba91

Add purple_proxy_connect_udp. Closes #6589. Patch from ccpaging with two modifications by me: * Connections are attempted when a proxy is set (bypassing proxy). A note is logged about this. * Change the type of socket_type to int. committer: Paul Aurich <paul@darkrain42.org>
author Hu Yong <ccpaging@gmail.com>
date Fri, 05 Jun 2009 01:58:22 +0000
parents 2f10136a6d28
children 2ab648c328cc
files ChangeLog.API libpurple/proxy.c libpurple/proxy.h
diffstat 3 files changed, 166 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog.API	Fri Jun 05 01:46:29 2009 +0000
+++ b/ChangeLog.API	Fri Jun 05 01:58:22 2009 +0000
@@ -42,6 +42,7 @@
 		* purple_network_set_turn_server
 		* purple_network_get_stun_ip
 		* purple_network_get_turn_ip
+		* purple_proxy_connect_udp
 		* purple_prpl_get_media_caps
 		* purple_prpl_got_account_actions
 		* purple_prpl_initiate_media
--- a/libpurple/proxy.c	Fri Jun 05 01:46:29 2009 +0000
+++ b/libpurple/proxy.c	Fri Jun 05 01:58:22 2009 +0000
@@ -47,6 +47,7 @@
 	gchar *host;
 	int port;
 	int fd;
+	int socket_type;
 	guint inpa;
 	PurpleProxyInfo *gpi;
 	PurpleDnsQueryData *query_data;
@@ -676,6 +677,68 @@
 }
 
 static void
+proxy_connect_udp_none(PurpleProxyConnectData *connect_data, struct sockaddr *addr, socklen_t addrlen)
+{
+	int flags;
+
+	purple_debug_info("proxy", "UDP Connecting to %s:%d with no proxy\n",
+			connect_data->host, connect_data->port);
+
+	connect_data->fd = socket(addr->sa_family, SOCK_DGRAM, 0);
+	if (connect_data->fd < 0)
+	{
+		purple_proxy_connect_data_disconnect_formatted(connect_data,
+				_("Unable to create socket:\n%s"), g_strerror(errno));
+		return;
+	}
+
+	flags = fcntl(connect_data->fd, F_GETFL);
+	fcntl(connect_data->fd, F_SETFL, flags | O_NONBLOCK);
+#ifndef _WIN32
+	fcntl(connect_data->fd, F_SETFD, FD_CLOEXEC);
+#endif
+
+	if (connect(connect_data->fd, addr, addrlen) != 0)
+	{
+		if ((errno == EINPROGRESS) || (errno == EINTR))
+		{
+			purple_debug_info("proxy", "UDP Connection in progress\n");
+			connect_data->inpa = purple_input_add(connect_data->fd,
+					PURPLE_INPUT_WRITE, socket_ready_cb, connect_data);
+		}
+		else
+		{
+			purple_proxy_connect_data_disconnect(connect_data, g_strerror(errno));
+		}
+	}
+	else
+	{
+		/*
+		 * The connection happened IMMEDIATELY... strange, but whatever.
+		 */
+		int error = ETIMEDOUT;
+		int ret;
+
+		purple_debug_info("proxy", "UDP Connected immediately.\n");
+
+		ret = purple_input_get_error(connect_data->fd, &error);
+		if ((ret != 0) || (error != 0))
+		{
+			if (ret != 0)
+				error = errno;
+			purple_proxy_connect_data_disconnect(connect_data, g_strerror(error));
+			return;
+		}
+
+		/*
+		 * We want to call the "connected" callback eventually, but we
+		 * don't want to call it before we return, just in case.
+		 */
+		purple_timeout_add(10, clean_connect, connect_data);
+	}
+}
+
+static void
 proxy_connect_none(PurpleProxyConnectData *connect_data, struct sockaddr *addr, socklen_t addrlen)
 {
 	int flags;
@@ -2042,6 +2105,12 @@
 #endif
 	purple_debug_info("proxy", "Attempting connection to %s\n", ipaddr);
 
+	if (connect_data->socket_type == SOCK_DGRAM) {
+		proxy_connect_udp_none(connect_data, addr, addrlen);
+		g_free(addr);
+		return;
+	}
+
 	switch (purple_proxy_info_get_type(connect_data->gpi)) {
 		case PURPLE_PROXY_NONE:
 			proxy_connect_none(connect_data, addr, addrlen);
@@ -2193,6 +2262,7 @@
 
 	connect_data = g_new0(PurpleProxyConnectData, 1);
 	connect_data->fd = -1;
+	connect_data->socket_type = SOCK_STREAM;
 	connect_data->handle = handle;
 	connect_data->connect_cb = connect_cb;
 	connect_data->data = data;
@@ -2243,6 +2313,71 @@
 	return connect_data;
 }
 
+PurpleProxyConnectData *
+purple_proxy_connect_udp(void *handle, PurpleAccount *account,
+				   const char *host, int port,
+				   PurpleProxyConnectFunction connect_cb, gpointer data)
+{
+	const char *connecthost = host;
+	int connectport = port;
+	PurpleProxyConnectData *connect_data;
+
+	g_return_val_if_fail(host       != NULL, NULL);
+	g_return_val_if_fail(port       >  0,    NULL);
+	g_return_val_if_fail(connect_cb != NULL, NULL);
+
+	connect_data = g_new0(PurpleProxyConnectData, 1);
+	connect_data->fd = -1;
+	connect_data->socket_type = SOCK_DGRAM;
+	connect_data->handle = handle;
+	connect_data->connect_cb = connect_cb;
+	connect_data->data = data;
+	connect_data->host = g_strdup(host);
+	connect_data->port = port;
+	connect_data->gpi = purple_proxy_get_setup(account);
+
+	if ((purple_proxy_info_get_type(connect_data->gpi) != PURPLE_PROXY_NONE) &&
+		(purple_proxy_info_get_host(connect_data->gpi) == NULL ||
+		 purple_proxy_info_get_port(connect_data->gpi) <= 0)) {
+
+		purple_notify_error(NULL, NULL, _("Invalid proxy settings"), _("Either the host name or port number specified for your given proxy type is invalid."));
+		purple_proxy_connect_data_destroy(connect_data);
+		return NULL;
+	}
+
+	switch (purple_proxy_info_get_type(connect_data->gpi))
+	{
+		case PURPLE_PROXY_NONE:
+			break;
+
+		case PURPLE_PROXY_HTTP:
+		case PURPLE_PROXY_SOCKS4:
+		case PURPLE_PROXY_SOCKS5:
+		case PURPLE_PROXY_USE_ENVVAR:
+			purple_debug_info("proxy", "Ignoring Proxy type (%d) for UDP.\n",
+			                  purple_proxy_info_get_type(connect_data->gpi));
+			break;
+
+		default:
+			purple_debug_error("proxy", "Invalid Proxy type (%d) specified.\n",
+			                   purple_proxy_info_get_type(connect_data->gpi));
+			purple_proxy_connect_data_destroy(connect_data);
+			return NULL;
+	}
+
+	connect_data->query_data = purple_dnsquery_a(connecthost,
+			connectport, connection_host_resolved, connect_data);
+	if (connect_data->query_data == NULL)
+	{
+		purple_proxy_connect_data_destroy(connect_data);
+		return NULL;
+	}
+
+	handles = g_slist_prepend(handles, connect_data);
+
+	return connect_data;
+}
+
 /*
  * Combine some of this code with purple_proxy_connect()
  */
@@ -2260,6 +2395,7 @@
 
 	connect_data = g_new0(PurpleProxyConnectData, 1);
 	connect_data->fd = -1;
+	connect_data->socket_type = SOCK_STREAM;
 	connect_data->handle = handle;
 	connect_data->connect_cb = connect_cb;
 	connect_data->data = data;
--- a/libpurple/proxy.h	Fri Jun 05 01:46:29 2009 +0000
+++ b/libpurple/proxy.h	Fri Jun 05 01:58:22 2009 +0000
@@ -257,6 +257,35 @@
 			PurpleProxyConnectFunction connect_cb, gpointer data);
 
 /**
+ * Makes a connection to the specified host and port.  Note that this
+ * function name can be misleading--although it is called "proxy
+ * connect," it is used for establishing any outgoing UDP connection,
+ * whether through a proxy or not.
+ *
+ * @param handle     A handle that should be associated with this
+ *                   connection attempt.  The handle can be used
+ *                   to cancel the connection attempt using the
+ *                   purple_proxy_connect_cancel_with_handle()
+ *                   function.
+ * @param account    The account making the connection.
+ * @param host       The destination host.
+ * @param port       The destination port.
+ * @param connect_cb The function to call when the connection is
+ *                   established.  If the connection failed then
+ *                   fd will be -1 and error message will be set
+ *                   to something descriptive (hopefully).
+ * @param data       User-defined data.
+ *
+ * @return NULL if there was an error, or a reference to an
+ *         opaque data structure that can be used to cancel
+ *         the pending connection, if needed.
+ */
+PurpleProxyConnectData *purple_proxy_connect_udp(void *handle,
+			PurpleAccount *account,
+			const char *host, int port,
+			PurpleProxyConnectFunction connect_cb, gpointer data);
+
+/**
  * Makes a connection through a SOCKS5 proxy.
  *
  * @param handle     A handle that should be associated with this