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:
1  2 
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 --cc NEWS
--- 1/NEWS
--- 2/NEWS
+++ b/NEWS
@@@ -1,33 -1,14 +1,44 @@@
 +Version 1.1pre2              Juli 17 2011
 +
 + * .cookie files are renamed to .pid files, which are compatible with 1.0.x.
 +
 + * Experimental protocol enhancements that can be enabled with the option
 +   ExperimentalProtocol = yes:
 +
 +   * Ephemeral ECDH key exchange will be used for both the meta protocol and
 +     UDP session keys.
 +   * Key exchanges are signed with ECDSA.
 +   * ECDSA public keys are automatically exchanged after RSA authentication if
 +     nodes do not know each other's ECDSA public key yet.
 +
 +Version 1.1pre1              June 25 2011
 +
 + * Control interface allows control of a running tinc daemon. Used by:
 +   * tincctl, a commandline utility
 +   * tinc-gui, a preliminary GUI implemented in Python/wxWidgets
 +
 + * Code cleanups and reorganization. 
 +
 + * Repleacable cryptography backend, currently supports OpenSSL and libgcrypt.
 +
 + * Use libevent to handle I/O events and timeouts.
 +
 + * Use splay trees instead of AVL trees to manage internal datastructures.
 +
 + 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 --cc THANKS
Simple merge
Simple merge
diff --cc doc/tinc.texi
Simple merge
diff --cc have.h
Simple merge
diff --cc src/meta.c
@@@ -160,59 -155,57 +160,67 @@@ bool receive_meta(connection_t *c) 
                return false;
        }
  
 -      oldlen = c->buflen;
 -      c->buflen += lenin;
 +      do {
 +              if(c->protocol_minor >= 2) {
 +                      logger(DEBUG_META, LOG_DEBUG, "Receiving %d bytes of SPTPS data", inlen);
 +                      return sptps_receive_data(&c->sptps, bufp, inlen);
 +              }
 +
 +              if(!c->status.decryptin) {
 +                      endp = memchr(bufp, '\n', inlen);
 +                      if(endp)
 +                              endp++;
 +                      else
 +                              endp = bufp + inlen;
  
 -      while(lenin > 0) {
 -              /* Decrypt */
 +                      buffer_add(&c->inbuf, bufp, endp - bufp);
  
 -              if(c->status.decryptin && !decrypted) {
 -                      result = EVP_DecryptUpdate(c->inctx, (unsigned char *)inbuf, &lenout, (unsigned char *)c->buffer + oldlen, lenin);
 -                      if(!result || lenout != lenin) {
 -                              logger(LOG_ERR, "Error while decrypting metadata from %s (%s): %s",
 -                                              c->name, c->hostname, ERR_error_string(ERR_get_error(), NULL));
 +                      inlen -= endp - bufp;
 +                      bufp = endp;
 +              } else {
 +                      size_t outlen = inlen;
 +                      logger(DEBUG_META, LOG_DEBUG, "Received encrypted %d bytes", inlen);
 +
 +                      if(!cipher_decrypt(&c->incipher, bufp, inlen, buffer_prepare(&c->inbuf, inlen), &outlen, false) || inlen != outlen) {
 +                              logger(DEBUG_ALWAYS, LOG_ERR, "Error while decrypting metadata from %s (%s)",
 +                                         c->name, c->hostname);
                                return false;
                        }
 -                      memcpy(c->buffer + oldlen, inbuf, lenin);
 -                      decrypted = true;
 -              }
  
 -              /* Are we receiving a TCPpacket? */
 -
 -              if(c->tcplen) {
 -                      if(c->tcplen <= c->buflen) {
 -                              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;
 -                              memmove(c->buffer, c->buffer + c->tcplen, c->buflen);
 -                              oldlen = 0;
 -                              c->tcplen = 0;
 -                              continue;
 -                      } else {
 -                              break;
 -                      }
 +                      inlen = 0;
                }
  
 -              /* Otherwise we are waiting for a request */
 +              while(c->inbuf.len) {
 +                      /* Are we receiving a TCPpacket? */
 +
 +                      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 {
 +                                      break;
 +                              }
 +                      }
  
 -              reqlen = 0;
 +                      /* 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;
 +                      char *request = buffer_readline(&c->inbuf);
 +                      if(request) {
 +                              bool result = receive_request(c, request);
 +                              if(!result)
 +                                      return false;
 +                              continue;
 +                      } else {
                                break;
                        }
                }
diff --cc src/net.h
+++ b/src/net.h
@@@ -121,20 -121,38 +121,35 @@@ extern char *myport
  extern int contradicting_add_edge;
  extern int contradicting_del_edge;
  
 -extern volatile bool running;
 -
+ 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"
  
  extern void retry_outgoing(outgoing_t *);
 -extern void handle_incoming_vpn_data(int);
 +extern void handle_incoming_vpn_data(int, short, void *);
  extern void finish_connecting(struct connection_t *);
 -extern void do_outgoing_connection(struct connection_t *);
 -extern bool handle_new_meta_connection(int);
 +extern bool do_outgoing_connection(struct connection_t *);
 +extern void handle_new_meta_connection(int, short, void *);
  extern int setup_listen_socket(const sockaddr_t *);
  extern int setup_vpn_in_socket(const sockaddr_t *);
 -extern void send_packet(const struct node_t *, vpn_packet_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);
@@@ -574,26 -582,52 +574,52 @@@ void send_packet(node_t *n, vpn_packet_
  /* Broadcast a packet using the minimum spanning tree */
  
  void broadcast_packet(const node_t *from, vpn_packet_t *packet) {
 -      avl_node_t *node;
 +      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;
  
 -      ifdebug(TRAFFIC) logger(LOG_INFO, "Broadcasting packet of %d bytes from %s (%s)",
 +      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;
        }
  }
  
diff --cc src/net_setup.c
  #include "xalloc.h"
  
  char *myport;
 +static struct event device_ev;
  devops_t devops;
  
 -bool read_rsa_public_key(connection_t *c) {
+ 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;
 +
 +      splay_tree_t *config_tree;
        FILE *fp;
        char *fname;
 -      char *key;
 +      char *p;
 +      bool result = false;
  
 -      if(!c->rsa_key) {
 -              c->rsa_key = RSA_new();
 -//            RSA_blinding_on(c->rsa_key, NULL);
 -      }
 +      xasprintf(&fname, "%s/hosts/%s", confbase, n->name);
  
 -      /* First, check for simple PublicKey statement */
 +      init_configuration(&config_tree);
 +      if(!read_config_file(config_tree, fname))
 +              goto exit;
  
 -      if(get_config_string(lookup_config(c->config_tree, "PublicKey"), &key)) {
 -              BN_hex2bn(&c->rsa_key->n, key);
 -              BN_hex2bn(&c->rsa_key->e, "FFFF");
 -              free(key);
 -              return true;
 +      /* First, check for simple ECDSAPublicKey statement */
 +
 +      if(get_config_string(lookup_config(config_tree, "ECDSAPublicKey"), &p)) {
 +              result = ecdsa_set_base64_public_key(&n->ecdsa, p);
 +              free(p);
 +              goto exit;
        }
  
 -      /* Else, check for PublicKeyFile statement and read it */
 +      /* Else, check for ECDSAPublicKeyFile statement and read it */
  
 -      if(get_config_string(lookup_config(c->config_tree, "PublicKeyFile"), &fname)) {
 -              fp = fopen(fname, "r");
 +      free(fname);
  
 -              if(!fp) {
 -                      logger(LOG_ERR, "Error reading RSA public key file `%s': %s",
 -                                 fname, strerror(errno));
 -                      free(fname);
 -                      return false;
 -              }
 +      if(!get_config_string(lookup_config(config_tree, "ECDSAPublicKeyFile"), &fname))
 +              xasprintf(&fname, "%s/hosts/%s", confbase, n->name);
  
 -              free(fname);
 -              c->rsa_key = PEM_read_RSAPublicKey(fp, &c->rsa_key, NULL, NULL);
 -              fclose(fp);
 +      fp = fopen(fname, "r");
  
 -              if(c->rsa_key)
 -                      return true;            /* Woohoo. */
 +      if(!fp) {
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Error reading ECDSA public key file `%s': %s", fname, strerror(errno));
 +              goto exit;
 +      }
  
 -              /* If it fails, try PEM_read_RSA_PUBKEY. */
 -              fp = fopen(fname, "r");
 +      result = ecdsa_read_pem_public_key(&n->ecdsa, fp);
 +      fclose(fp);
  
 -              if(!fp) {
 -                      logger(LOG_ERR, "Error reading RSA public key file `%s': %s",
 -                                 fname, strerror(errno));
 -                      free(fname);
 -                      return false;
 -              }
 +exit:
 +      exit_configuration(&config_tree);
 +      free(fname);
 +      return result;
 +}
  
 -              free(fname);
 -              c->rsa_key = PEM_read_RSA_PUBKEY(fp, &c->rsa_key, NULL, NULL);
 -              fclose(fp);
 +bool read_ecdsa_public_key(connection_t *c) {
 +      FILE *fp;
 +      char *fname;
 +      char *p;
 +      bool result;
  
 -              if(c->rsa_key) {
 -//                            RSA_blinding_on(c->rsa_key, NULL);
 -                      return true;
 -              }
 +      /* First, check for simple ECDSAPublicKey statement */
  
 -              logger(LOG_ERR, "Reading RSA public key file `%s' failed: %s",
 -                         fname, strerror(errno));
 -              return false;
 +      if(get_config_string(lookup_config(c->config_tree, "ECDSAPublicKey"), &p)) {
 +              result = ecdsa_set_base64_public_key(&c->ecdsa, p);
 +              free(p);
 +              return result;
        }
  
 -      /* Else, check if a harnessed public key is in the config file */
 +      /* Else, check for ECDSAPublicKeyFile statement and read it */
 +
 +      if(!get_config_string(lookup_config(c->config_tree, "ECDSAPublicKeyFile"), &fname))
 +              xasprintf(&fname, "%s/hosts/%s", confbase, c->name);
  
 -      xasprintf(&fname, "%s/hosts/%s", confbase, c->name);
        fp = fopen(fname, "r");
  
        if(!fp) {
@@@ -340,6 -275,44 +346,44 @@@ void load_all_subnets(void) 
        closedir(dir);
  }
  
 -              logger(LOG_ERR, "Invalid name for myself!");
+ 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)
  */
@@@ -362,11 -337,10 +408,11 @@@ static bool setup_myself(void) 
        myself->connection->hostname = xstrdup("MYSELF");
  
        myself->connection->options = 0;
 -      myself->connection->protocol_version = PROT_CURRENT;
 +      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(LOG_ERR, "Name for tinc daemon required!");
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Name for tinc daemon required!");
                return false;
        }
  
                sockaddr2str(&sa, NULL, &myport);
        }
  
 -                      logger(LOG_ERR, "Unknown proxy type %s!", proxy);
+       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, "Argument expected for proxy type exec!");
++                      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(LOG_ERR, "Host and port argument expected for proxy!");
++                                      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");
  
        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(LOG_ERR, "Invalid broadcast mode!");
++                      logger(DEBUG_ALWAYS, LOG_ERR, "Invalid broadcast mode!");
+                       return false;
+               }
+               free(mode);
+       }
  
  #if !defined(SOL_IP) || !defined(IP_TOS)
        if(priorityinheritance)
@@@ -290,18 -292,67 +290,68 @@@ void retry_outgoing(outgoing_t *outgoin
  }
  
  void finish_connecting(connection_t *c) {
 -      ifdebug(CONNECTIONS) logger(LOG_INFO, "Connected to %s (%s)", c->name, c->hostname);
 +      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 = now;
 +      c->last_ping_time = time(NULL);
 +      c->status.connecting = false;
  
        send_id(c);
  }
  
 -              logger(LOG_ERR, "Could not create socketpair: %s\n", strerror(errno));
+ 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(LOG_DEBUG, "Using proxy %s", command);
++              logger(DEBUG_ALWAYS, LOG_ERR, "Could not create socketpair: %s\n", strerror(errno));
+               return;
+       }
+       if(fork()) {
+               c->socket = fd[0];
+               close(fd[1]);
 -              logger(LOG_ERR, "Could not execute %s: %s\n", command, strerror(errno));
++              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(LOG_ERR, "%s exited with non-zero status %d", command, result);
++              logger(DEBUG_ALWAYS, LOG_ERR, "Could not execute %s: %s\n", command, strerror(errno));
+       else if(result)
 -      logger(LOG_ERR, "Proxy type exec not supported on this platform!");
++              logger(DEBUG_ALWAYS, LOG_ERR, "%s exited with non-zero status %d", command, result);
+       exit(result);
+ #else
 -void do_outgoing_connection(connection_t *c) {
++      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) {
@@@ -354,17 -405,24 +404,24 @@@ begin
  
        c->hostname = sockaddr2hostname(&c->address);
  
 -      ifdebug(CONNECTIONS) logger(LOG_INFO, "Trying to connect to %s (%s)", c->name,
 +      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;
 -              ifdebug(CONNECTIONS) logger(LOG_INFO, "Using proxy at %s port %s", proxyhost, proxyport);
++              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) {
 -              ifdebug(CONNECTIONS) logger(LOG_ERR, "Creating socket for %s failed: %s", c->hostname, sockstrerror(sockerrno));
 +              logger(DEBUG_CONNECTIONS, LOG_ERR, "Creating socket for %s failed: %s", c->hostname, sockstrerror(sockerrno));
                goto begin;
        }
  
diff --cc src/netutl.c
Simple merge
diff --cc src/protocol.c
@@@ -85,41 -84,86 +85,55 @@@ bool send_request(connection_t *c, cons
                return false;
        }
  
 -      ifdebug(PROTOCOL) {
 -              sscanf(buffer, "%d", &request);
 -              ifdebug(META)
 -                      logger(LOG_DEBUG, "Sending %s to %s (%s): %s",
 -                                 request_name[request], c->name, c->hostname, buffer);
 -              else
 -                      logger(LOG_DEBUG, "Sending %s to %s (%s)", request_name[request],
 -                                 c->name, c->hostname);
 -      }
 +      logger(DEBUG_META, LOG_DEBUG, "Sending %s to %s (%s): %s", request_name[atoi(request)], c->name, c->hostname, request);
  
 -      buffer[len++] = '\n';
 +      request[len++] = '\n';
  
        if(c == everyone) {
 -              broadcast_meta(NULL, buffer, len);
 +              broadcast_meta(NULL, request, len);
                return true;
        } else
 -              return send_meta(c, buffer, len);
 +              return send_meta(c, request, len);
  }
  
 -void forward_request(connection_t *from) {
 -      int request;
 -
 -      ifdebug(PROTOCOL) {
 -              sscanf(from->buffer, "%d", &request);
 -              ifdebug(META)
 -                      logger(LOG_DEBUG, "Forwarding %s from %s (%s): %s",
 -                                 request_name[request], from->name, from->hostname,
 -                                 from->buffer);
 -              else
 -                      logger(LOG_DEBUG, "Forwarding %s from %s (%s)",
 -                                 request_name[request], from->name, from->hostname);
 -      }
 -
 -      from->buffer[from->reqlen - 1] = '\n';
 +void forward_request(connection_t *from, const char *request) {
 +      logger(DEBUG_META, LOG_DEBUG, "Forwarding %s from %s (%s): %s", request_name[atoi(request)], from->name, from->hostname, request);
  
 -      broadcast_meta(from, from->buffer, from->reqlen);
 +      // Create a temporary newline-terminated copy of the request
 +      int len = strlen(request);
 +      char tmp[len + 1];
 +      memcpy(tmp, request, len);
 +      tmp[len] = '\n';
 +      broadcast_meta(from, tmp, sizeof tmp);
  }
  
 -bool receive_request(connection_t *c) {
 -      int request;
 -
 +bool receive_request(connection_t *c, const char *request) {
+       if(proxytype == PROXY_HTTP && c->allow_request == ID) {
 -              if(!c->buffer[0] || c->buffer[0] == '\r')
++              if(!request[0] || request[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");
++              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(LOG_DEBUG, "Proxy request rejected: %s", c->buffer + 9);
++                              logger(DEBUG_ALWAYS, LOG_DEBUG, "Proxy request rejected: %s", request + 9);
+                               return false;
+                       }
+               }
+       }
 -      if(sscanf(c->buffer, "%d", &request) == 1) {
 -              if((request < 0) || (request >= LAST) || !request_handlers[request]) {
 -                      ifdebug(META)
 -                              logger(LOG_DEBUG, "Unknown request from %s (%s): %s",
 -                                         c->name, c->hostname, c->buffer);
 -                      else
 -                              logger(LOG_ERR, "Unknown request from %s (%s)",
 -                                         c->name, c->hostname);
 +      int reqno = atoi(request);
  
 +      if(reqno || *request == '0') {
 +              if((reqno < 0) || (reqno >= LAST) || !request_handlers[reqno]) {
 +                      logger(DEBUG_META, LOG_DEBUG, "Unknown request from %s (%s): %s", c->name, c->hostname, request);
                        return false;
                } else {
 -                      ifdebug(PROTOCOL) {
 -                              ifdebug(META)
 -                                      logger(LOG_DEBUG, "Got %s from %s (%s): %s",
 -                                                 request_name[request], c->name, c->hostname,
 -                                                 c->buffer);
 -                              else
 -                                      logger(LOG_DEBUG, "Got %s from %s (%s)",
 -                                                 request_name[request], c->name, c->hostname);
 -                      }
 +                      logger(DEBUG_META, LOG_DEBUG, "Got %s from %s (%s): %s", request_name[reqno], c->name, c->hostname, request);
                }
  
 -              if((c->allow_request != ALL) && (c->allow_request != request)) {
 -                      logger(LOG_ERR, "Unauthorized request from %s (%s)", c->name,
 -                                 c->hostname);
 +              if((c->allow_request != ALL) && (c->allow_request != reqno)) {
 +                      logger(DEBUG_ALWAYS, LOG_ERR, "Unauthorized request from %s (%s)", c->name, c->hostname);
                        return false;
                }
  
  #include "utils.h"
  #include "xalloc.h"
  
 -                              logger(LOG_ERR, "Cannot connect to an IPv6 host through a SOCKS 4 proxy!");
+ 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, "Address family %hx not supported for SOCKS 5 proxies!", c->address.sa.sa_family);
++                              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(LOG_ERR, "Proxy type not implemented yet");
++                              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(LOG_ERR, "Unknown proxy type");
++                      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) {
 -      return send_request(c, "%d %s %d", ID, myself->connection->name,
 -                                              myself->connection->protocol_version);
 +      gettimeofday(&c->start, NULL);
 +
 +      int minor = 0;
 +
 +      if(experimental) {
 +              if(c->config_tree && !read_ecdsa_public_key(c))
 +                      minor = 1;
 +              else
 +                      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);
  }
  
 -bool id_h(connection_t *c) {
 +bool id_h(connection_t *c, const char *request) {
        char name[MAX_STRING_SIZE];
  
 -      if(sscanf(c->buffer, "%*d " MAX_STRING " %d", name, &c->protocol_version) != 2) {
 -              logger(LOG_ERR, "Got bad %s from %s (%s)", "ID", c->name,
 +      if(sscanf(request, "%*d " MAX_STRING " %d.%d", name, &c->protocol_major, &c->protocol_minor) < 2) {
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "ID", c->name,
                           c->hostname);
                return false;
        }
diff --cc src/route.c
@@@ -41,9 -40,7 +42,8 @@@ 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;
  
  /* Sizes of various headers */
  
diff --cc src/route.h
@@@ -41,10 -48,8 +48,9 @@@ 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;
  
  extern mac_t mymac;
  
diff --cc src/utils.c
index cf46221,0000000..e750450
mode 100644,000000..100644
--- /dev/null
@@@ -1,160 -1,0 +1,162 @@@
-       static char buf[1024], *newline;
 +/*
 +    utils.c -- gathering of some stupid small functions
 +    Copyright (C) 1999-2005 Ivo Timmermans
 +                  2000-2009 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 "../src/logger.h"
 +#include "utils.h"
 +
 +static const char hexadecimals[] = "0123456789ABCDEF";
 +static const char base64imals[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 +
 +static int charhex2bin(char c) {
 +      if(isdigit(c))
 +              return c - '0';
 +      else
 +              return toupper(c) - 'A' + 10;
 +}
 +
 +static int charb64decode(char c) {
 +      if(c >= 'a')
 +              return c - 'a' + 26;
 +      else if(c >= 'A')
 +              return c - 'A';
 +      else if(c >= '0') 
 +              return c - '0' + 52;
 +      else if(c == '+')
 +              return 62;
 +      else
 +              return 63;
 +}
 +
 +int hex2bin(const char *src, char *dst, int length) {
 +      int i;
 +      for(i = 0; i < length && src[i * 2] && src[i * 2 + 1]; i++)
 +              dst[i] = charhex2bin(src[i * 2]) * 16 + charhex2bin(src[i * 2 + 1]);
 +      return i;
 +}
 +
 +int bin2hex(const char *src, char *dst, int length) {
 +      int i;
 +      for(i = length - 1; i >= 0; i--) {
 +              dst[i * 2 + 1] = hexadecimals[(unsigned char) src[i] & 15];
 +              dst[i * 2] = hexadecimals[(unsigned char) src[i] >> 4];
 +      }
 +      dst[length * 2] = 0;
 +      return length * 2;
 +}
 +
 +int b64decode(const char *src, char *dst, int length) {
 +      int i;
 +      uint32_t triplet = 0;
 +      unsigned char *udst = (unsigned char *)dst;
 +
 +      for(i = 0; i < length / 3 * 4 && src[i]; i++) {
 +              triplet |= charb64decode(src[i]) << (6 * (i & 3));
 +              if((i & 3) == 3) {
 +                      udst[0] = triplet & 0xff; triplet >>= 8;
 +                      udst[1] = triplet & 0xff; triplet >>= 8;
 +                      udst[2] = triplet;
 +                      triplet = 0;
 +                      udst += 3;
 +              }
 +      }
 +      if((i & 3) == 3) {
 +              udst[0] = triplet & 0xff; triplet >>= 8;
 +              udst[1] = triplet & 0xff;
 +              return i / 4 * 3 + 2;
 +      } else if((i & 3) == 2) {
 +              udst[0] = triplet & 0xff;
 +              return i / 4 * 3 + 1;
 +      } else {
 +              return i / 4 * 3;
 +      }
 +}
 +
 +int b64encode(const char *src, char *dst, int length) {
 +      uint32_t triplet;
 +      const unsigned char *usrc = (unsigned char *)src;
 +      int si = length / 3 * 3;
 +      int di = length / 3 * 4;
 +
 +      switch(length % 3) {
 +              case 2: 
 +                      triplet = usrc[si] | usrc[si + 1] << 8;
 +                      dst[di] = base64imals[triplet & 63]; triplet >>= 6;
 +                      dst[di + 1] = base64imals[triplet & 63]; triplet >>= 6;
 +                      dst[di + 2] = base64imals[triplet];
 +                      dst[di + 3] = 0;
 +                      length = di + 2;
 +                      break;
 +              case 1:
 +                      triplet = usrc[si];
 +                      dst[di] = base64imals[triplet & 63]; triplet >>= 6;
 +                      dst[di + 1] = base64imals[triplet];
 +                      dst[di + 2] = 0;
 +                      length = di + 1;
 +                      break;
 +              default:
 +                      dst[di] = 0;
 +                      length = di;
 +                      break;
 +      }
 +
 +      while(si > 0) {
 +              di -= 4;
 +              si -= 3;
 +              triplet = usrc[si] | usrc[si + 1] << 8 | usrc[si + 2] << 16;
 +              dst[di] = base64imals[triplet & 63]; triplet >>= 6;
 +              dst[di + 1] = base64imals[triplet & 63]; triplet >>= 6;
 +              dst[di + 2] = base64imals[triplet & 63]; triplet >>= 6;
 +              dst[di + 3] = base64imals[triplet];
 +      }
 +
 +      return length;
 +}
 +
 +#if defined(HAVE_MINGW) || defined(HAVE_CYGWIN)
 +#ifdef HAVE_CYGWIN
 +#include <w32api/windows.h>
 +#endif
 +
 +const char *winerror(int err) {
-               NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, sizeof(buf), NULL)) {
++      static char buf[1024], *ptr;
++
++      ptr = buf + sprintf(buf, "(%d) ", err);
 +
 +      if (!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
-       if((newline = strchr(buf, '\r')))
-               *newline = '\0';
++              NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), ptr, sizeof(buf) - (ptr - buf), NULL)) {
 +              strncpy(buf, "(unable to format errormessage)", sizeof(buf));
 +      };
 +
++      if((ptr = strchr(buf, '\r')))
++              *ptr = '\0';
 +
 +      return buf;
 +}
 +#endif
 +
 +unsigned int bitfield_to_int(const void *bitfield, size_t size) {
 +      unsigned int value = 0;
 +      if(size > sizeof value)
 +              size = sizeof value;
 +      memcpy(&value, bitfield, size);
 +      return value;
 +}