Add ability to use proxies to connect to hostnames when there is no nameserver.
authorGuus Sliepen <guus@tinc-vpn.org>
Fri, 6 Nov 2015 07:48:35 +0000 (08:48 +0100)
committerGuus Sliepen <guus@tinc-vpn.org>
Fri, 6 Nov 2015 16:54:11 +0000 (17:54 +0100)
This adds support for SOCKS4a, and enhances the support for SOCKS5 and
HTTP.

src/Makefile.am
src/meta.c
src/net.h
src/net_setup.c
src/net_socket.c
src/netutl.c
src/protocol.c
src/protocol.h
src/protocol_auth.c
src/proxy.c [new file with mode: 0644]
src/proxy.h [new file with mode: 0644]

index 937800d..ebd52ab 100644 (file)
@@ -40,6 +40,7 @@ tincd_SOURCES = \
        protocol_misc.c \
        protocol_key.c \
        protocol_subnet.c \
        protocol_misc.c \
        protocol_key.c \
        protocol_subnet.c \
+       proxy.c proxy.h \
        raw_socket_device.c \
        route.c route.h \
        subnet.c subnet.h \
        raw_socket_device.c \
        route.c route.h \
        subnet.c subnet.h \
index e62c3b7..daa55e4 100644 (file)
@@ -30,6 +30,7 @@
 #include "meta.h"
 #include "net.h"
 #include "protocol.h"
 #include "meta.h"
 #include "net.h"
 #include "protocol.h"
+#include "proxy.h"
 #include "utils.h"
 #include "xalloc.h"
 
 #include "utils.h"
 #include "xalloc.h"
 
@@ -160,6 +161,17 @@ bool receive_meta(connection_t *c) {
        c->buflen += lenin;
 
        while(lenin > 0) {
        c->buflen += lenin;
 
        while(lenin > 0) {
+               reqlen = 0;
+
+               /* Is it proxy metadata? */
+
+               if(c->allow_request == PROXY) {
+                       reqlen = receive_proxy_meta(c, oldlen, lenin);
+                       if(reqlen < 0)
+                               return false;
+                       goto consume;
+               }
+
                /* Decrypt */
 
                if(c->status.decryptin && !decrypted) {
                /* Decrypt */
 
                if(c->status.decryptin && !decrypted) {
@@ -177,74 +189,32 @@ bool receive_meta(connection_t *c) {
 
                if(c->tcplen) {
                        if(c->tcplen <= c->buflen) {
 
                if(c->tcplen) {
                        if(c->tcplen <= c->buflen) {
-                               if(!c->node) {
-                                       if(c->outgoing && proxytype == PROXY_SOCKS4 && c->allow_request == ID) {
-                                               if(c->buffer[0] == 0 && c->buffer[1] == 0x5a) {
-                                                       ifdebug(CONNECTIONS) logger(LOG_DEBUG, "Proxy request granted");
-                                               } else {
-                                                       logger(LOG_ERR, "Proxy request rejected");
-                                                       return false;
-                                               }
-                                       } else if(c->outgoing && proxytype == PROXY_SOCKS5 && c->allow_request == ID) {
-                                               if(c->buffer[0] != 5) {
-                                                       logger(LOG_ERR, "Invalid response from proxy server");
-                                                       return false;
-                                               }
-                                               if(c->buffer[1] == (char)0xff) {
-                                                       logger(LOG_ERR, "Proxy request rejected: unsuitable authentication method");
-                                                       return false;
-                                               }
-                                               if(c->buffer[2] != 5) {
-                                                       logger(LOG_ERR, "Invalid response from proxy server");
-                                                       return false;
-                                               }
-                                               if(c->buffer[3] == 0) {
-                                                       ifdebug(CONNECTIONS) logger(LOG_DEBUG, "Proxy request granted");
-                                               } else {
-                                                       logger(LOG_ERR, "Proxy request rejected");
-                                                       return false;
-                                               }
-                                       } else {
-                                               logger(LOG_ERR, "c->tcplen set but c->node is NULL!");
-                                               abort();
-                                       }
-                               } else {
-                                       if(c->allow_request == ALL) {
-                                               receive_tcppacket(c, c->buffer, c->tcplen);
-                                       } else {
-                                               logger(LOG_ERR, "Got unauthorized TCP packet from %s (%s)", c->name, c->hostname);
-                                               return false;
-                                       }
+                               if(c->allow_request != ALL) {
+                                       logger(LOG_ERR, "Got unauthorized TCP packet from %s (%s)", c->name, c->hostname);
+                                       return false;
                                }
 
                                }
 
-                               c->buflen -= c->tcplen;
-                               lenin -= c->tcplen - oldlen;
-                               memmove(c->buffer, c->buffer + c->tcplen, c->buflen);
-                               oldlen = 0;
+                               receive_tcppacket(c, c->buffer, c->tcplen);
+                               reqlen = c->tcplen;
                                c->tcplen = 0;
                                c->tcplen = 0;
-                               continue;
-                       } else {
-                               break;
                        }
                        }
-               }
-
-               /* Otherwise we are waiting for a request */
-
-               reqlen = 0;
+               } else {
+                       /* Otherwise we are waiting for a request */
 
 
-               for(i = oldlen; i < c->buflen; i++) {
-                       if(c->buffer[i] == '\n') {
-                               c->buffer[i] = '\0';    /* replace end-of-line by end-of-string so we can use sscanf */
-                               reqlen = i + 1;
-                               break;
+                       for(i = oldlen; i < c->buflen; i++) {
+                               if(c->buffer[i] == '\n') {
+                                       c->buffer[i] = '\0';    /* replace end-of-line by end-of-string so we can use sscanf */
+                                       c->reqlen = reqlen = i + 1;
+                                       break;
+                               }
                        }
                        }
-               }
 
 
-               if(reqlen) {
-                       c->reqlen = reqlen;
-                       if(!receive_request(c))
+                       if(reqlen && !receive_request(c))
                                return false;
                                return false;
+               }
 
 
+consume:
+               if(reqlen) {
                        c->buflen -= reqlen;
                        lenin -= reqlen - oldlen;
                        memmove(c->buffer, c->buffer + reqlen, c->buflen);
                        c->buflen -= reqlen;
                        lenin -= reqlen - oldlen;
                        memmove(c->buffer, c->buffer + reqlen, c->buflen);
index 0982020..8de3a2c 100644 (file)
--- a/src/net.h
+++ b/src/net.h
@@ -123,20 +123,6 @@ extern time_t now;
 extern int contradicting_add_edge;
 extern int contradicting_del_edge;
 
 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 */
 extern volatile bool running;
 
 /* Yes, very strange placement indeed, but otherwise the typedefs get all tangled up */
index 863616b..50d5680 100644 (file)
@@ -39,6 +39,7 @@
 #include "netutl.h"
 #include "process.h"
 #include "protocol.h"
 #include "netutl.h"
 #include "process.h"
 #include "protocol.h"
+#include "proxy.h"
 #include "route.h"
 #include "subnet.h"
 #include "utils.h"
 #include "route.h"
 #include "subnet.h"
 #include "utils.h"
 char *myport;
 devops_t devops;
 
 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 *pubname;
 bool read_rsa_public_key(connection_t *c) {
        FILE *fp;
        char *pubname;
index c167c2f..e63da75 100644 (file)
@@ -31,6 +31,7 @@
 #include "net.h"
 #include "netutl.h"
 #include "protocol.h"
 #include "net.h"
 #include "netutl.h"
 #include "protocol.h"
+#include "proxy.h"
 #include "utils.h"
 #include "xalloc.h"
 
 #include "utils.h"
 #include "xalloc.h"
 
@@ -354,8 +355,19 @@ static void do_outgoing_pipe(connection_t *c, char *command) {
 #endif
 }
 
 #endif
 }
 
+static bool is_valid_host_port(const char *host, const char *port) {
+       for(const char *p = host; *p; p++)
+               if(!isalnum(*p) && *p != '-' && *p != '.')
+                       return false;
+
+       for(const char *p = port; *p; p++)
+               if(!isalnum(*p))
+                       return false;
+
+       return true;
+}
+
 void do_outgoing_connection(connection_t *c) {
 void do_outgoing_connection(connection_t *c) {
-       char *address, *port, *space;
        struct addrinfo *proxyai = NULL;
        int result;
 
        struct addrinfo *proxyai = NULL;
        int result;
 
@@ -375,6 +387,8 @@ begin:
                        return;
                }
 
                        return;
                }
 
+               char *address, *port, *space;
+
                get_config_string(c->outgoing->cfg, &address);
 
                space = strchr(address, ' ');
                get_config_string(c->outgoing->cfg, &address);
 
                space = strchr(address, ' ');
@@ -387,11 +401,23 @@ begin:
                }
 
                c->outgoing->ai = str2addrinfo(address, port, SOCK_STREAM);
                }
 
                c->outgoing->ai = str2addrinfo(address, port, SOCK_STREAM);
-               free(address);
-               free(port);
+
+               // If we cannot resolve the address, maybe we are using a proxy that can?
+               if(!c->outgoing->ai && proxytype != PROXY_NONE && is_valid_host_port(address, port)) {
+                       memset(&c->address, 0, sizeof c->address);
+                       c->address.sa.sa_family = AF_UNKNOWN;
+                       c->address.unknown.address = address;
+                       c->address.unknown.port = port;
+               } else {
+                       free(address);
+                       free(port);
+               }
 
                c->outgoing->aip = c->outgoing->ai;
                c->outgoing->cfg = lookup_config_next(c->config_tree, c->outgoing->cfg);
 
                c->outgoing->aip = c->outgoing->ai;
                c->outgoing->cfg = lookup_config_next(c->config_tree, c->outgoing->cfg);
+
+               if(!c->outgoing->ai && proxytype != PROXY_NONE)
+                       goto connect;
        }
 
        if(!c->outgoing->aip) {
        }
 
        if(!c->outgoing->aip) {
@@ -404,6 +430,7 @@ begin:
        memcpy(&c->address, c->outgoing->aip->ai_addr, c->outgoing->aip->ai_addrlen);
        c->outgoing->aip = c->outgoing->aip->ai_next;
 
        memcpy(&c->address, c->outgoing->aip->ai_addr, c->outgoing->aip->ai_addrlen);
        c->outgoing->aip = c->outgoing->aip->ai_next;
 
+connect:
        if(c->hostname)
                free(c->hostname);
 
        if(c->hostname)
                free(c->hostname);
 
index 275c64f..2abbe86 100644 (file)
@@ -33,7 +33,7 @@ bool hostnames = false;
   Return NULL on failure.
 */
 struct addrinfo *str2addrinfo(const char *address, const char *service, int socktype) {
   Return NULL on failure.
 */
 struct addrinfo *str2addrinfo(const char *address, const char *service, int socktype) {
-       struct addrinfo *ai, hint = {0};
+       struct addrinfo *ai = NULL, hint = {0};
        int err;
 
        hint.ai_family = addressfamily;
        int err;
 
        hint.ai_family = addressfamily;
@@ -55,7 +55,7 @@ struct addrinfo *str2addrinfo(const char *address, const char *service, int sock
 }
 
 sockaddr_t str2sockaddr(const char *address, const char *port) {
 }
 
 sockaddr_t str2sockaddr(const char *address, const char *port) {
-       struct addrinfo *ai, hint = {0};
+       struct addrinfo *ai = NULL, hint = {0};
        sockaddr_t result;
        int err;
 
        sockaddr_t result;
        int err;
 
index b385fbc..5d5518f 100644 (file)
@@ -125,20 +125,6 @@ void forward_request(connection_t *from) {
 bool receive_request(connection_t *c) {
        int request;
 
 bool receive_request(connection_t *c) {
        int request;
 
-       if(c->outgoing && 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)
        if(sscanf(c->buffer, "%d", &request) == 1) {
                if((request < 0) || (request >= LAST) || !request_handlers[request]) {
                        ifdebug(META)
index 6c46c72..54d0379 100644 (file)
@@ -36,6 +36,7 @@
 /* Request numbers */
 
 typedef enum request_t {
 /* Request numbers */
 
 typedef enum request_t {
+       PROXY = -2,
        ALL = -1,                                       /* Guardian for allow_request */
        ID = 0, METAKEY, CHALLENGE, CHAL_REPLY, ACK,
        STATUS, ERROR, TERMREQ,
        ALL = -1,                                       /* Guardian for allow_request */
        ID = 0, METAKEY, CHALLENGE, CHAL_REPLY, ACK,
        STATUS, ERROR, TERMREQ,
index 971341f..e54fe23 100644 (file)
 #include "netutl.h"
 #include "node.h"
 #include "protocol.h"
 #include "netutl.h"
 #include "node.h"
 #include "protocol.h"
+#include "proxy.h"
 #include "utils.h"
 #include "xalloc.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_SOCKS5: {
-                       int len = 3 + 6 + (c->address.sa.sa_family == AF_INET ? 4 : 16);
-                       c->tcplen = 2;
-                       if(proxypass)
-                               len += 3 + strlen(proxyuser) + strlen(proxypass);
-                       char s5req[len];
-                       int i = 0;
-                       s5req[i++] = 5;
-                       s5req[i++] = 1;
-                       if(proxypass) {
-                               s5req[i++] = 2;
-                               s5req[i++] = 1;
-                               s5req[i++] = strlen(proxyuser);
-                               strcpy(s5req + i, proxyuser);
-                               i += strlen(proxyuser);
-                               s5req[i++] = strlen(proxypass);
-                               strcpy(s5req + i, proxypass);
-                               i += strlen(proxypass);
-                               c->tcplen += 2;
-                       } else {
-                               s5req[i++] = 0;
-                       }
-                       s5req[i++] = 5;
-                       s5req[i++] = 1;
-                       s5req[i++] = 0;
-                       if(c->address.sa.sa_family == AF_INET) {
-                               s5req[i++] = 1;
-                               memcpy(s5req + i, &c->address.in.sin_addr, 4);
-                               i += 4;
-                               memcpy(s5req + i, &c->address.in.sin_port, 2);
-                               i += 2;
-                               c->tcplen += 10;
-                       } else if(c->address.sa.sa_family == AF_INET6) {
-                               s5req[i++] = 3;
-                               memcpy(s5req + i, &c->address.in6.sin6_addr, 16);
-                               i += 16;
-                               memcpy(s5req + i, &c->address.in6.sin6_port, 2);
-                               i += 2;
-                               c->tcplen += 22;
-                       } else {
-                               logger(LOG_ERR, "Address family %x not supported for SOCKS 5 proxies!", c->address.sa.sa_family);
-                               return false;
-                       }
-                       if(i > len)
-                               abort();
-                       return send_meta(c, s5req, sizeof s5req);
-               }
-               case PROXY_SOCKS4A:
-                       logger(LOG_ERR, "Proxy type not implemented yet");
-                       return false;
-               case PROXY_EXEC:
-                       return true;
-               default:
-                       logger(LOG_ERR, "Unknown proxy type");
-                       return false;
-       }
-}
-
 bool send_id(connection_t *c) {
        if(proxytype && c->outgoing)
                if(!send_proxyrequest(c))
 bool send_id(connection_t *c) {
        if(proxytype && c->outgoing)
                if(!send_proxyrequest(c))
diff --git a/src/proxy.c b/src/proxy.c
new file mode 100644 (file)
index 0000000..165d3f3
--- /dev/null
@@ -0,0 +1,287 @@
+/*
+    proxy.c -- Proxy handling functions.
+    Copyright (C) 2015 Guus Sliepen <guus@tinc-vpn.org>
+
+    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; either version 2 of the License, or
+    (at your option) any later version.
+
+    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, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "system.h"
+
+#include "connection.h"
+#include "logger.h"
+#include "meta.h"
+#include "netutl.h"
+#include "protocol.h"
+#include "proxy.h"
+#include "utils.h" //
+
+proxytype_t proxytype;
+char *proxyhost;
+char *proxyport;
+char *proxyuser;
+char *proxypass;
+
+static void update_address_ipv4(connection_t *c, void *address, void *port) {
+       sockaddrfree(&c->address);
+       memset(&c->address, 0, sizeof c->address);
+       c->address.sa.sa_family = AF_INET;
+       if(address)
+               memcpy(&c->address.in.sin_addr, address, sizeof(ipv4_t));
+       if(port)
+               memcpy(&c->address.in.sin_port, port, sizeof(uint16_t));
+       // OpenSSH -D returns all zero address, set it to 0.0.0.1 to prevent spamming ourselves.
+       if(!memcmp(&c->address.in.sin_addr, "\0\0\0\0", 4))
+               memcpy(&c->address.in.sin_addr, "\0\0\0\01", 4);
+}
+
+static void update_address_ipv6(connection_t *c, void *address, void *port) {
+       sockaddrfree(&c->address);
+       memset(&c->address, 0, sizeof c->address);
+       c->address.sa.sa_family = AF_INET6;
+       if(address)
+               memcpy(&c->address.in6.sin6_addr, address, sizeof(ipv6_t));
+       if(port)
+               memcpy(&c->address.in6.sin6_port, port, sizeof(uint16_t));
+       // OpenSSH -D returns all zero address, set it to 0100:: to prevent spamming ourselves.
+       if(!memcmp(&c->address.in6.sin6_addr, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16))
+               memcpy(&c->address.in6.sin6_addr, "\01\0\0\0\0\0\0\0", 8);
+}
+
+bool send_proxyrequest(connection_t *c) {
+       switch(proxytype) {
+       case PROXY_SOCKS4:
+               if(c->address.sa.sa_family != AF_INET) {
+                       logger(LOG_ERR, "Can only connect to numeric IPv4 addresses through a SOCKS 4 proxy!");
+                       return false;
+               }
+       case PROXY_SOCKS4A: {
+               if(c->address.sa.sa_family != AF_INET && c->address.sa.sa_family != AF_UNKNOWN) {
+                       logger(LOG_ERR, "Can only connect to IPv4 addresses or hostnames through a SOCKS 4a proxy!");
+                       return false;
+               }
+               int len = 9;
+               if(proxyuser)
+                       len += strlen(proxyuser);
+               if(c->address.sa.sa_family == AF_UNKNOWN)
+                       len += 1 + strlen(c->address.unknown.address);
+               char s4req[len];
+               s4req[0] = 4;
+               s4req[1] = 1;
+               if(c->address.sa.sa_family == AF_INET) {
+                       memcpy(s4req + 2, &c->address.in.sin_port, 2);
+                       memcpy(s4req + 4, &c->address.in.sin_addr, 4);
+               } else {
+                       uint16_t port = htons(atoi(c->address.unknown.port));
+                       memcpy(s4req + 2, &port, 2);
+                       memcpy(s4req + 4, "\0\0\0\1", 4);
+                       strcpy(s4req + (9 + (proxyuser ? strlen(proxyuser) : 0)), c->address.unknown.address);
+               }
+               if(proxyuser)
+                       strcpy(s4req + 8, proxyuser);
+               else
+                       s4req[8] = 0;
+               s4req[sizeof s4req - 1] = 0;
+               c->allow_request = PROXY;
+               return send_meta(c, s4req, sizeof s4req);
+       }
+
+       case PROXY_SOCKS5: {
+               int len = 3 + 6;
+               if(c->address.sa.sa_family == AF_INET) {
+                       len += 4;
+               } else if(c->address.sa.sa_family == AF_INET6) {
+                       len += 16;
+               } else if(c->address.sa.sa_family == AF_UNKNOWN) {
+                       len += 1 + strlen(c->address.unknown.address);
+               } else {
+                       logger(LOG_ERR, "Address family %x not supported for SOCKS 5 proxies!", c->address.sa.sa_family);
+                       return false;
+               }
+               if(proxypass)
+                       len += 3 + strlen(proxyuser) + strlen(proxypass);
+               char s5req[len];
+               int i = 0;
+               s5req[i++] = 5;
+               s5req[i++] = 1;
+               if(proxypass) {
+                       s5req[i++] = 2;
+                       s5req[i++] = 1;
+                       s5req[i++] = strlen(proxyuser);
+                       strcpy(s5req + i, proxyuser);
+                       i += strlen(proxyuser);
+                       s5req[i++] = strlen(proxypass);
+                       strcpy(s5req + i, proxypass);
+                       i += strlen(proxypass);
+               } else {
+                       s5req[i++] = 0;
+               }
+               s5req[i++] = 5;
+               s5req[i++] = 1;
+               s5req[i++] = 0;
+               if(c->address.sa.sa_family == AF_INET) {
+                       s5req[i++] = 1;
+                       memcpy(s5req + i, &c->address.in.sin_addr, 4);
+                       i += 4;
+                       memcpy(s5req + i, &c->address.in.sin_port, 2);
+                       i += 2;
+               } else if(c->address.sa.sa_family == AF_INET6) {
+                       s5req[i++] = 4;
+                       memcpy(s5req + i, &c->address.in6.sin6_addr, 16);
+                       i += 16;
+                       memcpy(s5req + i, &c->address.in6.sin6_port, 2);
+                       i += 2;
+               } else if(c->address.sa.sa_family == AF_UNKNOWN) {
+                       s5req[i++] = 3;
+                       int len = strlen(c->address.unknown.address);
+                       s5req[i++] = len;
+                       memcpy(s5req + i, c->address.unknown.address, len);
+                       i += len;
+                       uint16_t port = htons(atoi(c->address.unknown.port));
+                       memcpy(s5req + i, &port, 2);
+                       i += 2;
+               } else {
+                       logger(LOG_ERR, "Unknown address family while trying to connect to SOCKS5 proxy");
+                       return false;
+               }
+               if(i > len)
+                       abort();
+               c->allow_request = PROXY;
+               return send_meta(c, s5req, sizeof s5req);
+       }
+
+       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);
+               c->allow_request = PROXY;
+               return true;
+       }
+
+       case PROXY_EXEC:
+               return true;
+
+       default:
+               logger(LOG_ERR, "Unknown proxy type");
+               return false;
+       }
+}
+
+int receive_proxy_meta(connection_t *c, int start, int lenin) {
+       switch(proxytype) {
+       case PROXY_SOCKS4:
+       case PROXY_SOCKS4A:
+               if(c->buflen < 8)
+                       return 0;
+               if(c->buffer[0] == 0 && c->buffer[1] == 0x5a) {
+                       if(c->address.sa.sa_family == AF_UNKNOWN)
+                               update_address_ipv4(c, c->buffer + 4, c->buffer + 2);
+
+                       ifdebug(CONNECTIONS) logger(LOG_DEBUG, "Proxy request granted");
+                       c->allow_request = ID;
+                       return 8;
+               } else {
+                       logger(LOG_ERR, "Proxy request rejected");
+                       return -1;
+               }
+
+       case PROXY_SOCKS5:
+               if(c->buflen < 2)
+                       return 0;
+               if(c->buffer[0] != 0x05 || c->buffer[1] == 0xff) {
+                       logger(LOG_ERR, "Proxy authentication method rejected");
+                       return -1;
+               }
+               int offset = 2;
+               if(c->buffer[1] == 0x02) {
+                       if(c->buflen < 4)
+                               return 0;
+                       if(c->buffer[2] != 0x05 || c->buffer[3] != 0x00) {
+                               logger(LOG_ERR, "Proxy username/password rejected");
+                               return -1;
+                       }
+                       offset += 2;
+               }
+               if(c->buflen - offset < 7)
+                       return 0;
+               if(c->buffer[offset] != 0x05  || c->buffer[offset + 1] != 0x00) {
+                       logger(LOG_ERR, "Proxy request rejected");
+                       return -1;
+               }
+               int replen = offset + 6;
+               switch(c->buffer[offset + 3]) {
+                       case 0x01: // IPv4
+                               if(c->address.sa.sa_family == AF_UNKNOWN)
+                                       update_address_ipv4(c, c->buffer + offset + 4, c->buffer + offset + 8);
+                               replen += 4;
+                               break;
+                       case 0x03: // Hostname
+                               if(c->address.sa.sa_family == AF_UNKNOWN)
+                                       update_address_ipv4(c, "\0\0\0\1", "\0\0");
+                               replen += ((uint8_t *)c->buffer)[offset + 4];
+                               break;
+                       case 0x04: // IPv6
+                               if(c->address.sa.sa_family == AF_UNKNOWN)
+                                       update_address_ipv6(c, c->buffer + offset + 4, c->buffer + offset + 20);
+                               replen += 16;
+                               break;
+                       default:
+                               logger(LOG_ERR, "Proxy reply malformed");
+                               return -1;
+               }
+               if(c->buflen < replen) {
+                       return 0;
+               } else {
+                       ifdebug(CONNECTIONS) logger(LOG_DEBUG, "Proxy request granted");
+                       c->allow_request = ID;
+                       return replen;
+               }
+
+       case PROXY_HTTP: {
+               char *p = memchr(c->buffer, '\n', c->buflen);
+               if(!p || p - c->buffer >= c->buflen)
+                       return 0;
+               p = memchr(p + 1, '\n', c->buflen - (p + 1 - c->buffer));
+               if(!p)
+                       return 0;
+
+               if(c->buflen < 9)
+                       return 0;
+
+               if(!strncasecmp(c->buffer, "HTTP/1.1 ", 9)) {
+                       if(!strncmp(c->buffer + 9, "200", 3)) {
+                               if(c->address.sa.sa_family == AF_UNKNOWN)
+                                       update_address_ipv4(c, "\0\0\0\1", "\0\0");
+                               logger(LOG_DEBUG, "Proxy request granted");
+                               replen = p  + 1 - c->buffer;
+                               c->allow_request = ID;
+                               return replen;
+                       } else {
+                               logger(LOG_ERR, "Proxy request rejected: %s", c->buffer + 9);
+                               return false;
+                       }
+               } else {
+                       logger(LOG_ERR, "Proxy reply malformed");
+                       return -1;
+               }
+       }
+
+       default:
+               abort();
+       }
+}
diff --git a/src/proxy.h b/src/proxy.h
new file mode 100644 (file)
index 0000000..a982943
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+    proxy.h -- header for proxy.c
+    Copyright (C) 2015 Guus Sliepen <guus@tinc-vpn.org>
+
+    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; either version 2 of the License, or
+    (at your option) any later version.
+
+    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, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef __TINC_PROXY_H__
+#define __TINC_PROXY_H__
+
+#include "connection.h"
+
+typedef enum proxytype_t {
+       PROXY_NONE = 0,
+       PROXY_SOCKS4,
+       PROXY_SOCKS4A,
+       PROXY_SOCKS5,
+       PROXY_HTTP,
+       PROXY_EXEC,
+} proxytype_t;
+
+extern proxytype_t proxytype;
+extern char *proxyhost;
+extern char *proxyport;
+extern char *proxyuser;
+extern char *proxypass;
+
+extern bool send_proxyrequest(struct connection_t *c);
+extern int receive_proxy_meta(struct connection_t *c, int start, int lenin);
+
+#endif