This commit is contained in:
Bob Mottram 2016-08-24 21:02:02 +00:00 committed by GitHub
commit 5b6b9f1af8
20 changed files with 1063 additions and 50 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, 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 -> bar joins, bar changes his nick to foo, foo splits again ->
Glib warning "is already in split list (how?)" .. :) Glib warning "is already in split list (how?)" .. :)

View file

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

View file

@ -565,11 +565,12 @@ static GIOChannel *irssi_ssl_get_iochannel(GIOChannel *handle, int port, SERVER_
return gchan; 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; 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) if (handle == NULL)
return NULL; return NULL;
ssl_handle = irssi_ssl_get_iochannel(handle, port, server); ssl_handle = irssi_ssl_get_iochannel(handle, port, server);

View file

@ -0,0 +1,201 @@
/* --*- 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 = (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(struct network_proxy const *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,
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 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(struct network_proxy const *proxy, IPADDR const *hint_ip,
char const *address, int port)
{
struct network_proxy_http *self = (struct network_proxy_http *)proxy->privdata;
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, 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,28 @@
/* --*- 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 {
char *password;
};
struct network_proxy *network_proxy_http_create(void);
#endif /* H_IRSSI_SRC_CORE_PROXY_HTTP_H */

View file

@ -0,0 +1,118 @@
/* --*- 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>
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, 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 = (gchar *)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 */

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,339 @@
/* --*- 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 = (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(struct network_proxy const *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,
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 = (struct network_proxy_socks5 *)proxy->privdata;
GIOChannel *ch;
GIOFlags old_flags;
gchar const *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);
g_io_channel_set_buffered(ch, true);
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;
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,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_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 /* H_IRSSI_SRC_CORE_PROXY_SOCKS5_H */

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;
}

84
src/core/network-proxy.h Normal file
View file

@ -0,0 +1,84 @@
/* --*- 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 *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 {
/* 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 *, IPADDR const *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 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 */

View file

@ -20,6 +20,7 @@
#include "module.h" #include "module.h"
#include "network.h" #include "network.h"
#include "network-proxy.h"
#include <sys/un.h> #include <sys/un.h>
@ -145,7 +146,7 @@ GIOChannel *net_connect(const char *addr, int port, IPADDR *my_ip)
} }
/* Connect to socket with ip address */ /* 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; union sockaddr_union so;
int handle, ret, opt = 1; int handle, ret, opt = 1;
@ -196,6 +197,16 @@ GIOChannel *net_connect_ip(IPADDR *ip, int port, IPADDR *my_ip)
return g_io_channel_new(handle); 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 */ /* Connect to named UNIX socket */
GIOChannel *net_connect_unix(const char *path) GIOChannel *net_connect_unix(const char *path)
{ {

View file

@ -29,6 +29,7 @@ struct _IPADDR {
#define IPADDR_IS_V6(ip) ((ip)->family != AF_INET) #define IPADDR_IS_V6(ip) ((ip)->family != AF_INET)
struct network_proxy;
extern IPADDR ip4_any; extern IPADDR ip4_any;
GIOChannel *g_io_channel_new(int handle); GIOChannel *g_io_channel_new(int handle);
@ -39,10 +40,14 @@ int net_ip_compare(IPADDR *ip1, IPADDR *ip2);
/* Connect to socket */ /* Connect to socket */
GIOChannel *net_connect(const char *addr, int port, IPADDR *my_ip) G_GNUC_DEPRECATED; GIOChannel *net_connect(const char *addr, int port, IPADDR *my_ip) G_GNUC_DEPRECATED;
/* Connect to socket with ip address and SSL*/ /* 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); int irssi_ssl_handshake(GIOChannel *handle);
/* Connect to socket with ip address */ /* 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 */ /* Connect to named UNIX socket */
GIOChannel *net_connect_unix(const char *path); GIOChannel *net_connect_unix(const char *path);
/* Disconnect socket */ /* Disconnect socket */

View file

@ -5,10 +5,7 @@ int chat_type; /* chat_protocol_lookup(xx) */
int refcount; int refcount;
/* if we're connecting via proxy, or just NULLs */ struct network_proxy *proxy;
char *proxy;
int proxy_port;
char *proxy_string, *proxy_string_after, *proxy_password;
unsigned short family; /* 0 = don't care, AF_INET or AF_INET6 */ unsigned short family; /* 0 = don't care, AF_INET or AF_INET6 */
char *tag; /* try to keep this tag when connected to server */ char *tag; /* try to keep this tag when connected to server */

View file

@ -29,6 +29,7 @@
#include "servers-reconnect.h" #include "servers-reconnect.h"
#include "settings.h" #include "settings.h"
#include "network-proxy.h"
GSList *reconnects; GSList *reconnects;
static int last_reconnect_tag; static int last_reconnect_tag;
@ -157,11 +158,7 @@ server_connect_copy_skeleton(SERVER_CONNECT_REC *src, int connect_info)
server_connect_ref(dest); server_connect_ref(dest);
dest->type = module_get_uniq_id("SERVER CONNECT", 0); dest->type = module_get_uniq_id("SERVER CONNECT", 0);
dest->reconnection = src->reconnection; dest->reconnection = src->reconnection;
dest->proxy = g_strdup(src->proxy); dest->proxy = src->proxy ? src->proxy->clone(src->proxy) : NULL;
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->tag = g_strdup(src->tag); dest->tag = g_strdup(src->tag);

View file

@ -28,6 +28,7 @@
#include "chatnets.h" #include "chatnets.h"
#include "servers.h" #include "servers.h"
#include "servers-setup.h" #include "servers-setup.h"
#include "network-proxy.h"
GSList *setupservers; GSList *setupservers;
@ -129,15 +130,6 @@ static void server_setup_fill(SERVER_CONNECT_REC *conn,
conn->username = g_strdup(settings_get_str("user_name")); conn->username = g_strdup(settings_get_str("user_name"));
conn->realname = g_strdup(settings_get_str("real_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 */ /* source IP */
if (source_host_ip4 != NULL) { if (source_host_ip4 != NULL) {
conn->own_ip4 = g_new(IPADDR, 1); conn->own_ip4 = g_new(IPADDR, 1);
@ -148,6 +140,10 @@ static void server_setup_fill(SERVER_CONNECT_REC *conn,
memcpy(conn->own_ip6, source_host_ip6, sizeof(IPADDR)); 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); signal_emit("server setup fill connect", 1, conn);
} }
@ -597,7 +593,9 @@ void servers_setup_init(void)
settings_add_int("proxy", "proxy_port", 6667); settings_add_int("proxy", "proxy_port", 6667);
settings_add_str("proxy", "proxy_string", "CONNECT %s %d"); settings_add_str("proxy", "proxy_string", "CONNECT %s %d");
settings_add_str("proxy", "proxy_string_after", ""); settings_add_str("proxy", "proxy_string_after", "");
settings_add_str("proxy", "proxy_username", "");
settings_add_str("proxy", "proxy_password", ""); settings_add_str("proxy", "proxy_password", "");
settings_add_str("proxy", "proxy_type", "simple");
setupservers = NULL; setupservers = NULL;
source_host_ip4 = source_host_ip6 = NULL; source_host_ip4 = source_host_ip6 = NULL;

View file

@ -34,6 +34,7 @@
#include "servers-setup.h" #include "servers-setup.h"
#include "channels.h" #include "channels.h"
#include "queries.h" #include "queries.h"
#include "network-proxy.h"
GSList *servers, *lookup_servers; GSList *servers, *lookup_servers;
@ -219,10 +220,15 @@ static void server_real_connect(SERVER_REC *server, IPADDR *ip,
if (ip != NULL) { if (ip != NULL) {
own_ip = IPADDR_IS_V6(ip) ? server->connrec->own_ip6 : server->connrec->own_ip4; own_ip = IPADDR_IS_V6(ip) ? server->connrec->own_ip6 : server->connrec->own_ip4;
port = server->connrec->proxy != NULL ? port = server->connrec->port;
server->connrec->proxy_port : server->connrec->port;
handle = server->connrec->use_ssl ? 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 { } else {
handle = net_connect_unix(unix_socket); handle = net_connect_unix(unix_socket);
} }
@ -419,7 +425,7 @@ int server_start_connect(SERVER_REC *server)
server->connect_pipe[1] = g_io_channel_new(fd[1]); server->connect_pipe[1] = g_io_channel_new(fd[1]);
connect_address = server->connrec->proxy != NULL ? connect_address = server->connrec->proxy != NULL ?
server->connrec->proxy : server->connrec->address; server->connrec->proxy->host : server->connrec->address;
server->connect_pid = server->connect_pid =
net_gethostbyname_nonblock(connect_address, net_gethostbyname_nonblock(connect_address,
server->connect_pipe[1], server->connect_pipe[1],
@ -614,10 +620,8 @@ void server_connect_unref(SERVER_CONNECT_REC *conn)
if (conn->connect_handle != NULL) if (conn->connect_handle != NULL)
net_disconnect(conn->connect_handle); net_disconnect(conn->connect_handle);
g_free_not_null(conn->proxy); if (conn->proxy)
g_free_not_null(conn->proxy_string); conn->proxy->destroy(conn->proxy);
g_free_not_null(conn->proxy_string_after);
g_free_not_null(conn->proxy_password);
g_free_not_null(conn->tag); g_free_not_null(conn->tag);
g_free_not_null(conn->address); g_free_not_null(conn->address);

View file

@ -41,6 +41,7 @@
#include "servers-reconnect.h" #include "servers-reconnect.h"
#include "servers-redirect.h" #include "servers-redirect.h"
#include "modes.h" #include "modes.h"
#include "network-proxy.h"
#include "settings.h" #include "settings.h"
#include "recode.h" #include "recode.h"
@ -213,22 +214,19 @@ static void server_init(IRC_SERVER_REC *server)
IRC_SERVER_CONNECT_REC *conn; IRC_SERVER_CONNECT_REC *conn;
char *address, *ptr, *username, *cmd; char *address, *ptr, *username, *cmd;
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); g_return_if_fail(server != NULL);
conn = server->connrec; conn = server->connrec;
if (conn->proxy != NULL && conn->proxy_password != NULL && if (conn->proxy && conn->proxy->send_string)
*conn->proxy_password != '\0') { conn->proxy->send_string(conn->proxy, &send_info);
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->sasl_mechanism != SASL_MECHANISM_NONE) if (conn->sasl_mechanism != SASL_MECHANISM_NONE)
cap_toggle(server, "sasl", TRUE); cap_toggle(server, "sasl", TRUE);
@ -269,11 +267,8 @@ static void server_init(IRC_SERVER_REC *server)
g_free(cmd); g_free(cmd);
g_free(username); g_free(username);
if (conn->proxy != NULL && conn->proxy_string_after != NULL) { if (conn->proxy && conn->proxy->send_string_after)
cmd = g_strdup_printf(conn->proxy_string_after, conn->address, conn->port); conn->proxy->send_string_after(conn->proxy, &send_info);
irc_send_cmd_now(server, cmd);
g_free(cmd);
}
server->isupport = g_hash_table_new((GHashFunc) g_istr_hash, server->isupport = g_hash_table_new((GHashFunc) g_istr_hash,
(GCompareFunc) g_istr_equal); (GCompareFunc) g_istr_equal);
@ -344,6 +339,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' */ /* Returns TRUE if `command' is sent to `target' */
static int command_has_target(const char *cmd, const char *target) static int command_has_target(const char *cmd, const char *target)
{ {

View file

@ -136,6 +136,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_send_data(IRC_SERVER_REC *server, const char *data, int len);
void irc_server_init_isupport(IRC_SERVER_REC *server); 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_start_cmd_timeout(void);
void irc_servers_init(void); void irc_servers_init(void);