diff libpurple/proxy.c @ 24106:1fb2e7951f9b

Fix the SOCKS5 CHAP authentication to parse several messages if they are read at in the intial read. This allows us to accept the CHALLENGE attrib in a different message than the ALGORITHMS attrib since the IETF draft is somewhat ambiguous about this. Also include some additional debugging output for the CHAP processing. Thanks to Jonathan Rudolph ("simple" on IRC) for debugging.
author Daniel Atallah <daniel.atallah@gmail.com>
date Thu, 11 Sep 2008 22:00:23 +0000
parents 4a1bbe955690
children 71fecd0f4ce6
line wrap: on
line diff
--- a/libpurple/proxy.c	Thu Sep 11 21:25:05 2008 +0000
+++ b/libpurple/proxy.c	Thu Sep 11 22:00:23 2008 +0000
@@ -1505,62 +1505,31 @@
 }
 
 static void
-s5_readchap(gpointer data, gint source, PurpleInputCondition cond)
+s5_readchap(gpointer data, gint source, PurpleInputCondition cond);
+
+/*
+ * Return how many bytes we processed
+ * -1 means we've shouldn't keep reading from the buffer
+ */
+static gssize
+s5_parse_chap_msg(PurpleProxyConnectData *connect_data)
 {
-	guchar *cmdbuf, *buf;
-	PurpleProxyConnectData *connect_data = data;
+	guchar *buf, *cmdbuf = connect_data->read_buffer;
 	int len, navas, currentav;
 
-	purple_debug(PURPLE_DEBUG_INFO, "socks5 proxy", "Got CHAP response.\n");
-
-	if (connect_data->read_buffer == NULL) {
-		/* A big enough butfer to read the message header (2 bytes) and at least one complete attribute and value (1 + 1 + 255). */
-		connect_data->read_buf_len = 259;
-		connect_data->read_buffer = g_malloc(connect_data->read_buf_len);
-		connect_data->read_len = 0;
-	}
-
-	if (connect_data->read_buf_len - connect_data->read_len == 0) {
-		/*If the stuff below is right, this shouldn't be possible. */
-		purple_debug_error("socks5 proxy", "This is about to suck because the read buffer is full (shouldn't happen).\n");
-	}
-
-	len = read(connect_data->fd, connect_data->read_buffer + connect_data->read_len,
-		connect_data->read_buf_len - connect_data->read_len);
-
-	if (len == 0)
-	{
-		purple_proxy_connect_data_disconnect(connect_data,
-				_("Server closed the connection."));
-		return;
-	}
-
-	if (len < 0)
-	{
-		if (errno == EAGAIN)
-			/* No worries */
-			return;
-
-		/* Error! */
-		purple_proxy_connect_data_disconnect_formatted(connect_data,
-				_("Lost connection with server:\n%s"), g_strerror(errno));
-		return;
-	}
-
-	connect_data->read_len += len;
-	if (connect_data->read_len < 2)
-		return;
-
-	cmdbuf = connect_data->read_buffer;
+	purple_debug_misc("socks5 proxy", "Reading CHAP message: %x\n", *cmdbuf);
 
 	if (*cmdbuf != 0x01) {
 		purple_proxy_connect_data_disconnect(connect_data,
 				_("Received invalid data on connection with server."));
-		return;
+		return -1;
 	}
 	cmdbuf++;
 
 	navas = *cmdbuf;
+
+	purple_debug_misc("socks5 proxy", "Expecting %d attribute(s).\n", navas);
+
 	cmdbuf++;
 
 	for (currentav = 0; currentav < navas; currentav++) {
@@ -1575,7 +1544,10 @@
 			memmove((connect_data->read_buffer + 2), cmdbuf, len);
 			/* Decrease the read count accordingly */
 			connect_data->read_len = len + 2;
-			return;
+
+			purple_debug_info("socks5 proxy", "Need more data to retrieve attribute %d.\n", currentav);
+
+			return -1;
 		}
 
 		buf = cmdbuf + 2;
@@ -1605,7 +1577,7 @@
 					purple_proxy_connect_data_disconnect(connect_data,
 							_("Authentication failed"));
 				}
-				return;
+				return -1;
 			case 0x03:
 				purple_debug_info("socks5 proxy", "Received CHALLENGE\n");
 				/* Server wants our credentials */
@@ -1632,7 +1604,7 @@
 					PURPLE_INPUT_WRITE, proxy_do_write, connect_data);
 
 				proxy_do_write(connect_data, connect_data->fd, PURPLE_INPUT_WRITE);
-				return;
+				return -1;
 			case 0x11:
 				purple_debug_info("socks5 proxy", "Received ALGORIGTHMS of %x\n", buf[0]);
 				/* Server wants to select an algorithm */
@@ -1646,7 +1618,7 @@
 						"Disconnecting...");
 					purple_proxy_connect_data_disconnect(connect_data,
 							_("Received invalid data on connection with server."));
-					return;
+					return -1;
 				}
 				break;
 			default:
@@ -1655,10 +1627,85 @@
 		cmdbuf = buf + cmdbuf[1];
 	}
 
+	return (cmdbuf - connect_data->read_buffer);
+}
+
+static void
+s5_readchap(gpointer data, gint source, PurpleInputCondition cond)
+{
+	gssize msg_ret;
+	PurpleProxyConnectData *connect_data = data;
+	int len;
+
+	purple_debug(PURPLE_DEBUG_INFO, "socks5 proxy", "Got CHAP response.\n");
+
+	if (connect_data->read_buffer == NULL) {
+		/* A big enough butfer to read the message header (2 bytes) and at least one complete attribute and value (1 + 1 + 255). */
+		connect_data->read_buf_len = 259;
+		connect_data->read_buffer = g_malloc(connect_data->read_buf_len);
+		connect_data->read_len = 0;
+	}
+
+	if (connect_data->read_buf_len - connect_data->read_len == 0) {
+		/*If the stuff below is right, this shouldn't be possible. */
+		purple_debug_error("socks5 proxy", "This is about to suck because the read buffer is full (shouldn't happen).\n");
+	}
+
+	len = read(connect_data->fd, connect_data->read_buffer + connect_data->read_len,
+		connect_data->read_buf_len - connect_data->read_len);
+
+	if (len == 0) {
+		purple_proxy_connect_data_disconnect(connect_data,
+				_("Server closed the connection."));
+		return;
+	}
+
+	if (len < 0) {
+		if (errno == EAGAIN)
+			/* No worries */
+			return;
+
+		/* Error! */
+		purple_proxy_connect_data_disconnect_formatted(connect_data,
+				_("Lost connection with server:\n%s"), g_strerror(errno));
+		return;
+	}
+
+	connect_data->read_len += len;
+
+	/* We may have read more than one message into the buffer, we need to make sure to process them all */
+	while (1) {
+
+		/* We need more to be able to read this message */
+		if (connect_data->read_len < 2)
+			return;
+
+		msg_ret = s5_parse_chap_msg(connect_data);
+	
+		if (msg_ret < 0)
+			return;
+
+		/* See if we have another message already in the buffer */
+		if ((len = connect_data->read_len - msg_ret) > 0) {
+
+			/* Move on to the next message */
+			memmove(connect_data->read_buffer, connect_data->read_buffer + msg_ret, len);
+			/* Decrease the read count accordingly */
+			connect_data->read_len = len;
+
+			/* Try to read the message that connect_data->read_buffer now points to */
+			continue;
+		}
+
+		break;
+	}
+
 	/* Fell through.  We ran out of CHAP events to process, but haven't
 	 * succeeded or failed authentication - there may be more to come.
 	 * If this is the case, come straight back here. */
 
+	purple_debug_info("socks5 proxy", "Waiting for another message from which to read CHAP info.\n");
+
 	/* We've processed all the available attributes, so get ready for a whole new message */
  	g_free(connect_data->read_buffer);
 	connect_data->read_buffer = NULL;