[PATCH 3/5] PROXY/HTTP: added methods for HTTP proxies

Enrico Scholz <[email protected]> Wed, 27 Feb 2008 11:38:21 +0100
Newsgroups gmane.network.irc.irssi.devel
Message-ID <ffcdda43151b1a81d96f2c05b64a1de6e9f36ec0.1204046798.git.enrico.scholz@informatik.tu-chemnitz.de>
This patch adds code for connecting through HTTP proxies. Open issues are:

* support of proxy authentication
* a possible DOS due to the usage of g_io_channel_read_line_string() which
  does not allow to specify a maximum length of line.

To use this method:
* set 'proxy_type' to 'http'

Signed-off-by: Enrico Scholz <[email protected]>
---
 src/core/network-proxy-http.c |  193 +++++++++++++++++++++++++++++++++++++++++
 src/core/network-proxy-http.h |   29 ++++++
 2 files changed, 222 insertions(+), 0 deletions(-)
 create mode 100644 src/core/network-proxy-http.c
 create mode 100644 src/core/network-proxy-http.h

diff --git a/src/core/network-proxy-http.c b/src/core/network-proxy-http.c
new file mode 100644
index 0000000..3e79d78
--- /dev/null
+++ b/src/core/network-proxy-http.c
@@ -0,0 +1,193 @@
+/*	--*- c -*--
+ * Copyright (C) 2008 Enrico Scholz <[email protected]>
+ *
+ * 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; version 2 and/or 3 of the License.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "network-proxy-http.h"
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "network.h"
+#include "network-proxy-priv.h"
+
+static void
+network_proxy_http_destroy(struct network_proxy *proxy)
+{
+	struct _network_proxy_http	*self = container_of(proxy, struct _network_proxy_http, proxy);
+
+	g_free((void *)self->password);
+	_network_proxy_destroy(proxy);
+
+	g_free(self);
+}
+
+static struct network_proxy *
+network_proxy_http_clone(struct network_proxy const *proxy)
+{
+	struct _network_proxy_http	*self = container_of(proxy, struct _network_proxy_http, proxy);
+	struct _network_proxy_http	*res;
+
+	res = g_malloc0(sizeof *res);
+
+	_network_proxy_clone(&res->proxy, &self->proxy);
+	res->password = g_strdup(self->password);
+	return &res->proxy;
+}
+
+static bool
+send_connect(struct _network_proxy_http *proxy, GIOChannel *ch, char const *address, uint16_t port)
+{
+	char				port_str[6];
+
+	(void)proxy;
+	sprintf(port_str, "%u", port);
+
+	if (!_network_proxy_send_all(ch, "CONNECT ", -1) ||
+	    !_network_proxy_send_all(ch, address,    -1) ||
+	    !_network_proxy_send_all(ch, ":",        -1) ||
+	    !_network_proxy_send_all(ch, port_str,   -1) ||
+	    !_network_proxy_send_all(ch, " HTTP/1.0\r\n\r\n", -1) ||
+	    !_network_proxy_flush(ch))
+		return -1;
+
+	return true;
+}
+
+static int
+read_response(struct _network_proxy_http *proxy, GIOChannel *ch)
+{
+	GIOStatus			status;
+	GString				line = { .str = NULL };
+	gsize				term_pos;
+	GError				*err = NULL;
+	int				state = 0;
+	int				rc = 0;
+	gchar				*resp = NULL;
+
+	(void)proxy;
+	for (;;) {
+		/* TODO: a malicious proxy can DOS us by sending much data
+		 * without a line break */
+		while ((status=g_io_channel_read_line_string(ch, &line, &term_pos,
+							     &err))==G_IO_STATUS_AGAIN)
+		{
+			/* noop */
+		}
+
+		if (status!=G_IO_STATUS_NORMAL) {
+			g_warning("failed to read HTTP response: %s", err->message);
+			goto err;
+		}
+
+		if (state==0) {
+			if (g_str_has_prefix(line.str, "HTTP/1.0 ")) {
+				resp = g_strndup(line.str+9, line.len-9-2);
+				rc   = g_ascii_strtoull(resp, NULL, 10);
+			} else {
+				g_warning("unexpected HTTP response: '%s'", line.str);
+				goto err;
+			}
+
+			/* state=1 ... read additional response headers
+			 *             (ignored for now) */
+			state=1;
+		}
+
+		if (line.len==2)	/* only the \r\n terminators */
+			break;
+	}
+
+	if (rc!=200)
+		g_warning("unexpected HTTP response code: %s", resp);
+
+	g_free(resp);
+	g_free(line.str);
+	return rc;
+
+err:
+	g_free(resp);
+	g_free(line.str);
+	return -1;
+}
+
+static GIOChannel *
+network_proxy_http_connect(struct network_proxy const *proxy, IPADDR const *hint_ip,
+			   char const *address, int port)
+{
+	struct _network_proxy_http	*self = container_of(proxy, struct _network_proxy_http, proxy);
+	GIOChannel			*ch;
+	GIOFlags			old_flags;
+	GError				*err = NULL;
+	gchar const			*line_term;
+	gint				line_term_sz;
+
+	if (hint_ip)
+		ch = net_connect_ip(hint_ip, self->proxy.port, NULL);
+	else
+		ch = net_connect(self->proxy.host, self->proxy.port, NULL);
+
+	if (!ch)
+		return NULL;
+
+	/* set \r\n line delims */
+	line_term = g_io_channel_get_line_term(ch, &line_term_sz);
+	g_io_channel_set_line_term(ch, "\r\n", 2);
+
+	/* set to non-blocking */
+	old_flags = g_io_channel_get_flags(ch);
+	if (g_io_channel_set_flags(ch, old_flags & ~G_IO_FLAG_NONBLOCK, &err)!=G_IO_STATUS_NORMAL)
+		goto err;
+
+	if (!send_connect(self, ch, address, port) ||
+	    read_response(self, ch)!=200)
+		goto err;
+
+	if (g_io_channel_set_flags(ch, old_flags, &err)!=G_IO_STATUS_NORMAL)
+		goto err;
+
+	g_io_channel_set_line_term(ch, line_term, line_term_sz);
+	return ch;
+err:
+	if (err) {
+		g_warning("something went wrong while preparing HTTP proxy request: %s",
+			  err->message);
+		g_error_free(err);
+	}
+
+	net_disconnect(ch);
+	return NULL;
+
+}
+
+
+struct network_proxy *
+_network_proxy_http_create(void)
+{
+	struct _network_proxy_http	*res;
+
+	res = g_malloc0(sizeof *res);
+
+	_network_proxy_create(&res->proxy);
+	res->password    = g_strdup(settings_get_str("proxy_password"));
+
+	res->proxy.destroy = network_proxy_http_destroy;
+	res->proxy.connect = network_proxy_http_connect;
+	res->proxy.clone   = network_proxy_http_clone;
+
+	return &res->proxy;
+}
diff --git a/src/core/network-proxy-http.h b/src/core/network-proxy-http.h
new file mode 100644
index 0000000..92405fa
--- /dev/null
+++ b/src/core/network-proxy-http.h
@@ -0,0 +1,29 @@
+/*	--*- c -*--
+ * Copyright (C) 2008 Enrico Scholz <[email protected]>
+ *
+ * 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; version 2 and/or 3 of the License.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef H_IRSSI_SRC_CORE_PROXY_HTTP_H
+#define H_IRSSI_SRC_CORE_PROXY_HTTP_H
+
+#include "network-proxy.h"
+
+struct _network_proxy_http {
+	struct network_proxy	proxy;
+	char const		*password;
+};
+
+struct network_proxy *		_network_proxy_http_create(void);
+
+#endif	/* H_IRSSI_SRC_CORE_PROXY_HTTP_H */
-- 
1.5.4.1