Home
Reading
Searching
Subscribe
Sponsors
Statistics
Posting
Contact
Spam
Lists
Links
About
Hosting
Filtering
Features
Download
Marketing
Archives
FAQ
Blog
|
|
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).
Signed-off-by: Enrico Scholz
---
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 | 27 +++++----
src/irc/core/irc-servers.c | 36 ++++++------
12 files changed, 305 insertions(+), 52 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 2ed1618..24d3f76 100644
--- a/src/core/network-openssl.c
+++ b/src/core/network-openssl.c
@@ -307,11 +307,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
+ *
+ * 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
+
+/* 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
+ *
+ * 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
+
+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
+ *
+ * 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
+#include
+
+/* 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 f0503f3..197bd74 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
@@ -160,7 +161,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;
@@ -217,6 +218,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 a3a4eb4..b9fb71a 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 87bcf56..f83fd94 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 66eb35e..44ddf3c 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);
}
@@ -558,6 +554,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 2e9d11f..eaeaab6 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],
@@ -619,11 +626,9 @@ 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);
g_free_not_null(conn->chatnet);
diff --git a/src/irc/core/irc-servers.c b/src/irc/core/irc-servers.c
index bb846a0..589bcce 100644
--- a/src/irc/core/irc-servers.c
+++ b/src/irc/core/irc-servers.c
@@ -38,6 +38,7 @@
#include "servers-reconnect.h"
#include "servers-redirect.h"
#include "modes.h"
+#include "network-proxy.h"
#include "settings.h"
@@ -98,29 +99,31 @@ static void send_message(SERVER_REC *server, const char
*target,
g_free(str);
}
+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 */
cmd = g_strdup_printf("PASS %s", conn->password);
@@ -153,11 +156,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.5.4.1
|
|