irssi-import / bugs.irssi.org

bugs.irssi.org archive
https://github.com/irssi/irssi/issues
0 stars 0 forks source link

[PATCH] Proxy enhancements (incl. native SOCKS5 support) #691

Open irssibot opened 15 years ago

irssibot commented 15 years ago

These patches were reported more than a year ago to the maillist, but they were ignored there. While upgrading to 0.8.13 I had to rediff them against and provide them through this bugtracker.

irssibot commented 15 years ago

0001-PROXY-implemented-native-proxy-support.patch

From cf560c898025bde50c6285d8bccce9dcb430d445 Mon Sep 17 00:00:00 2001
From: Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
Date: Tue, 26 Feb 2008 17:29:10 +0100
Subject: [PATCH 1/5] PROXY: implemented native proxy support

This patch creates a hook into the net_connect*() methods which call a
method to connect to a proxy.

Previous solution to send certain strings in the normal IRC dialog was
some kind of hack as most proxies require some kind of negotation.

E.g. HTTP proxies sent a 'HTTP/1.0 200 Connection established' HTTP header
and clients have to wait for it.  Else, sent bytes of the following IRC
login will be dropped silently.

With old method, it is also impossible to tunnel SSL IRC connections
through the proxy as proxy speaks plain text or a special protocol while
e.g. 'CONNECT ... HTTP/1.0' will be encrypted with key of IRC server.

There are further enhancements possible: the whole net_connect stuff
should be made asynchronously. Currently, only the hostname is resolved
in the background (which makes little sense of local proxies usually).
---
 src/core/Makefile.am          |    3 +
 src/core/network-openssl.c    |    4 +-
 src/core/network-proxy-priv.h |  128 +++++++++++++++++++++++++++++++++++++++++
 src/core/network-proxy.c      |   30 ++++++++++
 src/core/network-proxy.h      |   81 ++++++++++++++++++++++++++
 src/core/network.c            |   15 +++++-
 src/core/network.h            |    6 +-
 src/core/server-connect-rec.h |    5 +-
 src/core/servers-reconnect.c  |    7 +--
 src/core/servers-setup.c      |   15 ++---
 src/core/servers.c            |   25 +++++---
 src/irc/core/irc-servers.c    |   34 ++++++------
 12 files changed, 303 insertions(+), 50 deletions(-)
 create mode 100644 src/core/network-proxy-priv.h
 create mode 100644 src/core/network-proxy.c
 create mode 100644 src/core/network-proxy.h

diff --git a/src/core/Makefile.am b/src/core/Makefile.am
index 59ae0a8..f60dedf 100644
--- a/src/core/Makefile.am
+++ b/src/core/Makefile.am
@@ -31,6 +31,9 @@ libcore_a_SOURCES = \
    net-sendbuffer.c \
    network.c \
    network-openssl.c \
+   network-proxy.c \
+   network-proxy.h \
+   network-proxy-priv.h \
    nicklist.c \
    nickmatch-cache.c \
    pidwait.c \
diff --git a/src/core/network-openssl.c b/src/core/network-openssl.c
index f78a497..4700427 100644
--- a/src/core/network-openssl.c
+++ b/src/core/network-openssl.c
@@ -333,11 +333,11 @@ static GIOChannel *irssi_ssl_get_iochannel(GIOChannel *handle, const char *mycer
    return gchan;
 }

-GIOChannel *net_connect_ip_ssl(IPADDR *ip, int port, IPADDR *my_ip, const char *cert, const char *pkey, const char *cafile, const char *capath, gboolean verify)
+GIOChannel *net_connect_proxy_ssl(struct network_proxy const *proxy, char const *host, int port, IPADDR *ip, IPADDR *my_ip, const char *cert, const char *pkey, const char *cafile, const char *capath, gboolean verify)
 {
    GIOChannel *handle, *ssl_handle;

-   handle = net_connect_ip(ip, port, my_ip);
+   handle = net_connect_proxy(proxy, host, port, ip, my_ip);
    if (handle == NULL)
        return NULL;
    ssl_handle  = irssi_ssl_get_iochannel(handle, cert, pkey, cafile, capath, verify);
diff --git a/src/core/network-proxy-priv.h b/src/core/network-proxy-priv.h
new file mode 100644
index 0000000..0a8f244
--- /dev/null
+++ b/src/core/network-proxy-priv.h
@@ -0,0 +1,128 @@
+/* --*- c -*--
+ * Copyright (C) 2008 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
+ *
+ * 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_PRIV_H
+#define H_IRSSI_SRC_CORE_PROXY_PRIV_H
+
+#include "settings.h"
+#include <stdbool.h>
+
+/* stolen from linux kernel */
+#define container_of(ptr, type, member)  __extension__ ({      \
+   const __typeof__( ((type *)0)->member ) *__mptr = (ptr);    \
+   (type *)( (char *)__mptr - offsetof(type,member) );})
+
+
+inline static void
+_network_proxy_create(struct network_proxy *dst)
+{
+   dst->port  = settings_get_int("proxy_port");
+   dst->host  = g_strdup(settings_get_str("proxy_address"));
+}
+
+inline static void
+_network_proxy_clone(struct network_proxy *dst, struct network_proxy const *src)
+{
+   dst->host  = g_strdup(src->host);
+   dst->port  = src->port;
+
+   dst->destroy = src->destroy;
+   dst->connect = src->connect;
+   dst->clone   = src->clone;
+}
+
+inline static void
+_network_proxy_destroy(struct network_proxy *proxy)
+{
+   g_free((void *)proxy->host);
+}
+
+
+
+inline static bool
+_network_proxy_send_all(GIOChannel *ch, void const *buf, ssize_t len)
+{
+   GError              *err = NULL;
+   gsize               written;
+   GIOStatus           status;
+
+   while ((status=g_io_channel_write_chars(ch, buf, len, &written,
+                       &err))==G_IO_STATUS_AGAIN)
+       continue;
+
+   if (status==G_IO_STATUS_NORMAL)
+       return true;
+
+   if (err) {
+       g_warning("failed to send proxy request: %s", err->message);
+       g_error_free(err);
+   }
+
+   return false;
+}
+
+inline static bool
+_network_proxy_recv_all(GIOChannel *ch, void *buf_v, size_t len)
+{
+   GError              *err = NULL;
+   gchar               *buf = buf_v;
+
+   while (len>0) {
+       GIOStatus       status;
+       gsize           l;
+
+       status = g_io_channel_read_chars(ch, buf, len, &l, &err);
+       if (status==G_IO_STATUS_AGAIN)
+           continue;
+       if (status!=G_IO_STATUS_NORMAL)
+           break;
+
+       buf += l;
+       len -= l;
+   }
+
+   if (len==0)
+       return true;
+
+   if (err) {
+       g_warning("failed to send proxy request: %s", err->message);
+       g_error_free(err);
+   }
+
+   return false;
+}
+
+inline static bool
+_network_proxy_flush(GIOChannel *ch)
+{
+   GError          *err = NULL;
+   GIOStatus       status;
+
+   while ((status=g_io_channel_flush(ch, &err))==G_IO_STATUS_AGAIN)
+       continue;
+
+   if (status==G_IO_STATUS_NORMAL)
+       return true;
+
+   if (err) {
+       g_warning("failed to flush proxy channel: %s", err->message);
+       g_error_free(err);
+   }
+
+   return false;
+}
+
+#endif /* H_IRSSI_SRC_CORE_PROXY_PRIV_H */
diff --git a/src/core/network-proxy.c b/src/core/network-proxy.c
new file mode 100644
index 0000000..cedf96b
--- /dev/null
+++ b/src/core/network-proxy.c
@@ -0,0 +1,30 @@
+/* --*- c -*--
+ * Copyright (C) 2008 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
+ *
+ * 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.h"
+#include <string.h>
+
+struct network_proxy *
+network_proxy_create(char const *type)
+{
+   if (type==NULL)
+       return NULL;
+
+   g_error("unsupported proxy type '%s'", type);
+   return NULL;
+}
diff --git a/src/core/network-proxy.h b/src/core/network-proxy.h
new file mode 100644
index 0000000..cdc3d05
--- /dev/null
+++ b/src/core/network-proxy.h
@@ -0,0 +1,81 @@
+/* --*- c -*--
+ * Copyright (C) 2008 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
+ *
+ * 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_H
+#define H_IRSSI_SRC_CORE_PROXY_H
+
+#include <glib.h>
+#include <stdint.h>
+
+/* helper structure for the send_string*() functions of the network_proxy
+ * class */
+struct network_proxy_send_string_info
+{
+   char const      *host;  /* hostname of the IRC server */
+   uint16_t        port;   /* portnumber of the IRC server */
+
+   /* function which is used to send string; usually irc_send_cmd_now() */
+   void            (*func)(void *obj, char const *);
+
+   /* object for func */
+   void            *obj;
+};
+
+struct network_proxy {
+   /* destroys the network_proxy structure which must not be used anymore
+    * after; this memberfunction is mandatory */
+   void            (*destroy)(struct network_proxy *);
+
+   /* connects through the proxy; this memberfunction is mandatory
+    *
+    * \arg hint_ip   the asynchronously resolved ip of the proxy; when
+    *                NULL, method will resolve it itself
+    * \arg address   the hostname where proxy shall connect to
+    * \arg port      port address where proxy shall connect to
+    */
+   GIOChannel *        (*connect)(struct network_proxy const *, IPADDR const *hint_ip,
+                      char const *address, int port);
+
+   /* clones the given network_proxy object; this memberfunction is
+    * mandatory */
+   struct network_proxy *  (*clone)(struct network_proxy const *);
+
+
+   /* sends a string after connection has been established but before IRC
+    * authentication begins; this memberfunction is optional
+    */
+   void            (*send_string)(struct network_proxy const *,
+                          struct network_proxy_send_string_info const *);
+
+   /* sends a string after connection IRC authentication suceeded; this
+    * memberfunction is optional
+    */
+   void            (*send_string_after)(struct network_proxy const *,
+                            struct network_proxy_send_string_info const *);
+
+
+   /* hostname of proxy host */
+   char const      *host;
+
+   /* portnumber of proxy */
+   int         port;
+};
+
+/* factory method to create a proxy object based upon value of 'type' */
+struct network_proxy *     network_proxy_create(char const *type);
+
+
+#endif /* H_IRSSI_SRC_CORE_PROXY_H */
diff --git a/src/core/network.c b/src/core/network.c
index c2aa3de..66d1b05 100644
--- a/src/core/network.c
+++ b/src/core/network.c
@@ -20,6 +20,7 @@

 #include "module.h"
 #include "network.h"
+#include "network-proxy.h"

 #include <sys/un.h>

@@ -158,7 +159,7 @@ GIOChannel *net_connect(const char *addr, int port, IPADDR *my_ip)
 }

 /* Connect to socket with ip address */
-GIOChannel *net_connect_ip(IPADDR *ip, int port, IPADDR *my_ip)
+GIOChannel *net_connect_ip(IPADDR const *ip, int port, IPADDR *my_ip)
 {
    union sockaddr_union so;
    int handle, ret, opt = 1;
@@ -215,6 +216,18 @@ GIOChannel *net_connect_ip(IPADDR *ip, int port, IPADDR *my_ip)
    return g_io_channel_new(handle);
 }

+/* Connect to socket */
+GIOChannel *net_connect_proxy(struct network_proxy const *proxy,
+                 char const *host, int port, IPADDR *ip, IPADDR *my_ip)
+{
+
+   if (proxy)
+       return proxy->connect(proxy, ip, host, port);
+   else
+       return net_connect_ip(ip, port, my_ip);
+}
+
+
 /* Connect to named UNIX socket */
 GIOChannel *net_connect_unix(const char *path)
 {
diff --git a/src/core/network.h b/src/core/network.h
index 65505ea..80cd984 100644
--- a/src/core/network.h
+++ b/src/core/network.h
@@ -39,6 +39,7 @@ struct _IPADDR {

 #define IPADDR_IS_V6(ip) ((ip)->family != AF_INET)

+struct network_proxy;
 extern IPADDR ip4_any;

 /* returns 1 if IPADDRs are the same */
@@ -47,10 +48,11 @@ int net_ip_compare(IPADDR *ip1, IPADDR *ip2);
 /* Connect to socket */
 GIOChannel *net_connect(const char *addr, int port, IPADDR *my_ip);
 /* Connect to socket with ip address and SSL*/
-GIOChannel *net_connect_ip_ssl(IPADDR *ip, int port, IPADDR *my_ip, const char *cert, const char *pkey, const char *cafile, const char *capath, gboolean verify);
+GIOChannel *net_connect_ip(IPADDR const *ip, int port, IPADDR *my_ip);
+GIOChannel *net_connect_proxy_ssl(struct network_proxy const *proxy, char const *host, int port, IPADDR *ip, IPADDR *my_ip, const char *cert, const char *pkey, const char *cafile, const char *capath, gboolean verify);
 int irssi_ssl_handshake(GIOChannel *handle);
 /* Connect to socket with ip address */
-GIOChannel *net_connect_ip(IPADDR *ip, int port, IPADDR *my_ip);
+GIOChannel *net_connect_proxy(struct network_proxy const *proxy, char const *host, int port, IPADDR *ip, IPADDR *my_ip);
 /* Connect to named UNIX socket */
 GIOChannel *net_connect_unix(const char *path);
 /* Disconnect socket */
diff --git a/src/core/server-connect-rec.h b/src/core/server-connect-rec.h
index cfbe3eb..e95ca2f 100644
--- a/src/core/server-connect-rec.h
+++ b/src/core/server-connect-rec.h
@@ -5,10 +5,7 @@ int chat_type; /* chat_protocol_lookup(xx) */

 int refcount;

-/* if we're connecting via proxy, or just NULLs */
-char *proxy;
-int proxy_port;
-char *proxy_string, *proxy_string_after, *proxy_password;
+struct network_proxy   *proxy;

 unsigned short family; /* 0 = don't care, AF_INET or AF_INET6 */
 char *tag; /* try to keep this tag when connected to server */
diff --git a/src/core/servers-reconnect.c b/src/core/servers-reconnect.c
index c8a7262..3a93964 100644
--- a/src/core/servers-reconnect.c
+++ b/src/core/servers-reconnect.c
@@ -29,6 +29,7 @@
 #include "servers-reconnect.h"

 #include "settings.h"
+#include "network-proxy.h"

 GSList *reconnects;
 static int last_reconnect_tag;
@@ -157,11 +158,7 @@ server_connect_copy_skeleton(SERVER_CONNECT_REC *src, int connect_info)
         server_connect_ref(dest);
    dest->type = module_get_uniq_id("SERVER CONNECT", 0);
    dest->reconnection = src->reconnection;
-   dest->proxy = g_strdup(src->proxy);
-        dest->proxy_port = src->proxy_port;
-   dest->proxy_string = g_strdup(src->proxy_string);
-   dest->proxy_string_after = g_strdup(src->proxy_string_after);
-   dest->proxy_password = g_strdup(src->proxy_password);
+   dest->proxy = src->proxy ? src->proxy->clone(src->proxy) : NULL;

    dest->tag = g_strdup(src->tag);

diff --git a/src/core/servers-setup.c b/src/core/servers-setup.c
index 1eb6949..b35ecfc 100644
--- a/src/core/servers-setup.c
+++ b/src/core/servers-setup.c
@@ -28,6 +28,7 @@
 #include "chatnets.h"
 #include "servers.h"
 #include "servers-setup.h"
+#include "network-proxy.h"

 GSList *setupservers;

@@ -126,15 +127,6 @@ static void server_setup_fill(SERVER_CONNECT_REC *conn,
    conn->username = g_strdup(settings_get_str("user_name"));
    conn->realname = g_strdup(settings_get_str("real_name"));

-   /* proxy settings */
-   if (settings_get_bool("use_proxy")) {
-       conn->proxy = g_strdup(settings_get_str("proxy_address"));
-       conn->proxy_port = settings_get_int("proxy_port");
-       conn->proxy_string = g_strdup(settings_get_str("proxy_string"));
-       conn->proxy_string_after = g_strdup(settings_get_str("proxy_string_after"));
-       conn->proxy_password = g_strdup(settings_get_str("proxy_password"));
-   }
-
    /* source IP */
    if (source_host_ip4 != NULL) {
        conn->own_ip4 = g_new(IPADDR, 1);
@@ -145,6 +137,10 @@ static void server_setup_fill(SERVER_CONNECT_REC *conn,
        memcpy(conn->own_ip6, source_host_ip6, sizeof(IPADDR));
    }

+   /* proxy settings */
+   if (settings_get_bool("use_proxy"))
+       conn->proxy = network_proxy_create(settings_get_str("proxy_type"));
+
    signal_emit("server setup fill connect", 1, conn);
 }

@@ -551,6 +547,7 @@ void servers_setup_init(void)
    settings_add_str("proxy", "proxy_string", "CONNECT %s %d");
    settings_add_str("proxy", "proxy_string_after", "");
    settings_add_str("proxy", "proxy_password", "");
+   settings_add_str("proxy", "proxy_type", "simple");

         setupservers = NULL;
    source_host_ip4 = source_host_ip6 = NULL;
diff --git a/src/core/servers.c b/src/core/servers.c
index d5844e7..2511db4 100644
--- a/src/core/servers.c
+++ b/src/core/servers.c
@@ -34,6 +34,7 @@
 #include "servers-setup.h"
 #include "channels.h"
 #include "queries.h"
+#include "network-proxy.h"

 GSList *servers, *lookup_servers;

@@ -221,12 +222,18 @@ static void server_real_connect(SERVER_REC *server, IPADDR *ip,
        own_ip = ip == NULL ? NULL :
            (IPADDR_IS_V6(ip) ? server->connrec->own_ip6 :
             server->connrec->own_ip4);
-       port = server->connrec->proxy != NULL ?
-           server->connrec->proxy_port : server->connrec->port;
+       port = server->connrec->port;
        handle = server->connrec->use_ssl ?
-           net_connect_ip_ssl(ip, port, own_ip, server->connrec->ssl_cert, server->connrec->ssl_pkey,
-server->connrec->ssl_cafile, server->connrec->ssl_capath, server->connrec->ssl_verify) :
-           net_connect_ip(ip, port, own_ip);
+           net_connect_proxy_ssl(server->connrec->proxy,
+                         server->connrec->address, port,
+                         ip, own_ip,
+                         server->connrec->ssl_cert,
+                         server->connrec->ssl_pkey,
+                         server->connrec->ssl_cafile,
+                         server->connrec->ssl_capath, server->connrec->ssl_verify) :
+           net_connect_proxy(server->connrec->proxy,
+                     server->connrec->address, port,
+                     ip, own_ip);
    } else {
        handle = net_connect_unix(unix_socket);
    }
@@ -423,7 +430,7 @@ int server_start_connect(SERVER_REC *server)
        server->connect_pipe[1] = g_io_channel_unix_new(fd[1]);

        connect_address = server->connrec->proxy != NULL ?
-           server->connrec->proxy : server->connrec->address;
+           server->connrec->proxy->host : server->connrec->address;
        server->connect_pid =
            net_gethostbyname_nonblock(connect_address,
                           server->connect_pipe[1],
@@ -618,10 +625,8 @@ void server_connect_unref(SERVER_CONNECT_REC *conn)
    if (conn->connect_handle != NULL)
        net_disconnect(conn->connect_handle);

-   g_free_not_null(conn->proxy);
-   g_free_not_null(conn->proxy_string);
-   g_free_not_null(conn->proxy_string_after);
-   g_free_not_null(conn->proxy_password);
+   if (conn->proxy)
+       conn->proxy->destroy(conn->proxy);

    g_free_not_null(conn->tag);
    g_free_not_null(conn->address);
diff --git a/src/irc/core/irc-servers.c b/src/irc/core/irc-servers.c
index 23960a3..1f3cd86 100644
--- a/src/irc/core/irc-servers.c
+++ b/src/irc/core/irc-servers.c
@@ -37,6 +37,7 @@
 #include "servers-reconnect.h"
 #include "servers-redirect.h"
 #include "modes.h"
+#include "network-proxy.h"

 #include "settings.h"
 #include "recode.h"
@@ -101,28 +102,30 @@ static void send_message(SERVER_REC *server, const char *target,
    g_free(recoded);
 }

+static void
+irc_send_cmd_now_wrapper(void *srv, char const *cmd)
+{
+   irc_send_cmd_now(srv, cmd);
+}
+
 static void server_init(IRC_SERVER_REC *server)
 {
    IRC_SERVER_CONNECT_REC *conn;
    char *address, *ptr, *username, *cmd;
    GTimeVal now;
+   struct network_proxy_send_string_info const send_info = {
+       .host   =  server->connrec->address,
+       .port   =  server->connrec->port,
+       .func   =  irc_send_cmd_now_wrapper,
+       .obj    =  server
+   };

    g_return_if_fail(server != NULL);

    conn = server->connrec;

-   if (conn->proxy != NULL && conn->proxy_password != NULL &&
-       *conn->proxy_password != '\0') {
-       cmd = g_strdup_printf("PASS %s", conn->proxy_password);
-       irc_send_cmd_now(server, cmd);
-       g_free(cmd);
-   }
-
-   if (conn->proxy != NULL && conn->proxy_string != NULL) {
-       cmd = g_strdup_printf(conn->proxy_string, conn->address, conn->port);
-       irc_send_cmd_now(server, cmd);
-       g_free(cmd);
-   }
+   if (conn->proxy && conn->proxy->send_string)
+       conn->proxy->send_string(conn->proxy, &send_info);

    if (conn->password != NULL && *conn->password != '\0') {
                 /* send password */
@@ -156,11 +159,8 @@ static void server_init(IRC_SERVER_REC *server)
    g_free(cmd);
    g_free(username);

-   if (conn->proxy != NULL && conn->proxy_string_after != NULL) {
-       cmd = g_strdup_printf(conn->proxy_string_after, conn->address, conn->port);
-       irc_send_cmd_now(server, cmd);
-       g_free(cmd);
-   }
+   if (conn->proxy && conn->proxy->send_string_after)
+       conn->proxy->send_string_after(conn->proxy, &send_info);

    server->isupport = g_hash_table_new((GHashFunc) g_istr_hash,
                        (GCompareFunc) g_istr_equal);
-- 
1.6.2.5
irssibot commented 15 years ago

PROXY/HTTP: added methods for HTTP proxies

irssibot commented 15 years ago

0002-PROXY-HTTP-added-methods-for-HTTP-proxies.patch

From 7fd800dcb348312fabb1a12024bea168dc21f67c Mon Sep 17 00:00:00 2001
From: Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
Date: Tue, 26 Feb 2008 17:57:14 +0100
Subject: [PATCH 2/5] PROXY/HTTP: added methods for HTTP proxies

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'
---
 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 <enrico.scholz@informatik.tu-chemnitz.de>
+ *
+ * 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 <enrico.scholz@informatik.tu-chemnitz.de>
+ *
+ * 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.6.2.5
irssibot commented 15 years ago

PROXY/SIMPLE: added simple proxy method

irssibot commented 15 years ago

0003-PROXY-SIMPLE-added-simple-proxy-method.patch

From f3ba26386606984a2ac9e57bfe996da587f0a43d Mon Sep 17 00:00:00 2001
From: Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
Date: Tue, 26 Feb 2008 17:06:58 +0100
Subject: [PATCH 3/5] PROXY/SIMPLE: added simple proxy method

This method implements the string + string_after mechanism implemented by
previous irssi versions.

To use, set
* proxy_type to 'simple' or keep it empty
* string + string_after in the known ways
---
 src/core/network-proxy-simple.c |  121 +++++++++++++++++++++++++++++++++++++++
 src/core/network-proxy-simple.h |   32 ++++++++++
 2 files changed, 153 insertions(+), 0 deletions(-)
 create mode 100644 src/core/network-proxy-simple.c
 create mode 100644 src/core/network-proxy-simple.h

diff --git a/src/core/network-proxy-simple.c b/src/core/network-proxy-simple.c
new file mode 100644
index 0000000..a7ac1fa
--- /dev/null
+++ b/src/core/network-proxy-simple.c
@@ -0,0 +1,121 @@
+/* --*- c -*--
+ * Copyright (C) 2008 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
+ *
+ * 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-simple.h"
+
+#include "network-proxy-priv.h"
+#include "network.h"
+
+static void
+network_proxy_simple_destroy(struct network_proxy *proxy)
+{
+   struct _network_proxy_simple    *self = container_of(proxy, struct _network_proxy_simple, proxy);
+
+   g_free((void *)self->password);
+   g_free((void *)self->string_after);
+   g_free((void *)self->string);
+
+   _network_proxy_destroy(proxy);
+
+   g_free(self);
+}
+
+static struct network_proxy *
+network_proxy_simple_clone(struct network_proxy const *proxy)
+{
+   struct _network_proxy_simple    *self = container_of(proxy, struct _network_proxy_simple, proxy);
+   struct _network_proxy_simple    *res;
+
+   res = g_malloc0(sizeof *res);
+
+   _network_proxy_clone(&res->proxy, &self->proxy);
+
+   res->string       = g_strdup(self->string);
+   res->string_after = g_strdup(self->string_after);
+   res->password     = g_strdup(self->password);
+   return &res->proxy;
+}
+
+static GIOChannel *
+network_proxy_simple_connect(struct network_proxy const *proxy, IPADDR const *hint_ip,
+                char const *address, int port)
+{
+   struct _network_proxy_simple    *self = container_of(proxy, struct _network_proxy_simple, proxy);
+
+   (void)address;
+   (void)port;
+   if (hint_ip)
+       return net_connect_ip(hint_ip, self->proxy.port, NULL);
+   else
+       return net_connect(self->proxy.host, self->proxy.port, NULL);
+}
+
+static void
+network_proxy_simple_send_string(struct network_proxy const *proxy,
+                struct network_proxy_send_string_info const *info)
+{
+   struct _network_proxy_simple    *self = container_of(proxy, struct _network_proxy_simple, proxy);
+   char                *cmd;
+
+   if (self->password && self->password[0]) {
+       cmd = g_strdup_printf("PASS %s", self->password);
+       info->func(info->obj, cmd);
+       g_free(cmd);
+   }
+
+   if (self->string && self->string[0]) {
+       cmd = g_strdup_printf(self->string, info->host, info->port);
+       info->func(info->obj, cmd);
+       g_free(cmd);
+   }
+}
+
+static void
+network_proxy_simple_send_string_after(struct network_proxy const *proxy,
+                struct network_proxy_send_string_info const *info)
+{
+   struct _network_proxy_simple    *self = container_of(proxy, struct _network_proxy_simple, proxy);
+   char                *cmd;
+
+   if (self->string_after && self->string_after[0]) {
+       cmd = g_strdup_printf(self->string_after, info->host, info->port);
+       info->func(info->obj, cmd);
+       g_free(cmd);
+   }
+}
+
+struct network_proxy *
+_network_proxy_simple_create(void)
+{
+   struct _network_proxy_simple    *res;
+
+   res = g_malloc0(sizeof *res);
+
+   _network_proxy_create(&res->proxy);
+   res->string        = g_strdup(settings_get_str("proxy_string"));
+   res->string_after  = g_strdup(settings_get_str("proxy_string_after"));
+   res->password      = g_strdup(settings_get_str("proxy_password"));
+
+   res->proxy.destroy = network_proxy_simple_destroy;
+   res->proxy.connect = network_proxy_simple_connect;
+   res->proxy.clone   = network_proxy_simple_clone;
+
+   res->proxy.send_string       = network_proxy_simple_send_string;
+   res->proxy.send_string_after = network_proxy_simple_send_string_after;
+
+   return &res->proxy;
+}
diff --git a/src/core/network-proxy-simple.h b/src/core/network-proxy-simple.h
new file mode 100644
index 0000000..b2132cf
--- /dev/null
+++ b/src/core/network-proxy-simple.h
@@ -0,0 +1,32 @@
+/* --*- c -*--
+ * Copyright (C) 2008 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
+ *
+ * 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_SIMPLE_H
+#define H_IRSSI_SRC_CORE_PROXY_SIMPLE_H
+
+#include "network-proxy.h"
+
+struct _network_proxy_simple {
+   struct network_proxy    proxy;
+
+   char const      *string_after;
+   char const      *string;
+   char const      *password;
+};
+
+struct network_proxy *     _network_proxy_simple_create(void);
+
+#endif /* H_IRSSI_SRC_CORE_PROXY_SIMPLE_H */
-- 
1.6.2.5
irssibot commented 15 years ago

PROXY/SOCKS5: added methods for SOCKS5 proxies

irssibot commented 15 years ago

0004-PROXY-SOCKS5-added-methods-for-SOCKS5-proxies.patch

From 6919a876f08977414f3cbebf770b396660bf9bf3 Mon Sep 17 00:00:00 2001
From: Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
Date: Tue, 26 Feb 2008 18:04:40 +0100
Subject: [PATCH 4/5] PROXY/SOCKS5: added methods for SOCKS5 proxies

This patch adds code for connecting through SOCKS5 proxies. It was
primarily written for use with TOR, so there are some open issues:

* it only allows to make proxy requests with full hostnames; ipv4/ipv6 is
  not supported

* GSSAPI authentication (which is mentioned as mandatory in RFC 1928) is
  not implemented

* plaintext authentication is untested

To use it
* set 'proxy_type' to 'socks5'
---
 src/core/network-proxy-socks5.c |  338 +++++++++++++++++++++++++++++++++++++++
 src/core/network-proxy-socks5.h |   31 ++++
 2 files changed, 369 insertions(+), 0 deletions(-)
 create mode 100644 src/core/network-proxy-socks5.c
 create mode 100644 src/core/network-proxy-socks5.h

diff --git a/src/core/network-proxy-socks5.c b/src/core/network-proxy-socks5.c
new file mode 100644
index 0000000..64a8f51
--- /dev/null
+++ b/src/core/network-proxy-socks5.c
@@ -0,0 +1,338 @@
+/* --*- c -*--
+ * Copyright (C) 2008 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
+ *
+ * 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-socks5.h"
+
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "network.h"
+#include "network-proxy-priv.h"
+
+/* RFC 1928 */
+struct client_greeting
+{
+   uint8_t     ver;
+   uint8_t     nmethods;
+   uint8_t     methods[];
+} __attribute__((__packed__));
+
+struct server_greeting
+{
+   uint8_t     ver;
+   uint8_t     method;
+} __attribute__((__packed__));
+
+struct server_response_plain
+{
+   uint8_t     ver;
+   uint8_t     status;
+} __attribute__((__packed__));
+
+struct client_request
+{
+   uint8_t     ver;
+   uint8_t     cmd;
+   uint8_t     rsv;
+   uint8_t     atyp;
+   uint8_t     dst[];
+} __attribute__((__packed__));
+
+struct server_response
+{
+   uint8_t     ver;
+   uint8_t     rep;
+   uint8_t     res;
+   uint8_t     atyp;
+   uint8_t     bnd[];
+} __attribute__((__packed__));
+
+static void
+network_proxy_socks5_destroy(struct network_proxy *proxy)
+{
+   struct _network_proxy_socks5    *self = container_of(proxy, struct _network_proxy_socks5, proxy);
+
+   g_free((void *)self->password);
+   g_free((void *)self->username);
+   _network_proxy_destroy(proxy);
+   g_free(self);
+}
+
+static struct network_proxy *
+network_proxy_socks5_clone(struct network_proxy const *proxy)
+{
+   struct _network_proxy_socks5    *self = container_of(proxy, struct _network_proxy_socks5, proxy);
+   struct _network_proxy_socks5    *res;
+
+   res = g_malloc0(sizeof *res);
+
+   _network_proxy_clone(&res->proxy, &self->proxy);
+   res->username = g_strdup(self->username);
+   res->password = g_strdup(self->password);
+   return &res->proxy;
+}
+
+static bool
+socks5_connect_unauthorized(GIOChannel *ch)
+{
+   /* nothing to do here */
+   (void)ch;
+   return true;
+}
+
+/* TODO: test this method! */
+static bool
+socks5_connect_plain(struct _network_proxy_socks5 const *proxy, GIOChannel *ch)
+{
+   uint8_t             ver  = 0x01;
+   uint8_t             ulen = strlen(proxy->username);
+   uint8_t             plen = proxy->password ? strlen(proxy->password) : 0;
+   struct server_response_plain    resp;
+
+   if (ulen==0 ||
+       !_network_proxy_send_all(ch, &ver, sizeof ver) ||
+       !_network_proxy_send_all(ch, &ulen, sizeof ulen) ||
+       !_network_proxy_send_all(ch, proxy->username, ulen) ||
+       !_network_proxy_send_all(ch, &plen, sizeof plen) ||
+       (plen>0 && !_network_proxy_send_all(ch, proxy->password, plen)) ||
+       !_network_proxy_flush(ch) ||
+       !_network_proxy_recv_all(ch, &resp, sizeof resp))
+       return false;
+
+   if (resp.ver!=0x01) {
+       g_warning("unexpected plaintext response version %#04x", resp.ver);
+       return false;
+   }
+
+   if (resp.status!=0x00) {
+       g_warning("socks5 authentication error (%#04x)", resp.status);
+       return false;
+   }
+
+   return true;
+}
+
+static bool
+socks5_connect(struct _network_proxy_socks5 const *proxy, GIOChannel *ch,
+          char const *address, uint16_t port)
+{
+   bool                rc;
+
+   struct server_greeting      s_greeting;
+   struct server_response      s_response;
+
+
+   /* Phase 1: exchange greeting */
+   {
+       struct client_greeting      c_greeting = {
+           .ver      = 0x05,
+           .nmethods = proxy->username && proxy->username[0] ? 2 : 1
+       };
+       /* HACK: order is important because it depends upon
+        * c_greeting.nmethods  */
+       char const          methods[] = {
+           0x00,           /* no authentication */
+           0x02            /* username/password */
+       };
+       if (!_network_proxy_send_all(ch, &c_greeting, sizeof c_greeting) ||
+           !_network_proxy_send_all(ch, methods,     c_greeting.nmethods) ||
+           !_network_proxy_flush(ch) ||
+           !_network_proxy_recv_all(ch, &s_greeting, sizeof s_greeting))
+           goto err;
+
+       if (s_greeting.ver!=5) {
+           g_warning("version mismatch during initial socks5 greeting; got version %#04x",
+                 s_greeting.ver);
+           goto err;
+       }
+   }
+
+   /* Phase 2: authentication */
+   {
+       switch (s_greeting.method) {
+       case 0x00: rc = socks5_connect_unauthorized(ch); break;
+       case 0x02: rc = socks5_connect_plain(proxy, ch); break;
+       default:
+           g_warning("unsupported authentication method %#04x", s_greeting.method);
+           rc = false;
+       }
+
+       if (!rc)
+           goto err;
+   }
+
+   /* Phase 3: connection request */
+   {
+       struct client_request       c_request = {
+           .ver      = 0x05,
+           .cmd      = 0x01,   /* CONNECT */
+           .atyp     = 0x03,   /* domain name */
+       };
+       uint8_t             address_len = strlen(address);
+       uint16_t            dst_port = htons(port);
+       uint16_t            bnd_port;
+       char                bnd_address[257];
+
+       if (!_network_proxy_send_all(ch, &c_request,   sizeof c_request) ||
+           !_network_proxy_send_all(ch, &address_len, sizeof address_len) ||
+           !_network_proxy_send_all(ch, address,      address_len) ||
+           !_network_proxy_send_all(ch, &dst_port,    sizeof dst_port) ||
+           !_network_proxy_flush(ch) ||
+           !_network_proxy_recv_all(ch, &s_response,  sizeof s_response))
+           goto err;
+
+       if (s_response.ver != 0x05) {
+           g_warning("version mismatch in socks5 response; got version %#04x",
+                 s_response.ver);
+           goto err;
+       }
+
+       rc = false;
+       switch (s_response.rep) {
+       case 0x00: rc = true; break;    /* succeeded */
+       case 0x01: g_warning("SOCKS5: general SOCKS server failure"); break;
+       case 0x02: g_warning("SOCKS5: connection not allowed by ruleset"); break;
+       case 0x03: g_warning("SOCKS5: Network unreachable"); break;
+       case 0x04: g_warning("SOCKS5: Host unreachable"); break;
+       case 0x05: g_warning("SOCKS5: Connection refused"); break;
+       case 0x06: g_warning("SOCKS5: TTL expired"); break;
+       case 0x07: g_warning("SOCKS5: Command not supported"); break;
+       case 0x08: g_warning("SOCKS5: Address type not supported"); break;
+       default:   g_warning("SOCKS5: unknown error %#04x", s_response.rep); break;
+       }
+
+       if (!rc)
+           goto err;
+
+       switch(s_response.atyp) {
+       case 0x01: {
+           struct in_addr  ip;
+           if (!_network_proxy_recv_all(ch, &ip,     sizeof ip) ||
+               !inet_ntop(AF_INET, &ip, bnd_address, sizeof bnd_address))
+               rc = false;
+           break;
+       }
+
+       case 0x04: {
+           struct in6_addr ip;
+           if (!_network_proxy_recv_all(ch, &ip,      sizeof ip) ||
+               !inet_ntop(AF_INET6, &ip, bnd_address, sizeof bnd_address))
+               rc = false;
+           break;
+       }
+
+       case 0x03: {
+           uint8_t     tmp;
+           if (!_network_proxy_recv_all(ch, &tmp, sizeof tmp) ||
+               tmp==0 ||
+               !_network_proxy_recv_all(ch, &bnd_address, tmp))
+               rc = false;
+           else
+               bnd_address[tmp] = '\0';
+       }
+
+       default:
+           g_warning("SOCKS5: unsupported address family in response: %#04x",
+                 s_response.atyp);
+           rc = false;
+       }
+
+       if (!rc ||
+           !_network_proxy_recv_all(ch, &bnd_port, sizeof bnd_port))
+           goto err;
+
+       bnd_port = ntohs(bnd_port);
+       g_debug("SOCKS5: bound to %s:%u", bnd_address, bnd_port);
+   }
+
+   return true;
+
+err:
+   g_warning("connecting through socks5 proxy failed");
+   return  false;
+}
+
+
+static GIOChannel *
+network_proxy_socks5_connect(struct network_proxy const *proxy, IPADDR const *hint_ip,
+                char const *address, int port)
+{
+   struct _network_proxy_socks5    *self = container_of(proxy, struct _network_proxy_socks5, proxy);
+   GIOChannel          *ch;
+
+   GIOFlags            old_flags;
+   gchar const         *old_enc;
+   gboolean            old_buf;
+   GError              *err = NULL;
+
+   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;
+
+   old_enc   = g_io_channel_get_encoding(ch);
+   old_flags = g_io_channel_get_flags(ch);
+   old_buf   = g_io_channel_get_buffered(ch);
+
+   if (g_io_channel_set_encoding(ch, NULL, &err)!=G_IO_STATUS_NORMAL ||
+       g_io_channel_set_flags(ch, old_flags & ~G_IO_FLAG_NONBLOCK, &err)!=G_IO_STATUS_NORMAL)
+       goto err;
+
+   g_io_channel_set_buffered(ch, false);
+
+   if (!socks5_connect(self, ch, address, port))
+       goto err;
+
+   g_io_channel_set_buffered(ch, old_buf);
+
+   if (g_io_channel_set_flags(ch, old_flags, &err) !=G_IO_STATUS_NORMAL ||
+       g_io_channel_set_encoding(ch, old_enc, &err)!=G_IO_STATUS_NORMAL)
+       goto err;
+
+   return ch;
+
+err:
+   if (err) {
+       g_warning("something went wrong while preparing SOCKS5 proxy request: %s",
+             err->message);
+       g_error_free(err);
+   }
+
+   net_disconnect(ch);
+   return NULL;
+}
+
+struct network_proxy *
+_network_proxy_socks5_create(void)
+{
+   struct _network_proxy_socks5    *res;
+
+   res = g_malloc0(sizeof *res);
+
+   _network_proxy_create(&res->proxy);
+   res->username    = g_strdup(settings_get_str("proxy_username"));
+   res->password    = g_strdup(settings_get_str("proxy_password"));
+
+   res->proxy.destroy = network_proxy_socks5_destroy;
+   res->proxy.connect = network_proxy_socks5_connect;
+   res->proxy.clone   = network_proxy_socks5_clone;
+
+   return &res->proxy;
+}
diff --git a/src/core/network-proxy-socks5.h b/src/core/network-proxy-socks5.h
new file mode 100644
index 0000000..963bad3
--- /dev/null
+++ b/src/core/network-proxy-socks5.h
@@ -0,0 +1,31 @@
+/* --*- c -*--
+ * Copyright (C) 2008 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
+ *
+ * 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_SOCKS5_H
+#define H_IRSSI_SRC_CORE_PROXY_SOCKS5_H
+
+#include "network-proxy.h"
+
+struct _network_proxy_socks5 {
+   struct network_proxy    proxy;
+
+   char const      *username;
+   char const      *password;
+};
+
+struct network_proxy *     _network_proxy_socks5_create(void);
+
+#endif /* H_IRSSI_SRC_CORE_PROXY_SOCKS5_H */
-- 
1.6.2.5
irssibot commented 15 years ago

PROXY: merge proxy methods into buildsystem

irssibot commented 15 years ago

0005-PROXY-merge-proxy-methods-into-buildsystem.patch

From 795ced8616b0be73086ce19cb3bf7b258332c7e4 Mon Sep 17 00:00:00 2001
From: Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
Date: Tue, 26 Feb 2008 18:06:02 +0100
Subject: [PATCH 5/5] PROXY: merge proxy methods into buildsystem

This patch adds the code and rules to build the various proxy methods.
---
 src/core/Makefile.am     |    6 ++++++
 src/core/network-proxy.c |   12 ++++++++++++
 2 files changed, 18 insertions(+), 0 deletions(-)

diff --git a/src/core/Makefile.am b/src/core/Makefile.am
index f60dedf..a1f9b20 100644
--- a/src/core/Makefile.am
+++ b/src/core/Makefile.am
@@ -33,6 +33,12 @@ libcore_a_SOURCES = \
    network-openssl.c \
    network-proxy.c \
    network-proxy.h \
+   network-proxy-simple.c \
+   network-proxy-simple.h \
+   network-proxy-http.c \
+   network-proxy-http.h \
+   network-proxy-socks5.c \
+   network-proxy-socks5.h \
    network-proxy-priv.h \
    nicklist.c \
    nickmatch-cache.c \
diff --git a/src/core/network-proxy.c b/src/core/network-proxy.c
index cedf96b..cb5f941 100644
--- a/src/core/network-proxy.c
+++ b/src/core/network-proxy.c
@@ -18,6 +18,9 @@

 #include "network-proxy.h"
 #include <string.h>
+#include "network-proxy-simple.h"
+#include "network-proxy-http.h"
+#include "network-proxy-socks5.h"

 struct network_proxy *
 network_proxy_create(char const *type)
@@ -25,6 +28,15 @@ network_proxy_create(char const *type)
    if (type==NULL)
        return NULL;

+   if (strcmp(type, "simple")==0 || type[0]=='\0')
+       return _network_proxy_simple_create();
+
+   if (strcmp(type, "http")==0)
+       return _network_proxy_http_create();
+
+   if (strcmp(type, "socks5")==0)
+       return _network_proxy_socks5_create();
+
    g_error("unsupported proxy type '%s'", type);
    return NULL;
 }
-- 
1.6.2.5
irssibot commented 15 years ago

Now managed at http://github.com/ensc/irssi-proxy/tree/master

irssibot commented 14 years ago

This is on the list of things we should really look at before 0.8.16.

I've only taken a very quick look at the patches. The functionality it provides certainly seems useful.

I've got my doubts (but am still undecided) about the "container_of" usage, mostly because it seems inconsistent with how such things are handled elsewhere in irssi code.

irssibot commented 14 years ago
irssibot commented 14 years ago

We've got inheritance without two memory allocations in irssi too (see *-rec.h). I'm not saying that's a better mechanism (I'd even say it definitely isn't), but I'm not sure we want to mix (and am hoping on input from other irssi devs...).

irssibot commented 14 years ago

Sorry I didn't see this patch earlier... a quick search returned #477. Please link bugs and their patches in the future.

Anyway, I've implemented basic http proxy support like this (see attachment)

I see your patch supports more protocols. However, it is not irssi style, so I guess it needs to be rewritten.

Any comments on what to do?

irssibot commented 14 years ago

network-proxy.patch

diff --git a/src/core/Makefile.am b/src/core/Makefile.am
index 59ae0a8..ce8bb64 100644
--- a/src/core/Makefile.am
+++ b/src/core/Makefile.am
@@ -34,6 +34,7 @@ libcore_a_SOURCES = \
    nicklist.c \
    nickmatch-cache.c \
    pidwait.c \
+   proxy.c \
    queries.c \
    rawlog.c \
    recode.c \
@@ -83,6 +84,7 @@ pkginc_core_HEADERS = \
    nicklist.h \
    nickmatch-cache.h \
    pidwait.h \
+   proxy.h \
    queries.h \
    rawlog.h \
    recode.h \
diff --git a/src/core/core.c b/src/core/core.c
index b9debbb..d4d2000 100644
--- a/src/core/core.c
+++ b/src/core/core.c
@@ -40,6 +40,7 @@
 #include "rawlog.h"
 #include "ignore.h"
 #include "recode.h"
+#include "proxy.h"

 #include "channels.h"
 #include "queries.h"
@@ -238,6 +239,7 @@ void core_init(void)
    chatnets_init();
         expandos_init();
    ignore_init();
+   proxy_init();
    servers_init();
         write_buffer_init();
    log_init();
@@ -285,6 +287,7 @@ void core_deinit(void)
    log_deinit();
         write_buffer_deinit();
    servers_deinit();
+   proxy_deinit();
    ignore_deinit();
         expandos_deinit();
    chatnets_deinit();
diff --git a/src/core/proxy.c b/src/core/proxy.c
new file mode 100644
index 0000000..974585d
--- /dev/null
+++ b/src/core/proxy.c
@@ -0,0 +1,108 @@
+/*
+ proxy.c : network proxy support
+
+    Copyright (C) 2010 Jochen Eisinger
+
+    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 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 "module.h"
+#include "proxy.h"
+
+#include "network.h"
+#include "net-sendbuffer.h"
+#include "servers.h"
+#include "signals.h"
+
+static void proxy_server_connected(SERVER_REC *server)
+{
+   server->connect_time = time(NULL);
+   servers = g_slist_append(servers, server);
+   signal_emit("server connected", 1, server);
+}
+
+static void proxy_connect_http_init(SERVER_REC *server, GIOChannel *handle)
+{
+   const char response[] = "HTTP/1.0 200 ";
+   char *str;
+   int error;
+   int ret;
+
+   g_return_if_fail(IS_SERVER(server));
+
+   error = net_geterror(handle);
+   if (error != 0) {
+       server->connection_lost = TRUE;
+       server_connect_failed(server, g_strerror(error));
+       return;
+   }
+
+   if ((ret = net_sendbuffer_receive_line(server->handle, &str, TRUE)) > 0) {
+       if (!strncmp(str, response, strlen(response))) {
+           g_source_remove(server->connect_tag);
+           server->connect_tag = -1;
+           proxy_server_connected(server);
+           return;
+       }
+   }
+
+   if (ret == 0)
+       return;
+
+   server->connection_lost = TRUE;
+   server_connect_failed(server, str);
+}
+
+static void proxy_connect_http(SERVER_REC *server)
+{
+   char *connect_str;
+
+   connect_str = g_strdup_printf("CONNECT %s:%d HTTP/1.0\n\n",
+            server->connrec->proxy != NULL ? server->connrec->proxy : server->connrec->address,
+            server->connrec->proxy != NULL ? server->connrec->proxy_port : server->connrec->port);
+   if (!connect_str || net_sendbuffer_send(server->handle, connect_str, strlen(connect_str))) {
+       server->connection_lost = TRUE;
+       server_connect_failed(server, "HTTP Proxy connect failed");
+       return;
+   }
+   g_free(connect_str);
+
+   server->connect_tag = g_input_add(net_sendbuffer_handle(server->handle),
+                                          G_INPUT_WRITE | G_INPUT_READ,
+                     (GInputFunction)
+                     proxy_connect_http_init,
+                     server);
+}
+
+static void sig_server_proxy_connect(SERVER_REC *server)
+{
+   if (!server->connrec->network_proxy)
+       proxy_server_connected(server);
+
+   if (server->connrec->network_proxy_type && !strcmp(server->connrec->network_proxy_type, "HTTP"))
+       proxy_connect_http(server);
+   else
+       proxy_server_connected(server);
+}
+
+void proxy_init(void)
+{
+   signal_add_last("server proxy connect", sig_server_proxy_connect);
+}
+
+void proxy_deinit(void)
+{
+   signal_remove("server proxy connect", sig_server_proxy_connect);
+}
diff --git a/src/core/proxy.h b/src/core/proxy.h
new file mode 100644
index 0000000..da08a77
--- /dev/null
+++ b/src/core/proxy.h
@@ -0,0 +1,7 @@
+#ifndef __PROXY_H
+#define __PROXY_H
+
+void proxy_init(void);
+void proxy_deinit(void);
+
+#endif
diff --git a/src/core/server-connect-rec.h b/src/core/server-connect-rec.h
index cfbe3eb..8443cb2 100644
--- a/src/core/server-connect-rec.h
+++ b/src/core/server-connect-rec.h
@@ -10,6 +10,11 @@ char *proxy;
 int proxy_port;
 char *proxy_string, *proxy_string_after, *proxy_password;

+/* if we're using a network proxy to connect, or just NULLs */
+char *network_proxy;
+int network_proxy_port;
+char *network_proxy_type;
+
 unsigned short family; /* 0 = don't care, AF_INET or AF_INET6 */
 char *tag; /* try to keep this tag when connected to server */
 char *address;
diff --git a/src/core/servers-reconnect.c b/src/core/servers-reconnect.c
index c8a7262..d16afd2 100644
--- a/src/core/servers-reconnect.c
+++ b/src/core/servers-reconnect.c
@@ -162,6 +162,9 @@ server_connect_copy_skeleton(SERVER_CONNECT_REC *src, int connect_info)
    dest->proxy_string = g_strdup(src->proxy_string);
    dest->proxy_string_after = g_strdup(src->proxy_string_after);
    dest->proxy_password = g_strdup(src->proxy_password);
+   dest->network_proxy = g_strdup(src->network_proxy);
+   dest->network_proxy_port = src->network_proxy_port;
+   dest->network_proxy_type = g_strdup(src->network_proxy_type);

    dest->tag = g_strdup(src->tag);

diff --git a/src/core/servers-setup.c b/src/core/servers-setup.c
index 83b90db..28c58ff 100644
--- a/src/core/servers-setup.c
+++ b/src/core/servers-setup.c
@@ -135,6 +135,12 @@ static void server_setup_fill(SERVER_CONNECT_REC *conn,
        conn->proxy_password = g_strdup(settings_get_str("proxy_password"));
    }

+   if (settings_get_bool("use_network_proxy")) {
+       conn->network_proxy = g_strdup(settings_get_str("network_proxy_address"));
+       conn->network_proxy_port = settings_get_int("network_proxy_port");
+       conn->network_proxy_type = g_strdup(settings_get_str("network_proxy_type"));
+   }
+
    /* source IP */
    if (source_host_ip4 != NULL) {
        conn->own_ip4 = g_new(IPADDR, 1);
@@ -542,6 +548,11 @@ void servers_setup_init(void)
    settings_add_str("proxy", "proxy_string_after", "");
    settings_add_str("proxy", "proxy_password", "");

+   settings_add_bool("network_proxy", "use_network_proxy", FALSE);
+   settings_add_str("network_proxy", "network_proxy_address", "");
+   settings_add_int("network_proxy", "network_proxy_port", 8080);
+   settings_add_str("network_proxy", "network_proxy_type", "HTTP");
+
         setupservers = NULL;
    source_host_ip4 = source_host_ip6 = NULL;
         old_source_host = NULL;
diff --git a/src/core/servers.c b/src/core/servers.c
index d4827b6..78a2faf 100644
--- a/src/core/servers.c
+++ b/src/core/servers.c
@@ -141,10 +141,7 @@ static char *server_create_tag(SERVER_CONNECT_REC *conn)
 /* Connection to server finished, fill the rest of the fields */
 void server_connect_finished(SERVER_REC *server)
 {
-   server->connect_time = time(NULL);
-
-   servers = g_slist_append(servers, server);
-   signal_emit("server connected", 1, server);
+   signal_emit("server proxy connect", 1, server);
 }

 static void server_connect_callback_init(SERVER_REC *server, GIOChannel *handle)
@@ -221,8 +218,12 @@ static void server_real_connect(SERVER_REC *server, IPADDR *ip,
        own_ip = ip == NULL ? NULL :
            (IPADDR_IS_V6(ip) ? server->connrec->own_ip6 :
             server->connrec->own_ip4);
-       port = server->connrec->proxy != NULL ?
-           server->connrec->proxy_port : server->connrec->port;
+                if (server->connrec->network_proxy != NULL)
+           port = server->connrec->network_proxy_port;
+       else if (server->connrec->proxy != NULL)
+           port = server->connrec->proxy_port;
+       else
+           port = server->connrec->port;
        handle = server->connrec->use_ssl ?
            net_connect_ip_ssl(ip, port, server->connrec->address, own_ip, server->connrec->ssl_cert, server->connrec->ssl_pkey,
 server->connrec->ssl_cafile, server->connrec->ssl_capath, server->connrec->ssl_verify) :
@@ -422,8 +423,12 @@ int server_start_connect(SERVER_REC *server)
        server->connect_pipe[0] = g_io_channel_new(fd[0]);
        server->connect_pipe[1] = g_io_channel_new(fd[1]);

-       connect_address = server->connrec->proxy != NULL ?
-           server->connrec->proxy : server->connrec->address;
+                if (server->connrec->network_proxy != NULL)
+           connect_address = server->connrec->network_proxy;
+       else if (server->connrec->proxy != NULL)
+           connect_address = server->connrec->proxy;
+       else
+           connect_address = server->connrec->address;
        server->connect_pid =
            net_gethostbyname_nonblock(connect_address,
                           server->connect_pipe[1],
@@ -623,6 +628,9 @@ void server_connect_unref(SERVER_CONNECT_REC *conn)
    g_free_not_null(conn->proxy_string_after);
    g_free_not_null(conn->proxy_password);

+   g_free_not_null(conn->network_proxy);
+   g_free_not_null(conn->network_proxy_type);
+
    g_free_not_null(conn->tag);
    g_free_not_null(conn->address);
    g_free_not_null(conn->chatnet);
irssibot commented 14 years ago

What is the idea behind this -rec.h stuff? Is it there to support compilers which do not understand nested struct's?

On first glance its seems to be very unportable; e.g. in

--- base-rec.h ---

char x; char y; int z;

--- foo.h ---

struct base {

include "base-rec.h"

};

struct foo { char a;

include "base-rec.h"

};

--

foo::z and base::z will be on different positions relative to the ::x attribute. This means, when you cast the 'base-rec' object in 'foo' to 'base' you will access bad memory.

irssibot commented 14 years ago

it's not about what's better. if you don't intend to rewrite irssi completely, please stick with the current irssi style.

irssibot commented 14 years ago

I do not want to rewrite irssi completely; but I don't want to provide hacky and unportable code (which begins at identifiers like __PROXY_H or _SERVER_CONNECT_REC which are forbidden/reserved by the C standard and continues with random attribute location due to inheritance-by-preprocessor hack) in new contributions.

Because I am using irssi, I am basically willing to rewrite the whole -rec stuff in a portable way. But my experiences with this patchset shows, that I might have to wait other two years until I get a single comment about these changes. As they will touch a lot of code, it will be (too) much work to keep them in sync with upstream.

irssibot commented 14 years ago

That would be a welcome contribution I think, but note that your current code is not portable either (you use __type_of and attribute__ unconditionally).

irssibot commented 14 years ago

struct foo { char a;

include "base-rec.h"

Irssi doesn't do it like that, the include is always the first thing in the struct.

irssibot commented 14 years ago

I looked at the patches again, considering porting them to irssi-style... but a bigger problem with them is that it is doing synchronous io. That's not so easily fixed...

irssibot commented 14 years ago

See #383. See #477.