cpw.malu.xmpp.jingle_ft: 107e704a: Refactor out socks5-listening code to it...

malu at pidgin.im malu at pidgin.im
Wed May 5 16:20:49 EDT 2010


-----------------------------------------------------------------
Revision: 107e704a6ea54af028832ea8a416128b4fcf206e
Ancestor: 2075a801884dd95a558f1d8cacc9231dec200833
Author: malu at pidgin.im
Date: 2010-05-05T20:15:50
Branch: im.pidgin.cpw.malu.xmpp.jingle_ft
URL: http://d.pidgin.im/viewmtn/revision/info/107e704a6ea54af028832ea8a416128b4fcf206e

Added files:
        libpurple/protocols/jabber/socks5.c
        libpurple/protocols/jabber/socks5.h
Modified files:
        libpurple/protocols/jabber/Makefile.am
        libpurple/protocols/jabber/Makefile.mingw
        libpurple/protocols/jabber/jingle/s5b.c

ChangeLog: 

Refactor out socks5-listening code to its own module.
TODO: update si.c to use this too

-------------- next part --------------
============================================================
--- libpurple/protocols/jabber/socks5.c	66f50e5ae1872036c7c6f695ddad418a5f20aa14
+++ libpurple/protocols/jabber/socks5.c	66f50e5ae1872036c7c6f695ddad418a5f20aa14
@@ -0,0 +1,429 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301,  USA
+ */
+
+#include "socks5.h"
+#include "debug.h"
+#include "jutil.h"
+#include "network.h"
+
+typedef struct {
+	JabberSocks5ConnectCallback *connect_cb;
+	JabberSocks5ErrorCallback *error_cb;
+	gchar *dstaddr;
+	int watcher;
+	gint local_fd;
+	gsize rxlen;
+	gsize rxmaxlen;
+	char *rxqueue;
+	gpointer user_data;
+} JabberSocks5ListenDataPriv;
+
+
+static void
+jabber_socks5_send_read_again_resp_cb(gpointer data, gint source,
+	PurpleInputCondition cond)
+{
+	JabberSocks5ListenDataPriv *priv = (JabberSocks5ListenDataPriv *) data;
+	int len;
+
+	purple_debug_info("jabber-socks5", "in jabber_socks5_send_read_again_cb\n");
+	len = write(source, priv->rxqueue + priv->rxlen, priv->rxmaxlen - priv->rxlen);
+
+	if (len < 0 && errno == EAGAIN)
+		return;
+	else if (len < 0) {
+		purple_input_remove(priv->watcher);
+		priv->watcher = 0;
+		g_free(priv->rxqueue);
+		priv->rxqueue = NULL;
+		close(source);
+		if (priv->error_cb)
+			priv->error_cb(priv->user_data);
+		return;
+	}
+	priv->rxlen += len;
+
+	if (priv->rxlen < priv->rxmaxlen)
+		return;
+
+	purple_input_remove(priv->watcher);
+	priv->watcher = 0;
+	g_free(priv->rxqueue);
+	priv->rxqueue = NULL;
+
+	/* Before actually starting sending the file, we need to wait until the
+	 * recipient sends the IQ result with <candidate-used/>
+	 */
+	purple_debug_info("jabber-socks5", "SOCKS5 connection negotiation completed. "
+					  "Waiting for IQ result to start transfer.\n");
+
+	if (priv->connect_cb)
+		priv->connect_cb(source, priv->user_data);
+}
+
+
+static void
+jabber_socks5_send_read_again_cb(gpointer data, gint source,
+	PurpleInputCondition cond)
+{
+	JabberSocks5ListenDataPriv *priv = (JabberSocks5ListenDataPriv *) data;
+	gchar *dstaddr = priv->dstaddr;
+	const gchar *host = purple_network_get_my_ip(-1);
+	char buffer[42]; /* 40 for DST.ADDR + 2 bytes for port number*/
+	int len;
+	char *hash;
+
+	purple_debug_info("jabber-socks5", 
+		"in jabber_socks5_send_read_again_cb\n");
+
+	if(priv->rxlen < 5) {
+		purple_debug_info("jabber-socks5", "reading the first 5 bytes\n");
+		len = read(source, buffer, 5 - priv->rxlen);
+		if(len < 0 && errno == EAGAIN)
+			return;
+		else if(len <= 0) {
+			purple_input_remove(priv->watcher);
+			priv->watcher = 0;
+			close(source);
+			if (priv->error_cb)
+				priv->error_cb(priv->user_data);
+			return;
+		}
+		priv->rxqueue = g_realloc(priv->rxqueue, len + priv->rxlen);
+		memcpy(priv->rxqueue + priv->rxlen, buffer, len);
+		priv->rxlen += len;
+		return;
+	} else if (priv->rxqueue[0] != 0x05 || priv->rxqueue[1] != 0x01 ||
+			priv->rxqueue[3] != 0x03 || priv->rxqueue[4] != 40) {
+		purple_debug_info("jabber-socks5", 
+			"Invalid socks5 conn req. header[0x%x,0x%x,0x%x,0x%x,0x%x]\n",
+			priv->rxqueue[0], priv->rxqueue[1], priv->rxqueue[2],
+			priv->rxqueue[3], priv->rxqueue[4]);
+		purple_input_remove(priv->watcher);
+		priv->watcher = 0;
+		close(source);
+		if (priv->error_cb)
+			priv->error_cb(priv->user_data);
+		return;
+	} else if (priv->rxlen - 5 <  priv->rxqueue[4] + 2) {
+		purple_debug_info("jabber-socks5", 
+			"reading %d "
+		    " bytes for DST.ADDR + port num (trying to read %" G_GSIZE_FORMAT " now)\n",
+			priv->rxqueue[4] + 2,
+			priv->rxqueue[4] + 2 - (priv->rxlen - 5));
+		len = read(source, buffer, priv->rxqueue[4] + 2 - (priv->rxlen - 5));
+
+		if (len < 0 && errno == EAGAIN)
+			return;
+		else if (len <= 0) {
+			purple_input_remove(priv->watcher);
+			priv->watcher = 0;
+			close(source);
+			if (priv->error_cb)
+				priv->error_cb(priv->user_data);
+			return;
+		}
+		priv->rxqueue = g_realloc(priv->rxqueue, len + priv->rxlen);
+		memcpy(priv->rxqueue + priv->rxlen, buffer, len);
+		priv->rxlen += len;
+	}
+
+	/* Have we not read all of DST.ADDR and the following 2-byte port number? */
+	if (priv->rxlen - 5 < priv->rxqueue[4] + 2)
+		return;
+
+	purple_input_remove(priv->watcher);
+	priv->watcher = 0;
+		
+	/* Per XEP-0065, the 'host' must be SHA1(SID + from JID + to JID) */
+	hash = jabber_calculate_data_hash(dstaddr, strlen(dstaddr), "sha1");
+	purple_debug_info("jabber-socks5", "dstaddr: %s\n", dstaddr);
+	purple_debug_info("jabber-socks5", "expecting to receive hash %s\n", hash);
+	
+	if (strncmp(hash, priv->rxqueue + 5, 40) ||
+			priv->rxqueue[45] != 0x00 || priv->rxqueue[46] != 0x00) {
+		if (priv->rxqueue[45] != 0x00 || priv->rxqueue[46] != 0x00)
+			purple_debug_error("jabber-socks5", 
+				"Got SOCKS5 BS conn with the wrong DST.PORT "
+				" (must be 0 - got[0x%x,0x%x]).\n",
+			priv->rxqueue[45], priv->rxqueue[46]);
+		else
+			purple_debug_error("jabber-socks5", 
+				"Got SOCKS5 BS conn with the wrong DST.ADDR"
+				" (expected '%s' - got '%.40s').\n",
+			hash, priv->rxqueue + 5);
+
+		close(source);
+		if (priv->error_cb)
+			priv->error_cb(priv->user_data);
+		g_free(hash);
+		return;
+	}
+
+	g_free(hash);
+	g_free(priv->rxqueue);
+	priv->rxqueue = NULL;
+
+	priv->rxmaxlen = 5 + strlen(host) + 2;
+	priv->rxqueue = g_malloc(priv->rxmaxlen);
+	priv->rxlen = 0;
+
+	priv->rxqueue[0] = 0x05;
+	priv->rxqueue[1] = 0x00;
+	priv->rxqueue[2] = 0x00;
+	priv->rxqueue[3] = 0x03;
+	priv->rxqueue[4] = strlen(host);
+	memcpy(priv->rxqueue + 5, host, strlen(host));
+	priv->rxqueue[5 + strlen(host)] = 0x00;
+	priv->rxqueue[6 + strlen(host)] = 0x00;
+
+	priv->watcher = purple_input_add(source, PURPLE_INPUT_WRITE,
+		jabber_socks5_send_read_again_resp_cb, data);
+	jabber_socks5_send_read_again_resp_cb(data, source,
+		PURPLE_INPUT_WRITE);
+}
+
+
+static void
+jabber_socks5_send_read_response_cb(gpointer data, gint source,
+	PurpleInputCondition cond)
+{
+	JabberSocks5ListenDataPriv *priv = (JabberSocks5ListenDataPriv *) data;
+	int len;
+	
+	purple_debug_info("jabber-socks5", "in jabber_socks5_send_read_response_cb\n");
+	
+	len = write(source, priv->rxqueue + priv->rxlen, priv->rxmaxlen - priv->rxlen);
+
+	if (len < 0 && errno == EAGAIN)
+		return;
+	else if (len < 0) {
+		purple_input_remove(priv->watcher);
+		priv->watcher = 0;
+		g_free(priv->rxqueue);
+		priv->rxqueue = NULL;
+		close(source);
+		if (priv->error_cb)
+			priv->error_cb(priv->user_data);
+		return;
+	}
+	priv->rxlen += len;
+
+	if (priv->rxlen < priv->rxmaxlen) {
+		if (priv->error_cb)
+			priv->error_cb(priv->user_data);
+		return;
+	}
+
+	purple_input_remove(priv->watcher);
+	priv->watcher = 0;
+
+	/* If we sent a "Success", wait for a response, otherwise give up and cancel */
+	if (priv->rxqueue[1] == 0x00) {
+		priv->watcher = purple_input_add(source, PURPLE_INPUT_READ,
+			jabber_socks5_send_read_again_cb, data);
+		g_free(priv->rxqueue);
+		priv->rxqueue = NULL;
+		priv->rxlen = 0;
+	} else {
+		close(source);
+		if (priv->error_cb)
+			priv->error_cb(priv->user_data);
+	}
+}
+
+static void
+jabber_socks5_send_read_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	JabberSocks5ListenDataPriv *priv = (JabberSocks5ListenDataPriv *) data;
+	int i;
+	int len;
+	char buffer[256];
+
+	purple_debug_info("jabber-socks5", "in jabber_socks5_send_read_cb\n");
+	
+	priv->local_fd = source;
+
+	/* Try to read the SOCKS5 header */
+	if (priv->rxlen < 2) {
+		purple_debug_info("jabber-socks5", "reading those first two bytes\n");
+		len = read(source, buffer, 2 - priv->rxlen);
+		if(len < 0 && errno == EAGAIN)
+			return;
+		else if(len <= 0) {
+			purple_input_remove(priv->watcher);
+			priv->watcher = 0;
+			close(source);
+			if (priv->error_cb)
+				priv->error_cb(priv->user_data);
+			return;
+		}
+		priv->rxqueue = 
+			g_realloc(priv->rxqueue, len + priv->rxlen);
+		memcpy(priv->rxqueue + priv->rxlen, buffer, len);
+		priv->rxlen += len;
+		return;
+	} else if (priv->rxlen - 2 < priv->rxqueue[1]) {
+		purple_debug_info("jabber-socks5", 
+			"reading %d "
+		    " bytes for auth methods (trying to read %" G_GSIZE_FORMAT " now)\n",
+			priv->rxqueue[1], priv->rxqueue[1] - (priv->rxlen - 2));
+		len = read(source, buffer, priv->rxqueue[1] - (priv->rxlen - 2));
+
+		if(len < 0 && errno == EAGAIN)
+			return;
+		else if(len <= 0) {
+			purple_input_remove(priv->watcher);
+			priv->watcher = 0;
+			close(source);
+			if (priv->error_cb)
+				priv->error_cb(priv->user_data);
+			return;
+		}
+		priv->rxqueue = 
+			g_realloc(priv->rxqueue, len + priv->rxlen);
+		memcpy(priv->rxqueue + priv->rxlen, buffer, len);
+		priv->rxlen += len;
+	}
+
+	/* Have we not read all the auth. method bytes? */
+	if(priv->rxlen - 2 < priv->rxqueue[1])
+		return;
+
+	purple_input_remove(priv->watcher);
+	priv->watcher = 0;
+
+	purple_debug_info("jabber-socks5", "checking to make sure we're socks FIVE\n");
+
+	if(priv->rxqueue[0] != 0x05) {
+		purple_debug_error("jabber-socks5", "it's not socks FIVE, giving up\n");
+		close(source);
+		if (priv->error_cb)
+			priv->error_cb(priv->user_data);
+		return;
+	}
+
+	purple_debug_info("jabber-socks5", "going to test %hhu different methods\n", 
+		priv->rxqueue[1]);
+
+	for (i = 0; i < priv->rxqueue[1]; i++) {
+		purple_debug_info("jabber-socks5", "testing %hhu\n", 
+			priv->rxqueue[i+2]);
+		if (priv->rxqueue[i+2] == 0x00) {
+			g_free(priv->rxqueue);
+			priv->rxlen = 0;
+			priv->rxmaxlen = 2;
+			priv->rxqueue = g_malloc(priv->rxmaxlen);
+			priv->rxqueue[0] = 0x05;
+			priv->rxqueue[1] = 0x00;
+			priv->watcher = purple_input_add(source, PURPLE_INPUT_WRITE,
+				jabber_socks5_send_read_response_cb, priv);
+			jabber_socks5_send_read_response_cb(priv, source, PURPLE_INPUT_WRITE);
+			priv->rxqueue = NULL;
+			priv->rxlen = 0;
+			return;
+		}
+	}
+
+	/* question to self: is this called if there was no methods given? */
+	g_free(priv->rxqueue);
+	priv->rxlen = 0;
+	priv->rxmaxlen = 2;
+	priv->rxqueue = g_malloc(priv->rxmaxlen);
+	priv->rxqueue[0] = 0x05;
+	priv->rxqueue[1] = 0xFF;
+	priv->watcher = purple_input_add(source, PURPLE_INPUT_WRITE,
+		jabber_socks5_send_read_response_cb, priv);
+	jabber_socks5_send_read_response_cb(priv,
+		source, PURPLE_INPUT_WRITE);
+}
+
+
+static void
+jabber_socks5_send_connected_cb(gpointer data, gint source,
+		PurpleInputCondition cond)
+{
+	JabberSocks5ListenDataPriv *priv = (JabberSocks5ListenDataPriv *) data;
+	int acceptfd = accept(source, NULL, 0);
+	int flags;
+	
+	purple_debug_info("jabber-socks5", "in jabber_socks5_send_connected_cb\n");
+	
+	if(acceptfd == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))
+		return;
+	else if(acceptfd == -1) {
+		purple_debug_warning("jabber-socks5", "accept: %s\n", g_strerror(errno));
+		/* Don't cancel the ft - allow it to fall to the next streamhost.*/
+		return;
+	}
+
+	purple_input_remove(priv->watcher);
+	close(source);
+
+	flags = fcntl(acceptfd, F_GETFL);
+	fcntl(acceptfd, F_SETFL, flags | O_NONBLOCK);
+#ifndef _WIN32
+	fcntl(acceptfd, F_SETFD, FD_CLOEXEC);
+#endif
+
+	priv->watcher = purple_input_add(acceptfd, PURPLE_INPUT_READ,
+					 jabber_socks5_send_read_cb, priv);
+}
+
+
+JabberSocks5ListenData *
+jabber_socks5_listen(int socket, const gchar *dstaddr,
+    JabberSocks5ConnectCallback *connect_cb,
+    JabberSocks5ErrorCallback *error_cb, gpointer user_data)
+{
+	JabberSocks5ListenDataPriv *priv = g_new0(JabberSocks5ListenDataPriv, 1);
+	JabberSocks5ListenData *listen_data = g_new0(JabberSocks5ListenData, 1);
+	
+	priv->connect_cb = connect_cb;
+	priv->error_cb = error_cb;
+	priv->user_data = user_data;
+	priv->dstaddr = g_strdup(dstaddr);
+	priv->watcher = purple_input_add(socket, PURPLE_INPUT_READ,
+			jabber_socks5_send_connected_cb, priv);
+	
+	listen_data->priv = priv;
+	
+	return listen_data;
+}
+
+void
+jabber_socks5_listen_cancel(JabberSocks5ListenData *listen_data)
+{
+	JabberSocks5ListenDataPriv *priv =
+		(JabberSocks5ListenDataPriv *) listen_data->priv;
+
+	if (priv->watcher)
+		purple_input_remove(priv->watcher);
+}
+
+void
+jabber_socks5_listen_data_free(JabberSocks5ListenData *listen_data)
+{
+	JabberSocks5ListenDataPriv *priv =
+		(JabberSocks5ListenDataPriv *) listen_data->priv;
+
+	if (priv->rxqueue)
+		g_free(priv->rxqueue);
+	if (priv->dstaddr)
+		g_free(priv->dstaddr);
+	g_free(priv);
+	g_free(listen_data);
+}
============================================================
--- libpurple/protocols/jabber/socks5.h	ced2ee3d959300aa9e95a3f07989628b01fea2f6
+++ libpurple/protocols/jabber/socks5.h	ced2ee3d959300aa9e95a3f07989628b01fea2f6
@@ -0,0 +1,36 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301,  USA
+ */
+
+#ifndef PURPLE_JABBER_SOCKS5_H_
+#define PURPLE_JABBER_SOCKS5_H_
+
+#include "internal.h"
+
+typedef struct {	
+	gpointer priv;
+} JabberSocks5ListenData;
+
+typedef void (JabberSocks5ConnectCallback)(int fd, gpointer user_data);
+typedef void (JabberSocks5ErrorCallback)(gpointer user_data);
+
+JabberSocks5ListenData *jabber_socks5_listen(int socket, const gchar *dstadr,
+    JabberSocks5ConnectCallback *connect_cb,
+    JabberSocks5ErrorCallback *error_cb, gpointer user_data);
+
+void jabber_socks5_listen_cancel(JabberSocks5ListenData *listen_data);
+void jabber_socks5_listen_data_free(JabberSocks5ListenData *listen_data);
+
+#endif /* PURPLE_JABBER_SOCKS5_H_ */
============================================================
--- libpurple/protocols/jabber/Makefile.am	3cc9e1d13797ed32ccd79e1c9ea6be2724a75316
+++ libpurple/protocols/jabber/Makefile.am	8aa6d3dea8dabe33ba733b83f679d45bfda039d0
@@ -73,6 +73,8 @@ JABBERSOURCES = \
 			  roster.h \
 			  si.c \
 			  si.h \
+			  socks5.c \
+			  socks5.h \
 			  useravatar.c \
 			  useravatar.h \
 			  usermood.c \
============================================================
--- libpurple/protocols/jabber/Makefile.mingw	ee64a101fee7fb8c7a54976867dcbef43dc7f326
+++ libpurple/protocols/jabber/Makefile.mingw	c64e117ee26658f06c15fa4d0b3816a7688f3c39
@@ -78,6 +78,7 @@ C_SRC =	\
 			presence.c \
 			roster.c \
 			si.c \
+			socks5.c \
 			useravatar.c \
 			usermood.c \
 			usernick.c \
============================================================
--- libpurple/protocols/jabber/jingle/s5b.c	c21466dbb6474dcbbcf221257cd7e2c006c5b642
+++ libpurple/protocols/jabber/jingle/s5b.c	c94b63fbddfe41b254a4241a2aadd0f355cbc65e
@@ -20,6 +20,7 @@
 #include "session.h"
 #include "content.h"
 #include "s5b.h"
+#include "socks5.h"
 #include "debug.h"
 #include "network.h"
 #include "xmlnode.h"
@@ -202,10 +203,7 @@ struct _JingleS5BPrivate {
 	PurpleNetworkListenData *listen_data;
 	PurpleProxyInfo *ppi;
 	guint connect_timeout;
-	int watcher;
-	char *rxqueue;
-	gsize rxlen;
-	gsize rxmaxlen;
+	JabberSocks5ListenData *socks5_listen_data;
 	GList *remote_candidates;
 	GList *local_candidates;
 	GList *remaining_candidates; /* pointer to untested remote SHs */
@@ -537,10 +535,11 @@ jingle_s5b_surrender(JingleS5B *s5b)
 		close(s5b->priv->local_fd);
 		s5b->priv->local_fd = -1;
 	}
-	
-	if (s5b->priv->watcher) {
-		purple_input_remove(s5b->priv->watcher);
-		s5b->priv->watcher = 0;
+
+	if (s5b->priv->socks5_listen_data) {
+		jabber_socks5_listen_cancel(s5b->priv->socks5_listen_data);
+		jabber_socks5_listen_data_free(s5b->priv->socks5_listen_data);
+		s5b->priv->socks5_listen_data = NULL;
 	}
 	
 	if (s5b->priv->connect_timeout) {
@@ -560,10 +559,10 @@ jingle_s5b_take_command(JingleS5B *s5b)
 		close(s5b->priv->remote_fd);
 		s5b->priv->remote_fd = -1;
 	}
-	
-	if (s5b->priv->watcher) {
-		purple_input_remove(s5b->priv->watcher);
-		s5b->priv->watcher = 0;
+
+	if (s5b->priv->socks5_listen_data) {
+		jabber_socks5_listen_data_free(s5b->priv->socks5_listen_data);
+		s5b->priv->socks5_listen_data = NULL;
 	}
 	
 	if (s5b->priv->connect_timeout) {
@@ -588,396 +587,52 @@ jingle_s5b_stop_connection_attempts(Jing
 		purple_timeout_remove(s5b->priv->connect_timeout);
 		s5b->priv->connect_timeout = 0;
 	}
-
-	if (s5b->priv->watcher != 0) {
-		purple_input_remove(s5b->priv->watcher);
-		s5b->priv->watcher = 0;
-	}
 }
 
-
+/* holds information regarding listening socks5 connection */
 typedef struct {
 	JingleSession *session;
 	JingleS5B *s5b;
 } JingleS5BConnectData;
 
+
+/* callbacks for socks5 listner */
 static void
-jingle_s5b_send_read_again_resp_cb(gpointer data, gint source,
-	PurpleInputCondition cond)
+jingle_s5b_listen_connect_cb(int fd, gpointer data)
 {
 	JingleS5B *s5b = ((JingleS5BConnectData *) data)->s5b;
-	int len;
 
 	g_free(data);
+	jabber_socks5_listen_data_free(s5b->priv->socks5_listen_data);
+	s5b->priv->socks5_listen_data = NULL;
 
-	purple_debug_info("jingle-s5b", "in jingle_s5b_send_read_again_cb\n");
-	len = write(source, s5b->priv->rxqueue + s5b->priv->rxlen,
-		s5b->priv->rxmaxlen - s5b->priv->rxlen);
-	if (len < 0 && errno == EAGAIN)
-		return;
-	else if (len < 0) {
-		purple_input_remove(s5b->priv->watcher);
-		s5b->priv->watcher = 0;
-		g_free(s5b->priv->rxqueue);
-		s5b->priv->rxqueue = NULL;
-		close(source);
-		if (s5b->priv->error_cb && s5b->priv->error_content)
-			s5b->priv->error_cb(s5b->priv->error_content);
-		return;
-	}
-	s5b->priv->rxlen += len;
-
-	if (s5b->priv->rxlen < s5b->priv->rxmaxlen)
-		return;
-
-	purple_input_remove(s5b->priv->watcher);
-	s5b->priv->watcher = 0;
-	g_free(s5b->priv->rxqueue);
-	s5b->priv->rxqueue = NULL;
-
 	/* Before actually starting sending the file, we need to wait until the
 	 * recipient sends the IQ result with <candidate-used/>
 	 */
 	purple_debug_info("jingle-s5b", "SOCKS5 connection negotiation completed. "
 					  "Waiting for IQ result to start file transfer.\n");
 	/* set the local fd as connected */
-	s5b->priv->local_fd = source;
+	s5b->priv->local_fd = fd;
 	s5b->priv->is_remote_connected = TRUE;
 }
 
 static void
-jingle_s5b_send_read_again_cb(gpointer data, gint source,
-	PurpleInputCondition cond)
+jingle_s5b_listen_error_cb(gpointer data)
 {
 	JingleS5B *s5b = ((JingleS5BConnectData *) data)->s5b;
-	JingleSession *session = ((JingleS5BConnectData *) data)->session;
-	const gchar *who = jingle_session_get_remote_jid(session);
-	JabberID *dstjid = jabber_id_new(who);
-	JabberStream *js = jingle_session_get_js(session);
-	char buffer[42]; /* 40 for DST.ADDR + 2 bytes for port number*/
-	int len;
-	char *dstaddr, *hash;
-	const char *host;
 
-	purple_debug_info("jingle-s5b", 
-		"in jingle_s5b_send_read_again_cb\n");
-
-	if(s5b->priv->rxlen < 5) {
-		purple_debug_info("jingle-s5b", "reading the first 5 bytes\n");
-		len = read(source, buffer, 5 - s5b->priv->rxlen);
-		if(len < 0 && errno == EAGAIN)
-			return;
-		else if(len <= 0) {
-			purple_input_remove(s5b->priv->watcher);
-			s5b->priv->watcher = 0;
-			close(source);
-			if (s5b->priv->error_cb && s5b->priv->error_content)
-				s5b->priv->error_cb(s5b->priv->error_content);
-			g_free(data);
-			return;
-		}
-		s5b->priv->rxqueue = 
-			g_realloc(s5b->priv->rxqueue, len + s5b->priv->rxlen);
-		memcpy(s5b->priv->rxqueue + s5b->priv->rxlen, buffer, len);
-		s5b->priv->rxlen += len;
-		return;
-	} else if(s5b->priv->rxqueue[0] != 0x05 || s5b->priv->rxqueue[1] != 0x01 ||
-			s5b->priv->rxqueue[3] != 0x03 || s5b->priv->rxqueue[4] != 40) {
-		purple_debug_info("jingle-s5b", 
-			"Invalid socks5 conn req. header[0x%x,0x%x,0x%x,0x%x,0x%x]\n",
-			s5b->priv->rxqueue[0], s5b->priv->rxqueue[1], s5b->priv->rxqueue[2],
-			s5b->priv->rxqueue[3], s5b->priv->rxqueue[4]);
-		purple_input_remove(s5b->priv->watcher);
-		s5b->priv->watcher = 0;
-		close(source);
-		if (s5b->priv->error_cb && s5b->priv->error_content)
-			s5b->priv->error_cb(s5b->priv->error_content);
-		g_free(data);
-		return;
-	} else if(s5b->priv->rxlen - 5 <  s5b->priv->rxqueue[4] + 2) {
-		purple_debug_info("jingle-s5b", 
-			"reading %d "
-		    " bytes for DST.ADDR + port num (trying to read %" G_GSIZE_FORMAT " now)\n",
-			s5b->priv->rxqueue[4] + 2,
-			s5b->priv->rxqueue[4] + 2 - (s5b->priv->rxlen - 5));
-		len = read(source, buffer,
-			s5b->priv->rxqueue[4] + 2 - (s5b->priv->rxlen - 5));
-
-		if(len < 0 && errno == EAGAIN)
-			return;
-		else if(len <= 0) {
-			purple_input_remove(s5b->priv->watcher);
-			s5b->priv->watcher = 0;
-			close(source);
-			if (s5b->priv->error_cb && s5b->priv->error_content)
-				s5b->priv->error_cb(s5b->priv->error_content);
-			g_free(data);
-			return;
-		}
-		s5b->priv->rxqueue = 
-			g_realloc(s5b->priv->rxqueue, len + s5b->priv->rxlen);
-		memcpy(s5b->priv->rxqueue + s5b->priv->rxlen, buffer, len);
-		s5b->priv->rxlen += len;
-	}
-
-	/* Have we not read all of DST.ADDR and the following 2-byte port number? */
-	if(s5b->priv->rxlen - 5 < s5b->priv->rxqueue[4] + 2)
-		return;
-
-	purple_input_remove(s5b->priv->watcher);
-	s5b->priv->watcher = 0;
-
-	dstaddr = g_strdup_printf("%s%s@%s/%s%s@%s/%s", s5b->priv->sid, 
-		js->user->node, js->user->domain, js->user->resource, 
-		dstjid->node, dstjid->domain, dstjid->resource);
-
-	g_free(dstjid);
-		
-	/* Per XEP-0065, the 'host' must be SHA1(SID + from JID + to JID) */
-	hash = jabber_calculate_data_hash(dstaddr, strlen(dstaddr), "sha1");
-	purple_debug_info("jingle-s5b", "dstaddr: %s\n", dstaddr);
-	purple_debug_info("jingle-s5b", "expecting to receive hash %s\n", hash);
+	g_free(data);
+	jabber_socks5_listen_data_free(s5b->priv->socks5_listen_data);
+	s5b->priv->socks5_listen_data = NULL;
 	
-	if (strncmp(hash, s5b->priv->rxqueue + 5, 40) ||
-			s5b->priv->rxqueue[45] != 0x00 || s5b->priv->rxqueue[46] != 0x00) {
-		if (s5b->priv->rxqueue[45] != 0x00 || s5b->priv->rxqueue[46] != 0x00)
-			purple_debug_error("jingle-s5b", 
-				"Got SOCKS5 BS conn with the wrong DST.PORT "
-				" (must be 0 - got[0x%x,0x%x]).\n",
-			s5b->priv->rxqueue[45], s5b->priv->rxqueue[46]);
-		else
-			purple_debug_error("jingle-s5b", 
-				"Got SOCKS5 BS conn with the wrong DST.ADDR"
-				" (expected '%s' - got '%.40s').\n",
-			hash, s5b->priv->rxqueue + 5);
+	purple_debug_info("jingle-s5b", "SOCKS5 connection negotiation failed. "
+	    "Calling content handler's s5b error (fallback) handler.\n");
 
-		close(source);
-		if (s5b->priv->error_cb && s5b->priv->error_content)
+	if (s5b->priv->error_cb && s5b->priv->error_content)
 			s5b->priv->error_cb(s5b->priv->error_content);
-		g_free(data);
-		g_free(hash);
-		g_free(dstaddr);
-		return;
-	}
-
-	g_free(hash);
-	g_free(dstaddr);
-
-	g_free(s5b->priv->rxqueue);
-	host = purple_network_get_my_ip(js->fd);
-
-	s5b->priv->rxmaxlen = 5 + strlen(host) + 2;
-	s5b->priv->rxqueue = g_malloc(s5b->priv->rxmaxlen);
-	s5b->priv->rxlen = 0;
-
-	s5b->priv->rxqueue[0] = 0x05;
-	s5b->priv->rxqueue[1] = 0x00;
-	s5b->priv->rxqueue[2] = 0x00;
-	s5b->priv->rxqueue[3] = 0x03;
-	s5b->priv->rxqueue[4] = strlen(host);
-	memcpy(s5b->priv->rxqueue + 5, host, strlen(host));
-	s5b->priv->rxqueue[5+strlen(host)] = 0x00;
-	s5b->priv->rxqueue[6+strlen(host)] = 0x00;
-
-	s5b->priv->watcher = purple_input_add(source, PURPLE_INPUT_WRITE,
-		jingle_s5b_send_read_again_resp_cb, data);
-	jingle_s5b_send_read_again_resp_cb(data, source,
-		PURPLE_INPUT_WRITE);
 }
 
 static void
-jingle_s5b_send_read_response_cb(gpointer data, gint source,
-	PurpleInputCondition cond)
-{
-	JingleS5B *s5b = ((JingleS5BConnectData *) data)->s5b;
-	int len;
-	
-	purple_debug_info("jingle-s5b", "in jingle_s5b_send_read_response_cb\n");
-	
-	len = write(source, s5b->priv->rxqueue + s5b->priv->rxlen, 
-		s5b->priv->rxmaxlen - s5b->priv->rxlen);
-	if (len < 0 && errno == EAGAIN)
-		return;
-	else if (len < 0) {
-		purple_input_remove(s5b->priv->watcher);
-		s5b->priv->watcher = 0;
-		g_free(s5b->priv->rxqueue);
-		s5b->priv->rxqueue = NULL;
-		close(source);
-		if (s5b->priv->error_cb && s5b->priv->error_content)
-			s5b->priv->error_cb(s5b->priv->error_content);
-		g_free(data);
-		return;
-	}
-	s5b->priv->rxlen += len;
-
-	if (s5b->priv->rxlen < s5b->priv->rxmaxlen) {
-		if (s5b->priv->error_cb && s5b->priv->error_content)
-			s5b->priv->error_cb(s5b->priv->error_content);
-		g_free(data);
-		return;
-	}
-
-	purple_input_remove(s5b->priv->watcher);
-	s5b->priv->watcher = 0;
-
-	/* If we sent a "Success", wait for a response, otherwise give up and cancel */
-	if (s5b->priv->rxqueue[1] == 0x00) {
-		s5b->priv->watcher = purple_input_add(source, PURPLE_INPUT_READ,
-			jingle_s5b_send_read_again_cb, data);
-		g_free(s5b->priv->rxqueue);
-		s5b->priv->rxqueue = NULL;
-		s5b->priv->rxlen = 0;
-	} else {
-		close(source);
-		if (s5b->priv->error_cb && s5b->priv->error_content)
-			s5b->priv->error_cb(s5b->priv->error_content);
-		g_free(data);
-	}
-}
-	
-static void
-jingle_s5b_send_read_cb(gpointer data, gint source, PurpleInputCondition cond)
-{
-	JingleS5B *s5b = ((JingleS5BConnectData *) data)->s5b;
-	int i;
-	int len;
-	char buffer[256];
-
-	purple_debug_info("jingle-s5b", "in jingle_s5b_send_read_cb\n");
-	
-	s5b->priv->local_fd = source;
-
-	/* Try to read the SOCKS5 header */
-	if(s5b->priv->rxlen < 2) {
-		purple_debug_info("jingle-s5b", "reading those first two bytes\n");
-		len = read(source, buffer, 2 - s5b->priv->rxlen);
-		if(len < 0 && errno == EAGAIN)
-			return;
-		else if(len <= 0) {
-			purple_input_remove(s5b->priv->watcher);
-			s5b->priv->watcher = 0;
-			close(source);
-			if (s5b->priv->error_cb && s5b->priv->error_content)
-				s5b->priv->error_cb(s5b->priv->error_content);
-			g_free(data);
-			return;
-		}
-		s5b->priv->rxqueue = 
-			g_realloc(s5b->priv->rxqueue, len + s5b->priv->rxlen);
-		memcpy(s5b->priv->rxqueue + s5b->priv->rxlen, buffer, len);
-		s5b->priv->rxlen += len;
-		return;
-	} else if(s5b->priv->rxlen - 2 < s5b->priv->rxqueue[1]) {
-		purple_debug_info("jingle-s5b", 
-			"reading %d "
-		    " bytes for auth methods (trying to read %" G_GSIZE_FORMAT " now)\n",
-			s5b->priv->rxqueue[1], s5b->priv->rxqueue[1] - (s5b->priv->rxlen - 2));
-		len = read(source, buffer, s5b->priv->rxqueue[1] - (s5b->priv->rxlen - 2));
-
-		if(len < 0 && errno == EAGAIN)
-			return;
-		else if(len <= 0) {
-			purple_input_remove(s5b->priv->watcher);
-			s5b->priv->watcher = 0;
-			close(source);
-			if (s5b->priv->error_cb && s5b->priv->error_content)
-				s5b->priv->error_cb(s5b->priv->error_content);
-			g_free(data);
-			return;
-		}
-		s5b->priv->rxqueue = 
-			g_realloc(s5b->priv->rxqueue, len + s5b->priv->rxlen);
-		memcpy(s5b->priv->rxqueue + s5b->priv->rxlen, buffer, len);
-		s5b->priv->rxlen += len;
-	}
-
-	/* Have we not read all the auth. method bytes? */
-	if(s5b->priv->rxlen - 2 < s5b->priv->rxqueue[1])
-		return;
-
-	purple_input_remove(s5b->priv->watcher);
-	s5b->priv->watcher = 0;
-
-	purple_debug_info("jingle-s5b", "checking to make sure we're socks FIVE\n");
-
-	if(s5b->priv->rxqueue[0] != 0x05) {
-		purple_debug_error("jingle-s5b", "it's not socks FIVE, giving up\n");
-		close(source);
-		if (s5b->priv->error_cb && s5b->priv->error_content)
-			s5b->priv->error_cb(s5b->priv->error_content);
-		g_free(data);
-		return;
-	}
-
-	purple_debug_info("jingle-s5b", "going to test %hhu different methods\n", 
-		s5b->priv->rxqueue[1]);
-
-	for(i = 0; i < s5b->priv->rxqueue[1]; i++) {
-		purple_debug_info("jingle-s5b", "testing %hhu\n", 
-			s5b->priv->rxqueue[i+2]);
-		if(s5b->priv->rxqueue[i+2] == 0x00) {
-			g_free(s5b->priv->rxqueue);
-			s5b->priv->rxlen = 0;
-			s5b->priv->rxmaxlen = 2;
-			s5b->priv->rxqueue = g_malloc(s5b->priv->rxmaxlen);
-			s5b->priv->rxqueue[0] = 0x05;
-			s5b->priv->rxqueue[1] = 0x00;
-			s5b->priv->watcher = purple_input_add(source, PURPLE_INPUT_WRITE,
-				jingle_s5b_send_read_response_cb, data);
-			jingle_s5b_send_read_response_cb(data, source, PURPLE_INPUT_WRITE);
-			s5b->priv->rxqueue = NULL;
-			s5b->priv->rxlen = 0;
-			return;
-		}
-	}
-
-	/* question to self: is this called if there was no methods given? */
-	g_free(s5b->priv->rxqueue);
-	s5b->priv->rxlen = 0;
-	s5b->priv->rxmaxlen = 2;
-	s5b->priv->rxqueue = g_malloc(s5b->priv->rxmaxlen);
-	s5b->priv->rxqueue[0] = 0x05;
-	s5b->priv->rxqueue[1] = 0xFF;
-	s5b->priv->watcher = purple_input_add(source, PURPLE_INPUT_WRITE,
-		jingle_s5b_send_read_response_cb, data);
-	jingle_s5b_send_read_response_cb(data,
-		source, PURPLE_INPUT_WRITE);
-}
-
-static void
-jingle_s5b_send_connected_cb(gpointer data, gint source,
-		PurpleInputCondition cond)
-{
-	JingleS5B *s5b = ((JingleS5BConnectData *) data)->s5b;
-	int acceptfd = accept(source, NULL, 0);
-	int flags;
-	
-	purple_debug_info("jingle-s5b", "in jingle_s5b_send_connected_cb\n");
-	
-	if(acceptfd == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))
-		return;
-	else if(acceptfd == -1) {
-		purple_debug_warning("jingle-s5b", "accept: %s\n", g_strerror(errno));
-		/* Don't cancel the ft - allow it to fall to the next streamhost.*/
-		return;
-	}
-
-	purple_input_remove(s5b->priv->watcher);
-	close(source);
-	s5b->priv->local_fd = -1;
-
-	flags = fcntl(acceptfd, F_GETFL);
-	fcntl(acceptfd, F_SETFL, flags | O_NONBLOCK);
-#ifndef _WIN32
-	fcntl(acceptfd, F_SETFD, FD_CLOEXEC);
-#endif
-
-	s5b->priv->watcher = purple_input_add(acceptfd, PURPLE_INPUT_READ,
-					 jingle_s5b_send_read_cb, data);
-}
-
-static void
 jingle_s5b_listen_cb(int sock, gpointer data)
 {
 	JingleS5B *s5b = ((JingleS5BConnectData *) data)->s5b;
@@ -994,7 +649,13 @@ jingle_s5b_listen_cb(int sock, gpointer 
 		gchar *jid = g_strdup_printf("%s@%s/%s", js->user->node,
 			js->user->domain, js->user->resource);
 		gboolean has_public_ip = FALSE;
-
+		const gchar *who = jingle_session_get_remote_jid(session);
+		JabberID *dstjid = jabber_id_new(who);
+		gchar *dstaddr = g_strdup_printf("%s%s%s@%s/%s", s5b->priv->sid, 
+			jid, dstjid->node, dstjid->domain, dstjid->resource);
+		
+		jabber_id_free(dstjid);
+		
 		purple_debug_info("jingle-s5b", "successfully open port %d locally\n",
 			local_port);
 
@@ -1025,9 +686,10 @@ jingle_s5b_listen_cb(int sock, gpointer 
 		s5b->priv->local_fd = sock;
 		
 		/* The listener for the local proxy */
-		s5b->priv->watcher = purple_input_add(sock, PURPLE_INPUT_READ,
-			jingle_s5b_send_connected_cb, data);
-		
+		s5b->priv->socks5_listen_data = jabber_socks5_listen(sock, dstaddr,
+		    jingle_s5b_listen_connect_cb, jingle_s5b_listen_error_cb, data);
+
+		g_free(dstaddr);
 		g_free(jid);
 	}
 	


More information about the Commits mailing list