Merge branch 'master' of git://tinc-vpn.org/tinc into 1.1
authorGuus Sliepen <guus@tinc-vpn.org>
Tue, 26 Jun 2012 11:24:20 +0000 (13:24 +0200)
committerGuus Sliepen <guus@tinc-vpn.org>
Tue, 26 Jun 2012 11:24:20 +0000 (13:24 +0200)
Conflicts:
NEWS
README
configure.in
lib/utils.c
src/linux/device.c
src/meta.c
src/net.h
src/net_setup.c
src/net_socket.c
src/protocol.c
src/protocol_auth.c
src/tincd.c

16 files changed:
NEWS
THANKS
doc/tinc.conf.5.in
doc/tinc.texi
have.h
src/meta.c
src/net.h
src/net_packet.c
src/net_setup.c
src/net_socket.c
src/netutl.c
src/protocol.c
src/protocol_auth.c
src/route.c
src/route.h
src/utils.c

diff --git a/NEWS b/NEWS
index a385047..191c2f2 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -28,6 +28,17 @@ Version 1.1pre1              June 25 2011
  Thanks to Scott Lamb and Sven-Haegar Koch for their contributions to this
  version of tinc.
 
+Version 1.0.19               June 25 2012
+
+ * Allow :: notation in IPv6 Subnets.
+
+ * Add support for systemd style socket activation.
+
+ * Allow environment variables to be used for the Name option.
+
+ * Add basic support for SOCKS proxies, HTTP proxies, and proxying through an
+   external command.
+
 Version 1.0.18               March 25 2012
 
  * Fixed IPv6 in switch mode by turning off DecrementTTL by default.
diff --git a/THANKS b/THANKS
index 521f1a2..6fa8f9c 100644 (file)
--- a/THANKS
+++ b/THANKS
@@ -3,6 +3,7 @@ We would like to thank the following people for their contributions to tinc:
 * Alexander Reil and Gemeinde Berg
 * Allesandro Gatti
 * Andreas van Cranenburgh
+* Anthony G. Basile
 * Armijn Hemel
 * Brandon Black
 * Cris van Pelt
index 8f19de0..40b013b 100644 (file)
@@ -159,8 +159,25 @@ It is possible to bind only to a single interface with this variable.
 .Pp
 This option may not work on all platforms.
 
-.It Va Broadcast Li = yes | no Po yes Pc Bq experimental
-When disabled, tinc will drop all broadcast and multicast packets, in both router and switch mode.
+.It Va Broadcast Li = no | mst | direct Po mst Pc Bq experimental
+This option selects the way broadcast packets are sent to other daemons.
+NOTE: all nodes in a VPN must use the same
+.Va Broadcast
+mode, otherwise routing loops can form.
+
+.Bl -tag -width indent
+.It no
+Broadcast packets are never sent to other nodes.
+
+.It mst
+Broadcast packets are sent and forwarded via the VPN's Minimum Spanning Tree.
+This ensures broadcast packets reach all nodes.
+
+.It direct
+Broadcast packets are sent directly to all nodes that can be reached directly.
+Broadcast packets received from other nodes are never forwarded.
+If the IndirectData option is also set, broadcast packets will only be sent to nodes which we have a meta connection to.
+.El
 
 .It Va ConnectTo Li = Ar name
 Specifies which other tinc daemon to connect to on startup.
@@ -409,6 +426,19 @@ while no routing table is managed.
 .It Va Name Li = Ar name Bq required
 This is the name which identifies this tinc daemon.
 It must be unique for the virtual private network this daemon will connect to.
+The Name may only consist of alphanumeric and underscore characters.
+
+If 
+.Va Name
+starts with a
+.Li $ ,
+then the contents of the environment variable that follows will be used.
+In that case, invalid characters will be converted to underscores.
+If
+.Va Name
+is
+.Li $HOST ,
+but no such environment variable exist, the hostname will be read using the gethostnname() system call.
 
 .It Va PingInterval Li = Ar seconds Pq 60
 The number of seconds of inactivity that
@@ -441,8 +471,41 @@ 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 = socks4 | socks5 | http | exec Ar ... Bq experimental
+Use a proxy when making outgoing connections.
+The following proxy types are currently supported:
+.Bl -tag -width indent
+.It socks4 Ar address Ar port Op Ar username
+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.
+Only IPv4 connections can be proxied using SOCKS 4.
+.It socks5 Ar address Ar port Op Ar username Ar password
+Connect to the proxy using the SOCKS version 5 protocol.
+If a
+.Ar username
+and
+.Ar password
+are given, basic username/password authentication will be used,
+otherwise no authentication will be used.
+.It http Ar address Ar port
+Connects to the proxy and sends a HTTP CONNECT request.
+.It exec Ar command
+Executes the given
+.Ar command
+which should set up the outgoing connection.
+The environment variables
+.Ev NAME ,
+.Ev NODE ,
+.Ev REMOTEADDRES
+and
+.Ev REMOTEPORT
+are available.
+.El
+
 .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 b4fb1f1..b595aec 100644 (file)
@@ -801,8 +801,23 @@ variable.
 This option may not work on all platforms.
 
 @cindex Broadcast
-@item Broadcast = <yes | no> (yes) [experimental]
-When disabled, tinc will drop all broadcast and multicast packets, in both router and switch mode.
+@item Broadcast = <no | mst | direct> (mst) [experimental]
+This option selects the way broadcast packets are sent to other daemons.
+@emph{NOTE: all nodes in a VPN must use the same Broadcast mode, otherwise routing loops can form.}
+
+@table @asis
+@item no
+Broadcast packets are never sent to other nodes.
+
+@item mst
+Broadcast packets are sent and forwarded via the VPN's Minimum Spanning Tree.
+This ensures broadcast packets reach all nodes.
+
+@item direct
+Broadcast packets are sent directly to all nodes that can be reached directly.
+Broadcast packets received from other nodes are never forwarded.
+If the IndirectData option is also set, broadcast packets will only be sent to nodes which we have a meta connection to.
+@end table
 
 @cindex ConnectTo
 @item ConnectTo = <@var{name}>
@@ -1031,6 +1046,11 @@ This only has effect when Mode is set to "switch".
 This is a symbolic name for this connection.
 The name should consist only of alfanumeric and underscore characters (a-z, A-Z, 0-9 and _).
 
+If Name starts with a $, then the contents of the environment variable that follows will be used.
+In that case, invalid characters will be converted to underscores.
+If Name is $HOST, but no such environment variable exist,
+the hostname will be read using the gethostnname() system call.
+
 @cindex PingInterval
 @item PingInterval = <@var{seconds}> (60)
 The number of seconds of inactivity that tinc will wait before sending a
@@ -1068,6 +1088,33 @@ 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 = socks4 | socks4 | http | exec @var{...} [experimental]
+Use a proxy when making outgoing connections.
+The following proxy types are currently supported:
+
+@table @asis
+@cindex socks4
+@item socks4 <@var{address}> <@var{port}> [<@var{username}>]
+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 socks5
+@item socks4 <@var{address}> <@var{port}> [<@var{username}> <@var{password}>]
+Connect to the proxy using the SOCKS version 5 protocol.
+If a @var{username} and @var{password} are given, basic username/password authentication will be used,
+otherwise no authentication will be used.
+
+@cindex http
+@item http <@var{address}> <@var{port}>
+Connects to the proxy and sends a HTTP CONNECT request.
+
+@cindex exec
+@item exec <@var{command}>
+Executes the given command which should set up the outgoing connection.
+The environment variables @env{NAME}, @env{NODE}, @env{REMOTEADDRES} and @env{REMOTEPORT} are available.
+@end table
+
 @cindex ReplayWindow
 @item ReplayWindow = <bytes> (16)
 This is the size of the replay tracking window for each remote node, in bytes.
diff --git a/have.h b/have.h
index 0ab8134..d47f204 100644 (file)
--- a/have.h
+++ b/have.h
@@ -42,6 +42,7 @@
 
 #ifdef HAVE_MINGW
 #include <w32api.h>
+#include <winsock2.h>
 #include <windows.h>
 #include <ws2tcpip.h>
 #endif
index 13c8495..a272baf 100644 (file)
@@ -196,7 +196,15 @@ bool receive_meta(connection_t *c) {
                        if(c->tcplen) {
                                char *tcpbuffer = buffer_read(&c->inbuf, c->tcplen);
                                if(tcpbuffer) {
-                                       receive_tcppacket(c, tcpbuffer, c->tcplen);
+                                       if(proxytype == PROXY_SOCKS4 && c->allow_request == ID) {
+                                               if(tcpbuffer[0] == 0 && tcpbuffer[1] == 0x5a) {
+                                                       logger(DEBUG_CONNECTIONS, LOG_DEBUG, "Proxy request granted");
+                                               } else {
+                                                       logger(DEBUG_CONNECTIONS, LOG_ERR, "Proxy request rejected");
+                                                       return false;
+                                               }
+                                       } else 
+                                               receive_tcppacket(c, tcpbuffer, c->tcplen);
                                        c->tcplen = 0;
                                        continue;
                                } else {
index 27b5eb5..d1dde61 100644 (file)
--- a/src/net.h
+++ b/src/net.h
@@ -121,6 +121,20 @@ extern char *myport;
 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;
+
 /* Yes, very strange placement indeed, but otherwise the typedefs get all tangled up */
 #include "connection.h"
 #include "node.h"
@@ -135,6 +149,7 @@ extern int setup_vpn_in_socket(const sockaddr_t *);
 extern void send_packet(struct node_t *, vpn_packet_t *);
 extern void receive_tcppacket(struct connection_t *, const char *, int);
 extern void broadcast_packet(const struct node_t *, vpn_packet_t *);
+extern char *get_name(void);
 extern bool setup_network(void);
 extern void setup_outgoing_connection(struct outgoing_t *);
 extern void try_outgoing_connections(void);
index ca6aff3..cbdc15c 100644 (file)
@@ -576,24 +576,50 @@ void send_packet(node_t *n, vpn_packet_t *packet) {
 void broadcast_packet(const node_t *from, vpn_packet_t *packet) {
        splay_node_t *node;
        connection_t *c;
+       node_t *n;
+
+       // Always give ourself a copy of the packet.
+       if(from != myself)
+               send_packet(myself, packet);
+
+       // In TunnelServer mode, do not forward broadcast packets.
+        // The MST might not be valid and create loops.
+       if(tunnelserver || broadcast_mode == BMODE_NONE)
+               return;
 
        logger(DEBUG_TRAFFIC, LOG_INFO, "Broadcasting packet of %d bytes from %s (%s)",
                           packet->len, from->name, from->hostname);
 
-       if(from != myself) {
-               send_packet(myself, packet);
+       switch(broadcast_mode) {
+               // In MST mode, broadcast packets travel via the Minimum Spanning Tree.
+               // This guarantees all nodes receive the broadcast packet, and
+               // usually distributes the sending of broadcast packets over all nodes.
+               case BMODE_MST:
+                       for(node = connection_tree->head; node; node = node->next) {
+                               c = node->data;
 
-               // In TunnelServer mode, do not forward broadcast packets.
-                // The MST might not be valid and create loops.
-               if(tunnelserver)
-                       return;
-       }
+                               if(c->status.active && c->status.mst && c != from->nexthop->connection)
+                                       send_packet(c->node, packet);
+                       }
+                       break;
+
+               // In direct mode, we send copies to each node we know of.
+               // However, this only reaches nodes that can be reached in a single hop.
+               // We don't have enough information to forward broadcast packets in this case.
+               case BMODE_DIRECT:
+                       if(from != myself)
+                               break;
+
+                       for(node = node_udp_tree->head; node; node = node->next) {
+                               n = node->data;
 
-       for(node = connection_tree->head; node; node = node->next) {
-               c = node->data;
+                               if(n->status.reachable && ((n->via == myself && n->nexthop == n) || n->via == n))
+                                       send_packet(n, packet);
+                       }
+                       break;
 
-               if(c->status.active && c->status.mst && c != from->nexthop->connection)
-                       send_packet(c->node, packet);
+               default:
+                       break;
        }
 }
 
index 4afb31b..3285a32 100644 (file)
@@ -46,6 +46,12 @@ char *myport;
 static struct event device_ev;
 devops_t devops;
 
+char *proxyhost;
+char *proxyport;
+char *proxyuser;
+char *proxypass;
+proxytype_t proxytype;
+
 bool node_read_ecdsa_public_key(node_t *n) {
        if(ecdsa_active(&n->ecdsa))
                return true;
@@ -340,6 +346,44 @@ void load_all_subnets(void) {
        closedir(dir);
 }
 
+char *get_name(void) {
+       char *name = NULL;
+
+       get_config_string(lookup_config(config_tree, "Name"), &name);
+
+       if(!name)
+               return NULL;
+
+       if(*name == '$') {
+               char *envname = getenv(name + 1);
+               if(!envname) {
+                       if(strcmp(name + 1, "HOST")) {
+                               fprintf(stderr, "Invalid Name: environment variable %s does not exist\n", name + 1);
+                               return false;
+                       }
+                       envname = alloca(32);
+                       if(gethostname(envname, 32)) {
+                               fprintf(stderr, "Could not get hostname: %s\n", strerror(errno));
+                               return false;
+                       }
+                       envname[31] = 0;
+               }
+               free(name);
+               name = xstrdup(envname);
+               for(char *c = name; *c; c++)
+                       if(!isalnum(*c))
+                               *c = '_';
+       }
+
+       if(!check_id(name)) {
+               logger(DEBUG_ALWAYS, LOG_ERR, "Invalid name for myself!");
+               free(name);
+               return false;
+       }
+
+       return name;
+}
+
 /*
   Configure node_t myself and set up the local sockets (listen only)
 */
@@ -349,6 +393,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;
@@ -365,17 +411,11 @@ static bool setup_myself(void) {
        myself->connection->protocol_major = PROT_MAJOR;
        myself->connection->protocol_minor = PROT_MINOR;
 
-       if(!get_config_string(lookup_config(config_tree, "Name"), &name)) {     /* Not acceptable */
+       if(!(name = get_name())) {
                logger(DEBUG_ALWAYS, LOG_ERR, "Name for tinc daemon required!");
                return false;
        }
 
-       if(!check_id(name)) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Invalid name for myself!");
-               free(name);
-               return false;
-       }
-
        myself->name = name;
        myself->connection->name = xstrdup(name);
        xasprintf(&fname, "%s/hosts/%s", confbase, name);
@@ -404,6 +444,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(DEBUG_ALWAYS, LOG_ERR, "Unknown proxy type %s!", proxy);
+                       return false;
+               }
+
+               switch(proxytype) {
+                       case PROXY_NONE:
+                       default:
+                               break;
+
+                       case PROXY_EXEC:
+                               if(!space || !*space) {
+                                       logger(DEBUG_ALWAYS, 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(DEBUG_ALWAYS, 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)
+                                       proxypass = xstrdup(proxypass);
+                               break;
+               }
+
+               free(proxy);
+       }
+
        /* Read in all the subnets specified in the host configuration file */
 
        cfg = lookup_config(config_tree, "Subnet");
@@ -474,7 +576,19 @@ static bool setup_myself(void) {
 
        get_config_bool(lookup_config(config_tree, "PriorityInheritance"), &priorityinheritance);
        get_config_bool(lookup_config(config_tree, "DecrementTTL"), &decrement_ttl);
-       get_config_bool(lookup_config(config_tree, "Broadcast"), &broadcast);
+       if(get_config_string(lookup_config(config_tree, "Broadcast"), &mode)) {
+               if(!strcasecmp(mode, "no"))
+                       broadcast_mode = BMODE_NONE;
+               else if(!strcasecmp(mode, "yes") || !strcasecmp(mode, "mst"))
+                       broadcast_mode = BMODE_MST;
+               else if(!strcasecmp(mode, "direct"))
+                       broadcast_mode = BMODE_DIRECT;
+               else {
+                       logger(DEBUG_ALWAYS, LOG_ERR, "Invalid broadcast mode!");
+                       return false;
+               }
+               free(mode);
+       }
 
 #if !defined(SOL_IP) || !defined(IP_TOS)
        if(priorityinheritance)
index af3f2fe..7f3db5c 100644 (file)
@@ -292,7 +292,8 @@ void retry_outgoing(outgoing_t *outgoing) {
 void finish_connecting(connection_t *c) {
        logger(DEBUG_CONNECTIONS, LOG_INFO, "Connected to %s (%s)", c->name, c->hostname);
 
-       configure_tcp(c);
+       if(proxytype != PROXY_EXEC)
+               configure_tcp(c);
 
        c->last_ping_time = time(NULL);
        c->status.connecting = false;
@@ -300,8 +301,57 @@ void finish_connecting(connection_t *c) {
        send_id(c);
 }
 
+static void do_outgoing_pipe(connection_t *c, char *command) {
+#ifndef HAVE_MINGW
+       int fd[2];
+
+       if(socketpair(AF_UNIX, SOCK_STREAM, 0, fd)) {
+               logger(DEBUG_ALWAYS, LOG_ERR, "Could not create socketpair: %s\n", strerror(errno));
+               return;
+       }
+
+       if(fork()) {
+               c->socket = fd[0];
+               close(fd[1]);
+               logger(DEBUG_CONNECTIONS, LOG_DEBUG, "Using proxy %s", command);
+               return;
+       }
+
+       close(0);
+       close(1);
+       close(fd[0]);
+       dup2(fd[1], 0);
+       dup2(fd[1], 1);
+       close(fd[1]);
+
+       // Other filedescriptors should be closed automatically by CLOEXEC
+
+       char *host = NULL;
+       char *port = NULL;
+
+       sockaddr2str(&c->address, &host, &port);
+       setenv("REMOTEADDRESS", host, true);
+       setenv("REMOTEPORT", port, true);
+       setenv("NODE", c->name, true);
+       setenv("NAME", myself->name, true);
+       if(netname)
+               setenv("NETNAME", netname, true);
+
+       int result = system(command);
+       if(result < 0)
+               logger(DEBUG_ALWAYS, LOG_ERR, "Could not execute %s: %s\n", command, strerror(errno));
+       else if(result)
+               logger(DEBUG_ALWAYS, LOG_ERR, "%s exited with non-zero status %d", command, result);
+       exit(result);
+#else
+       logger(DEBUG_ALWAYS, LOG_ERR, "Proxy type exec not supported on this platform!");
+       return;
+#endif
+}
+
 bool do_outgoing_connection(connection_t *c) {
        char *address, *port, *space;
+       struct addrinfo *proxyai = NULL;
        int result;
 
        if(!c->outgoing) {
@@ -357,32 +407,48 @@ begin:
        logger(DEBUG_CONNECTIONS, 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);
+               configure_tcp(c);
+       } else if(proxytype == PROXY_EXEC) {
+               do_outgoing_pipe(c, proxyhost);
+       } else {
+               proxyai = str2addrinfo(proxyhost, proxyport, SOCK_STREAM);
+               if(!proxyai)
+                       goto begin;
+               logger(DEBUG_CONNECTIONS, LOG_INFO, "Using proxy at %s port %s", proxyhost, proxyport);
+               c->socket = socket(proxyai->ai_family, SOCK_STREAM, IPPROTO_TCP);
+       }
 
        if(c->socket == -1) {
                logger(DEBUG_CONNECTIONS, LOG_ERR, "Creating socket for %s failed: %s", c->hostname, sockstrerror(sockerrno));
                goto begin;
        }
 
-#if defined(SOL_IPV6) && defined(IPV6_V6ONLY)
-       int option = 1;
-       if(c->address.sa.sa_family == AF_INET6)
-               setsockopt(c->socket, SOL_IPV6, IPV6_V6ONLY, (void *)&option, sizeof option);
+#ifdef FD_CLOEXEC
+       fcntl(c->socket, F_SETFD, FD_CLOEXEC);
 #endif
 
-       bind_to_interface(c->socket);
-
-       /* Optimize TCP settings */
+       if(proxytype != PROXY_EXEC) {
+#if defined(SOL_IPV6) && defined(IPV6_V6ONLY)
+               int option = 1;
+               if(c->address.sa.sa_family == AF_INET6)
+                       setsockopt(c->socket, SOL_IPV6, IPV6_V6ONLY, (void *)&option, sizeof option);
+#endif
 
-       configure_tcp(c);
+               bind_to_interface(c->socket);
+       }
 
        /* 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 if(proxytype == PROXY_EXEC) {
+               result = 0;
+       } else {
+               result = connect(c->socket, proxyai->ai_addr, proxyai->ai_addrlen);
+               freeaddrinfo(proxyai);
+       }
 
        if(result == -1) {
                if(sockinprogress(sockerrno)) {
index 9d848dc..340a2da 100644 (file)
@@ -82,8 +82,10 @@ void sockaddr2str(const sockaddr_t *sa, char **addrstr, char **portstr) {
        int err;
 
        if(sa->sa.sa_family == AF_UNKNOWN) {
-               *addrstr = xstrdup(sa->unknown.address);
-               *portstr = xstrdup(sa->unknown.port);
+               if(addrstr)
+                       *addrstr = xstrdup(sa->unknown.address);
+               if(portstr)
+                       *portstr = xstrdup(sa->unknown.port);
                return;
        }
 
index 1e63f2e..1c5b6cf 100644 (file)
@@ -108,6 +108,20 @@ void forward_request(connection_t *from, const char *request) {
 }
 
 bool receive_request(connection_t *c, const char *request) {
+       if(proxytype == PROXY_HTTP && c->allow_request == ID) {
+               if(!request[0] || request[0] == '\r')
+                       return true;
+               if(!strncasecmp(request, "HTTP/1.1 ", 9)) {
+                       if(!strncmp(request + 9, "200", 3)) {
+                               logger(DEBUG_CONNECTIONS, LOG_DEBUG, "Proxy request granted");
+                               return true;
+                       } else {
+                               logger(DEBUG_ALWAYS, LOG_DEBUG, "Proxy request rejected: %s", request + 9);
+                               return false;
+                       }
+               }
+       }
+
        int reqno = atoi(request);
 
        if(reqno || *request == '0') {
index 057b88e..ccb7976 100644 (file)
 #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(DEBUG_ALWAYS, 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(DEBUG_ALWAYS, LOG_ERR, "Address family %hx 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(DEBUG_ALWAYS, LOG_ERR, "Proxy type not implemented yet");
+                       return false;
+               case PROXY_EXEC:
+                       return true;
+               default:
+                       logger(DEBUG_ALWAYS, LOG_ERR, "Unknown proxy type");
+                       return false;
+       }
+}
+
 bool send_id(connection_t *c) {
        gettimeofday(&c->start, NULL);
 
@@ -54,6 +140,10 @@ bool send_id(connection_t *c) {
                        minor = myself->connection->protocol_minor;
        }
 
+       if(proxytype)
+               if(!send_proxyrequest(c))
+                       return false;
+
        return send_request(c, "%d %s %d.%d", ID, myself->connection->name, myself->connection->protocol_major, minor);
 }
 
index 5bf6e92..4c4312c 100644 (file)
 
 rmode_t routing_mode = RMODE_ROUTER;
 fmode_t forwarding_mode = FMODE_INTERNAL;
+bmode_t broadcast_mode = BMODE_MST;
 bool decrement_ttl = false;
 bool directonly = false;
 bool priorityinheritance = false;
 int macexpire = 600;
 bool overwrite_mac = false;
-bool broadcast = true;
 mac_t mymac = {{0xFE, 0xFD, 0, 0, 0, 0}};
 bool pcap = false;
 
@@ -447,7 +447,7 @@ static void route_ipv4(node_t *source, vpn_packet_t *packet) {
        if(!checklength(source, packet, ether_size + ip_size))
                return;
 
-       if(broadcast && (((packet->data[30] & 0xf0) == 0xe0) || (
+       if(broadcast_mode && (((packet->data[30] & 0xf0) == 0xe0) || (
                        packet->data[30] == 255 &&
                        packet->data[31] == 255 &&
                        packet->data[32] == 255 &&
@@ -744,7 +744,7 @@ static void route_ipv6(node_t *source, vpn_packet_t *packet) {
                return;
        }
 
-       if(broadcast && packet->data[38] == 255)
+       if(broadcast_mode && packet->data[38] == 255)
                broadcast_packet(source, packet);
        else
                route_ipv6_unicast(source, packet);
@@ -834,8 +834,7 @@ static void route_mac(node_t *source, vpn_packet_t *packet) {
        subnet = lookup_subnet_mac(NULL, &dest);
 
        if(!subnet) {
-               if(broadcast)
-                       broadcast_packet(source, packet);
+               broadcast_packet(source, packet);
                return;
        }
 
index 46dc3bd..6f4a4e5 100644 (file)
@@ -36,12 +36,18 @@ typedef enum fmode_t {
        FMODE_KERNEL,
 } fmode_t;
 
+typedef enum bmode_t {
+       BMODE_NONE = 0,
+       BMODE_MST,
+       BMODE_DIRECT,
+} bmode_t;
+
 extern rmode_t routing_mode;
 extern fmode_t forwarding_mode;
+extern bmode_t broadcast_mode;
 extern bool decrement_ttl;
 extern bool directonly;
 extern bool overwrite_mac;
-extern bool broadcast;
 extern bool priorityinheritance;
 extern int macexpire;
 extern bool pcap;
index cf46221..e750450 100644 (file)
@@ -137,15 +137,17 @@ int b64encode(const char *src, char *dst, int length) {
 #endif
 
 const char *winerror(int err) {
-       static char buf[1024], *newline;
+       static char buf[1024], *ptr;
+
+       ptr = buf + sprintf(buf, "(%d) ", err);
 
        if (!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
-               NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, sizeof(buf), NULL)) {
+               NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), ptr, sizeof(buf) - (ptr - buf), NULL)) {
                strncpy(buf, "(unable to format errormessage)", sizeof(buf));
        };
 
-       if((newline = strchr(buf, '\r')))
-               *newline = '\0';
+       if((ptr = strchr(buf, '\r')))
+               *ptr = '\0';
 
        return buf;
 }