forked from PsychoticNinja/irssi
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'
This commit is contained in:
parent
47419298ef
commit
bb276c0b80
338
src/core/network-proxy-socks5.c
Normal file
338
src/core/network-proxy-socks5.c
Normal file
@ -0,0 +1,338 @@
|
||||
/* --*- c -*--
|
||||
* Copyright (C) 2008 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 and/or 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "module.h"
|
||||
#include "network-proxy-socks5.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "network.h"
|
||||
#include "network-proxy-priv.h"
|
||||
|
||||
/* RFC 1928 */
|
||||
struct client_greeting
|
||||
{
|
||||
uint8_t ver;
|
||||
uint8_t nmethods;
|
||||
uint8_t methods[];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct server_greeting
|
||||
{
|
||||
uint8_t ver;
|
||||
uint8_t method;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct server_response_plain
|
||||
{
|
||||
uint8_t ver;
|
||||
uint8_t status;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct client_request
|
||||
{
|
||||
uint8_t ver;
|
||||
uint8_t cmd;
|
||||
uint8_t rsv;
|
||||
uint8_t atyp;
|
||||
uint8_t dst[];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct server_response
|
||||
{
|
||||
uint8_t ver;
|
||||
uint8_t rep;
|
||||
uint8_t res;
|
||||
uint8_t atyp;
|
||||
uint8_t bnd[];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
static void
|
||||
network_proxy_socks5_destroy(struct network_proxy *proxy)
|
||||
{
|
||||
struct _network_proxy_socks5 *self = container_of(proxy, struct _network_proxy_socks5, proxy);
|
||||
|
||||
g_free((void *)self->password);
|
||||
g_free((void *)self->username);
|
||||
_network_proxy_destroy(proxy);
|
||||
g_free(self);
|
||||
}
|
||||
|
||||
static struct network_proxy *
|
||||
network_proxy_socks5_clone(struct network_proxy const *proxy)
|
||||
{
|
||||
struct _network_proxy_socks5 *self = container_of(proxy, struct _network_proxy_socks5, proxy);
|
||||
struct _network_proxy_socks5 *res;
|
||||
|
||||
res = g_malloc0(sizeof *res);
|
||||
|
||||
_network_proxy_clone(&res->proxy, &self->proxy);
|
||||
res->username = g_strdup(self->username);
|
||||
res->password = g_strdup(self->password);
|
||||
return &res->proxy;
|
||||
}
|
||||
|
||||
static bool
|
||||
socks5_connect_unauthorized(GIOChannel *ch)
|
||||
{
|
||||
/* nothing to do here */
|
||||
(void)ch;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* TODO: test this method! */
|
||||
static bool
|
||||
socks5_connect_plain(struct _network_proxy_socks5 const *proxy, GIOChannel *ch)
|
||||
{
|
||||
uint8_t ver = 0x01;
|
||||
uint8_t ulen = strlen(proxy->username);
|
||||
uint8_t plen = proxy->password ? strlen(proxy->password) : 0;
|
||||
struct server_response_plain resp;
|
||||
|
||||
if (ulen==0 ||
|
||||
!_network_proxy_send_all(ch, &ver, sizeof ver) ||
|
||||
!_network_proxy_send_all(ch, &ulen, sizeof ulen) ||
|
||||
!_network_proxy_send_all(ch, proxy->username, ulen) ||
|
||||
!_network_proxy_send_all(ch, &plen, sizeof plen) ||
|
||||
(plen>0 && !_network_proxy_send_all(ch, proxy->password, plen)) ||
|
||||
!_network_proxy_flush(ch) ||
|
||||
!_network_proxy_recv_all(ch, &resp, sizeof resp))
|
||||
return false;
|
||||
|
||||
if (resp.ver!=0x01) {
|
||||
g_warning("unexpected plaintext response version %#04x", resp.ver);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (resp.status!=0x00) {
|
||||
g_warning("socks5 authentication error (%#04x)", resp.status);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
socks5_connect(struct _network_proxy_socks5 const *proxy, GIOChannel *ch,
|
||||
char const *address, uint16_t port)
|
||||
{
|
||||
bool rc;
|
||||
|
||||
struct server_greeting s_greeting;
|
||||
struct server_response s_response;
|
||||
|
||||
|
||||
/* Phase 1: exchange greeting */
|
||||
{
|
||||
struct client_greeting c_greeting = {
|
||||
.ver = 0x05,
|
||||
.nmethods = proxy->username && proxy->username[0] ? 2 : 1
|
||||
};
|
||||
/* HACK: order is important because it depends upon
|
||||
* c_greeting.nmethods */
|
||||
char const methods[] = {
|
||||
0x00, /* no authentication */
|
||||
0x02 /* username/password */
|
||||
};
|
||||
if (!_network_proxy_send_all(ch, &c_greeting, sizeof c_greeting) ||
|
||||
!_network_proxy_send_all(ch, methods, c_greeting.nmethods) ||
|
||||
!_network_proxy_flush(ch) ||
|
||||
!_network_proxy_recv_all(ch, &s_greeting, sizeof s_greeting))
|
||||
goto err;
|
||||
|
||||
if (s_greeting.ver!=5) {
|
||||
g_warning("version mismatch during initial socks5 greeting; got version %#04x",
|
||||
s_greeting.ver);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
/* Phase 2: authentication */
|
||||
{
|
||||
switch (s_greeting.method) {
|
||||
case 0x00: rc = socks5_connect_unauthorized(ch); break;
|
||||
case 0x02: rc = socks5_connect_plain(proxy, ch); break;
|
||||
default:
|
||||
g_warning("unsupported authentication method %#04x", s_greeting.method);
|
||||
rc = false;
|
||||
}
|
||||
|
||||
if (!rc)
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Phase 3: connection request */
|
||||
{
|
||||
struct client_request c_request = {
|
||||
.ver = 0x05,
|
||||
.cmd = 0x01, /* CONNECT */
|
||||
.atyp = 0x03, /* domain name */
|
||||
};
|
||||
uint8_t address_len = strlen(address);
|
||||
uint16_t dst_port = htons(port);
|
||||
uint16_t bnd_port;
|
||||
char bnd_address[257];
|
||||
|
||||
if (!_network_proxy_send_all(ch, &c_request, sizeof c_request) ||
|
||||
!_network_proxy_send_all(ch, &address_len, sizeof address_len) ||
|
||||
!_network_proxy_send_all(ch, address, address_len) ||
|
||||
!_network_proxy_send_all(ch, &dst_port, sizeof dst_port) ||
|
||||
!_network_proxy_flush(ch) ||
|
||||
!_network_proxy_recv_all(ch, &s_response, sizeof s_response))
|
||||
goto err;
|
||||
|
||||
if (s_response.ver != 0x05) {
|
||||
g_warning("version mismatch in socks5 response; got version %#04x",
|
||||
s_response.ver);
|
||||
goto err;
|
||||
}
|
||||
|
||||
rc = false;
|
||||
switch (s_response.rep) {
|
||||
case 0x00: rc = true; break; /* succeeded */
|
||||
case 0x01: g_warning("SOCKS5: general SOCKS server failure"); break;
|
||||
case 0x02: g_warning("SOCKS5: connection not allowed by ruleset"); break;
|
||||
case 0x03: g_warning("SOCKS5: Network unreachable"); break;
|
||||
case 0x04: g_warning("SOCKS5: Host unreachable"); break;
|
||||
case 0x05: g_warning("SOCKS5: Connection refused"); break;
|
||||
case 0x06: g_warning("SOCKS5: TTL expired"); break;
|
||||
case 0x07: g_warning("SOCKS5: Command not supported"); break;
|
||||
case 0x08: g_warning("SOCKS5: Address type not supported"); break;
|
||||
default: g_warning("SOCKS5: unknown error %#04x", s_response.rep); break;
|
||||
}
|
||||
|
||||
if (!rc)
|
||||
goto err;
|
||||
|
||||
switch(s_response.atyp) {
|
||||
case 0x01: {
|
||||
struct in_addr ip;
|
||||
if (!_network_proxy_recv_all(ch, &ip, sizeof ip) ||
|
||||
!inet_ntop(AF_INET, &ip, bnd_address, sizeof bnd_address))
|
||||
rc = false;
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x04: {
|
||||
struct in6_addr ip;
|
||||
if (!_network_proxy_recv_all(ch, &ip, sizeof ip) ||
|
||||
!inet_ntop(AF_INET6, &ip, bnd_address, sizeof bnd_address))
|
||||
rc = false;
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x03: {
|
||||
uint8_t tmp;
|
||||
if (!_network_proxy_recv_all(ch, &tmp, sizeof tmp) ||
|
||||
tmp==0 ||
|
||||
!_network_proxy_recv_all(ch, &bnd_address, tmp))
|
||||
rc = false;
|
||||
else
|
||||
bnd_address[tmp] = '\0';
|
||||
}
|
||||
|
||||
default:
|
||||
g_warning("SOCKS5: unsupported address family in response: %#04x",
|
||||
s_response.atyp);
|
||||
rc = false;
|
||||
}
|
||||
|
||||
if (!rc ||
|
||||
!_network_proxy_recv_all(ch, &bnd_port, sizeof bnd_port))
|
||||
goto err;
|
||||
|
||||
bnd_port = ntohs(bnd_port);
|
||||
g_debug("SOCKS5: bound to %s:%u", bnd_address, bnd_port);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
err:
|
||||
g_warning("connecting through socks5 proxy failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static GIOChannel *
|
||||
network_proxy_socks5_connect(struct network_proxy const *proxy, IPADDR const *hint_ip,
|
||||
char const *address, int port)
|
||||
{
|
||||
struct _network_proxy_socks5 *self = container_of(proxy, struct _network_proxy_socks5, proxy);
|
||||
GIOChannel *ch;
|
||||
|
||||
GIOFlags old_flags;
|
||||
gchar const *old_enc;
|
||||
gboolean old_buf;
|
||||
GError *err = NULL;
|
||||
|
||||
if (hint_ip)
|
||||
ch = net_connect_ip(hint_ip, self->proxy.port, NULL);
|
||||
else
|
||||
ch = net_connect(self->proxy.host, self->proxy.port, NULL);
|
||||
|
||||
if (!ch)
|
||||
return NULL;
|
||||
|
||||
old_enc = g_io_channel_get_encoding(ch);
|
||||
old_flags = g_io_channel_get_flags(ch);
|
||||
old_buf = g_io_channel_get_buffered(ch);
|
||||
|
||||
if (g_io_channel_set_encoding(ch, NULL, &err)!=G_IO_STATUS_NORMAL ||
|
||||
g_io_channel_set_flags(ch, old_flags & ~G_IO_FLAG_NONBLOCK, &err)!=G_IO_STATUS_NORMAL)
|
||||
goto err;
|
||||
|
||||
g_io_channel_set_buffered(ch, false);
|
||||
|
||||
if (!socks5_connect(self, ch, address, port))
|
||||
goto err;
|
||||
|
||||
g_io_channel_set_buffered(ch, old_buf);
|
||||
|
||||
if (g_io_channel_set_flags(ch, old_flags, &err) !=G_IO_STATUS_NORMAL ||
|
||||
g_io_channel_set_encoding(ch, old_enc, &err)!=G_IO_STATUS_NORMAL)
|
||||
goto err;
|
||||
|
||||
return ch;
|
||||
|
||||
err:
|
||||
if (err) {
|
||||
g_warning("something went wrong while preparing SOCKS5 proxy request: %s",
|
||||
err->message);
|
||||
g_error_free(err);
|
||||
}
|
||||
|
||||
net_disconnect(ch);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct network_proxy *
|
||||
_network_proxy_socks5_create(void)
|
||||
{
|
||||
struct _network_proxy_socks5 *res;
|
||||
|
||||
res = g_malloc0(sizeof *res);
|
||||
|
||||
_network_proxy_create(&res->proxy);
|
||||
res->username = g_strdup(settings_get_str("proxy_username"));
|
||||
res->password = g_strdup(settings_get_str("proxy_password"));
|
||||
|
||||
res->proxy.destroy = network_proxy_socks5_destroy;
|
||||
res->proxy.connect = network_proxy_socks5_connect;
|
||||
res->proxy.clone = network_proxy_socks5_clone;
|
||||
|
||||
return &res->proxy;
|
||||
}
|
31
src/core/network-proxy-socks5.h
Normal file
31
src/core/network-proxy-socks5.h
Normal file
@ -0,0 +1,31 @@
|
||||
/* --*- c -*--
|
||||
* Copyright (C) 2008 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 and/or 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef H_IRSSI_SRC_CORE_PROXY_SOCKS5_H
|
||||
#define H_IRSSI_SRC_CORE_PROXY_SOCKS5_H
|
||||
|
||||
#include "network-proxy.h"
|
||||
|
||||
struct _network_proxy_socks5 {
|
||||
struct network_proxy proxy;
|
||||
|
||||
char const *username;
|
||||
char const *password;
|
||||
};
|
||||
|
||||
struct network_proxy * _network_proxy_socks5_create(void);
|
||||
|
||||
#endif /* H_IRSSI_SRC_CORE_PROXY_SOCKS5_H */
|
Loading…
x
Reference in New Issue
Block a user