Compare commits

...

15 Commits

Author SHA1 Message Date
hawken93
510fa50c07 remove container_of, remove some underscores in the process 2014-10-05 01:03:46 +02:00
hawken93
c668e1add0 add missing proxy_username setting 2014-10-05 01:02:50 +02:00
hawken93
c2505203d0 Removed casts in g_free(), therefore also removed some const declarations 2014-10-04 23:22:50 +02:00
hawken93
67d343fd3a Fix compile warnings 2014-10-04 01:14:19 +02:00
hawken93
17ecad8285 Fixed stuff broken by the coding style cleanup 2014-10-04 00:52:26 +02:00
hawken93
714b0241cb Change coding style 2014-10-03 23:41:57 +02:00
hawken93
a35e5b4f9d Change coding style 2014-10-03 23:30:37 +02:00
hawken93
f153922759 no patching unrelated stuff 2014-10-03 17:43:50 +02:00
hawken93
d5727e1648 Porting some stuff to modern git version 2014-10-03 17:34:24 +02:00
Enrico Scholz
9fcd69b9ef http-proxy: fixed bad return value 2014-10-03 15:06:20 +02:00
Enrico Scholz
2e89752708 PROXY: merge proxy methods into buildsystem
This patch adds the code and rules to build the various proxy methods.
2014-10-03 15:06:20 +02:00
Enrico Scholz
bb276c0b80 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'
2014-10-03 15:06:20 +02:00
Enrico Scholz
47419298ef 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'
2014-10-03 15:06:20 +02:00
Enrico Scholz
3300e07f29 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
2014-10-03 15:06:20 +02:00
Enrico Scholz
0236ee5eaa 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).
2014-10-03 15:06:20 +02:00
20 changed files with 994 additions and 49 deletions

5
TODO
View File

@ -1,3 +1,8 @@
- New proxy code crashes if an invalid proxy_type setting is used
- Remove old socks code
- Lots of warnings at least when using socks5
- Clean up coding style
19:36 [IRCNet] [muzzy] more bugs in irssi, apparently the new version: foo splits out,
bar joins, bar changes his nick to foo, foo splits again ->
Glib warning "is already in split list (how?)" .. :)

View File

@ -31,6 +31,15 @@ libcore_a_SOURCES = \
net-sendbuffer.c \
network.c \
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 \
pidwait.c \

View File

@ -551,12 +551,12 @@ static GIOChannel *irssi_ssl_get_iochannel(GIOChannel *handle, int port, SERVER_
return gchan;
}
GIOChannel *net_connect_ip_ssl(IPADDR *ip, int port, IPADDR *my_ip, SERVER_REC *server)
GIOChannel *net_connect_proxy_ssl(const struct network_proxy *proxy, const char *host, int port,
IPADDR *ip, IPADDR *my_ip, SERVER_REC *server)
{
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, port, server);

View File

@ -0,0 +1,201 @@
/*
network-proxy-http.c : irssi
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 = (struct network_proxy_http *)proxy->privdata;
g_free(self->password);
g_free(self);
_network_proxy_destroy(proxy);
g_free(proxy);
}
static struct network_proxy *network_proxy_http_clone(const struct network_proxy *proxy)
{
struct network_proxy_http *self = (struct network_proxy_http *)proxy->privdata;
struct network_proxy_http *priv;
struct network_proxy *res;
res = g_malloc0(sizeof(struct network_proxy));
_network_proxy_clone(res, proxy);
priv = g_malloc0(sizeof(struct network_proxy_http));
res->privdata = (void *)priv;
priv->password = g_strdup(self->password);
return res;
}
static bool send_connect(struct network_proxy_http *proxy, GIOChannel *ch,
const char *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 false;
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(const struct network_proxy *proxy, const IPADDR *hint_ip,
const char *address, int port)
{
struct network_proxy_http *self = (struct network_proxy_http *)proxy->privdata;
GIOChannel *ch;
GIOFlags old_flags;
GError *err = NULL;
const gchar *line_term;
gint line_term_sz;
if (hint_ip)
ch = net_connect_ip(hint_ip, proxy->port, NULL);
else
ch = net_connect(proxy->host, 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 *res;
struct network_proxy_http *priv;
res = g_malloc0(sizeof(struct network_proxy));
_network_proxy_create(res);
priv = g_malloc0(sizeof(struct network_proxy_http));
res->privdata = (void *)priv;
priv->password = g_strdup(settings_get_str("proxy_password"));
res->destroy = network_proxy_http_destroy;
res->connect = network_proxy_http_connect;
res->clone = network_proxy_http_clone;
return res;
}

View File

@ -0,0 +1,12 @@
#ifndef H_IRSSI_SRC_CORE_PROXY_HTTP_H
#define H_IRSSI_SRC_CORE_PROXY_HTTP_H
#include "network-proxy.h"
struct network_proxy_http {
char *password;
};
struct network_proxy *network_proxy_http_create(void);
#endif

View File

@ -0,0 +1,100 @@
#ifndef H_IRSSI_SRC_CORE_PROXY_PRIV_H
#define H_IRSSI_SRC_CORE_PROXY_PRIV_H
#include "settings.h"
#include <stdbool.h>
inline static void _network_proxy_create(struct network_proxy *dst)
{
// TODO: Initialize all fields, to bring the struct to a known state
dst->privdata = NULL;
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, const struct network_proxy *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(proxy->host);
}
inline static bool _network_proxy_send_all(GIOChannel *ch, const void *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

View File

@ -0,0 +1,128 @@
/*
network-proxy-simple.c : irssi
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 = (struct network_proxy_simple *)proxy->privdata;
g_free(self->password);
g_free(self->string_after);
g_free(self->string);
g_free(self);
_network_proxy_destroy(proxy);
// We are responsible for the whole proxy struct
g_free(proxy);
}
static struct network_proxy *network_proxy_simple_clone(const struct network_proxy *proxy)
{
struct network_proxy_simple *self = (struct network_proxy_simple *)proxy->privdata;
struct network_proxy *res;
struct network_proxy_simple *newself;
// First make and set the parent struct
res = g_malloc0(sizeof(struct network_proxy));
_network_proxy_clone(res, proxy);
// Then allocate and set the private data
newself = g_malloc0(sizeof(struct network_proxy_simple));
res->privdata = (void *)newself;
newself->string = g_strdup(self->string);
newself->string_after = g_strdup(self->string_after);
newself->password = g_strdup(self->password);
return res;
}
static GIOChannel *network_proxy_simple_connect(const struct network_proxy *proxy,
const IPADDR *hint_ip, char const *address, int port)
{
if (hint_ip)
return net_connect_ip(hint_ip, proxy->port, NULL);
else
return net_connect(proxy->host, proxy->port, NULL);
}
static void network_proxy_simple_send_string(const struct network_proxy *proxy,
const struct network_proxy_send_string_info *info)
{
struct network_proxy_simple *self = (struct network_proxy_simple *)proxy->privdata;
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(const struct network_proxy *proxy,
const struct network_proxy_send_string_info *info)
{
struct network_proxy_simple *self = (struct network_proxy_simple *)proxy->privdata;
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 *proxy;
struct network_proxy_simple *self;
proxy = g_malloc0(sizeof(struct network_proxy));
// assume it could reset every variable to a known state
_network_proxy_create(proxy);
self = g_malloc0(sizeof(struct network_proxy_simple));
proxy->privdata = (void *)self;
self->string = g_strdup(settings_get_str("proxy_string"));
self->string_after = g_strdup(settings_get_str("proxy_string_after"));
self->password = g_strdup(settings_get_str("proxy_password"));
proxy->destroy = network_proxy_simple_destroy;
proxy->connect = network_proxy_simple_connect;
proxy->clone = network_proxy_simple_clone;
proxy->send_string = network_proxy_simple_send_string;
proxy->send_string_after = network_proxy_simple_send_string_after;
return proxy;
}

View File

@ -0,0 +1,14 @@
#ifndef H_IRSSI_SRC_CORE_PROXY_SIMPLE_H
#define H_IRSSI_SRC_CORE_PROXY_SIMPLE_H
#include "network-proxy.h"
struct network_proxy_simple {
char *string_after;
char *string;
char *password;
};
struct network_proxy *network_proxy_simple_create(void);
#endif

View File

@ -0,0 +1,343 @@
/*
network-proxy-socks5.c : irssi
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 = (struct network_proxy_socks5 *)proxy->privdata;
g_free(self->password);
g_free(self->username);
g_free(self);
_network_proxy_destroy(proxy);
g_free(proxy);
}
static struct network_proxy *network_proxy_socks5_clone(const struct network_proxy *proxy)
{
struct network_proxy_socks5 *self = (struct network_proxy_socks5 *)proxy->privdata;
struct network_proxy_socks5 *priv;
struct network_proxy *res;
res = g_malloc0(sizeof(struct network_proxy));
_network_proxy_clone(res, proxy);
priv = g_malloc0(sizeof(struct network_proxy_socks5));
res->privdata = (void *)priv;
priv->username = g_strdup(self->username);
priv->password = g_strdup(self->password);
return res;
}
static bool socks5_connect_unauthorized(GIOChannel *ch)
{
/* nothing to do here */
(void)ch;
return true;
}
/* TODO: test this method! */
static bool socks5_connect_plain(const struct network_proxy_socks5 *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(const struct network_proxy_socks5 *proxy, GIOChannel *ch,
const char *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(const struct network_proxy *proxy, const IPADDR *hint_ip,
const char *address, int port)
{
struct network_proxy_socks5 *self = (struct network_proxy_socks5 *)proxy->privdata;
GIOChannel *ch;
GIOFlags old_flags;
const gchar *old_enc;
gboolean old_buf;
GError *err = NULL;
if (hint_ip)
ch = net_connect_ip(hint_ip, proxy->port, NULL);
else
ch = net_connect(proxy->host, 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 *res;
struct network_proxy_socks5 *priv;
res = g_malloc0(sizeof(struct network_proxy));
_network_proxy_create(res);
priv = g_malloc0(sizeof(struct network_proxy_socks5));
res->privdata = (void *)priv;
priv->username = g_strdup(settings_get_str("proxy_username"));
priv->password = g_strdup(settings_get_str("proxy_password"));
res->destroy = network_proxy_socks5_destroy;
res->connect = network_proxy_socks5_connect;
res->clone = network_proxy_socks5_clone;
return res;
}

View File

@ -0,0 +1,13 @@
#ifndef H_IRSSI_SRC_CORE_PROXY_SOCKS5_H
#define H_IRSSI_SRC_CORE_PROXY_SOCKS5_H
#include "network-proxy.h"
struct network_proxy_socks5 {
char *username;
char *password;
};
struct network_proxy *network_proxy_socks5_create(void);
#endif

43
src/core/network-proxy.c Normal file
View File

@ -0,0 +1,43 @@
/*
network-proxy.c : irssi
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>
#include "network-proxy-simple.h"
#include "network-proxy-http.h"
#include "network-proxy-socks5.h"
struct network_proxy *network_proxy_create(const char *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;
}

65
src/core/network-proxy.h Normal file
View File

@ -0,0 +1,65 @@
#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 {
const char *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, const char *);
/* object for func */
void *obj;
};
struct network_proxy {
/* Contains private data for the chosen proxy type */
void *privdata;
/* 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)(const struct network_proxy *, const IPADDR *hint_ip,
const char *address, int port);
/* clones the given network_proxy object; this memberfunction is
* mandatory */
struct network_proxy *(*clone)(const struct network_proxy *);
/* sends a string after connection has been established but before IRC
* authentication begins; this memberfunction is optional
*/
void (*send_string)(const struct network_proxy *,
const struct network_proxy_send_string_info *);
/* sends a string after connection IRC authentication suceeded; this
* memberfunction is optional
*/
void (*send_string_after)(const struct network_proxy *,
const struct network_proxy_send_string_info *);
/* hostname of proxy host */
char *host;
/* portnumber of proxy */
int port;
};
/* factory method to create a proxy object based upon value of 'type' */
struct network_proxy *network_proxy_create(const char *type);
#endif

View File

@ -20,6 +20,7 @@
#include "module.h"
#include "network.h"
#include "network-proxy.h"
#include <sys/un.h>
@ -169,7 +170,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(const IPADDR *ip, int port, IPADDR *my_ip)
{
union sockaddr_union so;
int handle, ret, opt = 1;
@ -226,6 +227,16 @@ GIOChannel *net_connect_ip(IPADDR *ip, int port, IPADDR *my_ip)
return g_io_channel_new(handle);
}
/* Connect to socket */
GIOChannel *net_connect_proxy(const struct network_proxy *proxy,
const char *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)
{

View File

@ -39,6 +39,7 @@ struct _IPADDR {
#define IPADDR_IS_V6(ip) ((ip)->family != AF_INET)
struct network_proxy;
extern IPADDR ip4_any;
GIOChannel *g_io_channel_new(int handle);
@ -49,10 +50,13 @@ 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, SERVER_REC *server);
GIOChannel *net_connect_proxy_ssl(const struct network_proxy *proxy, const char *host, int port,
IPADDR *ip, IPADDR *my_ip, SERVER_REC *server);
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_ip(const IPADDR *ip, int port, IPADDR *my_ip);
GIOChannel *net_connect_proxy(const struct network_proxy *proxy, const char *host, int port,
IPADDR *ip, IPADDR *my_ip);
/* Connect to named UNIX socket */
GIOChannel *net_connect_unix(const char *path);
/* Disconnect socket */

View File

@ -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 */

View File

@ -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);

View File

@ -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);
}
@ -545,7 +541,9 @@ void servers_setup_init(void)
settings_add_int("proxy", "proxy_port", 6667);
settings_add_str("proxy", "proxy_string", "CONNECT %s %d");
settings_add_str("proxy", "proxy_string_after", "");
settings_add_str("proxy", "proxy_username", "");
settings_add_str("proxy", "proxy_password", "");
settings_add_str("proxy", "proxy_type", "simple");
setupservers = NULL;
source_host_ip4 = source_host_ip6 = NULL;

View File

@ -34,6 +34,7 @@
#include "servers-setup.h"
#include "channels.h"
#include "queries.h"
#include "network-proxy.h"
GSList *servers, *lookup_servers;
@ -221,10 +222,15 @@ 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) : net_connect_ip(ip, port, own_ip);
net_connect_proxy_ssl(server->connrec->proxy,
server->connrec->address, port,
ip, own_ip,
server) :
net_connect_proxy(server->connrec->proxy,
server->connrec->address, port,
ip, own_ip);
} else {
handle = net_connect_unix(unix_socket);
}
@ -421,7 +427,7 @@ int server_start_connect(SERVER_REC *server)
server->connect_pipe[1] = g_io_channel_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],
@ -616,10 +622,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);

View File

@ -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"
@ -195,23 +196,19 @@ static void server_init(IRC_SERVER_REC *server)
IRC_SERVER_CONNECT_REC *conn;
char *address, *ptr, *username, *cmd;
GTimeVal now;
const struct network_proxy_send_string_info 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 */
@ -245,11 +242,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);
@ -319,6 +313,11 @@ void irc_server_connect(SERVER_REC *server)
}
}
void irc_send_cmd_now_wrapper(void *server, const char *cmd)
{
return irc_send_cmd_now((IRC_SERVER_REC *)server, cmd);
}
/* Returns TRUE if `command' is sent to `target' */
static int command_has_target(const char *cmd, const char *target)
{

View File

@ -125,6 +125,8 @@ void irc_server_send_away(IRC_SERVER_REC *server, const char *reason);
void irc_server_send_data(IRC_SERVER_REC *server, const char *data, int len);
void irc_server_init_isupport(IRC_SERVER_REC *server);
void irc_send_cmd_now_wrapper(void *server, const char *cmd);
void irc_servers_start_cmd_timeout(void);
void irc_servers_init(void);