Add basic support for SOCKS 4 and HTTP CONNECT proxies.
authorGuus Sliepen <guus@tinc-vpn.org>
Wed, 18 Apr 2012 21:19:40 +0000 (23:19 +0200)
committerGuus Sliepen <guus@tinc-vpn.org>
Wed, 18 Apr 2012 21:19:40 +0000 (23:19 +0200)
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.

doc/tinc.conf.5.in
doc/tinc.texi
src/meta.c
src/net.h
src/net_setup.c
src/net_socket.c
src/protocol.c
src/protocol_auth.c

index b49e3de6b0ed87882791a2eaac4028e0affe1e3f..35f986b68dbac759ade800fd07033eadd50f20b4 100644 (file)
@@ -456,8 +456,27 @@ specified in the configuration file.
 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.
 
+.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
-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 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
index d7776826f9d0fdb93df8f5427ddbed48dc87a655..3decca056477766d4d66e03ff35243450ddd573c 100644 (file)
@@ -1050,6 +1050,25 @@ specified in the configuration file.
 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.
 
+@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
 @item ReplayWindow = <bytes> (16)
 This is the size of the replay tracking window for each remote node, in bytes.
index 4c52464c33d2a62f663f54c2b920832b8a20e910..1b3424606771b813aa3a15c096a8ad751eaafbcc 100644 (file)
@@ -177,7 +177,15 @@ bool receive_meta(connection_t *c) {
 
                if(c->tcplen) {
                        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;
                                lenin -= c->tcplen - oldlen;
index 7ff603c6014d3b0456fd74b9b8dca5a5eb2b1f40..2b50c5a2fab629965455db8b2205fe556055c782 100644 (file)
--- a/src/net.h
+++ b/src/net.h
@@ -122,6 +122,20 @@ extern time_t now;
 extern int contradicting_add_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;
 
 /* Yes, very strange placement indeed, but otherwise the typedefs get all tangled up */
index b8e17da9bbbdf4c8dc32b20ed18d18310921c46d..2eff09f3f6d7a1370269c462e55ee63c908844df 100644 (file)
 char *myport;
 devops_t devops;
 
+char *proxyhost;
+char *proxyport;
+char *proxyuser;
+char *proxypass;
+proxytype_t proxytype;
+
 bool read_rsa_public_key(connection_t *c) {
        FILE *fp;
        char *fname;
@@ -316,6 +322,8 @@ static bool setup_myself(void) {
        char *name, *hostname, *mode, *afname, *cipher, *digest, *type;
        char *fname = NULL;
        char *address = NULL;
+       char *proxy = NULL;
+       char *space;
        char *envp[5];
        struct addrinfo *ai, *aip, hint = {0};
        bool choice;
@@ -359,6 +367,68 @@ static bool setup_myself(void) {
                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 */
 
        cfg = lookup_config(config_tree, "Subnet");
index 94db11c0fa650a457d4a43c5e694136a28510335..544cd6e204f0bbeb0165ced1427448e5b8bf2b1b 100644 (file)
@@ -303,6 +303,7 @@ void finish_connecting(connection_t *c) {
 
 void do_outgoing_connection(connection_t *c) {
        char *address, *port, *space;
+       struct addrinfo *proxyai;
        int result;
 
        if(!c->outgoing) {
@@ -358,17 +359,25 @@ begin:
        ifdebug(CONNECTIONS) logger(LOG_INFO, "Trying to connect to %s (%s)", c->name,
                           c->hostname);
 
-       c->socket = socket(c->address.sa.sa_family, SOCK_STREAM, IPPROTO_TCP);
-
-#ifdef FD_CLOEXEC
-       fcntl(c->socket, F_SETFD, FD_CLOEXEC);
-#endif
+       if(!proxytype) {
+               c->socket = socket(c->address.sa.sa_family, SOCK_STREAM, IPPROTO_TCP);
+       } else {
+               proxyai = str2addrinfo(proxyhost, proxyport, SOCK_STREAM);
+               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) {
                ifdebug(CONNECTIONS) logger(LOG_ERR, "Creating socket for %s failed: %s", c->hostname, sockstrerror(sockerrno));
                goto begin;
        }
 
+#ifdef FD_CLOEXEC
+       fcntl(c->socket, F_SETFD, FD_CLOEXEC);
+#endif
+
 #if defined(SOL_IPV6) && defined(IPV6_V6ONLY)
        int option = 1;
        if(c->address.sa.sa_family == AF_INET6)
@@ -379,11 +388,16 @@ begin:
 
        /* Optimize TCP settings */
 
-       configure_tcp(c);
+//     configure_tcp(c);
 
        /* 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(sockinprogress(sockerrno)) {
index 1d91d088ff1cc932bda325ff7f30fcf6be5f0847..f36538e382899ca4f4d3a27ff4267899fb64f185 100644 (file)
@@ -68,7 +68,7 @@ bool check_id(const char *id) {
 bool send_request(connection_t *c, const char *format, ...) {
        va_list args;
        char buffer[MAXBUFSIZE];
-       int len, request;
+       int len, request = 0;
 
        /* Use vsnprintf instead of vxasprintf: faster, no memory
           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) {
        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((request < 0) || (request >= LAST) || !request_handlers[request]) {
                        ifdebug(META)
index 698806886ba363cfdd51984f5b2e5d44f7cc2642..e5c4c1663d70ad5baff2d845de82e3b773da4d4f 100644 (file)
@@ -31,6 +31,7 @@
 #include "edge.h"
 #include "graph.h"
 #include "logger.h"
+#include "meta.h"
 #include "net.h"
 #include "netutl.h"
 #include "node.h"
 #include "utils.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) {
+       if(proxytype)
+               if(!send_proxyrequest(c))
+                       return false;
+
        return send_request(c, "%d %s %d", ID, myself->connection->name,
                                                myself->connection->protocol_version);
 }