Add basic support for SOCKS 4 and HTTP CONNECT proxies.

When the Proxy option is used, outgoing connections will be made via the
specified proxy. There is no support for authentication methods or for having
the proxy forward incoming connections, and there is no attempt to proxy UDP.
This commit is contained in:
Guus Sliepen 2012-04-18 23:19:40 +02:00
parent 84531fb6e6
commit b58d95eb29
8 changed files with 211 additions and 10 deletions

View file

@ -456,8 +456,27 @@ specified in the configuration file.
When this option is used the priority of the tincd process will be adjusted. When this option is used the priority of the tincd process will be adjusted.
Increasing the priority may help to reduce latency and packet loss on the VPN. Increasing the priority may help to reduce latency and packet loss on the VPN.
.It Va Proxy Li = Ar type Ar address Ar port Oo Ar username Oc Bq experimental
Use the proxy at the given
.Ar address
and
.Ar port
when making outgoing connections.
The following proxy types are currently supported:
.Bl -tag -width indent
.It socks4
Connects to the proxy using the SOCKS version 4 protocol.
Optionally, a
.Ar username
can be supplied which will be passed on to the proxy server.
.It http
Connects to the proxy and sends a HTTP CONNECT request.
.El
No authentication methods are currently supported.
.It Va ReplayWindow Li = Ar bytes Pq 16 .It Va ReplayWindow Li = Ar bytes Pq 16
This is the size of the replay tracking window for each remote node, in bytes. vhis is the size of the replay tracking window for each remote node, in bytes.
The window is a bitfield which tracks 1 packet per bit, so for example The window is a bitfield which tracks 1 packet per bit, so for example
the default setting of 16 will track up to 128 packets in the window. In high the default setting of 16 will track up to 128 packets in the window. In high
bandwidth scenarios, setting this to a higher value can reduce packet loss from bandwidth scenarios, setting this to a higher value can reduce packet loss from

View file

@ -1050,6 +1050,25 @@ specified in the configuration file.
When this option is used the priority of the tincd process will be adjusted. When this option is used the priority of the tincd process will be adjusted.
Increasing the priority may help to reduce latency and packet loss on the VPN. Increasing the priority may help to reduce latency and packet loss on the VPN.
@cindex Proxy
@item Proxy = <@var{type}> <@var{address}> <@var{port}> [<@var{username}>] [experimental]
Use the proxy at the given @var{address} and @var{port} when making outgoing connections.
The following proxy types are currently supported:
@table @asis
@cindex socks4
@item socks4
Connects to the proxy using the SOCKS version 4 protocol.
Optionally, a @var{username} can be supplied which will be passed on to the proxy server.
@cindex http
@item http
Connects to the proxy and sends a HTTP CONNECT request.
@end table
No authentication methods are currently supported.
@cindex ReplayWindow @cindex ReplayWindow
@item ReplayWindow = <bytes> (16) @item ReplayWindow = <bytes> (16)
This is the size of the replay tracking window for each remote node, in bytes. This is the size of the replay tracking window for each remote node, in bytes.

View file

@ -177,7 +177,15 @@ bool receive_meta(connection_t *c) {
if(c->tcplen) { if(c->tcplen) {
if(c->tcplen <= c->buflen) { if(c->tcplen <= c->buflen) {
receive_tcppacket(c, c->buffer, c->tcplen); if(proxytype == PROXY_SOCKS4 && c->allow_request == ID) {
if(c->buffer[0] == 0 && c->buffer[1] == 0x5a) {
logger(LOG_DEBUG, "Proxy request granted");
} else {
logger(LOG_ERR, "Proxy request rejected");
return false;
}
} else
receive_tcppacket(c, c->buffer, c->tcplen);
c->buflen -= c->tcplen; c->buflen -= c->tcplen;
lenin -= c->tcplen - oldlen; lenin -= c->tcplen - oldlen;

View file

@ -122,6 +122,20 @@ extern time_t now;
extern int contradicting_add_edge; extern int contradicting_add_edge;
extern int contradicting_del_edge; extern int contradicting_del_edge;
extern char *proxyhost;
extern char *proxyport;
extern char *proxyuser;
extern char *proxypass;
typedef enum proxytype_t {
PROXY_NONE = 0,
PROXY_SOCKS4,
PROXY_SOCKS4A,
PROXY_SOCKS5,
PROXY_HTTP,
PROXY_EXEC,
} proxytype_t;
extern proxytype_t proxytype;
extern volatile bool running; extern volatile bool running;
/* Yes, very strange placement indeed, but otherwise the typedefs get all tangled up */ /* Yes, very strange placement indeed, but otherwise the typedefs get all tangled up */

View file

@ -47,6 +47,12 @@
char *myport; char *myport;
devops_t devops; devops_t devops;
char *proxyhost;
char *proxyport;
char *proxyuser;
char *proxypass;
proxytype_t proxytype;
bool read_rsa_public_key(connection_t *c) { bool read_rsa_public_key(connection_t *c) {
FILE *fp; FILE *fp;
char *fname; char *fname;
@ -316,6 +322,8 @@ static bool setup_myself(void) {
char *name, *hostname, *mode, *afname, *cipher, *digest, *type; char *name, *hostname, *mode, *afname, *cipher, *digest, *type;
char *fname = NULL; char *fname = NULL;
char *address = NULL; char *address = NULL;
char *proxy = NULL;
char *space;
char *envp[5]; char *envp[5];
struct addrinfo *ai, *aip, hint = {0}; struct addrinfo *ai, *aip, hint = {0};
bool choice; bool choice;
@ -359,6 +367,68 @@ static bool setup_myself(void) {
sockaddr2str(&sa, NULL, &myport); sockaddr2str(&sa, NULL, &myport);
} }
get_config_string(lookup_config(config_tree, "Proxy"), &proxy);
if(proxy) {
if((space = strchr(proxy, ' ')))
*space++ = 0;
if(!strcasecmp(proxy, "none")) {
proxytype = PROXY_NONE;
} else if(!strcasecmp(proxy, "socks4")) {
proxytype = PROXY_SOCKS4;
} else if(!strcasecmp(proxy, "socks4a")) {
proxytype = PROXY_SOCKS4A;
} else if(!strcasecmp(proxy, "socks5")) {
proxytype = PROXY_SOCKS5;
} else if(!strcasecmp(proxy, "http")) {
proxytype = PROXY_HTTP;
} else if(!strcasecmp(proxy, "exec")) {
proxytype = PROXY_EXEC;
} else {
logger(LOG_ERR, "Unknown proxy type %s!", proxy);
return false;
}
switch(proxytype) {
case PROXY_NONE:
default:
break;
case PROXY_EXEC:
if(!space || !*space) {
logger(LOG_ERR, "Argument expected for proxy type exec!");
return false;
}
proxyhost = xstrdup(space);
break;
case PROXY_SOCKS4:
case PROXY_SOCKS4A:
case PROXY_SOCKS5:
case PROXY_HTTP:
proxyhost = space;
if(space && (space = strchr(space, ' ')))
*space++ = 0, proxyport = space;
if(space && (space = strchr(space, ' ')))
*space++ = 0, proxyuser = space;
if(space && (space = strchr(space, ' ')))
*space++ = 0, proxypass = space;
if(!proxyhost || !*proxyhost || !proxyport || !*proxyport) {
logger(LOG_ERR, "Host and port argument expected for proxy!");
return false;
}
proxyhost = xstrdup(proxyhost);
proxyport = xstrdup(proxyport);
if(proxyuser && *proxyuser)
proxyuser = xstrdup(proxyuser);
if(proxypass && *proxypass)
proxyuser = xstrdup(proxypass);
break;
}
free(proxy);
}
/* Read in all the subnets specified in the host configuration file */ /* Read in all the subnets specified in the host configuration file */
cfg = lookup_config(config_tree, "Subnet"); cfg = lookup_config(config_tree, "Subnet");

View file

@ -303,6 +303,7 @@ void finish_connecting(connection_t *c) {
void do_outgoing_connection(connection_t *c) { void do_outgoing_connection(connection_t *c) {
char *address, *port, *space; char *address, *port, *space;
struct addrinfo *proxyai;
int result; int result;
if(!c->outgoing) { if(!c->outgoing) {
@ -358,17 +359,25 @@ begin:
ifdebug(CONNECTIONS) logger(LOG_INFO, "Trying to connect to %s (%s)", c->name, ifdebug(CONNECTIONS) logger(LOG_INFO, "Trying to connect to %s (%s)", c->name,
c->hostname); c->hostname);
c->socket = socket(c->address.sa.sa_family, SOCK_STREAM, IPPROTO_TCP); if(!proxytype) {
c->socket = socket(c->address.sa.sa_family, SOCK_STREAM, IPPROTO_TCP);
#ifdef FD_CLOEXEC } else {
fcntl(c->socket, F_SETFD, FD_CLOEXEC); proxyai = str2addrinfo(proxyhost, proxyport, SOCK_STREAM);
#endif if(!proxyai)
goto begin;
ifdebug(CONNECTIONS) logger(LOG_INFO, "Using proxy at %s port %s", proxyhost, proxyport);
c->socket = socket(proxyai->ai_family, SOCK_STREAM, IPPROTO_TCP);
}
if(c->socket == -1) { if(c->socket == -1) {
ifdebug(CONNECTIONS) logger(LOG_ERR, "Creating socket for %s failed: %s", c->hostname, sockstrerror(sockerrno)); ifdebug(CONNECTIONS) logger(LOG_ERR, "Creating socket for %s failed: %s", c->hostname, sockstrerror(sockerrno));
goto begin; goto begin;
} }
#ifdef FD_CLOEXEC
fcntl(c->socket, F_SETFD, FD_CLOEXEC);
#endif
#if defined(SOL_IPV6) && defined(IPV6_V6ONLY) #if defined(SOL_IPV6) && defined(IPV6_V6ONLY)
int option = 1; int option = 1;
if(c->address.sa.sa_family == AF_INET6) if(c->address.sa.sa_family == AF_INET6)
@ -379,11 +388,16 @@ begin:
/* Optimize TCP settings */ /* Optimize TCP settings */
configure_tcp(c); // configure_tcp(c);
/* Connect */ /* Connect */
result = connect(c->socket, &c->address.sa, SALEN(c->address.sa)); if(!proxytype) {
result = connect(c->socket, &c->address.sa, SALEN(c->address.sa));
} else {
result = connect(c->socket, proxyai->ai_addr, proxyai->ai_addrlen);
freeaddrinfo(proxyai);
}
if(result == -1) { if(result == -1) {
if(sockinprogress(sockerrno)) { if(sockinprogress(sockerrno)) {

View file

@ -68,7 +68,7 @@ bool check_id(const char *id) {
bool send_request(connection_t *c, const char *format, ...) { bool send_request(connection_t *c, const char *format, ...) {
va_list args; va_list args;
char buffer[MAXBUFSIZE]; char buffer[MAXBUFSIZE];
int len, request; int len, request = 0;
/* Use vsnprintf instead of vxasprintf: faster, no memory /* Use vsnprintf instead of vxasprintf: faster, no memory
fragmentation, cleanup is automatic, and there is a limit on the fragmentation, cleanup is automatic, and there is a limit on the
@ -125,6 +125,20 @@ void forward_request(connection_t *from) {
bool receive_request(connection_t *c) { bool receive_request(connection_t *c) {
int request; int request;
if(proxytype == PROXY_HTTP && c->allow_request == ID) {
if(!c->buffer[0] || c->buffer[0] == '\r')
return true;
if(!strncasecmp(c->buffer, "HTTP/1.1 ", 9)) {
if(!strncmp(c->buffer + 9, "200", 3)) {
logger(LOG_DEBUG, "Proxy request granted");
return true;
} else {
logger(LOG_DEBUG, "Proxy request rejected: %s", c->buffer + 9);
return false;
}
}
}
if(sscanf(c->buffer, "%d", &request) == 1) { if(sscanf(c->buffer, "%d", &request) == 1) {
if((request < 0) || (request >= LAST) || !request_handlers[request]) { if((request < 0) || (request >= LAST) || !request_handlers[request]) {
ifdebug(META) ifdebug(META)

View file

@ -31,6 +31,7 @@
#include "edge.h" #include "edge.h"
#include "graph.h" #include "graph.h"
#include "logger.h" #include "logger.h"
#include "meta.h"
#include "net.h" #include "net.h"
#include "netutl.h" #include "netutl.h"
#include "node.h" #include "node.h"
@ -38,7 +39,49 @@
#include "utils.h" #include "utils.h"
#include "xalloc.h" #include "xalloc.h"
static bool send_proxyrequest(connection_t *c) {
switch(proxytype) {
case PROXY_HTTP: {
char *host;
char *port;
sockaddr2str(&c->address, &host, &port);
send_request(c, "CONNECT %s:%s HTTP/1.1\r\n\r", host, port);
free(host);
free(port);
return true;
}
case PROXY_SOCKS4: {
if(c->address.sa.sa_family != AF_INET) {
logger(LOG_ERR, "Cannot connect to an IPv6 host through a SOCKS 4 proxy!");
return false;
}
char s4req[9 + (proxyuser ? strlen(proxyuser) : 0)];
s4req[0] = 4;
s4req[1] = 1;
memcpy(s4req + 2, &c->address.in.sin_port, 2);
memcpy(s4req + 4, &c->address.in.sin_addr, 4);
if(proxyuser)
strcpy(s4req + 8, proxyuser);
s4req[sizeof s4req - 1] = 0;
c->tcplen = 8;
return send_meta(c, s4req, sizeof s4req);
}
case PROXY_SOCKS4A:
case PROXY_SOCKS5:
logger(LOG_ERR, "Proxy type not implemented yet");
return false;
default:
logger(LOG_ERR, "Unknown proxy type");
return false;
}
}
bool send_id(connection_t *c) { bool send_id(connection_t *c) {
if(proxytype)
if(!send_proxyrequest(c))
return false;
return send_request(c, "%d %s %d", ID, myself->connection->name, return send_request(c, "%d %s %d", ID, myself->connection->name,
myself->connection->protocol_version); myself->connection->protocol_version);
} }