]> git.meshlink.io Git - meshlink/commitdiff
Merge branch 'master' of git://tinc-vpn.org/tinc into 1.1
authorGuus Sliepen <guus@tinc-vpn.org>
Mon, 26 Mar 2012 18:06:39 +0000 (19:06 +0100)
committerGuus Sliepen <guus@tinc-vpn.org>
Mon, 26 Mar 2012 18:06:39 +0000 (19:06 +0100)
Conflicts:
src/logger.c
src/net_setup.c

1  2 
configure.in
src/logger.c
src/net_setup.c
src/tincd.c

diff --combined configure.in
index 4d2bcbd6e0130f93d40a72b0264608b12316091b,7eaeca6fc17eddc751b13c097f404a5fb36ae7b2..bdac4babd7ac3712d4adea820843519d24236fcc
@@@ -3,8 -3,7 +3,8 @@@ dnl Process this file with autoconf to 
  AC_PREREQ(2.61)
  AC_INIT
  AC_CONFIG_SRCDIR([src/tincd.c])
 -AM_INIT_AUTOMAKE(tinc, 1.0.18)
 +AC_GNU_SOURCE
 +AM_INIT_AUTOMAKE(tinc, 1.1pre2)
  AC_CONFIG_HEADERS([config.h])
  AM_MAINTAINER_MODE
  
@@@ -74,30 -73,44 +74,44 @@@ case $host_os i
  esac
  
  AC_ARG_ENABLE(uml,
-   AS_HELP_STRING([--enable-uml], [enable support for User Mode Linux]),
-   [ AC_DEFINE(ENABLE_UML, 1, [Support for UML])
-     uml=true
-   ]
+   AS_HELP_STRING([--disable-uml], [enable support for User Mode Linux]),
+   [ AS_IF([test "x$enable_uml" = "xyes"],
+       [ AC_DEFINE(ENABLE_UML, 1, [Support for UML])
+         uml=true
+       ],
+       [uml=false])
+   ],
+   [uml=false]
  )
  
  AC_ARG_ENABLE(vde,
-   AS_HELP_STRING([--enable-vde], [enable support for Virtual Distributed Ethernet]),
-   [ AC_CHECK_HEADERS(libvdeplug_dyn.h, [], [AC_MSG_ERROR([VDE plug header files not found.]); break])
-     AC_DEFINE(ENABLE_VDE, 1, [Support for VDE])
-     vde=true
-   ]
+   AS_HELP_STRING([--disable-vde], [enable support for Virtual Distributed Ethernet]),
+   [ AS_IF([test "x$enable_vde" = "xyes"],
+       [ AC_CHECK_HEADERS(libvdeplug_dyn.h, [], [AC_MSG_ERROR([VDE plug header files not found.]); break])
+         AC_DEFINE(ENABLE_VDE, 1, [Support for VDE])
+         vde=true
+       ],
+       [vde=false])
+   ],
+   [vde=false]
  )
  
  AC_ARG_ENABLE(tunemu,
-   AS_HELP_STRING([--enable-tunemu], [enable support for the tunemu driver]),
-   [ AC_DEFINE(ENABLE_TUNEMU, 1, [Support for tunemu])
-     tunemu=true
-   ]
+   AS_HELP_STRING([--disable-tunemu], [enable support for the tunemu driver]),
+   [ AS_IF([test "x$enable_tunemu" = "xyes"],
+       [ AC_DEFINE(ENABLE_TUNEMU, 1, [Support for tunemu])
+         tunemu=true
+       ],
+       [tunemu=false])
+   ],
+   [tunemu=false]
  )
  
  AC_ARG_WITH(windows2000,
-   AS_HELP_STRING([--with-windows2000], [compile with support for Windows 2000. This disables support for tunneling over existing IPv6 networks.]),
-   [AC_DEFINE(WITH_WINDOWS2000, 1, [Compile with support for Windows 2000])]
+   AS_HELP_STRING([--without-windows2000], [compile with support for Windows 2000. This disables support for tunneling over existing IPv6 networks.]),
+   [ AS_IF([test "x$with_windows2000" = "xyes"],
+       [AC_DEFINE(WITH_WINDOWS2000, 1, [Compile with support for Windows 2000])])
+   ]
  )
  
  AM_CONDITIONAL(UML, test "$uml" = true)
@@@ -113,12 -126,14 +127,12 @@@ if test -d /sw/lib ; the
    LIBS="$LIBS -L/sw/lib"
  fi
  
 -dnl Checks for libraries.
 -
  dnl Checks for header files.
  dnl We do this in multiple stages, because unlike Linux all the other operating systems really suck and don't include their own dependencies.
  
  AC_HEADER_STDC
 -AC_CHECK_HEADERS([stdbool.h syslog.h sys/file.h sys/ioctl.h sys/mman.h sys/param.h sys/resource.h sys/socket.h sys/time.h time.h sys/uio.h sys/wait.h netdb.h arpa/inet.h dirent.h])
 -AC_CHECK_HEADERS([net/if.h net/if_types.h linux/if_tun.h net/if_tun.h net/tun/if_tun.h net/if_tap.h net/tap/if_tap.h net/ethernet.h net/if_arp.h netinet/in_systm.h netinet/in.h netinet/in6.h netpacket/packet.h],
 +AC_CHECK_HEADERS([stdbool.h syslog.h sys/file.h sys/ioctl.h sys/mman.h sys/param.h sys/resource.h sys/socket.h sys/time.h sys/uio.h sys/un.h sys/wait.h netdb.h arpa/inet.h dirent.h])
 +AC_CHECK_HEADERS([net/if.h net/if_types.h linux/if_tun.h net/if_tun.h net/tun/if_tun.h net/if_tap.h net/tap/if_tap.h net/ethernet.h net/if_arp.h netinet/in_systm.h netinet/in.h netinet/in6.h time.h netpacket/packet.h],
    [], [], [#include "have.h"]
  )
  AC_CHECK_HEADERS([netinet/if_ether.h netinet/ip.h netinet/ip6.h],
@@@ -143,10 -158,14 +157,10 @@@ AC_CHECK_TYPES([socklen_t, struct ether
  )
  
  dnl Checks for library functions.
 -AC_FUNC_MEMCMP
 -AC_FUNC_ALLOCA
  AC_TYPE_SIGNAL
 -AC_CHECK_FUNCS([asprintf daemon fchmod flock ftime fork get_current_dir_name gettimeofday mlockall pselect putenv random select strdup strerror strsignal strtol system unsetenv usleep vsyslog writev],
 +AC_CHECK_FUNCS([asprintf daemon fchmod flock ftime fork get_current_dir_name gettimeofday mlockall putenv random select strdup strerror strsignal strtol system time usleep unsetenv vsyslog writev],
    [], [], [#include "have.h"]
  )
 -AC_FUNC_MALLOC
 -AC_FUNC_REALLOC
  
  dnl Support for SunOS
  
@@@ -165,30 -184,20 +179,32 @@@ AC_CACHE_SAV
  
  dnl These are defined in files in m4/
  
 +AC_ARG_WITH(libgcrypt, AC_HELP_STRING([--with-libgcrypt], [enable use of libgcrypt instead of OpenSSL])], [])
 +
 +tinc_CURSES
 +tinc_LIBEVENT
  tinc_ZLIB
  tinc_LZO
 -tinc_OPENSSL
 +
 +if test "$with_libgcrypt" = yes; then
 +      AM_PATH_LIBGCRYPT([1.4.0], [], [])
 +      ln -sf gcrypt/cipher.c gcrypt/cipher.h gcrypt/crypto.c gcrypt/crypto.h gcrypt/digest.c gcrypt/digest.h gcrypt/ecdh.c gcrypt/ecdh.h gcrypt/ecdsa.c gcrypt/ecdsa.h gcrypt/ecdsagen.c gcrypt/ecdsagen.h gcrypt/prf.c gcrypt/prf.h gcrypt/rsa.c gcrypt/rsa.h gcrypt/rsagen.c gcrypt/rsagen.h src/
 +else
 +      tinc_OPENSSL
 +      ln -sf openssl/cipher.c openssl/cipher.h openssl/crypto.c openssl/crypto.h openssl/digest.c openssl/digest.h openssl/ecdh.c openssl/ecdh.h openssl/ecdsa.c openssl/ecdsa.h openssl/ecdsagen.c openssl/ecdsagen.h openssl/prf.c openssl/prf.h openssl/rsa.c openssl/rsa.h openssl/rsagen.c openssl/rsagen.h src/
 +fi
 +      
  
  dnl Check if support for jumbograms is requested 
  AC_ARG_ENABLE(jumbograms,
-   AS_HELP_STRING([--enable-jumbograms], [enable support for jumbograms (packets up to 9000 bytes)]),
-   [ AC_DEFINE(ENABLE_JUMBOGRAMS, 1, [Support for jumbograms (packets up to 9000 bytes)]) ]
+   AS_HELP_STRING([--disable-jumbograms], [enable support for jumbograms (packets up to 9000 bytes)]),
+   [ AS_IF([test "x$enable_jumbograms" = "xyes"],
+       [ AC_DEFINE(ENABLE_JUMBOGRAMS, 1, [Support for jumbograms (packets up to 9000 bytes)]) ])
+   ]
  )
  
  AC_SUBST(INCLUDES)
  
 -AC_CONFIG_FILES([Makefile src/Makefile doc/Makefile lib/Makefile m4/Makefile])
 +AC_CONFIG_FILES([Makefile src/Makefile doc/Makefile m4/Makefile gui/Makefile])
  
  AC_OUTPUT
diff --combined src/logger.c
index 028cbaef804ba053e285dc4d37b7716ac2d87f51,6765cc5abeba7994b1283734714577eeb44f3bf1..cc47a4a1cbfdedeb05e30b29e9a6dcc5d1cd2ee5
  #include "system.h"
  
  #include "conf.h"
 +#include "meta.h"
  #include "logger.h"
 +#include "connection.h"
 +#include "control_common.h"
  
  debug_t debug_level = DEBUG_NOTHING;
  static logmode_t logmode = LOGMODE_STDERR;
@@@ -35,7 -32,6 +35,7 @@@ static FILE *logfile = NULL
  static HANDLE loghandle = NULL;
  #endif
  static const char *logident = NULL;
 +bool logcontrol = false;
  
  void openlogger(const char *ident, logmode_t mode) {
        logident = ident;
@@@ -79,76 -75,61 +79,76 @@@ void reopenlogger() 
        fflush(logfile);
        FILE *newfile = fopen(logfilename, "a");
        if(!newfile) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Unable to reopen log file %s: %s\n", logfilename, strerror(errno));
 -              logger(LOG_ERR, "Unable to reopen log file %s: %s", logfilename, strerror(errno));
++              logger(DEBUG_ALWAYS, LOG_ERR, "Unable to reopen log file %s: %s", logfilename, strerror(errno));
                return;
        }
        fclose(logfile);
        logfile = newfile;
  }
  
 -void logger(int priority, const char *format, ...) {
 +void logger(int level, int priority, const char *format, ...) {
        va_list ap;
        char timestr[32] = "";
 +      char message[1024] = "";
        time_t now;
 +      static bool suppress = false;
 +
 +      // Bail out early if there is nothing to do.
 +      if(suppress)
 +              return;
 +
 +      if(!logcontrol && (level > debug_level || logmode == LOGMODE_NULL))
 +              return;
  
        va_start(ap, format);
 +      vsnprintf(message, sizeof message, format, ap);
 +      va_end(ap);
  
 -      switch(logmode) {
 -              case LOGMODE_STDERR:
 -                      vfprintf(stderr, format, ap);
 -                      fprintf(stderr, "\n");
 -                      fflush(stderr);
 -                      break;
 -              case LOGMODE_FILE:
 -                      now = time(NULL);
 -                      strftime(timestr, sizeof timestr, "%Y-%m-%d %H:%M:%S", localtime(&now));
 -                      fprintf(logfile, "%s %s[%ld]: ", timestr, logident, (long)logpid);
 -                      vfprintf(logfile, format, ap);
 -                      fprintf(logfile, "\n");
 -                      fflush(logfile);
 -                      break;
 -              case LOGMODE_SYSLOG:
 +      if(level <= debug_level) {
 +              switch(logmode) {
 +                      case LOGMODE_STDERR:
 +                              fprintf(stderr, "%s\n", message);
 +                              fflush(stderr);
 +                              break;
 +                      case LOGMODE_FILE:
 +                              now = time(NULL);
 +                              strftime(timestr, sizeof timestr, "%Y-%m-%d %H:%M:%S", localtime(&now));
 +                              fprintf(logfile, "%s %s[%ld]: %s\n", timestr, logident, (long)logpid, message);
 +                              fflush(logfile);
 +                              break;
 +                      case LOGMODE_SYSLOG:
  #ifdef HAVE_MINGW
 -                      {
 -                              char message[4096];
 -                              const char *messages[] = {message};
 -                              vsnprintf(message, sizeof(message), format, ap);
 -                              ReportEvent(loghandle, priority, 0, 0, NULL, 1, 0, messages, NULL);
 -                      }
 +                              {
 +                                      const char *messages[] = {message};
 +                                      ReportEvent(loghandle, priority, 0, 0, NULL, 1, 0, messages, NULL);
 +                              }
  #else
  #ifdef HAVE_SYSLOG_H
 -#ifdef HAVE_VSYSLOG
 -                      vsyslog(priority, format, ap);
 -#else
 -                      {
 -                              char message[4096];
 -                              vsnprintf(message, sizeof(message), format, ap);
                                syslog(priority, "%s", message);
 -                      }
 -#endif
 -                      break;
  #endif
  #endif
 -              case LOGMODE_NULL:
 -                      break;
 +                              break;
 +                      case LOGMODE_NULL:
 +                              break;
 +              }
        }
  
 -      va_end(ap);
 +      if(logcontrol) {
 +              suppress = true;
 +              logcontrol = false;
 +              for(splay_node_t *node = connection_tree->head; node; node = node->next) {
 +                      connection_t *c = node->data;
 +                      if(!c->status.log)
 +                              continue;
 +                      logcontrol = true;
 +                      if(level > (c->outcompression >= 0 ? c->outcompression : debug_level))
 +                              continue;
 +                      int len = strlen(message);
 +                      if(send_request(c, "%d %d %d", CONTROL, REQ_LOG, len))
 +                              send_meta(c, message, len);
 +              }
 +              suppress = false;
 +      }
  }
  
  void closelogger(void) {
diff --combined src/net_setup.c
index c181df9472e78c07f0e1ea6efae2d27259d9d574,d3940e72b36aa698404a9e70ac1c47167d03a17a..0d21cc059cfd1669a4d5bc1e656a97bf3d352800
  
  #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;
  
 -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/hosts/%s", confbase, n->name);
  
 -      /* First, check for simple PublicKey statement */
 +      init_configuration(&config_tree);
 +      if(!read_config_file(config_tree, fname))
 +              goto exit;
  
 -      if(get_config_string(lookup_config(c->config_tree, "PublicKey"), &key)) {
 -              BN_hex2bn(&c->rsa_key->n, key);
 -              BN_hex2bn(&c->rsa_key->e, "FFFF");
 -              free(key);
 -              return true;
 +      /* First, check for simple ECDSAPublicKey statement */
 +
 +      if(get_config_string(lookup_config(config_tree, "ECDSAPublicKey"), &p)) {
 +              result = ecdsa_set_base64_public_key(&n->ecdsa, p);
 +              free(p);
 +              goto exit;
        }
  
 -      /* Else, check for PublicKeyFile statement and read it */
 +      /* Else, check for ECDSAPublicKeyFile statement and read it */
  
 -      if(get_config_string(lookup_config(c->config_tree, "PublicKeyFile"), &fname)) {
 -              fp = fopen(fname, "r");
 +      free(fname);
  
 -              if(!fp) {
 -                      logger(LOG_ERR, "Error reading RSA public key file `%s': %s",
 -                                 fname, strerror(errno));
 -                      free(fname);
 -                      return false;
 -              }
 +      if(!get_config_string(lookup_config(config_tree, "ECDSAPublicKeyFile"), &fname))
 +              xasprintf(&fname, "%s/hosts/%s", confbase, n->name);
  
 -              free(fname);
 -              c->rsa_key = PEM_read_RSAPublicKey(fp, &c->rsa_key, NULL, NULL);
 -              fclose(fp);
 +      fp = fopen(fname, "r");
  
 -              if(c->rsa_key)
 -                      return true;            /* Woohoo. */
 +      if(!fp) {
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Error reading ECDSA public key file `%s': %s", fname, strerror(errno));
 +              goto exit;
 +      }
  
 -              /* If it fails, try PEM_read_RSA_PUBKEY. */
 -              fp = fopen(fname, "r");
 +      result = ecdsa_read_pem_public_key(&n->ecdsa, fp);
 +      fclose(fp);
  
 -              if(!fp) {
 -                      logger(LOG_ERR, "Error reading RSA public key file `%s': %s",
 -                                 fname, strerror(errno));
 -                      free(fname);
 -                      return false;
 -              }
 +exit:
 +      exit_configuration(&config_tree);
 +      free(fname);
 +      return result;
 +}
  
 -              free(fname);
 -              c->rsa_key = PEM_read_RSA_PUBKEY(fp, &c->rsa_key, NULL, NULL);
 -              fclose(fp);
 +bool read_ecdsa_public_key(connection_t *c) {
 +      FILE *fp;
 +      char *fname;
 +      char *p;
 +      bool result;
  
 -              if(c->rsa_key) {
 -//                            RSA_blinding_on(c->rsa_key, NULL);
 -                      return true;
 -              }
 +      /* First, check for simple ECDSAPublicKey statement */
  
 -              logger(LOG_ERR, "Reading RSA public key file `%s' failed: %s",
 -                         fname, strerror(errno));
 -              return false;
 +      if(get_config_string(lookup_config(c->config_tree, "ECDSAPublicKey"), &p)) {
 +              result = ecdsa_set_base64_public_key(&c->ecdsa, p);
 +              free(p);
 +              return result;
        }
  
 -      /* Else, check if a harnessed public key is in the config file */
 +      /* Else, check for ECDSAPublicKeyFile statement and read it */
 +
 +      if(!get_config_string(lookup_config(c->config_tree, "ECDSAPublicKeyFile"), &fname))
 +              xasprintf(&fname, "%s/hosts/%s", confbase, c->name);
  
 -      xasprintf(&fname, "%s/hosts/%s", confbase, c->name);
        fp = fopen(fname, "r");
  
        if(!fp) {
 -              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, "Reading ECDSA public key file `%s' failed: %s", fname, strerror(errno));
        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;
  
 -      /* Try again with PEM_read_RSA_PUBKEY. */
 +      /* First, check for simple PublicKey statement */
 +
 +      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/hosts/%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;
  
 -      logger(LOG_ERR, "No public key for %s specified!", c->name);
 +      /* Check for PrivateKeyFile statement and read it */
  
 -      return false;
 +      if(!get_config_string(lookup_config(config_tree, "ECDSAPrivateKeyFile"), &fname))
 +              xasprintf(&fname, "%s/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;
 +
 +      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;
 +
 +      /* First, check for simple PrivateKey statement */
  
 -      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!");
 +      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;
                }
 -              myself->connection->rsa_key = RSA_new();
 -//            RSA_blinding_on(myself->connection->rsa_key, NULL);
 -              BN_hex2bn(&myself->connection->rsa_key->d, key);
 -              BN_hex2bn(&myself->connection->rsa_key->n, pubkey);
 -              BN_hex2bn(&myself->connection->rsa_key->e, "FFFF");
 -              free(key);
 -              free(pubkey);
 +              result = rsa_set_hex_private_key(&myself->connection->rsa, n, "FFFF", d);
 +              free(n);
 +              free(d);
                return true;
        }
  
 +      /* 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);
  
        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});
  }
  
  /*
@@@ -288,7 -217,7 +288,7 @@@ 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);
        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;
        }
@@@ -362,16 -291,15 +362,16 @@@ static bool setup_myself(void) 
        myself->connection->hostname = xstrdup("MYSELF");
  
        myself->connection->options = 0;
 -      myself->connection->protocol_version = PROT_CURRENT;
 +      myself->connection->protocol_major = PROT_MAJOR;
 +      myself->connection->protocol_minor = PROT_MINOR;
  
        if(!get_config_string(lookup_config(config_tree, "Name"), &name)) {     /* Not acceptable */
 -              logger(LOG_ERR, "Name for tinc daemon required!");
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Name for tinc daemon required!");
                return false;
        }
  
        if(!check_id(name)) {
 -              logger(LOG_ERR, "Invalid name for myself!");
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Invalid name for myself!");
                free(name);
                return false;
        }
        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;
  
                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);
  
  #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
  
        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;
                else if(!strcasecmp(afname, "any"))
                        addressfamily = AF_UNSPEC;
                else {
 -                      logger(LOG_ERR, "Invalid address family!");
 +                      logger(DEBUG_ALWAYS, LOG_ERR, "Invalid address family!");
                        return false;
                }
                free(afname);
  
        /* 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(!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;
 +      if(!get_config_string(lookup_config(config_tree, "Cipher"), &cipher))
 +              cipher = xstrdup("blowfish");
  
 -      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;
  
 -      keyexpires = now + keylifetime;
 -      
 +      regenerate_key();
 +
        /* 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);
 +      int maclength = 4;
 +      get_config_int(lookup_config(config_tree, "MACLength"), &maclength);
  
 -                      if(!myself->indigest) {
 -                              logger(LOG_ERR, "Unrecognized digest type!");
 -                              return false;
 -                      }
 -              }
 -      } else
 -              myself->indigest = EVP_sha1();
 -
 -      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
        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 */
  
        /* Open sockets */
  
-       listen_sockets = 0;
-       cfg = lookup_config(config_tree, "BindToAddress");
-       do {
-               get_config_string(cfg, &address);
-               if(cfg)
-                       cfg = lookup_config_next(config_tree, cfg);
-               char *port = myport;
-               if(address) {
-                       char *space = strchr(address, ' ');
-                       if(space) {
-                               *space++ = 0;
-                               port = space;
-                       }
-                       if(!strcmp(address, "*"))
-                               *address = 0;
-               }
-               hint.ai_family = addressfamily;
-               hint.ai_socktype = SOCK_STREAM;
-               hint.ai_protocol = IPPROTO_TCP;
-               hint.ai_flags = AI_PASSIVE;
+       if(!do_detach && getenv("LISTEN_FDS")) {
+               sockaddr_t sa;
+               socklen_t salen;
  
-               err = getaddrinfo(address && *address ? address : NULL, port, &hint, &ai);
-               free(address);
+               listen_sockets = atoi(getenv("LISTEN_FDS"));
+ #ifdef HAVE_UNSETENV
+               unsetenv("LISTEN_FDS");
+ #endif
  
-               if(err || !ai) {
-                       logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "getaddrinfo",
-                                  gai_strerror(err));
+               if(listen_sockets > MAXSOCKETS) {
 -                      logger(LOG_ERR, "Too many listening sockets");
++                      logger(DEBUG_ALWAYS, LOG_ERR, "Too many listening sockets");
                        return false;
                }
  
-               for(aip = ai; aip; aip = aip->ai_next) {
-                       if(listen_sockets >= MAXSOCKETS) {
-                               logger(DEBUG_ALWAYS, LOG_ERR, "Too many listening sockets");
+               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;
                        }
  
-                       listen_socket[listen_sockets].tcp =
-                               setup_listen_socket((sockaddr_t *) aip->ai_addr);
-                       if(listen_socket[listen_sockets].tcp < 0)
-                               continue;
+                       listen_socket[i].tcp = i + 3;
  
-                       listen_socket[listen_sockets].udp =
-                               setup_vpn_in_socket((sockaddr_t *) aip->ai_addr);
+ #ifdef FD_CLOEXEC
+                       fcntl(i + 3, F_SETFD, FD_CLOEXEC);
+ #endif
  
-                       if(listen_socket[listen_sockets].udp < 0) {
-                               close(listen_socket[listen_sockets].tcp);
-                               continue;
-                       }
+                       listen_socket[i].udp = setup_vpn_in_socket(&sa);
+                       if(listen_socket[i].udp < 0)
+                               return false;
  
-                       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) {
 -                      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[listen_sockets].ev_udp,
-                                         listen_socket[listen_sockets].udp,
-                                         EV_READ|EV_PERSIST,
-                                         handle_incoming_vpn_data, (void *)(intptr_t)listen_sockets);
++                      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((sockaddr_t *) aip->ai_addr);
+                               hostname = sockaddr2hostname(&sa);
 -                              logger(LOG_NOTICE, "Listening on %s", hostname);
 +                              logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Listening on %s", hostname);
                                free(hostname);
                        }
  
-                       memcpy(&listen_socket[listen_sockets].sa, aip->ai_addr, aip->ai_addrlen);
-                       listen_sockets++;
+                       memcpy(&listen_socket[i].sa, &sa, salen);
                }
+       } else {
+               listen_sockets = 0;
+               cfg = lookup_config(config_tree, "BindToAddress");
+               do {
+                       get_config_string(cfg, &address);
+                       if(cfg)
+                               cfg = lookup_config_next(config_tree, cfg);
+                       char *port = myport;
+                       if(address) {
+                               char *space = strchr(address, ' ');
+                               if(space) {
+                                       *space++ = 0;
+                                       port = space;
+                               }
+                               if(!strcmp(address, "*"))
+                                       *address = 0;
+                       }
+                       hint.ai_family = addressfamily;
+                       hint.ai_socktype = SOCK_STREAM;
+                       hint.ai_protocol = IPPROTO_TCP;
+                       hint.ai_flags = AI_PASSIVE;
  
-               freeaddrinfo(ai);
-       } while(cfg);
+                       err = getaddrinfo(address && *address ? address : NULL, port, &hint, &ai);
+                       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].tcp =
+                                       setup_listen_socket((sockaddr_t *) aip->ai_addr);
+                               if(listen_socket[listen_sockets].tcp < 0)
+                                       continue;
+                               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);
+                               }
+                               memcpy(&listen_socket[listen_sockets].sa, aip->ai_addr, aip->ai_addrlen);
+                               listen_sockets++;
+                       }
+                       freeaddrinfo(ai);
+               } while(cfg);
+       }
  
        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;
        }
  
    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/tincd.c
index 1008f88aaec0865a5c604594f586f277a83f9f60,148e13e444ef2c7ef8188e04baa3e1a189504a55..3cbac4ba25f5daa4a29b2f0189dc8b466aff035a
  #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 ?
                                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;
 -
 -              case 1:
 -                      fprintf(stderr, "+");
 -                      break;
 -
 -              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, "?");
 -      }
 -}
 -
 -/*
 -  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 = NULL;
 -      char *filename;
 -
 -      get_config_string(lookup_config(config_tree, "Name"), &name);
 -
 -      if(name && !check_id(name)) {
 -              fprintf(stderr, "Invalid name for myself!\n");
 -              return false;
 -      }
 -
 -      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)
 -              return false;
 -
 -      fputc('\n', f);
 -      PEM_write_RSAPublicKey(f, rsa_key);
 -      fclose(f);
 -      free(filename);
 -      if(name)
 -              free(name);
 -
 -      return true;
 -}
 -
  /*
    Set all files and paths according to netname
  */
@@@ -238,7 -399,7 +238,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)
                                else
                                        xasprintf(&confbase, "%s", installdir);
                        }
 +                      if(!pidfilename)
 +                              xasprintf(&pidfilename, "%s/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);
  
 +      if(!pidfilename)
 +              xasprintf(&pidfilename, LOCALSTATEDIR "/run/%s.pid", identname);
 +
        if(netname) {
                if(!confbase)
                        xasprintf(&confbase, CONFDIR "/tinc/%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");
        }
  }
  
 -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;
                }
        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;
                }
@@@ -357,8 -516,8 +357,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);
  
 +      if(!event_init()) {
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Error initializing libevent!");
 +              return 1;
 +      }
 +
        g_argv = argv;
  
+       if(getenv("LISTEN_PID") && atoi(getenv("LISTEN_PID")) == getpid())
+               do_detach = false;
+ #ifdef HAVE_UNSETENV
+       unsetenv("LISTEN_PID");
+ #endif
        init_configuration(&config_tree);
  
        /* 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
@@@ -427,7 -597,7 +433,7 @@@ 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;
        }
        /* 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;