Merge branch 'master' into 1.1
authorGuus Sliepen <guus@tinc-vpn.org>
Sun, 30 Sep 2012 13:00:47 +0000 (15:00 +0200)
committerGuus Sliepen <guus@tinc-vpn.org>
Sun, 30 Sep 2012 13:00:47 +0000 (15:00 +0200)
Conflicts:
lib/utils.c
src/net_setup.c
src/process.c
src/protocol_auth.c
src/protocol_key.c
src/utils.h

1  2 
THANKS
src/net_setup.c
src/openssl/rsa.c
src/process.c
src/protocol_key.c
src/tincd.c
src/utils.c

diff --combined THANKS
--- 1/THANKS
--- 2/THANKS
+++ b/THANKS
@@@ -10,7 -10,6 +10,7 @@@ We would like to thank the following pe
  * Delf Eldkraft
  * dnk
  * Enrique Zanardi
 +* Erik Tews
  * Flynn Marquardt
  * Grzegorz Dymarek
  * Hans Bayle
@@@ -28,6 -27,7 +28,7 @@@
  * Mark Glines
  * Markus Goetz
  * Martin Kihlgren
+ * Martin Schürrer
  * Matias Carrasco
  * Max Rijevski
  * Menno Smits
@@@ -45,6 -45,7 +46,7 @@@
  * Teemu Kiviniemi
  * Timothy Redaelli
  * Tonnerre Lombard
+ * Vil Brekin
  * Wessel Dankers
  * Wouter van Heyst
  
diff --combined src/net_setup.c
  
  #include "system.h"
  
 -#include <openssl/pem.h>
 -#include <openssl/rsa.h>
 -#include <openssl/rand.h>
 -#include <openssl/err.h>
 -#include <openssl/evp.h>
 -
 -#include "avl_tree.h"
 +#include "splay_tree.h"
 +#include "cipher.h"
  #include "conf.h"
  #include "connection.h"
 +#include "control.h"
  #include "device.h"
 -#include "event.h"
 +#include "digest.h"
 +#include "ecdsa.h"
  #include "graph.h"
  #include "logger.h"
  #include "net.h"
  #include "process.h"
  #include "protocol.h"
  #include "route.h"
 +#include "rsa.h"
  #include "subnet.h"
  #include "utils.h"
  #include "xalloc.h"
  
  char *myport;
 +static struct event device_ev;
  devops_t devops;
  
  char *proxyhost;
@@@ -52,238 -53,175 +52,238 @@@ char *proxyuser
  char *proxypass;
  proxytype_t proxytype;
  
 -bool read_rsa_public_key(connection_t *c) {
 +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" SLASH "hosts" SLASH "%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)) {
 -              if(BN_hex2bn(&c->rsa_key->n, key) != strlen(key)) {
 -                      logger(LOG_ERR, "Invalid PublicKey for %s!", c->name);
 -                      return false;
 -              }
 -              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" SLASH "hosts" SLASH "%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" SLASH "hosts" SLASH "%s", confbase, c->name);
  
 -      xasprintf(&fname, "%s/hosts/%s", confbase, c->name);
        fp = fopen(fname, "r");
  
        if(!fp) {
 -              logger(LOG_ERR, "Error reading RSA public key file `%s': %s", fname, strerror(errno));
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Error reading ECDSA public key file `%s': %s",
 +                         fname, strerror(errno));
                free(fname);
                return false;
        }
  
 -      c->rsa_key = PEM_read_RSAPublicKey(fp, &c->rsa_key, NULL, NULL);
 +      result = ecdsa_read_pem_public_key(&c->ecdsa, fp);
        fclose(fp);
 +
 +      if(!result)
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Parsing ECDSA public key file `%s' failed.", fname);
        free(fname);
 +      return result;
 +}
  
 -      if(c->rsa_key)
 -              return true;
 +bool read_rsa_public_key(connection_t *c) {
 +      FILE *fp;
 +      char *fname;
 +      char *n;
 +      bool result;
 +
 +      /* First, check for simple PublicKey statement */
  
 -      /* Try again with PEM_read_RSA_PUBKEY. */
 +      if(get_config_string(lookup_config(c->config_tree, "PublicKey"), &n)) {
 +              result = rsa_set_hex_public_key(&c->rsa, n, "FFFF");
 +              free(n);
 +              return result;
 +      }
 +
 +      /* Else, check for PublicKeyFile statement and read it */
 +
 +      if(!get_config_string(lookup_config(c->config_tree, "PublicKeyFile"), &fname))
 +              xasprintf(&fname, "%s" SLASH "hosts" SLASH "%s", confbase, c->name);
  
 -      xasprintf(&fname, "%s/hosts/%s", confbase, c->name);
        fp = fopen(fname, "r");
  
        if(!fp) {
 -              logger(LOG_ERR, "Error reading RSA public key file `%s': %s", fname, strerror(errno));
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Error reading RSA public key file `%s': %s", fname, strerror(errno));
                free(fname);
                return false;
        }
  
 -      c->rsa_key = PEM_read_RSA_PUBKEY(fp, &c->rsa_key, NULL, NULL);
 -//    RSA_blinding_on(c->rsa_key, NULL);
 +      result = rsa_read_pem_public_key(&c->rsa, fp);
        fclose(fp);
 +
 +      if(!result) 
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Reading RSA public key file `%s' failed: %s", fname, strerror(errno));
        free(fname);
 +      return result;
 +}
  
 -      if(c->rsa_key)
 -              return true;
 +static bool read_ecdsa_private_key(void) {
 +      FILE *fp;
 +      char *fname;
 +      bool result;
 +
 +      /* Check for PrivateKeyFile statement and read it */
  
 -      logger(LOG_ERR, "No public key for %s specified!", c->name);
 +      if(!get_config_string(lookup_config(config_tree, "ECDSAPrivateKeyFile"), &fname))
 +              xasprintf(&fname, "%s" SLASH "ecdsa_key.priv", confbase);
 +
 +      fp = fopen(fname, "r");
 +
 +      if(!fp) {
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Error reading ECDSA private key file `%s': %s", fname, strerror(errno));
 +              free(fname);
 +              return false;
 +      }
 +
 +#if !defined(HAVE_MINGW) && !defined(HAVE_CYGWIN)
 +      struct stat s;
  
 -      return false;
 +      if(fstat(fileno(fp), &s)) {
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Could not stat ECDSA private key file `%s': %s'", fname, strerror(errno));
 +              free(fname);
 +              return false;
 +      }
 +
 +      if(s.st_mode & ~0100700)
 +              logger(DEBUG_ALWAYS, LOG_WARNING, "Warning: insecure file permissions for ECDSA private key file `%s'!", fname);
 +#endif
 +
 +      result = ecdsa_read_pem_private_key(&myself->connection->ecdsa, fp);
 +      fclose(fp);
 +
 +      if(!result) 
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Reading ECDSA private key file `%s' failed: %s", fname, strerror(errno));
 +      free(fname);
 +      return result;
  }
  
  static bool read_rsa_private_key(void) {
        FILE *fp;
 -      char *fname, *key, *pubkey;
 -      struct stat s;
 +      char *fname;
 +      char *n, *d;
 +      bool result;
  
 -      if(get_config_string(lookup_config(config_tree, "PrivateKey"), &key)) {
 -              if(!get_config_string(lookup_config(config_tree, "PublicKey"), &pubkey)) {
 -                      logger(LOG_ERR, "PrivateKey used but no PublicKey found!");
 -                      return false;
 -              }
 -              myself->connection->rsa_key = RSA_new();
 -//            RSA_blinding_on(myself->connection->rsa_key, NULL);
 -              if(BN_hex2bn(&myself->connection->rsa_key->d, key) != strlen(key)) {
 -                      logger(LOG_ERR, "Invalid PrivateKey for myself!");
 -                      return false;
 -              }
 -              if(BN_hex2bn(&myself->connection->rsa_key->n, pubkey) != strlen(pubkey)) {
 -                      logger(LOG_ERR, "Invalid PublicKey for myself!");
 +      /* First, check for simple PrivateKey statement */
 +
 +      if(get_config_string(lookup_config(config_tree, "PrivateKey"), &d)) {
 +              if(!get_config_string(lookup_config(config_tree, "PublicKey"), &n)) {
 +                      logger(DEBUG_ALWAYS, LOG_ERR, "PrivateKey used but no PublicKey found!");
 +                      free(d);
                        return false;
                }
 -              BN_hex2bn(&myself->connection->rsa_key->e, "FFFF");
 -              free(key);
 -              free(pubkey);
 -              return true;
 +              result = rsa_set_hex_private_key(&myself->connection->rsa, n, "FFFF", d);
 +              free(n);
 +              free(d);
-               return true;
++              return result;
        }
  
 +      /* Else, check for PrivateKeyFile statement and read it */
 +
        if(!get_config_string(lookup_config(config_tree, "PrivateKeyFile"), &fname))
 -              xasprintf(&fname, "%s/rsa_key.priv", confbase);
 +              xasprintf(&fname, "%s" SLASH "rsa_key.priv", confbase);
  
        fp = fopen(fname, "r");
  
        if(!fp) {
 -              logger(LOG_ERR, "Error reading RSA private key file `%s': %s",
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Error reading RSA private key file `%s': %s",
                           fname, strerror(errno));
                free(fname);
                return false;
        }
  
  #if !defined(HAVE_MINGW) && !defined(HAVE_CYGWIN)
 +      struct stat s;
 +
        if(fstat(fileno(fp), &s)) {
 -              logger(LOG_ERR, "Could not stat RSA private key file `%s': %s'",
 -                              fname, strerror(errno));
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Could not stat RSA private key file `%s': %s'", fname, strerror(errno));
                free(fname);
                return false;
        }
  
        if(s.st_mode & ~0100700)
 -              logger(LOG_WARNING, "Warning: insecure file permissions for RSA private key file `%s'!", fname);
 +              logger(DEBUG_ALWAYS, LOG_WARNING, "Warning: insecure file permissions for RSA private key file `%s'!", fname);
  #endif
  
 -      myself->connection->rsa_key = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
 +      result = rsa_read_pem_private_key(&myself->connection->rsa, fp);
        fclose(fp);
  
 -      if(!myself->connection->rsa_key) {
 -              logger(LOG_ERR, "Reading RSA private key file `%s' failed: %s",
 -                         fname, strerror(errno));
 -              free(fname);
 -              return false;
 +      if(!result) 
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Reading RSA private key file `%s' failed: %s", fname, strerror(errno));
 +      free(fname);
 +      return result;
 +}
 +
 +static struct event keyexpire_event;
 +
 +static void keyexpire_handler(int fd, short events, void *data) {
 +      regenerate_key();
 +}
 +
 +void regenerate_key(void) {
 +      if(timeout_initialized(&keyexpire_event)) {
 +              logger(DEBUG_STATUS, LOG_INFO, "Expiring symmetric keys");
 +              event_del(&keyexpire_event);
 +              send_key_changed();
 +      } else {
 +              timeout_set(&keyexpire_event, keyexpire_handler, NULL);
        }
  
 -      free(fname);
 -      return true;
 +      event_add(&keyexpire_event, &(struct timeval){keylifetime, 0});
  }
  
  /*
@@@ -294,15 -232,15 +294,15 @@@ void load_all_subnets(void) 
        struct dirent *ent;
        char *dname;
        char *fname;
 -      avl_tree_t *config_tree;
 +      splay_tree_t *config_tree;
        config_t *cfg;
        subnet_t *s, *s2;
        node_t *n;
  
 -      xasprintf(&dname, "%s/hosts", confbase);
 +      xasprintf(&dname, "%s" SLASH "hosts", confbase);
        dir = opendir(dname);
        if(!dir) {
 -              logger(LOG_ERR, "Could not open %s: %s", dname, strerror(errno));
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Could not open %s: %s", dname, strerror(errno));
                free(dname);
                return;
        }
                //      continue;
                #endif
  
 -              xasprintf(&fname, "%s/hosts/%s", confbase, ent->d_name);
 +              xasprintf(&fname, "%s" SLASH "hosts" SLASH "%s", confbase, ent->d_name);
                init_configuration(&config_tree);
                read_config_options(config_tree, ent->d_name);
                read_config_file(config_tree, fname);
@@@ -376,7 -314,7 +376,7 @@@ char *get_name(void) 
        }
  
        if(!check_id(name)) {
 -              logger(LOG_ERR, "Invalid name for myself!");
 +              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)
 -*/
 -static bool setup_myself(void) {
 -      config_t *cfg;
 -      subnet_t *subnet;
 -      char *name, *hostname, *mode, *afname, *cipher, *digest, *type;
 -      char *fname = NULL;
 -      char *address = NULL;
 +bool setup_myself_reloadable(void) {
        char *proxy = NULL;
 +      char *mode = NULL;
 +      char *afname = NULL;
        char *space;
 -      char *envp[5];
 -      struct addrinfo *ai, *aip, hint = {0};
        bool choice;
 -      int i, err;
 -      int replaywin_int;
 -
 -      myself = new_node();
 -      myself->connection = new_connection();
 -
 -      myself->hostname = xstrdup("MYSELF");
 -      myself->connection->hostname = xstrdup("MYSELF");
 -
 -      myself->connection->options = 0;
 -      myself->connection->protocol_version = PROT_CURRENT;
 -
 -      if(!(name = get_name())) {
 -              logger(LOG_ERR, "Name for tinc daemon required!");
 -              return false;
 -      }
 -
 -      myself->name = name;
 -      myself->connection->name = xstrdup(name);
 -      xasprintf(&fname, "%s/hosts/%s", confbase, name);
 -      read_config_options(config_tree, name);
 -      read_config_file(config_tree, fname);
 -      free(fname);
 -
 -      if(!read_rsa_private_key())
 -              return false;
 -
 -      if(!get_config_string(lookup_config(config_tree, "Port"), &myport))
 -              myport = xstrdup("655");
 -
 -      if(!atoi(myport)) {
 -              struct addrinfo *ai = str2addrinfo("localhost", myport, SOCK_DGRAM);
 -              sockaddr_t sa;
 -              if(!ai || !ai->ai_addr)
 -                      return false;
 -              free(myport);
 -              memcpy(&sa, ai->ai_addr, ai->ai_addrlen);
 -              sockaddr2str(&sa, NULL, &myport);
 -      }
  
        get_config_string(lookup_config(config_tree, "Proxy"), &proxy);
        if(proxy) {
                } else if(!strcasecmp(proxy, "exec")) {
                        proxytype = PROXY_EXEC;
                } else {
 -                      logger(LOG_ERR, "Unknown proxy type %s!", proxy);
 +                      logger(DEBUG_ALWAYS, LOG_ERR, "Unknown proxy type %s!", proxy);
                        return false;
                }
  
  
                        case PROXY_EXEC:
                                if(!space || !*space) {
 -                                      logger(LOG_ERR, "Argument expected for proxy type exec!");
 +                                      logger(DEBUG_ALWAYS, LOG_ERR, "Argument expected for proxy type exec!");
                                        return false;
                                }
                                proxyhost =  xstrdup(space);
                                if(space && (space = strchr(space, ' ')))
                                        *space++ = 0, proxypass = space;
                                if(!proxyhost || !*proxyhost || !proxyport || !*proxyport) {
 -                                      logger(LOG_ERR, "Host and port argument expected for proxy!");
 +                                      logger(DEBUG_ALWAYS, LOG_ERR, "Host and port argument expected for proxy!");
                                        return false;
                                }
                                proxyhost = xstrdup(proxyhost);
                free(proxy);
        }
  
 -      /* Read in all the subnets specified in the host configuration file */
 -
 -      cfg = lookup_config(config_tree, "Subnet");
 -
 -      while(cfg) {
 -              if(!get_config_subnet(cfg, &subnet))
 -                      return false;
 -
 -              subnet_add(myself, subnet);
 -
 -              cfg = lookup_config_next(config_tree, cfg);
 -      }
 -
 -      /* Check some options */
 -
        if(get_config_bool(lookup_config(config_tree, "IndirectData"), &choice) && choice)
                myself->options |= OPTION_INDIRECT;
  
                myself->options |= OPTION_INDIRECT;
  
        get_config_bool(lookup_config(config_tree, "DirectOnly"), &directonly);
 -      get_config_bool(lookup_config(config_tree, "StrictSubnets"), &strictsubnets);
 -      get_config_bool(lookup_config(config_tree, "TunnelServer"), &tunnelserver);
        get_config_bool(lookup_config(config_tree, "LocalDiscovery"), &localdiscovery);
 -      strictsubnets |= tunnelserver;
 -
 +      
        if(get_config_string(lookup_config(config_tree, "Mode"), &mode)) {
                if(!strcasecmp(mode, "router"))
                        routing_mode = RMODE_ROUTER;
                else if(!strcasecmp(mode, "hub"))
                        routing_mode = RMODE_HUB;
                else {
 -                      logger(LOG_ERR, "Invalid routing mode!");
 +                      logger(DEBUG_ALWAYS, LOG_ERR, "Invalid routing mode!");
                        return false;
                }
                free(mode);
                else if(!strcasecmp(mode, "kernel"))
                        forwarding_mode = FMODE_KERNEL;
                else {
 -                      logger(LOG_ERR, "Invalid forwarding mode!");
 +                      logger(DEBUG_ALWAYS, LOG_ERR, "Invalid forwarding mode!");
                        return false;
                }
                free(mode);
                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)
 -              logger(LOG_WARNING, "%s not supported on this platform", "PriorityInheritance");
 +              logger(DEBUG_ALWAYS, LOG_WARNING, "%s not supported on this platform", "PriorityInheritance");
  #endif
  
        if(!get_config_int(lookup_config(config_tree, "MACExpire"), &macexpire))
  
        if(get_config_int(lookup_config(config_tree, "MaxTimeout"), &maxtimeout)) {
                if(maxtimeout <= 0) {
 -                      logger(LOG_ERR, "Bogus maximum timeout!");
 +                      logger(DEBUG_ALWAYS, LOG_ERR, "Bogus maximum timeout!");
                        return false;
                }
        } else
                maxtimeout = 900;
  
 +      if(get_config_string(lookup_config(config_tree, "AddressFamily"), &afname)) {
 +              if(!strcasecmp(afname, "IPv4"))
 +                      addressfamily = AF_INET;
 +              else if(!strcasecmp(afname, "IPv6"))
 +                      addressfamily = AF_INET6;
 +              else if(!strcasecmp(afname, "any"))
 +                      addressfamily = AF_UNSPEC;
 +              else {
 +                      logger(DEBUG_ALWAYS, LOG_ERR, "Invalid address family!");
 +                      return false;
 +              }
 +              free(afname);
 +      }
 +
 +      get_config_bool(lookup_config(config_tree, "Hostnames"), &hostnames);
 +
 +      if(!get_config_int(lookup_config(config_tree, "KeyExpire"), &keylifetime))
 +              keylifetime = 3600;
 +
 +      return true;
 +}
 +
 +/*
 +  Configure node_t myself and set up the local sockets (listen only)
 +*/
 +static bool setup_myself(void) {
 +      config_t *cfg;
 +      subnet_t *subnet;
 +      char *name, *hostname, *cipher, *digest, *type;
 +      char *fname = NULL;
 +      char *address = NULL;
 +      char *envp[5];
 +      struct addrinfo *ai, *aip, hint = {0};
 +      int i, err;
 +      int replaywin_int;
 +
 +      myself = new_node();
 +      myself->connection = new_connection();
 +
 +      if(!get_config_string(lookup_config(config_tree, "Port"), &myport))
 +              myport = xstrdup("655");
 +
 +      xasprintf(&myself->hostname, "MYSELF port %s", myport);
 +      myself->connection->hostname = xstrdup(myself->hostname);
 +
 +      myself->connection->options = 0;
 +      myself->connection->protocol_major = PROT_MAJOR;
 +      myself->connection->protocol_minor = PROT_MINOR;
 +
 +      myself->options |= PROT_MINOR << 24;
 +
 +      if(!(name = get_name())) {
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Name for tinc daemon required!");
 +              return false;
 +      }
 +
 +      myself->name = name;
 +      myself->connection->name = xstrdup(name);
 +      xasprintf(&fname, "%s" SLASH "hosts" SLASH "%s", confbase, name);
 +      read_config_options(config_tree, name);
 +      read_config_file(config_tree, fname);
 +      free(fname);
 +
 +      get_config_bool(lookup_config(config_tree, "ExperimentalProtocol"), &experimental);
 +
 +      if(experimental && !read_ecdsa_private_key())
 +              return false;
 +
 +      if(!read_rsa_private_key())
 +              return false;
 +
 +      if(!atoi(myport)) {
 +              struct addrinfo *ai = str2addrinfo("localhost", myport, SOCK_DGRAM);
 +              sockaddr_t sa;
 +              if(!ai || !ai->ai_addr)
 +                      return false;
 +              free(myport);
 +              memcpy(&sa, ai->ai_addr, ai->ai_addrlen);
 +              sockaddr2str(&sa, NULL, &myport);
 +      }
 +
 +      /* Read in all the subnets specified in the host configuration file */
 +
 +      cfg = lookup_config(config_tree, "Subnet");
 +
 +      while(cfg) {
 +              if(!get_config_subnet(cfg, &subnet))
 +                      return false;
 +
 +              subnet_add(myself, subnet);
 +
 +              cfg = lookup_config_next(config_tree, cfg);
 +      }
 +
 +      /* Check some options */
 +
 +      if(!setup_myself_reloadable())
 +              return false;
 +
 +      get_config_bool(lookup_config(config_tree, "StrictSubnets"), &strictsubnets);
 +      get_config_bool(lookup_config(config_tree, "TunnelServer"), &tunnelserver);
 +      strictsubnets |= tunnelserver;
 +
 +
 +
        if(get_config_int(lookup_config(config_tree, "UDPRcvBuf"), &udp_rcvbuf)) {
                if(udp_rcvbuf <= 0) {
 -                      logger(LOG_ERR, "UDPRcvBuf cannot be negative!");
 +                      logger(DEBUG_ALWAYS, LOG_ERR, "UDPRcvBuf cannot be negative!");
                        return false;
                }
        }
  
        if(get_config_int(lookup_config(config_tree, "UDPSndBuf"), &udp_sndbuf)) {
                if(udp_sndbuf <= 0) {
 -                      logger(LOG_ERR, "UDPSndBuf cannot be negative!");
 +                      logger(DEBUG_ALWAYS, LOG_ERR, "UDPSndBuf cannot be negative!");
                        return false;
                }
        }
  
        if(get_config_int(lookup_config(config_tree, "ReplayWindow"), &replaywin_int)) {
                if(replaywin_int < 0) {
 -                      logger(LOG_ERR, "ReplayWindow cannot be negative!");
 +                      logger(DEBUG_ALWAYS, LOG_ERR, "ReplayWindow cannot be negative!");
                        return false;
                }
                replaywin = (unsigned)replaywin_int;
        }
  
 -      if(get_config_string(lookup_config(config_tree, "AddressFamily"), &afname)) {
 -              if(!strcasecmp(afname, "IPv4"))
 -                      addressfamily = AF_INET;
 -              else if(!strcasecmp(afname, "IPv6"))
 -                      addressfamily = AF_INET6;
 -              else if(!strcasecmp(afname, "any"))
 -                      addressfamily = AF_UNSPEC;
 -              else {
 -                      logger(LOG_ERR, "Invalid address family!");
 -                      return false;
 -              }
 -              free(afname);
 -      }
 -
 -      get_config_bool(lookup_config(config_tree, "Hostnames"), &hostnames);
 -
        /* Generate packet encryption key */
  
 -      if(get_config_string
 -         (lookup_config(config_tree, "Cipher"), &cipher)) {
 -              if(!strcasecmp(cipher, "none")) {
 -                      myself->incipher = NULL;
 -              } else {
 -                      myself->incipher = EVP_get_cipherbyname(cipher);
 +      if(!get_config_string(lookup_config(config_tree, "Cipher"), &cipher))
 +              cipher = xstrdup("blowfish");
  
 -                      if(!myself->incipher) {
 -                              logger(LOG_ERR, "Unrecognized cipher type!");
 -                              return false;
 -                      }
 -              }
 -      } else
 -              myself->incipher = EVP_bf_cbc();
 -
 -      if(myself->incipher)
 -              myself->inkeylength = myself->incipher->key_len + myself->incipher->iv_len;
 -      else
 -              myself->inkeylength = 1;
 -
 -      myself->connection->outcipher = EVP_bf_ofb();
 +      if(!cipher_open_by_name(&myself->incipher, cipher)) {
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Unrecognized cipher type!");
 +              return false;
 +      }
  
 -      if(!get_config_int(lookup_config(config_tree, "KeyExpire"), &keylifetime))
 -              keylifetime = 3600;
 +      regenerate_key();
  
 -      keyexpires = now + keylifetime;
 -      
        /* Check if we want to use message authentication codes... */
  
 -      if(get_config_string(lookup_config(config_tree, "Digest"), &digest)) {
 -              if(!strcasecmp(digest, "none")) {
 -                      myself->indigest = NULL;
 -              } else {
 -                      myself->indigest = EVP_get_digestbyname(digest);
 -
 -                      if(!myself->indigest) {
 -                              logger(LOG_ERR, "Unrecognized digest type!");
 -                              return false;
 -                      }
 -              }
 -      } else
 -              myself->indigest = EVP_sha1();
 +      int maclength = 4;
 +      get_config_int(lookup_config(config_tree, "MACLength"), &maclength);
  
 -      myself->connection->outdigest = EVP_sha1();
 +      if(maclength < 0) {
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Bogus MAC length!");
 +              return false;
 +      }
  
 -      if(get_config_int(lookup_config(config_tree, "MACLength"), &myself->inmaclength)) {
 -              if(myself->indigest) {
 -                      if(myself->inmaclength > myself->indigest->md_size) {
 -                              logger(LOG_ERR, "MAC length exceeds size of digest!");
 -                              return false;
 -                      } else if(myself->inmaclength < 0) {
 -                              logger(LOG_ERR, "Bogus MAC length!");
 -                              return false;
 -                      }
 -              }
 -      } else
 -              myself->inmaclength = 4;
 +      if(!get_config_string(lookup_config(config_tree, "Digest"), &digest))
 +              digest = xstrdup("sha1");
  
 -      myself->connection->outmaclength = 0;
 +      if(!digest_open_by_name(&myself->indigest, digest, maclength)) {
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Unrecognized digest type!");
 +              return false;
 +      }
  
        /* Compression */
  
        if(get_config_int(lookup_config(config_tree, "Compression"), &myself->incompression)) {
                if(myself->incompression < 0 || myself->incompression > 11) {
 -                      logger(LOG_ERR, "Bogus compression level!");
 +                      logger(DEBUG_ALWAYS, LOG_ERR, "Bogus compression level!");
                        return false;
                }
        } else
        myself->nexthop = myself;
        myself->via = myself;
        myself->status.reachable = true;
 +      myself->last_state_change = time(NULL);
 +      myself->status.sptps = experimental;
        node_add(myself);
  
        graph();
        if(!devops.setup())
                return false;
  
 +      if(device_fd >= 0) {
 +              event_set(&device_ev, device_fd, EV_READ|EV_PERSIST, handle_device_data, NULL);
 +
 +              if (event_add(&device_ev, NULL) < 0) {
 +                      logger(DEBUG_ALWAYS, LOG_ERR, "event_add failed: %s", strerror(errno));
 +                      devops.close();
 +                      return false;
 +              }
 +      }
 +
        /* Run tinc-up script to further initialize the tap interface */
        xasprintf(&envp[0], "NETNAME=%s", netname ? : "");
        xasprintf(&envp[1], "DEVICE=%s", device ? : "");
  
        execute_script("tinc-up", envp);
  
 -      for(i = 0; i < 5; i++)
 +      for(i = 0; i < 4; i++)
                free(envp[i]);
  
        /* Run subnet-up scripts for our own subnets */
  #endif
  
                if(listen_sockets > MAXSOCKETS) {
 -                      logger(LOG_ERR, "Too many listening sockets");
 +                      logger(DEBUG_ALWAYS, LOG_ERR, "Too many listening sockets");
                        return false;
                }
  
                for(i = 0; i < listen_sockets; i++) {
                        salen = sizeof sa;
                        if(getsockname(i + 3, &sa.sa, &salen) < 0) {
 -                              logger(LOG_ERR, "Could not get address of listen fd %d: %s", i + 3, sockstrerror(errno));
 +                              logger(DEBUG_ALWAYS, LOG_ERR, "Could not get address of listen fd %d: %s", i + 3, sockstrerror(errno));
                                return false;
                        }
  
                        if(listen_socket[i].udp < 0)
                                return false;
  
 -                      ifdebug(CONNECTIONS) {
 +                      event_set(&listen_socket[i].ev_tcp, listen_socket[i].tcp, EV_READ|EV_PERSIST, handle_new_meta_connection, NULL);
 +                      if(event_add(&listen_socket[i].ev_tcp, NULL) < 0) {
 +                              logger(DEBUG_ALWAYS, LOG_ERR, "event_add failed: %s", strerror(errno));
 +                              abort();
 +                      }
 +
 +                      event_set(&listen_socket[i].ev_udp, listen_socket[i].udp, EV_READ|EV_PERSIST, handle_incoming_vpn_data, (void *)(intptr_t)listen_sockets);
 +                      if(event_add(&listen_socket[listen_sockets].ev_udp, NULL) < 0) {
 +                              logger(DEBUG_ALWAYS, LOG_ERR, "event_add failed: %s", strerror(errno));
 +                              abort();
 +                      }
 +
 +                      if(debug_level >= DEBUG_CONNECTIONS) {
                                hostname = sockaddr2hostname(&sa);
 -                              logger(LOG_NOTICE, "Listening on %s", hostname);
 +                              logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Listening on %s", hostname);
                                free(hostname);
                        }
  
                        free(address);
  
                        if(err || !ai) {
 -                              logger(LOG_ERR, "System call `%s' failed: %s", "getaddrinfo",
 +                              logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "getaddrinfo",
                                           gai_strerror(err));
                                return false;
                        }
  
                        for(aip = ai; aip; aip = aip->ai_next) {
                                if(listen_sockets >= MAXSOCKETS) {
 -                                      logger(LOG_ERR, "Too many listening sockets");
 +                                      logger(DEBUG_ALWAYS, LOG_ERR, "Too many listening sockets");
                                        return false;
                                }
  
                                listen_socket[listen_sockets].udp =
                                        setup_vpn_in_socket((sockaddr_t *) aip->ai_addr);
  
 -                              if(listen_socket[listen_sockets].udp < 0)
 +                              if(listen_socket[listen_sockets].udp < 0) {
 +                                      close(listen_socket[listen_sockets].tcp);
                                        continue;
 +                              }
  
 -                              ifdebug(CONNECTIONS) {
 +                              event_set(&listen_socket[listen_sockets].ev_tcp,
 +                                                listen_socket[listen_sockets].tcp,
 +                                                EV_READ|EV_PERSIST,
 +                                                handle_new_meta_connection, NULL);
 +                              if(event_add(&listen_socket[listen_sockets].ev_tcp, NULL) < 0) {
 +                                      logger(DEBUG_ALWAYS, LOG_ERR, "event_add failed: %s", strerror(errno));
 +                                      abort();
 +                              }
 +
 +                              event_set(&listen_socket[listen_sockets].ev_udp,
 +                                                listen_socket[listen_sockets].udp,
 +                                                EV_READ|EV_PERSIST,
 +                                                handle_incoming_vpn_data, (void *)(intptr_t)listen_sockets);
 +                              if(event_add(&listen_socket[listen_sockets].ev_udp, NULL) < 0) {
 +                                      logger(DEBUG_ALWAYS, LOG_ERR, "event_add failed: %s", strerror(errno));
 +                                      abort();
 +                              }
 +
 +                              if(debug_level >= DEBUG_CONNECTIONS) {
                                        hostname = sockaddr2hostname((sockaddr_t *) aip->ai_addr);
 -                                      logger(LOG_NOTICE, "Listening on %s", hostname);
 +                                      logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Listening on %s", hostname);
                                        free(hostname);
                                }
  
        }
  
        if(listen_sockets)
 -              logger(LOG_NOTICE, "Ready");
 +              logger(DEBUG_ALWAYS, LOG_NOTICE, "Ready");
        else {
 -              logger(LOG_ERR, "Unable to create any listening socket!");
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Unable to create any listening socket!");
                return false;
        }
  
 +      last_config_check = time(NULL);
 +
        return true;
  }
  
    initialize network
  */
  bool setup_network(void) {
 -      now = time(NULL);
 -
 -      init_events();
        init_connections();
        init_subnets();
        init_nodes();
    close all open network connections
  */
  void close_network_connections(void) {
 -      avl_node_t *node, *next;
 +      splay_node_t *node, *next;
        connection_t *c;
        char *envp[5];
        int i;
                terminate_connection(c, false);
        }
  
 -      for(list_node_t *node = outgoing_list->head; node; node = node->next) {
 -              outgoing_t *outgoing = node->data;
 -
 -              if(outgoing->event)
 -                      event_del(outgoing->event);
 -      }
 -
        list_delete_list(outgoing_list);
  
        if(myself && myself->connection) {
        }
  
        for(i = 0; i < listen_sockets; i++) {
 +              event_del(&listen_socket[i].ev_tcp);
 +              event_del(&listen_socket[i].ev_udp);
                close(listen_socket[i].tcp);
                close(listen_socket[i].udp);
        }
        exit_subnets();
        exit_nodes();
        exit_connections();
 -      exit_events();
  
        execute_script("tinc-down", envp);
  
diff --combined src/openssl/rsa.c
index 9c880e6,0000000..efd63d5
mode 100644,000000..100644
--- /dev/null
@@@ -1,101 -1,0 +1,106 @@@
-       BN_hex2bn(&(*rsa)->n, n);
-       BN_hex2bn(&(*rsa)->e, e);
 +/*
 +    rsa.c -- RSA key handling
 +    Copyright (C) 2007 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 <openssl/pem.h>
 +#include <openssl/err.h>
 +
 +#include "logger.h"
 +#include "rsa.h"
 +
 +// Set RSA keys
 +
 +bool rsa_set_hex_public_key(rsa_t *rsa, char *n, char *e) {
 +      *rsa = RSA_new();
-       BN_hex2bn(&(*rsa)->n, n);
-       BN_hex2bn(&(*rsa)->e, e);
-       BN_hex2bn(&(*rsa)->d, d);
++      if(BN_hex2bn(&(*rsa)->n, n) != strlen(n))
++              return false;
++      if(BN_hex2bn(&(*rsa)->e, e) != strlen(e))
++              return false;
 +      return true;
 +}
 +
 +bool rsa_set_hex_private_key(rsa_t *rsa, char *n, char *e, char *d) {
 +      *rsa = RSA_new();
++      if(BN_hex2bn(&(*rsa)->n, n) != strlen(n))
++              return false;
++      if(BN_hex2bn(&(*rsa)->e, e) != strlen(e))
++              return false;
++      if(BN_hex2bn(&(*rsa)->d, d) != strlen(d))
++              return false;
 +      return true;
 +}
 +
 +// Read PEM RSA keys
 +
 +bool rsa_read_pem_public_key(rsa_t *rsa, FILE *fp) {
 +      *rsa = PEM_read_RSAPublicKey(fp, rsa, NULL, NULL);
 +
 +      if(*rsa)
 +              return true;
 +      
 +      *rsa = PEM_read_RSA_PUBKEY(fp, rsa, NULL, NULL);
 +
 +      if(*rsa)
 +              return true;
 +
 +      logger(DEBUG_ALWAYS, LOG_ERR, "Unable to read RSA public key: %s", ERR_error_string(ERR_get_error(), NULL));
 +      return false;
 +}
 +
 +bool rsa_read_pem_private_key(rsa_t *rsa, FILE *fp) {
 +      *rsa = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
 +
 +      if(*rsa)
 +              return true;
 +      
 +      logger(DEBUG_ALWAYS, LOG_ERR, "Unable to read RSA private key: %s", ERR_error_string(ERR_get_error(), NULL));
 +      return false;
 +}
 +
 +size_t rsa_size(rsa_t *rsa) {
 +      return RSA_size(*rsa);
 +}
 +
 +bool rsa_public_encrypt(rsa_t *rsa, void *in, size_t len, void *out) {
 +      if(RSA_public_encrypt(len, in, out, *rsa, RSA_NO_PADDING) == len)
 +              return true;
 +
 +      logger(DEBUG_ALWAYS, LOG_ERR, "Unable to perform RSA encryption: %s", ERR_error_string(ERR_get_error(), NULL));
 +      return false;   
 +}
 +
 +bool rsa_private_decrypt(rsa_t *rsa, void *in, size_t len, void *out) {
 +      if(RSA_private_decrypt(len, in, out, *rsa, RSA_NO_PADDING) == len)
 +              return true;
 +
 +      logger(DEBUG_ALWAYS, LOG_ERR, "Unable to perform RSA decryption: %s", ERR_error_string(ERR_get_error(), NULL));
 +      return false;   
 +}
 +
 +bool rsa_active(rsa_t *rsa) {
 +      return *rsa;
 +}
 +
 +void rsa_free(rsa_t *rsa) {
 +      if(*rsa) {
 +              RSA_free(*rsa);
 +              *rsa = NULL;
 +      }
 +}
diff --combined src/process.c
  
  #include "conf.h"
  #include "connection.h"
 +#include "control.h"
  #include "device.h"
  #include "edge.h"
  #include "logger.h"
  #include "net.h"
  #include "node.h"
 -#include "pidfile.h"
  #include "process.h"
  #include "subnet.h"
  #include "utils.h"
  
  /* If zero, don't detach from the terminal. */
  bool do_detach = true;
 -bool sighup = false;
  bool sigalrm = false;
  
  extern char *identname;
 -extern char *pidfilename;
  extern char **g_argv;
  extern bool use_logfile;
  
 -#ifndef HAVE_MINGW
 -static sigset_t emptysigset;
 -#endif
 -
 -static int saved_debug_level = -1;
 -
 -static void memory_full(int size) {
 -      logger(LOG_ERR, "Memory exhausted (couldn't allocate %d bytes), exitting.", size);
 -      exit(1);
 -}
 -
  /* Some functions the less gifted operating systems might lack... */
  
  #ifdef HAVE_MINGW
@@@ -53,7 -66,7 +53,7 @@@ static SC_HANDLE service = NULL
  static SERVICE_STATUS status = {0};
  static SERVICE_STATUS_HANDLE statushandle = 0;
  
 -bool install_service(void) {
 +static bool install_service(void) {
        char command[4096] = "\"";
        char **argp;
        bool space;
@@@ -61,7 -74,7 +61,7 @@@
  
        manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
        if(!manager) {
 -              logger(LOG_ERR, "Could not open service manager: %s", winerror(GetLastError()));
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Could not open service manager: %s", winerror(GetLastError()));
                return false;
        }
  
        
        if(!service) {
                DWORD lasterror = GetLastError();
 -              logger(LOG_ERR, "Could not create %s service: %s", identname, winerror(lasterror));
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Could not create %s service: %s", identname, winerror(lasterror));
                if(lasterror != ERROR_SERVICE_EXISTS)
                        return false;
        }
  
        if(service) {
                ChangeServiceConfig2(service, SERVICE_CONFIG_DESCRIPTION, &description);
 -              logger(LOG_INFO, "%s service installed", identname);
 +              logger(DEBUG_ALWAYS, LOG_INFO, "%s service installed", identname);
        } else {
                service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
        }
  
        if(!StartService(service, 0, NULL))
 -              logger(LOG_WARNING, "Could not start %s service: %s", identname, winerror(GetLastError()));
 +              logger(DEBUG_ALWAYS, LOG_WARNING, "Could not start %s service: %s", identname, winerror(GetLastError()));
        else
 -              logger(LOG_INFO, "%s service started", identname);
 -
 -      return true;
 -}
 -
 -bool remove_service(void) {
 -      manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
 -      if(!manager) {
 -              logger(LOG_ERR, "Could not open service manager: %s", winerror(GetLastError()));
 -              return false;
 -      }
 -
 -      service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
 -
 -      if(!service) {
 -              logger(LOG_ERR, "Could not open %s service: %s", identname, winerror(GetLastError()));
 -              return false;
 -      }
 -
 -      if(!ControlService(service, SERVICE_CONTROL_STOP, &status))
 -              logger(LOG_ERR, "Could not stop %s service: %s", identname, winerror(GetLastError()));
 -      else
 -              logger(LOG_INFO, "%s service stopped", identname);
 -
 -      if(!DeleteService(service)) {
 -              logger(LOG_ERR, "Could not remove %s service: %s", identname, winerror(GetLastError()));
 -              return false;
 -      }
 -
 -      logger(LOG_INFO, "%s service removed", identname);
 +              logger(DEBUG_ALWAYS, LOG_INFO, "%s service started", identname);
  
        return true;
  }
@@@ -119,21 -161,29 +119,21 @@@ DWORD WINAPI controlhandler(DWORD reque
                        SetServiceStatus(statushandle, &status);
                        return NO_ERROR;
                case SERVICE_CONTROL_STOP:
 -                      logger(LOG_NOTICE, "Got %s request", "SERVICE_CONTROL_STOP");
 +                      logger(DEBUG_ALWAYS, LOG_NOTICE, "Got %s request", "SERVICE_CONTROL_STOP");
                        break;
                case SERVICE_CONTROL_SHUTDOWN:
 -                      logger(LOG_NOTICE, "Got %s request", "SERVICE_CONTROL_SHUTDOWN");
 +                      logger(DEBUG_ALWAYS, LOG_NOTICE, "Got %s request", "SERVICE_CONTROL_SHUTDOWN");
                        break;
                default:
 -                      logger(LOG_WARNING, "Got unexpected request %d", request);
 +                      logger(DEBUG_ALWAYS, LOG_WARNING, "Got unexpected request %d", request);
                        return ERROR_CALL_NOT_IMPLEMENTED;
        }
  
 -      if(running) {
 -              running = false;
 -              status.dwWaitHint = 30000; 
 -              status.dwCurrentState = SERVICE_STOP_PENDING; 
 -              SetServiceStatus(statushandle, &status);
 -              return NO_ERROR;
 -      } else {
 -              status.dwWaitHint = 0; 
 -              status.dwCurrentState = SERVICE_STOPPED; 
 -              SetServiceStatus(statushandle, &status);
 -              exit(1);
 -      }
 -
 +      event_loopexit(NULL);
 +      status.dwWaitHint = 30000; 
 +      status.dwCurrentState = SERVICE_STOP_PENDING; 
 +      SetServiceStatus(statushandle, &status);
 +      return NO_ERROR;
  }
  
  VOID WINAPI run_service(DWORD argc, LPTSTR* argv) {
        statushandle = RegisterServiceCtrlHandlerEx(identname, controlhandler, NULL); 
  
        if (!statushandle) {
 -              logger(LOG_ERR, "System call `%s' failed: %s", "RegisterServiceCtrlHandlerEx", winerror(GetLastError()));
 +              logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "RegisterServiceCtrlHandlerEx", winerror(GetLastError()));
                err = 1;
        } else {
                status.dwWaitHint = 30000; 
@@@ -183,22 -233,92 +183,22 @@@ bool init_service(void) 
                        return false;
                }
                else
 -                      logger(LOG_ERR, "System call `%s' failed: %s", "StartServiceCtrlDispatcher", winerror(GetLastError()));
 +                      logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "StartServiceCtrlDispatcher", winerror(GetLastError()));
        }
  
        return true;
  }
  #endif
  
 -#ifndef HAVE_MINGW
 -/*
 -  check for an existing tinc for this net, and write pid to pidfile
 -*/
 -static bool write_pidfile(void) {
 -      pid_t pid;
 -
 -      pid = check_pid(pidfilename);
 -
 -      if(pid) {
 -              if(netname)
 -                      fprintf(stderr, "A tincd is already running for net `%s' with pid %ld.\n",
 -                                      netname, (long)pid);
 -              else
 -                      fprintf(stderr, "A tincd is already running with pid %ld.\n", (long)pid);
 -              return false;
 -      }
 -
 -      /* if it's locked, write-protected, or whatever */
 -      if(!write_pid(pidfilename)) {
 -              fprintf(stderr, "Could write pid file %s: %s\n", pidfilename, strerror(errno));
 -              return false;
 -      }
 -
 -      return true;
 -}
 -#endif
 -
 -/*
 -  kill older tincd for this net
 -*/
 -bool kill_other(int signal) {
 -#ifndef HAVE_MINGW
 -      pid_t pid;
 -
 -      pid = read_pid(pidfilename);
 -
 -      if(!pid) {
 -              if(netname)
 -                      fprintf(stderr, "No other tincd is running for net `%s'.\n",
 -                                      netname);
 -              else
 -                      fprintf(stderr, "No other tincd is running.\n");
 -              return false;
 -      }
 -
 -      errno = 0;                                      /* No error, sometimes errno is only changed on error */
 -
 -      /* ESRCH is returned when no process with that pid is found */
 -      if(kill(pid, signal) && errno == ESRCH) {
 -              if(netname)
 -                      fprintf(stderr, "The tincd for net `%s' is no longer running. ",
 -                                      netname);
 -              else
 -                      fprintf(stderr, "The tincd is no longer running. ");
 -
 -              fprintf(stderr, "Removing stale lock file.\n");
 -              remove_pid(pidfilename);
 -      }
 -
 -      return true;
 -#else
 -      return remove_service();
 -#endif
 -}
 -
  /*
 -  Detach from current terminal, write pidfile, kill parent
 +  Detach from current terminal
  */
  bool detach(void) {
 -      setup_signals();
 -
 -      /* First check if we can open a fresh new pidfile */
 -
  #ifndef HAVE_MINGW
 -      if(!write_pidfile())
 -              return false;
 -
 -      /* If we succeeded in doing that, detach */
 +      signal(SIGPIPE, SIG_IGN);
 +      signal(SIGUSR1, SIG_IGN);
 +      signal(SIGUSR2, SIG_IGN);
 +      signal(SIGWINCH, SIG_IGN);
  
        closelogger();
  #endif
                                        strerror(errno));
                        return false;
                }
 -
 -              /* Now UPDATE the pid in the pidfile, because we changed it... */
 -
 -              if(!write_pid(pidfilename)) {
 -                      fprintf(stderr, "Could not write pid file %s: %s\n", pidfilename, strerror(errno));
 -                      return false;
 -              }
  #else
                if(!statushandle)
 -                      exit(install_service());
 +                      exit(!install_service());
  #endif
        }
  
        openlogger(identname, use_logfile?LOGMODE_FILE:(do_detach?LOGMODE_SYSLOG:LOGMODE_STDERR));
  
 -      logger(LOG_NOTICE, "tincd %s (%s %s) starting, debug level %d",
 +      logger(DEBUG_ALWAYS, LOG_NOTICE, "tincd %s (%s %s) starting, debug level %d",
                           VERSION, __DATE__, __TIME__, debug_level);
  
 -      xalloc_fail_func = memory_full;
 -
        return true;
  }
  
@@@ -229,11 -358,12 +229,12 @@@ bool execute_script(const char *name, c
        int status, len;
        char *scriptname;
        int i;
+       char *interpreter = NULL;
  
  #ifndef HAVE_MINGW
 -      len = xasprintf(&scriptname, "\"%s/%s\"", confbase, name);
 +      len = xasprintf(&scriptname, "\"%s" SLASH "%s\"", confbase, name);
  #else
 -      len = xasprintf(&scriptname, "\"%s/%s.bat\"", confbase, name);
 +      len = xasprintf(&scriptname, "\"%s" SLASH "%s.bat\"", confbase, name);
  #endif
        if(len < 0)
                return false;
        }
  #endif
  
 -      ifdebug(STATUS) logger(LOG_INFO, "Executing script %s", name);
+       // Custom scripts interpreter
+       if(get_config_string(lookup_config(config_tree, "ScriptsInterpreter"), &interpreter)) {
+               // Force custom scripts interpreter allowing execution of scripts on android without execution flag (such as on /sdcard)
+               free(scriptname);
+               len = xasprintf(&scriptname, "%s \"%s/%s\"", interpreter, confbase, name);
+               free(interpreter);
+               if(len < 0)
+                       return false;
+       }
 +      logger(DEBUG_STATUS, LOG_INFO, "Executing script %s", name);
 +
  #ifdef HAVE_PUTENV
        /* Set environment */
        
        if(status != -1) {
                if(WIFEXITED(status)) { /* Child exited by itself */
                        if(WEXITSTATUS(status)) {
 -                              logger(LOG_ERR, "Script %s exited with non-zero status %d",
 +                              logger(DEBUG_ALWAYS, LOG_ERR, "Script %s exited with non-zero status %d",
                                           name, WEXITSTATUS(status));
                                return false;
                        }
                } else if(WIFSIGNALED(status)) {        /* Child was killed by a signal */
 -                      logger(LOG_ERR, "Script %s was killed by signal %d (%s)",
 +                      logger(DEBUG_ALWAYS, LOG_ERR, "Script %s was killed by signal %d (%s)",
                                   name, WTERMSIG(status), strsignal(WTERMSIG(status)));
                        return false;
                } else {                        /* Something strange happened */
 -                      logger(LOG_ERR, "Script %s terminated abnormally", name);
 +                      logger(DEBUG_ALWAYS, LOG_ERR, "Script %s terminated abnormally", name);
                        return false;
                }
        } else {
 -              logger(LOG_ERR, "System call `%s' failed: %s", "system", strerror(errno));
 +              logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "system", strerror(errno));
                return false;
        }
  #endif
  #endif
        return true;
  }
 -
 -
 -/*
 -  Signal handlers.
 -*/
 -
 -#ifndef HAVE_MINGW
 -static RETSIGTYPE sigterm_handler(int a) {
 -      logger(LOG_NOTICE, "Got %s signal", "TERM");
 -      if(running)
 -              running = false;
 -      else
 -              exit(1);
 -}
 -
 -static RETSIGTYPE sigquit_handler(int a) {
 -      logger(LOG_NOTICE, "Got %s signal", "QUIT");
 -      if(running)
 -              running = false;
 -      else
 -              exit(1);
 -}
 -
 -static RETSIGTYPE fatal_signal_square(int a) {
 -      logger(LOG_ERR, "Got another fatal signal %d (%s): not restarting.", a,
 -                 strsignal(a));
 -      exit(1);
 -}
 -
 -static RETSIGTYPE fatal_signal_handler(int a) {
 -      struct sigaction act;
 -      logger(LOG_ERR, "Got fatal signal %d (%s)", a, strsignal(a));
 -
 -      if(do_detach) {
 -              logger(LOG_NOTICE, "Trying to re-execute in 5 seconds...");
 -
 -              act.sa_handler = fatal_signal_square;
 -              act.sa_mask = emptysigset;
 -              act.sa_flags = 0;
 -              sigaction(SIGSEGV, &act, NULL);
 -
 -              close_network_connections();
 -              sleep(5);
 -              remove_pid(pidfilename);
 -              execvp(g_argv[0], g_argv);
 -      } else {
 -              logger(LOG_NOTICE, "Not restarting.");
 -              exit(1);
 -      }
 -}
 -
 -static RETSIGTYPE sighup_handler(int a) {
 -      logger(LOG_NOTICE, "Got %s signal", "HUP");
 -      sighup = true;
 -}
 -
 -static RETSIGTYPE sigint_handler(int a) {
 -      logger(LOG_NOTICE, "Got %s signal", "INT");
 -
 -      if(saved_debug_level != -1) {
 -              logger(LOG_NOTICE, "Reverting to old debug level (%d)",
 -                      saved_debug_level);
 -              debug_level = saved_debug_level;
 -              saved_debug_level = -1;
 -      } else {
 -              logger(LOG_NOTICE,
 -                      "Temporarily setting debug level to 5.  Kill me with SIGINT again to go back to level %d.",
 -                      debug_level);
 -              saved_debug_level = debug_level;
 -              debug_level = 5;
 -      }
 -}
 -
 -static RETSIGTYPE sigalrm_handler(int a) {
 -      logger(LOG_NOTICE, "Got %s signal", "ALRM");
 -      sigalrm = true;
 -}
 -
 -static RETSIGTYPE sigusr1_handler(int a) {
 -      dump_connections();
 -}
 -
 -static RETSIGTYPE sigusr2_handler(int a) {
 -      devops.dump_stats();
 -      dump_nodes();
 -      dump_edges();
 -      dump_subnets();
 -}
 -
 -static RETSIGTYPE sigwinch_handler(int a) {
 -      do_purge = true;
 -}
 -
 -static RETSIGTYPE unexpected_signal_handler(int a) {
 -      logger(LOG_WARNING, "Got unexpected signal %d (%s)", a, strsignal(a));
 -}
 -
 -static RETSIGTYPE ignore_signal_handler(int a) {
 -      ifdebug(SCARY_THINGS) logger(LOG_DEBUG, "Ignored signal %d (%s)", a, strsignal(a));
 -}
 -
 -static struct {
 -      int signal;
 -      void (*handler)(int);
 -} sighandlers[] = {
 -      {SIGHUP, sighup_handler},
 -      {SIGTERM, sigterm_handler},
 -      {SIGQUIT, sigquit_handler},
 -      {SIGSEGV, fatal_signal_handler},
 -      {SIGBUS, fatal_signal_handler},
 -      {SIGILL, fatal_signal_handler},
 -      {SIGPIPE, ignore_signal_handler},
 -      {SIGINT, sigint_handler},
 -      {SIGUSR1, sigusr1_handler},
 -      {SIGUSR2, sigusr2_handler},
 -      {SIGCHLD, ignore_signal_handler},
 -      {SIGALRM, sigalrm_handler},
 -      {SIGWINCH, sigwinch_handler},
 -      {SIGABRT, SIG_DFL},
 -      {0, NULL}
 -};
 -#endif
 -
 -void setup_signals(void) {
 -#ifndef HAVE_MINGW
 -      int i;
 -      struct sigaction act;
 -
 -      sigemptyset(&emptysigset);
 -      act.sa_handler = NULL;
 -      act.sa_mask = emptysigset;
 -      act.sa_flags = 0;
 -
 -      /* Set a default signal handler for every signal, errors will be
 -         ignored. */
 -      for(i = 1; i < NSIG; i++) {
 -              if(!do_detach)
 -                      act.sa_handler = SIG_DFL;
 -              else
 -                      act.sa_handler = unexpected_signal_handler;
 -              sigaction(i, &act, NULL);
 -      }
 -
 -      /* If we didn't detach, allow coredumps */
 -      if(!do_detach)
 -              sighandlers[3].handler = SIG_DFL;
 -
 -      /* Then, for each known signal that we want to catch, assign a
 -         handler to the signal, with error checking this time. */
 -      for(i = 0; sighandlers[i].signal; i++) {
 -              act.sa_handler = sighandlers[i].handler;
 -              if(sigaction(sighandlers[i].signal, &act, NULL) < 0)
 -                      fprintf(stderr, "Installing signal handler for signal %d (%s) failed: %s\n",
 -                                      sighandlers[i].signal, strsignal(sighandlers[i].signal),
 -                                      strerror(errno));
 -      }
 -#endif
 -}
diff --combined src/protocol_key.c
  
  #include "system.h"
  
 -#include <openssl/evp.h>
 -#include <openssl/err.h>
 -#include <openssl/rand.h>
 -
 -#include "avl_tree.h"
 +#include "splay_tree.h"
 +#include "cipher.h"
  #include "connection.h"
 +#include "crypto.h"
  #include "logger.h"
  #include "net.h"
  #include "netutl.h"
  #include "node.h"
 +#include "prf.h"
  #include "protocol.h"
 +#include "sptps.h"
  #include "utils.h"
  #include "xalloc.h"
  
  static bool mykeyused = false;
  
  void send_key_changed(void) {
 -      avl_node_t *node;
 +      splay_node_t *node;
        connection_t *c;
  
        send_request(everyone, "%d %x %s", KEY_CHANGED, rand(), myself->name);
  
        for(node = connection_tree->head; node; node = node->next) {
                c = node->data;
 -              if(c->status.active && c->node && c->node->status.reachable)
 -                      send_ans_key(c->node);
 +              if(c->status.active && c->node && c->node->status.reachable) {
 +                      if(!c->node->status.sptps)
 +                              send_ans_key(c->node);
 +              }
 +      }
 +
 +      /* Force key exchange for connections using SPTPS */
 +
 +      if(experimental) {
 +              for(node = node_tree->head; node; node = node->next) {
 +                      node_t *n = node->data;
 +                      if(n->status.reachable && n->status.validkey && n->status.sptps)
 +                              sptps_force_kex(&n->sptps);
 +              }
        }
  }
  
 -bool key_changed_h(connection_t *c) {
 +bool key_changed_h(connection_t *c, const char *request) {
        char name[MAX_STRING_SIZE];
        node_t *n;
  
 -      if(sscanf(c->buffer, "%*d %*x " MAX_STRING, name) != 1) {
 -              logger(LOG_ERR, "Got bad %s from %s (%s)", "KEY_CHANGED",
 +      if(sscanf(request, "%*d %*x " MAX_STRING, name) != 1) {
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "KEY_CHANGED",
                           c->name, c->hostname);
                return false;
        }
  
 -      if(!check_id(name)) {
 -              logger(LOG_ERR, "Got bad %s from %s (%s): %s", "KEY_CHANGED", c->name, c->hostname, "invalid name");
 -              return false;
 -      }
 -
 -      if(seen_request(c->buffer))
 +      if(seen_request(request))
                return true;
  
        n = lookup_node(name);
  
        if(!n) {
 -              logger(LOG_ERR, "Got %s from %s (%s) origin %s which does not exist",
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Got %s from %s (%s) origin %s which does not exist",
                           "KEY_CHANGED", c->name, c->hostname, name);
                return true;
        }
  
 -      n->status.validkey = false;
 -      n->last_req_key = 0;
 +      if(!n->status.sptps) {
 +              n->status.validkey = false;
 +              n->last_req_key = 0;
 +      }
  
        /* Tell the others */
  
        if(!tunnelserver)
 -              forward_request(c);
 +              forward_request(c, request);
  
        return true;
  }
  
 +static bool send_initial_sptps_data(void *handle, uint8_t type, const char *data, size_t len) {
 +      node_t *to = handle;
 +      to->sptps.send_data = send_sptps_data;
 +      char buf[len * 4 / 3 + 5];
 +      b64encode(data, buf, len);
 +      return send_request(to->nexthop->connection, "%d %s %s %d %s", REQ_KEY, myself->name, to->name, REQ_KEY, buf);
 +}
 +
  bool send_req_key(node_t *to) {
 +      if(to->status.sptps) {
 +              if(!node_read_ecdsa_public_key(to)) {
 +                      logger(DEBUG_PROTOCOL, LOG_DEBUG, "No ECDSA key known for %s (%s)", to->name, to->hostname);
 +                      send_request(to->nexthop->connection, "%d %s %s %d", REQ_KEY, myself->name, to->name, REQ_PUBKEY);
 +                      return true;
 +              }
 +                char label[25 + strlen(myself->name) + strlen(to->name)];
 +              snprintf(label, sizeof label, "tinc UDP key expansion %s %s", myself->name, to->name);
 +              sptps_stop(&to->sptps);
 +              to->status.validkey = false;
 +              to->incompression = myself->incompression;
 +              return sptps_start(&to->sptps, to, true, true, myself->connection->ecdsa, to->ecdsa, label, sizeof label, send_initial_sptps_data, receive_sptps_record); 
 +      }
 +
        return send_request(to->nexthop->connection, "%d %s %s", REQ_KEY, myself->name, to->name);
  }
  
 -bool req_key_h(connection_t *c) {
 +/* REQ_KEY is overloaded to allow arbitrary requests to be routed between two nodes. */
 +
 +static bool req_key_ext_h(connection_t *c, const char *request, node_t *from, int reqno) {
 +      switch(reqno) {
 +              case REQ_PUBKEY: {
 +                      char *pubkey = ecdsa_get_base64_public_key(&myself->connection->ecdsa);
 +                      send_request(from->nexthop->connection, "%d %s %s %d %s", REQ_KEY, myself->name, from->name, ANS_PUBKEY, pubkey);
 +                      free(pubkey);
 +                      return true;
 +              }
 +
 +              case ANS_PUBKEY: {
 +                      if(node_read_ecdsa_public_key(from)) {
 +                              logger(DEBUG_PROTOCOL, LOG_WARNING, "Got ANS_PUBKEY from %s (%s) even though we already have his pubkey", from->name, from->hostname);
 +                              return true;
 +                      }
 +
 +                      char pubkey[MAX_STRING_SIZE];
 +                      if(sscanf(request, "%*d %*s %*s %*d " MAX_STRING, pubkey) != 1 || !ecdsa_set_base64_public_key(&from->ecdsa, pubkey)) {
 +                              logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "ANS_PUBKEY", from->name, from->hostname, "invalid pubkey");
 +                              return true;
 +                      }
 +
 +                      logger(DEBUG_PROTOCOL, LOG_INFO, "Learned ECDSA public key from %s (%s)", from->name, from->hostname);
 +                      append_config_file(from->name, "ECDSAPublicKey", pubkey);
 +                      return true;
 +              }
 +
 +              case REQ_KEY: {
 +                      if(!node_read_ecdsa_public_key(from)) {
 +                              logger(DEBUG_PROTOCOL, LOG_DEBUG, "No ECDSA key known for %s (%s)", from->name, from->hostname);
 +                              send_request(from->nexthop->connection, "%d %s %s %d", REQ_KEY, myself->name, from->name, REQ_PUBKEY);
 +                              return true;
 +                      }
 +              }
 +
 +              case REQ_SPTPS: {
 +                      char buf[MAX_STRING_SIZE];
 +                      if(sscanf(request, "%*d %*s %*s %*d " MAX_STRING, buf) != 1) {
 +                              logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "REQ_KEY", from->name, from->hostname, "invalid SPTPS data");
 +                              return true;
 +                      }
 +                      int len = b64decode(buf, buf, strlen(buf));
 +
 +
 +                      char label[25 + strlen(from->name) + strlen(myself->name)];
 +                      snprintf(label, sizeof label, "tinc UDP key expansion %s %s", from->name, myself->name);
 +                      sptps_stop(&from->sptps);
 +                      from->status.validkey = false;
 +                      sptps_start(&from->sptps, from, false, true, myself->connection->ecdsa, from->ecdsa, label, sizeof label, send_sptps_data, receive_sptps_record); 
 +                      sptps_receive_data(&from->sptps, buf, len);
 +                      return true;
 +              }
 +
 +              default:
 +                      logger(DEBUG_ALWAYS, LOG_ERR, "Unknown extended REQ_KEY request from %s (%s): %s", from->name, from->hostname, request);
 +                      return true;
 +      }
 +}
 +
 +bool req_key_h(connection_t *c, const char *request) {
        char from_name[MAX_STRING_SIZE];
        char to_name[MAX_STRING_SIZE];
        node_t *from, *to;
 +      int reqno = 0;
  
 -      if(sscanf(c->buffer, "%*d " MAX_STRING " " MAX_STRING, from_name, to_name) != 2) {
 -              logger(LOG_ERR, "Got bad %s from %s (%s)", "REQ_KEY", c->name,
 +      if(sscanf(request, "%*d " MAX_STRING " " MAX_STRING " %d", from_name, to_name, &reqno) < 2) {
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "REQ_KEY", c->name,
                           c->hostname);
                return false;
        }
  
        if(!check_id(from_name) || !check_id(to_name)) {
 -              logger(LOG_ERR, "Got bad %s from %s (%s): %s", "REQ_KEY", c->name, c->hostname, "invalid name");
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "REQ_KEY", c->name, c->hostname, "invalid name");
                return false;
        }
  
        from = lookup_node(from_name);
  
        if(!from) {
 -              logger(LOG_ERR, "Got %s from %s (%s) origin %s which does not exist in our connection list",
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Got %s from %s (%s) origin %s which does not exist in our connection list",
                           "REQ_KEY", c->name, c->hostname, from_name);
                return true;
        }
        to = lookup_node(to_name);
  
        if(!to) {
 -              logger(LOG_ERR, "Got %s from %s (%s) destination %s which does not exist in our connection list",
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Got %s from %s (%s) destination %s which does not exist in our connection list",
                           "REQ_KEY", c->name, c->hostname, to_name);
                return true;
        }
  
        /* Check if this key request is for us */
  
 -      if(to == myself) {                      /* Yes, send our own key back */
 +      if(to == myself) {                      /* Yes */
 +              /* Is this an extended REQ_KEY message? */
 +              if(experimental && reqno)
 +                      return req_key_ext_h(c, request, from, reqno);
 +
 +              /* No, just send our key back */
                send_ans_key(from);
        } else {
                if(tunnelserver)
                        return true;
  
                if(!to->status.reachable) {
 -                      logger(LOG_WARNING, "Got %s from %s (%s) destination %s which is not reachable",
 +                      logger(DEBUG_PROTOCOL, LOG_WARNING, "Got %s from %s (%s) destination %s which is not reachable",
                                "REQ_KEY", c->name, c->hostname, to_name);
                        return true;
                }
  
 -              send_request(to->nexthop->connection, "%s", c->buffer);
 +              send_request(to->nexthop->connection, "%s", request);
        }
  
        return true;
  }
  
  bool send_ans_key(node_t *to) {
 -      // Set key parameters
 -      to->incipher = myself->incipher;
 -      to->inkeylength = myself->inkeylength;
 -      to->indigest = myself->indigest;
 -      to->inmaclength = myself->inmaclength;
 +      if(to->status.sptps)
 +              abort();
 +
 +      size_t keylen = cipher_keylength(&myself->incipher);
 +      char key[keylen * 2 + 1];
 +
 +      cipher_open_by_nid(&to->incipher, cipher_get_nid(&myself->incipher));
 +      digest_open_by_nid(&to->indigest, digest_get_nid(&myself->indigest), digest_length(&myself->indigest));
        to->incompression = myself->incompression;
  
 -      // Allocate memory for key
 -      to->inkey = xrealloc(to->inkey, to->inkeylength);
 +      randomize(key, keylen);
 +      cipher_set_key(&to->incipher, key, false);
 +      digest_set_key(&to->indigest, key, keylen);
  
 -      // Create a new key
 -      RAND_pseudo_bytes((unsigned char *)to->inkey, to->inkeylength);
 -      if(to->incipher)
 -              EVP_DecryptInit_ex(&to->inctx, to->incipher, NULL, (unsigned char *)to->inkey, (unsigned char *)to->inkey + to->incipher->key_len);
 +      bin2hex(key, key, keylen);
  
        // Reset sequence number and late packet window
        mykeyused = true;
        to->received_seqno = 0;
        if(replaywin) memset(to->late, 0, replaywin);
  
 -      // Convert to hexadecimal and send
 -      char key[2 * to->inkeylength + 1];
 -      bin2hex(to->inkey, key, to->inkeylength);
 -      key[to->inkeylength * 2] = '\0';
 -
        return send_request(to->nexthop->connection, "%d %s %s %s %d %d %d %d", ANS_KEY,
 -                      myself->name, to->name, key,
 -                      to->incipher ? to->incipher->nid : 0,
 -                      to->indigest ? to->indigest->type : 0, to->inmaclength,
 -                      to->incompression);
 +                                              myself->name, to->name, key,
 +                                              cipher_get_nid(&to->incipher),
 +                                              digest_get_nid(&to->indigest),
 +                                              (int)digest_length(&to->indigest),
 +                                              to->incompression);
  }
  
 -bool ans_key_h(connection_t *c) {
 +bool ans_key_h(connection_t *c, const char *request) {
        char from_name[MAX_STRING_SIZE];
        char to_name[MAX_STRING_SIZE];
        char key[MAX_STRING_SIZE];
 -      char address[MAX_STRING_SIZE] = "";
 -      char port[MAX_STRING_SIZE] = "";
 -      int cipher, digest, maclength, compression;
 +        char address[MAX_STRING_SIZE] = "";
 +        char port[MAX_STRING_SIZE] = "";
 +      int cipher, digest, maclength, compression, keylen;
        node_t *from, *to;
  
 -      if(sscanf(c->buffer, "%*d "MAX_STRING" "MAX_STRING" "MAX_STRING" %d %d %d %d "MAX_STRING" "MAX_STRING,
 +      if(sscanf(request, "%*d "MAX_STRING" "MAX_STRING" "MAX_STRING" %d %d %d %d "MAX_STRING" "MAX_STRING,
                from_name, to_name, key, &cipher, &digest, &maclength,
                &compression, address, port) < 7) {
 -              logger(LOG_ERR, "Got bad %s from %s (%s)", "ANS_KEY", c->name,
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "ANS_KEY", c->name,
                           c->hostname);
                return false;
        }
  
        if(!check_id(from_name) || !check_id(to_name)) {
 -              logger(LOG_ERR, "Got bad %s from %s (%s): %s", "ANS_KEY", c->name, c->hostname, "invalid name");
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "ANS_KEY", c->name, c->hostname, "invalid name");
                return false;
        }
  
        from = lookup_node(from_name);
  
        if(!from) {
 -              logger(LOG_ERR, "Got %s from %s (%s) origin %s which does not exist in our connection list",
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Got %s from %s (%s) origin %s which does not exist in our connection list",
                           "ANS_KEY", c->name, c->hostname, from_name);
                return true;
        }
        to = lookup_node(to_name);
  
        if(!to) {
 -              logger(LOG_ERR, "Got %s from %s (%s) destination %s which does not exist in our connection list",
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Got %s from %s (%s) destination %s which does not exist in our connection list",
                           "ANS_KEY", c->name, c->hostname, to_name);
                return true;
        }
                        return true;
  
                if(!to->status.reachable) {
 -                      logger(LOG_WARNING, "Got %s from %s (%s) destination %s which is not reachable",
 -                              "ANS_KEY", c->name, c->hostname, to_name);
 +                      logger(DEBUG_ALWAYS, LOG_WARNING, "Got %s from %s (%s) destination %s which is not reachable",
 +                                 "ANS_KEY", c->name, c->hostname, to_name);
                        return true;
                }
  
                if(!*address && from->address.sa.sa_family != AF_UNSPEC) {
                        char *address, *port;
 -                      ifdebug(PROTOCOL) logger(LOG_DEBUG, "Appending reflexive UDP address to ANS_KEY from %s to %s", from->name, to->name);
 +                      logger(DEBUG_PROTOCOL, LOG_DEBUG, "Appending reflexive UDP address to ANS_KEY from %s to %s", from->name, to->name);
                        sockaddr2str(&from->address, &address, &port);
 -                      send_request(to->nexthop->connection, "%s %s %s", c->buffer, address, port);
 +                      send_request(to->nexthop->connection, "%s %s %s", request, address, port);
                        free(address);
                        free(port);
                        return true;
                }
  
 -              return send_request(to->nexthop->connection, "%s", c->buffer);
 +              return send_request(to->nexthop->connection, "%s", request);
        }
  
 -      /* Update our copy of the origin's packet key */
 -      from->outkey = xrealloc(from->outkey, strlen(key) / 2);
 -      from->outkeylength = strlen(key) / 2;
 -      if(!hex2bin(key, from->outkey, from->outkeylength)) {
 -              logger(LOG_ERR, "Got bad %s from %s(%s): %s", "ANS_KEY", from->name, from->hostname, "invalid key");
+       /* Don't use key material until every check has passed. */
+       from->status.validkey = false;
 +      if(compression < 0 || compression > 11) {
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Node %s (%s) uses bogus compression level!", from->name, from->hostname);
                return true;
        }
  
 -      /* Check and lookup cipher and digest algorithms */
 +      from->outcompression = compression;
  
 -      if(cipher) {
 -              from->outcipher = EVP_get_cipherbynid(cipher);
 +      /* SPTPS or old-style key exchange? */
  
 -              if(!from->outcipher) {
 -                      logger(LOG_ERR, "Node %s (%s) uses unknown cipher!", from->name,
 -                                 from->hostname);
 -                      return true;
 -              }
 +      if(from->status.sptps) {
 +              char buf[strlen(key)];
 +              int len = b64decode(key, buf, strlen(key));
  
 -              if(from->outkeylength != from->outcipher->key_len + from->outcipher->iv_len) {
 -                      logger(LOG_ERR, "Node %s (%s) uses wrong keylength!", from->name,
 -                                 from->hostname);
 -                      return true;
 +              if(!sptps_receive_data(&from->sptps, buf, len))
 +                      logger(DEBUG_ALWAYS, LOG_ERR, "Error processing SPTPS data from %s (%s)", from->name, from->hostname);
 +
 +              if(from->status.validkey) {
 +                      if(*address && *port) {
 +                              logger(DEBUG_PROTOCOL, LOG_DEBUG, "Using reflexive UDP address from %s: %s port %s", from->name, address, port);
 +                              sockaddr_t sa = str2sockaddr(address, port);
 +                              update_node_udp(from, &sa);
 +                      }
 +
 +                      if(from->options & OPTION_PMTU_DISCOVERY)
 +                              send_mtu_probe(from);
                }
 -      } else {
 -              from->outcipher = NULL;
 +
 +              return true;
        }
  
 -      from->outmaclength = maclength;
 +      /* Check and lookup cipher and digest algorithms */
  
 -      if(digest) {
 -              from->outdigest = EVP_get_digestbynid(digest);
 +      if(!cipher_open_by_nid(&from->outcipher, cipher)) {
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Node %s (%s) uses unknown cipher!", from->name, from->hostname);
 +              return false;
 +      }
  
 -              if(!from->outdigest) {
 -                      logger(LOG_ERR, "Node %s (%s) uses unknown digest!", from->name,
 -                                 from->hostname);
 -                      return true;
 -              }
 +      if(!digest_open_by_nid(&from->outdigest, digest, maclength)) {
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Node %s (%s) uses unknown digest!", from->name, from->hostname);
 +              return false;
 +      }
  
 -              if(from->outmaclength > from->outdigest->md_size || from->outmaclength < 0) {
 -                      logger(LOG_ERR, "Node %s (%s) uses bogus MAC length!",
 -                                 from->name, from->hostname);
 -                      return true;
 -              }
 -      } else {
 -              from->outdigest = NULL;
 +      if(maclength != digest_length(&from->outdigest)) {
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Node %s (%s) uses bogus MAC length!", from->name, from->hostname);
 +              return false;
        }
  
 -      if(compression < 0 || compression > 11) {
 -              logger(LOG_ERR, "Node %s (%s) uses bogus compression level!", from->name, from->hostname);
 +      /* Process key */
 +
 +      keylen = hex2bin(key, key, sizeof key);
 +
 +      if(keylen != cipher_keylength(&from->outcipher)) {
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Node %s (%s) uses wrong keylength!", from->name, from->hostname);
                return true;
        }
 -      
 -      from->outcompression = compression;
  
 -      if(from->outcipher)
 -              if(!EVP_EncryptInit_ex(&from->outctx, from->outcipher, NULL, (unsigned char *)from->outkey, (unsigned char *)from->outkey + from->outcipher->key_len)) {
 -                      logger(LOG_ERR, "Error during initialisation of key from %s (%s): %s",
 -                                      from->name, from->hostname, ERR_error_string(ERR_get_error(), NULL));
 -                      return true;
 -              }
 +      /* Update our copy of the origin's packet key */
 +
 +      cipher_set_key(&from->outcipher, key, true);
 +      digest_set_key(&from->outdigest, key, keylen);
  
        from->status.validkey = true;
        from->sent_seqno = 0;
  
        if(*address && *port) {
 -              ifdebug(PROTOCOL) logger(LOG_DEBUG, "Using reflexive UDP address from %s: %s port %s", from->name, address, port);
 +              logger(DEBUG_PROTOCOL, LOG_DEBUG, "Using reflexive UDP address from %s: %s port %s", from->name, address, port);
                sockaddr_t sa = str2sockaddr(address, port);
                update_node_udp(from, &sa);
        }
  
 -      if(from->options & OPTION_PMTU_DISCOVERY && !from->mtuevent)
 +      if(from->options & OPTION_PMTU_DISCOVERY)
                send_mtu_probe(from);
  
        return true;
diff --combined src/tincd.c
  #endif
  
  #include <getopt.h>
 -#include "pidfile.h"
  
  #include "conf.h"
 +#include "control.h"
 +#include "crypto.h"
  #include "device.h"
  #include "logger.h"
  #include "net.h"
  char *program_name = NULL;
  
  /* If nonzero, display usage information and exit. */
 -bool show_help = false;
 +static bool show_help = false;
  
  /* If nonzero, print the version on standard output and exit.  */
 -bool show_version = false;
 -
 -/* If nonzero, it will attempt to kill a running tincd and exit. */
 -int kill_tincd = 0;
 -
 -/* If nonzero, generate public/private keypair for this host/net. */
 -int generate_keys = 0;
 +static bool show_version = false;
  
  /* If nonzero, use null ciphers and skip all key exchanges. */
  bool bypass_security = false;
  
  /* If nonzero, disable swapping for this process. */
 -bool do_mlock = false;
 +static bool do_mlock = false;
  
  /* If nonzero, chroot to netdir after startup. */
  static bool do_chroot = false;
@@@ -88,18 -93,20 +88,18 @@@ static const char *switchuser = NULL
  bool use_logfile = false;
  
  char *identname = NULL;                               /* program name for syslog */
 -char *pidfilename = NULL;                     /* pid file location */
  char *logfilename = NULL;                     /* log file location */
 +char *pidfilename = NULL;
  char **g_argv;                                        /* a copy of the cmdline arguments */
  
 -static int status;
 +static int status = 1;
  
  static struct option const long_options[] = {
        {"config", required_argument, NULL, 'c'},
 -      {"kill", optional_argument, NULL, 'k'},
        {"net", required_argument, NULL, 'n'},
        {"help", no_argument, NULL, 1},
        {"version", no_argument, NULL, 2},
        {"no-detach", no_argument, NULL, 'D'},
 -      {"generate-keys", optional_argument, NULL, 'K'},
        {"debug", optional_argument, NULL, 'd'},
        {"bypass-security", no_argument, NULL, 3},
        {"mlock", no_argument, NULL, 'L'},
@@@ -123,18 -130,20 +123,18 @@@ static void usage(bool status) 
                                program_name);
        else {
                printf("Usage: %s [option]...\n\n", program_name);
 -              printf("  -c, --config=DIR               Read configuration options from DIR.\n"
 -                              "  -D, --no-detach                Don't fork and detach.\n"
 -                              "  -d, --debug[=LEVEL]            Increase debug level or set it to LEVEL.\n"
 -                              "  -k, --kill[=SIGNAL]            Attempt to kill a running tincd and exit.\n"
 -                              "  -n, --net=NETNAME              Connect to net NETNAME.\n"
 -                              "  -K, --generate-keys[=BITS]     Generate public/private RSA keypair.\n"
 -                              "  -L, --mlock                    Lock tinc into main memory.\n"
 -                              "      --logfile[=FILENAME]       Write log entries to a logfile.\n"
 -                              "      --pidfile=FILENAME         Write PID to FILENAME.\n"
 -                              "  -o, --option=[HOST.]KEY=VALUE  Set global/host configuration value.\n"
 -                              "  -R, --chroot                   chroot to NET dir at startup.\n"
 -                              "  -U, --user=USER                setuid to given USER at startup.\n"
 -                              "      --help                     Display this help and exit.\n"
 -                              "      --version                  Output version information and exit.\n\n");
 +              printf( "  -c, --config=DIR              Read configuration options from DIR.\n"
 +                              "  -D, --no-detach               Don't fork and detach.\n"
 +                              "  -d, --debug[=LEVEL]           Increase debug level or set it to LEVEL.\n"
 +                              "  -n, --net=NETNAME             Connect to net NETNAME.\n"
 +                              "  -L, --mlock                   Lock tinc into main memory.\n"
 +                              "      --logfile[=FILENAME]      Write log entries to a logfile.\n"
 +                              "      --pidfile=FILENAME        Write PID and control socket cookie to FILENAME.\n"
 +                              "      --bypass-security         Disables meta protocol security, for debugging.\n"
 +                              "  -o, --option[HOST.]KEY=VALUE  Set global/host configuration value.\n"
 +                              "  -R, --chroot                  chroot to NET dir at startup.\n"
 +                              "  -U, --user=USER               setuid to given USER at startup.\n"                            "      --help                    Display this help and exit.\n"
 +                              "      --version                 Output version information and exit.\n\n");
                printf("Report bugs to tinc@tinc-vpn.org.\n");
        }
  }
@@@ -147,7 -156,7 +147,7 @@@ static bool parse_options(int argc, cha
  
        cmdline_conf = list_alloc((list_action_t)free_config);
  
 -      while((r = getopt_long(argc, argv, "c:DLd::k::n:o:K::RU:", long_options, &option_index)) != EOF) {
 +      while((r = getopt_long(argc, argv, "c:DLd::n:o:RU:", long_options, &option_index)) != EOF) {
                switch (r) {
                        case 0:                         /* long option */
                                break;
  
                        case 'L':                               /* no detach */
  #ifndef HAVE_MLOCKALL
 -                              logger(LOG_ERR, "%s not supported on this platform", "mlockall()");
 +                              logger(DEBUG_ALWAYS, LOG_ERR, "%s not supported on this platform", "mlockall()");
                                return false;
  #else
                                do_mlock = true;
                                        debug_level++;
                                break;
  
 -                      case 'k':                               /* kill old tincds */
 -#ifndef HAVE_MINGW
 -                              if(optarg) {
 -                                      if(!strcasecmp(optarg, "HUP"))
 -                                              kill_tincd = SIGHUP;
 -                                      else if(!strcasecmp(optarg, "TERM"))
 -                                              kill_tincd = SIGTERM;
 -                                      else if(!strcasecmp(optarg, "KILL"))
 -                                              kill_tincd = SIGKILL;
 -                                      else if(!strcasecmp(optarg, "USR1"))
 -                                              kill_tincd = SIGUSR1;
 -                                      else if(!strcasecmp(optarg, "USR2"))
 -                                              kill_tincd = SIGUSR2;
 -                                      else if(!strcasecmp(optarg, "WINCH"))
 -                                              kill_tincd = SIGWINCH;
 -                                      else if(!strcasecmp(optarg, "INT"))
 -                                              kill_tincd = SIGINT;
 -                                      else if(!strcasecmp(optarg, "ALRM"))
 -                                              kill_tincd = SIGALRM;
 -                                      else if(!strcasecmp(optarg, "ABRT"))
 -                                              kill_tincd = SIGABRT;
 -                                      else {
 -                                              kill_tincd = atoi(optarg);
 -
 -                                              if(!kill_tincd) {
 -                                                      fprintf(stderr, "Invalid argument `%s'; SIGNAL must be a number or one of HUP, TERM, KILL, USR1, USR2, WINCH, INT or ALRM.\n",
 -                                                                      optarg);
 -                                                      usage(true);
 -                                                      return false;
 -                                              }
 -                                      }
 -                              } else
 -                                      kill_tincd = SIGTERM;
 -#else
 -                                      kill_tincd = 1;
 -#endif
 -                              break;
 -
                        case 'n':                               /* net name given */
 -                              /* netname "." is special: a "top-level name" */
 -                              netname = strcmp(optarg, ".") != 0 ?
 -                                              xstrdup(optarg) : NULL;
 +                              netname = xstrdup(optarg);
                                break;
  
                        case 'o':                               /* option */
                                list_insert_tail(cmdline_conf, cfg);
                                break;
  
 -                      case 'K':                               /* generate public/private keypair */
 -                              if(optarg) {
 -                                      generate_keys = atoi(optarg);
 -
 -                                      if(generate_keys < 512) {
 -                                              fprintf(stderr, "Invalid argument `%s'; BITS must be a number equal to or greater than 512.\n",
 -                                                              optarg);
 -                                              usage(true);
 -                                              return false;
 -                                      }
 -
 -                                      generate_keys &= ~7;    /* Round it to bytes */
 -                              } else
 -                                      generate_keys = 2048;
 -                              break;
 -
                        case 'R':                               /* chroot to NETNAME dir */
                                do_chroot = true;
                                break;
                                        logfilename = xstrdup(optarg);
                                break;
  
 -                      case 5:                                 /* write PID to a file */
 +                      case 5:                                 /* open control socket here */
                                pidfilename = xstrdup(optarg);
                                break;
  
                }
        }
  
 -      return true;
 -}
 -
 -/* This function prettyprints the key generation process */
 -
 -static void indicator(int a, int b, void *p) {
 -      switch (a) {
 -              case 0:
 -                      fprintf(stderr, ".");
 -                      break;
 +      if(!netname && (netname = getenv("NETNAME")))
 +              netname = xstrdup(netname);
  
 -              case 1:
 -                      fprintf(stderr, "+");
 -                      break;
 +      /* netname "." is special: a "top-level name" */
  
 -              case 2:
 -                      fprintf(stderr, "-");
 -                      break;
 -
 -              case 3:
 -                      switch (b) {
 -                              case 0:
 -                                      fprintf(stderr, " p\n");
 -                                      break;
 -
 -                              case 1:
 -                                      fprintf(stderr, " q\n");
 -                                      break;
 -
 -                              default:
 -                                      fprintf(stderr, "?");
 -                      }
 -                      break;
 -
 -              default:
 -                      fprintf(stderr, "?");
 +      if(netname && (!*netname || !strcmp(netname, "."))) {
 +              free(netname);
 +              netname = NULL;
        }
 -}
 -
 -/*
 -  Generate a public/private RSA keypair, and ask for a file to store
 -  them in.
 -*/
 -static bool keygen(int bits) {
 -      RSA *rsa_key;
 -      FILE *f;
 -      char *name = get_name();
 -      char *filename;
 -
 -      fprintf(stderr, "Generating %d bits keys:\n", bits);
 -      rsa_key = RSA_generate_key(bits, 0x10001, indicator, NULL);
 -
 -      if(!rsa_key) {
 -              fprintf(stderr, "Error during key generation!\n");
 -              return false;
 -      } else
 -              fprintf(stderr, "Done.\n");
 -
 -      xasprintf(&filename, "%s/rsa_key.priv", confbase);
 -      f = ask_and_open(filename, "private RSA key");
 -
 -      if(!f)
 -              return false;
  
 -#ifdef HAVE_FCHMOD
 -      /* Make it unreadable for others. */
 -      fchmod(fileno(f), 0600);
 -#endif
 -              
 -      fputc('\n', f);
 -      PEM_write_RSAPrivateKey(f, rsa_key, NULL, NULL, 0, NULL, NULL);
 -      fclose(f);
 -      free(filename);
 -
 -      if(name)
 -              xasprintf(&filename, "%s/hosts/%s", confbase, name);
 -      else
 -              xasprintf(&filename, "%s/rsa_key.pub", confbase);
 -
 -      f = ask_and_open(filename, "public RSA key");
 -
 -      if(!f)
 +      if(netname && (strpbrk(netname, "\\/") || *netname == '.')) {
 +              fprintf(stderr, "Invalid character in netname!\n");
                return false;
 -
 -      fputc('\n', f);
 -      PEM_write_RSAPublicKey(f, rsa_key);
 -      fclose(f);
 -      free(filename);
 -      free(name);
 +      }
  
        return true;
  }
@@@ -251,7 -391,7 +251,7 @@@ static void make_names(void) 
  #ifdef HAVE_MINGW
        HKEY key;
        char installdir[1024] = "";
 -      long len = sizeof(installdir);
 +      long len = sizeof installdir;
  #endif
  
        if(netname)
        if(!RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\tinc", 0, KEY_READ, &key)) {
                if(!RegQueryValueEx(key, NULL, 0, 0, installdir, &len)) {
                        if(!logfilename)
 -                              xasprintf(&logfilename, "%s/log/%s.log", identname);
 +                              xasprintf(&logfilename, "%s" SLASH "log" SLASH "%s.log", identname);
                        if(!confbase) {
                                if(netname)
 -                                      xasprintf(&confbase, "%s/%s", installdir, netname);
 +                                      xasprintf(&confbase, "%s" SLASH "%s", installdir, netname);
                                else
                                        xasprintf(&confbase, "%s", installdir);
                        }
 +                      if(!pidfilename)
 +                              xasprintf(&pidfilename, "%s" SLASH "pid", confbase);
                }
                RegCloseKey(key);
                if(*installdir)
        }
  #endif
  
 -      if(!pidfilename)
 -              xasprintf(&pidfilename, LOCALSTATEDIR "/run/%s.pid", identname);
 -
        if(!logfilename)
 -              xasprintf(&logfilename, LOCALSTATEDIR "/log/%s.log", identname);
 +              xasprintf(&logfilename, LOCALSTATEDIR SLASH "log" SLASH "%s.log", identname);
 +
 +      if(!pidfilename)
 +              xasprintf(&pidfilename, LOCALSTATEDIR SLASH "run" SLASH "%s.pid", identname);
  
        if(netname) {
                if(!confbase)
 -                      xasprintf(&confbase, CONFDIR "/tinc/%s", netname);
 +                      xasprintf(&confbase, CONFDIR SLASH "tinc" SLASH "%s", netname);
                else
 -                      logger(LOG_INFO, "Both netname and configuration directory given, using the latter...");
 +                      logger(DEBUG_ALWAYS, LOG_INFO, "Both netname and configuration directory given, using the latter...");
        } else {
                if(!confbase)
 -                      xasprintf(&confbase, CONFDIR "/tinc");
 +                      xasprintf(&confbase, CONFDIR SLASH "tinc");
        }
  }
  
 -static void free_names() {
 +static void free_names(void) {
        if (identname) free(identname);
        if (netname) free(netname);
        if (pidfilename) free(pidfilename);
        if (confbase) free(confbase);
  }
  
 -static bool drop_privs() {
 +static bool drop_privs(void) {
  #ifdef HAVE_MINGW
        if (switchuser) {
 -              logger(LOG_ERR, "%s not supported on this platform", "-U");
 +              logger(DEBUG_ALWAYS, LOG_ERR, "%s not supported on this platform", "-U");
                return false;
        }
        if (do_chroot) {
 -              logger(LOG_ERR, "%s not supported on this platform", "-R");
 +              logger(DEBUG_ALWAYS, LOG_ERR, "%s not supported on this platform", "-R");
                return false;
        }
  #else
        if (switchuser) {
                struct passwd *pw = getpwnam(switchuser);
                if (!pw) {
 -                      logger(LOG_ERR, "unknown user `%s'", switchuser);
 +                      logger(DEBUG_ALWAYS, LOG_ERR, "unknown user `%s'", switchuser);
                        return false;
                }
                uid = pw->pw_uid;
                if (initgroups(switchuser, pw->pw_gid) != 0 ||
                    setgid(pw->pw_gid) != 0) {
 -                      logger(LOG_ERR, "System call `%s' failed: %s",
 +                      logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s",
                               "initgroups", strerror(errno));
                        return false;
                }
+ #ifndef __ANDROID__
+ // Not supported in android NDK
                endgrent();
                endpwent();
+ #endif
        }
        if (do_chroot) {
                tzset();        /* for proper timestamps in logs */
                if (chroot(confbase) != 0 || chdir("/") != 0) {
 -                      logger(LOG_ERR, "System call `%s' failed: %s",
 +                      logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s",
                               "chroot", strerror(errno));
                        return false;
                }
        }
        if (switchuser)
                if (setuid(uid) != 0) {
 -                      logger(LOG_ERR, "System call `%s' failed: %s",
 +                      logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s",
                               "setuid", strerror(errno));
                        return false;
                }
@@@ -370,8 -511,8 +373,8 @@@ int main(int argc, char **argv) 
        make_names();
  
        if(show_version) {
 -              printf("%s version %s (built %s %s, protocol %d)\n", PACKAGE,
 -                         VERSION, __DATE__, __TIME__, PROT_CURRENT);
 +              printf("%s version %s (built %s %s, protocol %d.%d)\n", PACKAGE,
 +                         VERSION, __DATE__, __TIME__, PROT_MAJOR, PROT_MINOR);
                printf("Copyright (C) 1998-2012 Ivo Timmermans, Guus Sliepen and others.\n"
                                "See the AUTHORS file for a complete list.\n\n"
                                "tinc comes with ABSOLUTELY NO WARRANTY.  This is free software,\n"
                return 0;
        }
  
 -      if(kill_tincd)
 -              return !kill_other(kill_tincd);
 +#ifdef HAVE_MINGW
 +      if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
 +              logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "WSAStartup", winerror(GetLastError()));
 +              return 1;
 +      }
 +#endif
  
        openlogger("tinc", use_logfile?LOGMODE_FILE:LOGMODE_STDERR);
  
  
        /* Slllluuuuuuurrrrp! */
  
 -      RAND_load_file("/dev/urandom", 1024);
 -
 -      ENGINE_load_builtin_engines();
 -      ENGINE_register_all_complete();
 -
 -      OpenSSL_add_all_algorithms();
 -
 -      if(generate_keys) {
 -              read_server_config();
 -              return !keygen(generate_keys);
 -      }
 +      srand(time(NULL));
 +      crypto_init();
  
        if(!read_server_config())
                return 1;
  
  #ifdef HAVE_LZO
        if(lzo_init() != LZO_E_OK) {
 -              logger(LOG_ERR, "Error initializing LZO compressor!");
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Error initializing LZO compressor!");
                return 1;
        }
  #endif
  
  #ifdef HAVE_MINGW
 -      if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
 -              logger(LOG_ERR, "System call `%s' failed: %s", "WSAStartup", winerror(GetLastError()));
 -              return 1;
 -      }
 -
        if(!do_detach || !init_service())
                return main2(argc, argv);
        else
@@@ -441,24 -592,16 +444,24 @@@ int main2(int argc, char **argv) 
         * This has to be done after daemon()/fork() so it works for child.
         * No need to do that in parent as it's very short-lived. */
        if(do_mlock && mlockall(MCL_CURRENT | MCL_FUTURE) != 0) {
 -              logger(LOG_ERR, "System call `%s' failed: %s", "mlockall",
 +              logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "mlockall",
                   strerror(errno));
                return 1;
        }
  #endif
  
 +      if(!event_init()) {
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Error initializing libevent!");
 +              return 1;
 +      }
 +
        /* Setup sockets and open device. */
  
        if(!setup_network())
 -              goto end;
 +              goto end_nonet;
 +
 +      if(!init_control())
 +              goto end_nonet;
  
        /* Initiate all outgoing connections. */
  
          if(get_config_string(lookup_config(config_tree, "ProcessPriority"), &priority)) {
                  if(!strcasecmp(priority, "Normal")) {
                          if (setpriority(NORMAL_PRIORITY_CLASS) != 0) {
 -                                logger(LOG_ERR, "System call `%s' failed: %s",
 +                                logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s",
                                         "setpriority", strerror(errno));
                                  goto end;
                          }
                  } else if(!strcasecmp(priority, "Low")) {
                          if (setpriority(BELOW_NORMAL_PRIORITY_CLASS) != 0) {
 -                                       logger(LOG_ERR, "System call `%s' failed: %s",
 +                                       logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s",
                                         "setpriority", strerror(errno));
                                  goto end;
                          }
                  } else if(!strcasecmp(priority, "High")) {
                          if (setpriority(HIGH_PRIORITY_CLASS) != 0) {
 -                                logger(LOG_ERR, "System call `%s' failed: %s",
 +                                logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s",
                                         "setpriority", strerror(errno));
                                  goto end;
                          }
                  } else {
 -                        logger(LOG_ERR, "Invalid priority `%s`!", priority);
 +                        logger(DEBUG_ALWAYS, LOG_ERR, "Invalid priority `%s`!", priority);
                          goto end;
                  }
          }
  
        /* Shutdown properly. */
  
 -      ifdebug(CONNECTIONS)
 +      if(debug_level >= DEBUG_CONNECTIONS)
                devops.dump_stats();
  
        close_network_connections();
  
  end:
 -      logger(LOG_NOTICE, "Terminating");
 +      exit_control();
  
 -#ifndef HAVE_MINGW
 -      remove_pid(pidfilename);
 -#endif
 +end_nonet:
 +      logger(DEBUG_ALWAYS, LOG_NOTICE, "Terminating");
  
        free(priority);
  
 -      EVP_cleanup();
 -      ENGINE_cleanup();
 -      CRYPTO_cleanup_all_ex_data();
 -      ERR_remove_state(0);
 -      ERR_free_strings();
 +      crypto_exit();
  
        exit_configuration(&config_tree);
 -      list_free(cmdline_conf);
 +      free(cmdline_conf);
        free_names();
  
        return status;
diff --combined src/utils.c
index e750450,0000000..129622b
mode 100644,000000..100644
--- /dev/null
@@@ -1,162 -1,0 +1,162 @@@
-       for(i = 0; i < length && src[i * 2] && src[i * 2 + 1]; i++)
 +/*
 +    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 && isxdigit(src[i * 2]) && isxdigit(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) {
 +      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), 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;
 +}